hotpath 0.9.0

Simple Rust profiler with memory and async data-flow insights - quickly find and debug performance bottlenecks.
Documentation
#[cfg(target_os = "linux")]
use quanta::Instant;

#[cfg(not(target_os = "linux"))]
use std::time::Instant;

use super::super::truncate_result;

#[must_use = "guard is dropped immediately without measuring anything"]
pub struct MeasurementGuard {
    name: &'static str,
    wrapper: bool,
    unsupported_async: bool,
    tid: u64,
    start: Instant,
}

impl MeasurementGuard {
    #[inline]
    pub fn new(name: &'static str, wrapper: bool, unsupported_async: bool) -> Self {
        if !unsupported_async {
            super::core::ALLOCATIONS.with(|stack| {
                let current_depth = stack.depth.get();
                stack.depth.set(current_depth + 1);
                assert!((stack.depth.get() as usize) < super::core::MAX_DEPTH);
                let depth = stack.depth.get() as usize;
                stack.elements[depth].bytes_total.set(0);
                stack.elements[depth].count_total.set(0);
                stack.elements[depth].unsupported_async.set(false);
            });
        }

        Self {
            name,
            wrapper,
            unsupported_async,
            tid: crate::tid::current_tid(),
            start: Instant::now(),
        }
    }
}

impl Drop for MeasurementGuard {
    #[inline]
    fn drop(&mut self) {
        let duration = self.start.elapsed();
        let cross_thread = crate::tid::current_tid() != self.tid;

        let (bytes_total, count_total, unsupported_async) =
            if self.unsupported_async || cross_thread {
                (0, 0, self.unsupported_async)
            } else {
                super::core::ALLOCATIONS.with(|stack| {
                    let depth = stack.depth.get() as usize;
                    let bytes = stack.elements[depth].bytes_total.get();
                    let count = stack.elements[depth].count_total.get();
                    let unsup_async = stack.elements[depth].unsupported_async.get();

                    stack.depth.set(stack.depth.get() - 1);

                    if !super::shared::is_alloc_self_enabled() {
                        let parent = stack.depth.get() as usize;
                        stack.elements[parent]
                            .bytes_total
                            .set(stack.elements[parent].bytes_total.get() + bytes);
                        stack.elements[parent]
                            .count_total
                            .set(stack.elements[parent].count_total.get() + count);
                        stack.elements[parent]
                            .unsupported_async
                            .set(stack.elements[parent].unsupported_async.get() | unsup_async);
                    }

                    (bytes, count, unsup_async)
                })
            };

        super::core::ALLOCATIONS.with(|stack| {
            stack.tracking_enabled.set(false);
        });

        let tid = if cross_thread { None } else { Some(self.tid) };
        super::state::send_alloc_measurement(
            self.name,
            bytes_total,
            count_total,
            duration,
            unsupported_async,
            self.wrapper,
            cross_thread,
            tid,
        );

        super::core::ALLOCATIONS.with(|stack| {
            stack.tracking_enabled.set(true);
        });
    }
}

#[must_use = "guard is dropped immediately without measuring anything"]
pub struct MeasurementGuardWithLog {
    name: &'static str,
    wrapper: bool,
    unsupported_async: bool,
    tid: u64,
    start: Instant,
    finished: bool,
}

impl MeasurementGuardWithLog {
    #[inline]
    pub fn new(name: &'static str, wrapper: bool, unsupported_async: bool) -> Self {
        if !unsupported_async {
            super::core::ALLOCATIONS.with(|stack| {
                let current_depth = stack.depth.get();
                stack.depth.set(current_depth + 1);
                assert!((stack.depth.get() as usize) < super::core::MAX_DEPTH);
                let depth = stack.depth.get() as usize;
                stack.elements[depth].bytes_total.set(0);
                stack.elements[depth].count_total.set(0);
                stack.elements[depth].unsupported_async.set(false);
            });
        }

        Self {
            name,
            wrapper,
            unsupported_async,
            tid: crate::tid::current_tid(),
            start: Instant::now(),
            finished: false,
        }
    }

    #[inline]
    pub fn finish_with_result<T: std::fmt::Debug>(mut self, result: &T) {
        self.finished = true;
        let result_str = truncate_result(format!("{:?}", result));

        let duration = self.start.elapsed();
        let cross_thread = crate::tid::current_tid() != self.tid;

        let (bytes_total, count_total, unsupported_async) =
            if self.unsupported_async || cross_thread {
                (0, 0, self.unsupported_async)
            } else {
                super::core::ALLOCATIONS.with(|stack| {
                    let depth = stack.depth.get() as usize;
                    let bytes = stack.elements[depth].bytes_total.get();
                    let count = stack.elements[depth].count_total.get();
                    let unsup_async = stack.elements[depth].unsupported_async.get();

                    stack.depth.set(stack.depth.get() - 1);

                    if !super::shared::is_alloc_self_enabled() {
                        let parent = stack.depth.get() as usize;
                        stack.elements[parent]
                            .bytes_total
                            .set(stack.elements[parent].bytes_total.get() + bytes);
                        stack.elements[parent]
                            .count_total
                            .set(stack.elements[parent].count_total.get() + count);
                        stack.elements[parent]
                            .unsupported_async
                            .set(stack.elements[parent].unsupported_async.get() | unsup_async);
                    }

                    (bytes, count, unsup_async)
                })
            };

        super::core::ALLOCATIONS.with(|stack| {
            stack.tracking_enabled.set(false);
        });

        let tid = if cross_thread { None } else { Some(self.tid) };
        super::state::send_alloc_measurement_with_log(
            self.name,
            bytes_total,
            count_total,
            duration,
            unsupported_async,
            self.wrapper,
            cross_thread,
            tid,
            Some(result_str),
        );

        super::core::ALLOCATIONS.with(|stack| {
            stack.tracking_enabled.set(true);
        });
    }
}

impl Drop for MeasurementGuardWithLog {
    #[inline]
    fn drop(&mut self) {
        if !self.finished {
            let duration = self.start.elapsed();
            let cross_thread = crate::tid::current_tid() != self.tid;

            let (bytes_total, count_total, unsupported_async) =
                if self.unsupported_async || cross_thread {
                    (0, 0, self.unsupported_async)
                } else {
                    super::core::ALLOCATIONS.with(|stack| {
                        let depth = stack.depth.get() as usize;
                        let bytes = stack.elements[depth].bytes_total.get();
                        let count = stack.elements[depth].count_total.get();
                        let unsup_async = stack.elements[depth].unsupported_async.get();

                        stack.depth.set(stack.depth.get() - 1);

                        if !super::shared::is_alloc_self_enabled() {
                            let parent = stack.depth.get() as usize;
                            stack.elements[parent]
                                .bytes_total
                                .set(stack.elements[parent].bytes_total.get() + bytes);
                            stack.elements[parent]
                                .count_total
                                .set(stack.elements[parent].count_total.get() + count);
                            stack.elements[parent]
                                .unsupported_async
                                .set(stack.elements[parent].unsupported_async.get() | unsup_async);
                        }

                        (bytes, count, unsup_async)
                    })
                };

            super::core::ALLOCATIONS.with(|stack| {
                stack.tracking_enabled.set(false);
            });

            let tid = if cross_thread { None } else { Some(self.tid) };
            super::state::send_alloc_measurement_with_log(
                self.name,
                bytes_total,
                count_total,
                duration,
                unsupported_async,
                self.wrapper,
                cross_thread,
                tid,
                None,
            );

            super::core::ALLOCATIONS.with(|stack| {
                stack.tracking_enabled.set(true);
            });
        }
    }
}