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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::fmt::Write;
use std::path::Path;
const DEPENDENCY_FILE_PREFIXES: &[&str] = &["/rustc/", "src/libstd/", "src/libpanic_unwind/", "src/libtest/"];
const DEPENDENCY_FILE_CONTAINS: &[&str] = &["/.cargo/registry/src/"];
const DEPENDENCY_SYM_PREFIXES: &[&str] = &[
"std::",
"core::",
"witcher::error::",
"<witcher::error::",
"witcher::backtrace::",
"backtrace::backtrace::",
"_rust_begin_unwind",
"color_traceback::",
"__rust_",
"___rust_",
"__pthread",
"_main",
"main",
"__scrt_common_main_seh",
"BaseThreadInitThunk",
"_start",
"__libc_start_main",
"start_thread",
"__GI__",
];
const DEPENDENCY_SYM_CONTAINS: &[&str] = &["as witcher::wrapper::Wrapper"];
pub(crate) fn new() -> Vec<Frame> {
let bt = backtrace::Backtrace::new();
bt.frames()
.iter()
.flat_map(|x| x.symbols())
.map(|sym| Frame {
symbol: match sym.name() {
Some(name) => format!("{:#}", name),
None => String::from("<unknown>"),
},
filename: simple_path(sym.filename()),
lineno: sym.lineno(),
column: sym.colno(),
})
.collect()
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Frame {
pub symbol: String,
pub filename: String,
pub lineno: Option<u32>,
pub column: Option<u32>,
}
impl Frame {
pub fn is_dependency(&self) -> bool {
if DEPENDENCY_SYM_PREFIXES.iter().any(|x| self.symbol.starts_with(x))
|| DEPENDENCY_SYM_CONTAINS.iter().any(|x| self.symbol.contains(x))
|| DEPENDENCY_FILE_PREFIXES.iter().any(|x| self.filename.starts_with(x))
|| DEPENDENCY_FILE_CONTAINS.iter().any(|x| self.filename.contains(x))
{
return true;
}
false
}
}
fn simple_path(filename: Option<&Path>) -> String {
let mut f = String::new();
if let Some(file) = filename {
let cwd = std::env::current_dir();
if let Ok(cwd) = &cwd {
if let Ok(suffix) = file.strip_prefix(cwd) {
write!(f, "{}", suffix.display()).omit();
return f;
}
}
write!(f, "{}", file.display()).omit();
} else {
write!(f, "<unknown>").omit();
}
f
}
trait Omit {
fn omit(&self);
}
impl Omit for std::fmt::Result {
fn omit(&self) {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frame_equality() {
let mut frame1 = Frame { symbol: String::from("symbol"), filename: String::from("filename"), lineno: Some(1), column: Some(2) };
let frame2 = Frame { symbol: String::from("symbol"), filename: String::from("filename"), lineno: Some(1), column: Some(2) };
assert_eq!(true, frame1 == frame2);
assert_eq!(frame1, frame2);
frame1.lineno = Some(3);
assert_eq!(true, frame1 != frame2);
assert_ne!(frame1, frame2);
}
#[test]
fn test_simple_path() {
let cwd = std::env::current_dir().unwrap();
assert_eq!("foo", simple_path(Some(Path::new(&cwd).join("foo").as_ref())));
assert_eq!("foobar", simple_path(Some(Path::new(&cwd).join("foobar").as_ref())));
assert_eq!("/rustc/123/src/libstd/foobar", simple_path(Some(Path::new("/rustc/123/src/libstd").join("foobar").as_ref())));
assert_eq!("<unknown>", simple_path(None));
}
#[test]
fn test_omit() {
let mut w = String::new();
write!(&mut w, "foobar").omit();
}
}