rust_debugging_locks/
stacktrace_util.rs1use std::collections::hash_map::DefaultHasher;
2use std::fmt;
3use std::fmt::Display;
4use std::hash::{Hash, Hasher};
5use std::path::PathBuf;
6use std::thread::ThreadId;
7use base58::ToBase58;
8
9pub struct Stracktrace {
10 pub frames: Vec<Frame>,
11 pub hash: String,
13}
14
15pub struct Frame {
16 pub method: String,
17 pub filename: String,
18 pub line_no: u32,
19}
20
21pub struct ThreadInfo {
22 pub thread_id: ThreadId,
23 pub name: String,
24}
25
26#[derive(Debug)]
27pub enum BacktrackError {
28 NoStartFrame,
29 NoDebugSymbols,
30}
31
32impl Display for ThreadInfo {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 write!(f, "{}:{:?}", self.name, self.thread_id)
36 }
37}
38
39impl Display for BacktrackError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 BacktrackError::NoStartFrame =>
43 write!(f, "Start Frame not found!"),
44 BacktrackError::NoDebugSymbols => {
45 write!(f, "No debug symbols! Did you build in release mode?")
46 }
47 }
48 }
49}
50
51
52impl std::error::Error for BacktrackError {}
53
54pub fn backtrack_frame(fn_skip_frame: fn(&str) -> bool) -> Result<Stracktrace, BacktrackError> {
63
64 const FRAMES_LIMIT: usize = 99;
65
66 let mut started = false;
67 let mut stop = false;
68 let mut symbols = 0;
69 let mut hasher = DefaultHasher::new();
70
71 let mut frames: Vec<Frame> = vec![];
73
74 backtrace::trace(|frame| {
75 backtrace::resolve_frame(frame, |symbol| {
76 if stop {
82 return;
83 }
84
85 if symbol.filename().is_none() {
86 return;
87 }
88
89 symbols += 1;
90
91 if frames.len() > FRAMES_LIMIT {
92 stop = true;
93 return;
94 }
95
96 if symbol.filename().unwrap().starts_with(PathBuf::from("/rustc")) {
98 stop = true;
99 return;
100 }
101
102 let symbol_name = symbol.name().unwrap().to_string();
104 if !symbol_name.starts_with("backtrace::backtrace::")
107 && !fn_skip_frame(symbol_name.as_str()) {
108
109 started = true;
110 }
113
114 if !started {
115 return;
116 }
117
118 let frame = Frame {
119 method: symbol.name().unwrap().to_string(),
120 filename: symbol.filename().unwrap().file_name().unwrap().to_str().unwrap().to_string(),
121 line_no: symbol.lineno().unwrap()
122 };
123
124 hasher.write(frame.method.as_bytes());
126 hasher.write_i32(0x2A66ED); hasher.write(frame.filename.as_bytes());
128 hasher.write_i32(0x2A66ED); hasher.write_u32(frame.line_no);
130 hasher.write_i32(0xF122ED); frames.push(frame);
133
134 });
135
136 !stop
137 });
138
139 if started == false {
140 if symbols == 0 {
141 return Err(BacktrackError::NoDebugSymbols);
143 } else {
144 return Err(BacktrackError::NoStartFrame);
145 }
146 } else {
147 let hash32 = hasher.finish() as u32;
148 let hash = hash32.to_be_bytes().to_base58();
149 return Ok(Stracktrace { frames, hash });
150 }
151
152}
153
154fn debug_frames(frames: &Result<Vec<Frame>, BacktrackError>) {
155 for frame in frames.as_ref().unwrap() {
156 println!("\t>{}:{}:{}", frame.filename, frame.method, frame.line_no);
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn stacktrace_from_method() {
166 let stacktrace = caller_function().unwrap();
167 assert!(stacktrace.frames.get(0).unwrap().method.starts_with("rust_debugging_locks::stacktrace_util::tests::caller_function::h"),
169 "method name: {}", stacktrace.frames.get(0).unwrap().method);
170 }
171
172 fn caller_function() -> Result<Stracktrace, BacktrackError> {
173 backtrack_frame(|symbol_name| !symbol_name.contains("::caller_function"))
174 }
175}