proc_daemon/
profiling.rs

1//! Optional profiling utilities (CPU and heap) behind the `profiling` feature.
2//!
3//! Safe-by-default helpers to start/stop CPU profiling to a file and to capture
4//! a heap profile snapshot on demand. No runtime overhead unless enabled.
5
6use std::fs::File;
7use std::io::Write;
8use std::path::Path;
9
10use crate::error::{Error, Result};
11use pprof::protos::Message;
12
13/// CPU profiler handle. Drop or call `stop()` to write the profile.
14pub struct CpuProfiler {
15    guard: pprof::ProfilerGuard<'static>,
16}
17
18impl CpuProfiler {
19    /// Start CPU profiling.
20    ///
21    /// # Errors
22    ///
23    /// Returns an error if the profiler cannot be started by the underlying `pprof` crate.
24    pub fn start() -> Result<Self> {
25        let guard = pprof::ProfilerGuard::new(100)
26            .map_err(|e| Error::runtime_with_source("failed to start CPU profiler", e))?;
27        Ok(Self { guard })
28    }
29
30    /// Stop and write a profile in protobuf format compatible with `go tool pprof`.
31    ///
32    /// # Errors
33    ///
34    /// Returns an error if building the report, encoding, creating the output file,
35    /// or writing the profile fails.
36    pub fn stop_to_file<P: AsRef<Path>>(self, path: P) -> Result<()> {
37        let report = self
38            .guard
39            .report()
40            .build()
41            .map_err(|e| Error::runtime_with_source("failed to build CPU profile report", e))?;
42        let profile = report.pprof().map_err(|e| {
43            Error::runtime_with_source("failed to encode CPU profile to protobuf", e)
44        })?;
45        let mut f = File::create(path.as_ref()).map_err(|e| {
46            Error::io_with_source(
47                format!("failed to create profile file: {}", path.as_ref().display()),
48                e,
49            )
50        })?;
51        // encode_to_vec available via prost::Message (re-exported as pprof::protos::Message)
52        let buf = profile.encode_to_vec();
53        f.write_all(&buf)
54            .map_err(|e| Error::io_with_source("failed to write CPU profile", e))?;
55        Ok(())
56    }
57}
58
59/// Heap profiling support (optional via `heap-profiling`).
60#[cfg(feature = "heap-profiling")]
61pub mod heap {
62    use crate::error::Result;
63    use std::path::Path;
64
65    /// Handle to an active heap profiler. Drop to finalize.
66    pub struct HeapProfiler {
67        _prof: dhat::Profiler,
68    }
69
70    impl HeapProfiler {
71        /// Start heap profiling. If `output` is provided, sets `DHAT_OUT` accordingly.
72        ///
73        /// # Errors
74        ///
75        /// This function does not currently return errors; it returns `Ok(Self)` on success.
76        pub fn start<P: AsRef<Path>>(output: Option<P>) -> Result<Self> {
77            if let Some(p) = output {
78                std::env::set_var("DHAT_OUT", p.as_ref());
79            }
80            let profiler = dhat::Profiler::new_heap();
81            Ok(Self { _prof: profiler })
82        }
83
84        /// Stop profiling by consuming the handle (drop writes the profile).
85        pub fn stop(self) {
86            // Drop occurs here
87        }
88    }
89}
90
91/// Fallback when heap-profiling feature is disabled.
92#[cfg(not(feature = "heap-profiling"))]
93pub mod heap {
94    use crate::error::{Error, ErrorCode, Result};
95    use std::path::Path;
96
97    /// Start heap profiling is unsupported without the feature.
98    pub fn start<P: AsRef<Path>>(_output: Option<P>) -> Result<()> {
99        Err(Error::platform_with_code(
100            ErrorCode::PlatformFeatureNotAvailable,
101            "heap profiling is not available in this build",
102            std::env::consts::OS,
103        ))
104    }
105}