1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::ptr;

use background_hang_monitor_api::{HangProfile, HangProfileSymbol};

const MAX_NATIVE_FRAMES: usize = 1024;

pub trait Sampler: Send {
    fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()>;
}

#[allow(dead_code)]
pub struct DummySampler;

impl DummySampler {
    #[allow(dead_code)]
    pub fn new_boxed() -> Box<dyn Sampler> {
        Box::new(DummySampler)
    }
}

impl Sampler for DummySampler {
    fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> {
        Err(())
    }
}

// Several types in this file are currently not used in a Linux or Windows build.
#[allow(dead_code)]
pub type Address = *const u8;

/// The registers used for stack unwinding
#[allow(dead_code)]
pub struct Registers {
    /// Instruction pointer.
    pub instruction_ptr: Address,
    /// Stack pointer.
    pub stack_ptr: Address,
    /// Frame pointer.
    pub frame_ptr: Address,
}

#[allow(dead_code)]
pub struct NativeStack {
    instruction_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
    #[allow(dead_code)]
    stack_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
    #[allow(dead_code)]
    count: usize,
}

impl NativeStack {
    #[allow(dead_code)]
    pub fn new() -> Self {
        NativeStack {
            instruction_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
            stack_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
            count: 0,
        }
    }

    #[allow(dead_code)]
    pub fn process_register(
        &mut self,
        instruction_ptr: *mut std::ffi::c_void,
        stack_ptr: *mut std::ffi::c_void,
    ) -> Result<(), ()> {
        if self.count >= MAX_NATIVE_FRAMES {
            return Err(());
        }
        self.instruction_ptrs[self.count] = instruction_ptr;
        self.stack_ptrs[self.count] = stack_ptr;
        self.count += 1;
        Ok(())
    }

    pub fn to_hangprofile(&self) -> HangProfile {
        let mut profile = HangProfile {
            backtrace: Vec::new(),
        };
        for ip in self.instruction_ptrs.iter().rev() {
            if ip.is_null() {
                continue;
            }
            backtrace::resolve(*ip, |symbol| {
                // TODO: use the demangled or C++ demangled symbols if available.
                let name = symbol
                    .name()
                    .map(|n| String::from_utf8_lossy(n.as_bytes()).to_string());
                let filename = symbol.filename().map(|n| n.to_string_lossy().to_string());
                let lineno = symbol.lineno();
                profile.backtrace.push(HangProfileSymbol {
                    name,
                    filename,
                    lineno,
                });
            });
        }
        profile
    }
}