forked from tikv/pprof-rs
-
Notifications
You must be signed in to change notification settings - Fork 2
/
frame_pointer.rs
116 lines (93 loc) · 3.39 KB
/
frame_pointer.rs
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
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2022 TiKV Project Authors. Licensed under Apache-2.0.
use std::ptr::null_mut;
use libc::c_void;
use crate::addr_validate::validate;
#[derive(Clone, Debug)]
pub struct Frame {
pub ip: usize,
}
extern "C" {
fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
}
impl super::Frame for Frame {
type S = backtrace::Symbol;
fn ip(&self) -> usize {
self.ip
}
fn resolve_symbol<F: FnMut(&Self::S)>(&self, cb: F) {
backtrace::resolve(self.ip as *mut c_void, cb);
}
fn symbol_address(&self) -> *mut libc::c_void {
if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
self.ip as *mut c_void
} else {
unsafe { _Unwind_FindEnclosingFunction(self.ip as *mut c_void) }
}
}
}
pub struct Trace {}
impl super::Trace for Trace {
type Frame = Frame;
fn trace<F: FnMut(&Self::Frame) -> bool>(ucontext: *mut libc::c_void, mut cb: F) {
let ucontext: *mut libc::ucontext_t = ucontext as *mut libc::ucontext_t;
if ucontext.is_null() {
return;
}
#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
let frame_pointer =
unsafe { (*ucontext).uc_mcontext.gregs[libc::REG_RBP as usize] as usize };
#[cfg(all(target_arch = "x86_64", target_os = "macos"))]
let frame_pointer = unsafe {
let mcontext = (*ucontext).uc_mcontext;
if mcontext.is_null() {
0
} else {
(*mcontext).__ss.__rbp as usize
}
};
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
let frame_pointer = unsafe { (*ucontext).uc_mcontext.regs[29] as usize };
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
let frame_pointer = unsafe {
let mcontext = (*ucontext).uc_mcontext;
if mcontext.is_null() {
0
} else {
(*mcontext).__ss.__fp as usize
}
};
let mut frame_pointer = frame_pointer as *mut FramePointerLayout;
let mut last_frame_pointer: *mut FramePointerLayout = null_mut();
loop {
// The stack grow from high address to low address.
// but we don't have a reasonable assumption for the hightest address
// the `__libc_stack_end` is not thread-local, and only represent the
// stack end of the main thread. For other thread, their stacks are allocated
// by the `pthread`.
//
// TODO: If we can hook the thread creation, we will have chance to get the
// stack end through `pthread_get_attr`.
// the frame pointer should never be smaller than the former one.
if !last_frame_pointer.is_null() && frame_pointer < last_frame_pointer {
break;
}
if !validate(frame_pointer as *const libc::c_void) {
break;
}
last_frame_pointer = frame_pointer;
// iterate to the next frame
let frame = Frame {
ip: unsafe { (*frame_pointer).ret },
};
if !cb(&frame) {
break;
}
frame_pointer = unsafe { (*frame_pointer).frame_pointer };
}
}
}
#[repr(C)]
struct FramePointerLayout {
frame_pointer: *mut FramePointerLayout,
ret: usize,
}