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::ptr;
6
7use background_hang_monitor_api::{HangProfile, HangProfileSymbol};
8
9const MAX_NATIVE_FRAMES: usize = 1024;
10
11pub trait Sampler: Send {
12    fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()>;
13}
14
15pub struct DummySampler;
16
17impl DummySampler {
18    #[allow(dead_code)]
19    pub fn new_boxed() -> Box<dyn Sampler> {
20        Box::new(DummySampler)
21    }
22}
23
24impl Sampler for DummySampler {
25    fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> {
26        Err(())
27    }
28}
29
30// Several types in this file are currently not used in a Linux or Windows build.
31pub type Address = *const u8;
32
33/// The registers used for stack unwinding
34#[allow(dead_code)]
35pub struct Registers {
36    /// Instruction pointer.
37    pub instruction_ptr: Address,
38    /// Stack pointer.
39    pub stack_ptr: Address,
40    /// Frame pointer.
41    pub frame_ptr: Address,
42}
43
44pub struct NativeStack {
45    instruction_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
46    stack_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
47    count: usize,
48}
49
50impl Default for NativeStack {
51    fn default() -> Self {
52        NativeStack {
53            instruction_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
54            stack_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
55            count: 0,
56        }
57    }
58}
59
60impl NativeStack {
61    pub fn process_register(
62        &mut self,
63        instruction_ptr: *mut std::ffi::c_void,
64        stack_ptr: *mut std::ffi::c_void,
65    ) -> Result<(), ()> {
66        if self.count >= MAX_NATIVE_FRAMES {
67            return Err(());
68        }
69        self.instruction_ptrs[self.count] = instruction_ptr;
70        self.stack_ptrs[self.count] = stack_ptr;
71        self.count += 1;
72        Ok(())
73    }
74
75    pub fn to_hangprofile(&self) -> HangProfile {
76        let mut profile = HangProfile {
77            backtrace: Vec::new(),
78        };
79        for ip in self.instruction_ptrs.iter().rev() {
80            if ip.is_null() {
81                continue;
82            }
83            backtrace::resolve(*ip, |symbol| {
84                let name = symbol
85                    .name()
86                    .map(|n| String::from_utf8_lossy(n.as_bytes()).to_string());
87                // demangle if possible -
88                // the `rustc_demangle` crate transparently supports both
89                // "legacy" (C++ style) and "v0" mangling formats.
90                #[cfg(feature = "sampler")]
91                let name = name.map(|n| rustc_demangle::demangle(&n).to_string());
92
93                let filename = symbol.filename().map(|n| n.to_string_lossy().to_string());
94                let lineno = symbol.lineno();
95                profile.backtrace.push(HangProfileSymbol {
96                    name,
97                    filename,
98                    lineno,
99                });
100            });
101        }
102        profile
103    }
104}