background_hang_monitor/
sampler.rs1use 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
16pub(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 #[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}