background_hang_monitor/
sampler.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::marker::PhantomData;
6use std::ptr;
7
8use background_hang_monitor_api::{HangProfile, HangProfileSymbol};
9
10const MAX_NATIVE_FRAMES: usize = 1024;
11
12pub trait Sampler: Send {
13    fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()>;
14}
15
16// Implementing this type on `PhantomData` allows avoiding dead code warnings.
17pub(crate) type DummySampler = PhantomData<()>;
18
19impl Sampler for DummySampler {
20    fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> {
21        Err(())
22    }
23}
24
25pub struct NativeStack {
26    instruction_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
27    stack_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
28    count: usize,
29}
30
31impl Default for NativeStack {
32    fn default() -> Self {
33        NativeStack {
34            instruction_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
35            stack_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
36            count: 0,
37        }
38    }
39}
40
41impl NativeStack {
42    #[cfg_attr(target_os = "windows", expect(dead_code))]
43    pub fn process_register(
44        &mut self,
45        instruction_ptr: *mut std::ffi::c_void,
46        stack_ptr: *mut std::ffi::c_void,
47    ) -> Result<(), ()> {
48        if self.count >= MAX_NATIVE_FRAMES {
49            return Err(());
50        }
51        self.instruction_ptrs[self.count] = instruction_ptr;
52        self.stack_ptrs[self.count] = stack_ptr;
53        self.count += 1;
54        Ok(())
55    }
56
57    pub fn to_hangprofile(&self) -> HangProfile {
58        let mut profile = HangProfile {
59            backtrace: Vec::new(),
60        };
61        for ip in self.instruction_ptrs.iter().rev() {
62            if ip.is_null() {
63                continue;
64            }
65            backtrace::resolve(*ip, |symbol| {
66                let name = symbol
67                    .name()
68                    .map(|n| String::from_utf8_lossy(n.as_bytes()).to_string());
69                // demangle if possible -
70                // the `rustc_demangle` crate transparently supports both
71                // "legacy" (C++ style) and "v0" mangling formats.
72                #[cfg(feature = "sampler")]
73                let name = name.map(|n| rustc_demangle::demangle(&n).to_string());
74
75                let filename = symbol.filename().map(|n| n.to_string_lossy().to_string());
76                let lineno = symbol.lineno();
77                profile.backtrace.push(HangProfileSymbol {
78                    name,
79                    filename,
80                    lineno,
81                });
82            });
83        }
84        profile
85    }
86}