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