Skip to content

Commit 6d06bce

Browse files
authored
fix: Add new patterns for backtrace cleaning (#86)
* fix: Add new patterns for backtrace cleaning * feat: Use fuzzy matching for well known system frame detection
1 parent 8adb62a commit 6d06bce

File tree

1 file changed

+75
-22
lines changed

1 file changed

+75
-22
lines changed

src/backtrace_support.rs

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ lazy_static!{
2020
"sentry::",
2121
"sentry_types::",
2222
// these are not modules but things like __rust_maybe_catch_panic
23-
// or _<T as core..convert..Into<U>>::into
2423
"__rust_",
25-
"_<",
2624
];
2725
#[cfg(feature = "with_failure")] {
2826
rv.push("failure::");
@@ -39,7 +37,7 @@ lazy_static!{
3937
rv.push("failure::backtrace::Backtrace::new");
4038
}
4139
#[cfg(feature = "with_log")] {
42-
rv.push("_<sentry..integrations..log..Logger as log..Log>::log");
40+
rv.push("<sentry::integrations::log::Logger as log::Log>::log");
4341
}
4442
#[cfg(feature = "with_error_chain")] {
4543
rv.push("error_chain::make_backtrace");
@@ -50,7 +48,7 @@ lazy_static!{
5048
#![allow(unused_mut)]
5149
let mut rv = Vec::new();
5250
#[cfg(feature = "with_error_chain")] {
53-
rv.push(("error_chain::make_backtrace", "_<T as core..convert..Into<U>>::into"));
51+
rv.push(("error_chain::make_backtrace", "<T as core::convert::Into<U>>::into"));
5452
}
5553
rv
5654
};
@@ -154,22 +152,26 @@ pub fn trim_stacktrace<F>(stacktrace: &mut Stacktrace, f: F)
154152
where
155153
F: Fn(&Frame, &Stacktrace) -> bool,
156154
{
157-
if let Some(cutoff) = stacktrace.frames.iter().rev().position(|frame| {
158-
if let Some(ref func) = frame.function {
159-
WELL_KNOWN_BORDER_FRAMES.contains(&func.as_str()) || f(frame, stacktrace)
160-
} else {
161-
false
162-
}
163-
}) {
155+
let known_cutoff = stacktrace
156+
.frames
157+
.iter()
158+
.rev()
159+
.position(|frame| match frame.function {
160+
Some(ref func) => is_well_known(&func) || f(frame, stacktrace),
161+
None => false,
162+
});
163+
164+
if let Some(cutoff) = known_cutoff {
164165
let secondary = {
165166
let func = stacktrace.frames[stacktrace.frames.len() - cutoff - 1]
166167
.function
167168
.as_ref()
168169
.unwrap();
170+
169171
SECONDARY_BORDER_FRAMES
170172
.iter()
171173
.filter_map(|&(primary, secondary)| {
172-
if primary == func {
174+
if function_starts_with(func, primary) {
173175
Some(secondary)
174176
} else {
175177
None
@@ -180,13 +182,14 @@ where
180182
stacktrace.frames.truncate(trunc);
181183

182184
if let Some(secondary) = secondary {
183-
if let Some(cutoff) = stacktrace.frames.iter().rev().position(|frame| {
184-
if let Some(ref func) = frame.function {
185-
func.as_str() == secondary
186-
} else {
187-
false
188-
}
189-
}) {
185+
let secondary_cutoff = stacktrace.frames.iter().rev().position(|frame| match frame
186+
.function
187+
{
188+
Some(ref func) => function_starts_with(&func, secondary),
189+
None => false,
190+
});
191+
192+
if let Some(cutoff) = secondary_cutoff {
190193
let trunc = stacktrace.frames.len() - cutoff - 1;
191194
stacktrace.frames.truncate(trunc);
192195
}
@@ -202,13 +205,32 @@ pub fn is_sys_function(func: &str) -> bool {
202205
.any(|m| function_starts_with(func, m))
203206
}
204207

208+
/// Checks if a function is a well-known system function
209+
fn is_well_known(func: &str) -> bool {
210+
WELL_KNOWN_BORDER_FRAMES
211+
.iter()
212+
.any(|m| function_starts_with(&func, m))
213+
}
214+
205215
/// Checks whether the function name starts with the given pattern.
206216
///
207217
/// In trait implementations, the original type name is wrapped in "_< ... >" and colons are
208218
/// replaced with dots. This function accounts for differences while checking.
209-
pub fn function_starts_with(mut func_name: &str, pattern: &str) -> bool {
210-
if func_name.starts_with("_<") {
211-
func_name = &func_name[2..];
219+
pub fn function_starts_with(mut func_name: &str, mut pattern: &str) -> bool {
220+
if pattern.starts_with('<') {
221+
while pattern.starts_with('<') {
222+
pattern = &pattern[1..];
223+
224+
if func_name.starts_with('<') {
225+
func_name = &func_name[1..];
226+
} else if func_name.starts_with("_<") {
227+
func_name = &func_name[2..];
228+
} else {
229+
return false;
230+
}
231+
}
232+
} else {
233+
func_name = func_name.trim_left_matches('<').trim_left_matches("_<");
212234
}
213235

214236
if !func_name.is_char_boundary(pattern.len()) {
@@ -250,4 +272,35 @@ mod tests {
250272
"tokio::"
251273
));
252274
}
275+
276+
#[test]
277+
fn test_function_starts_with_newimpl() {
278+
assert!(function_starts_with(
279+
"<futures::task_impl::Spawn<T>>::enter::{{closure}}",
280+
"futures::"
281+
));
282+
283+
assert!(!function_starts_with(
284+
"<futures::task_impl::Spawn<T>>::enter::{{closure}}",
285+
"tokio::"
286+
));
287+
}
288+
289+
#[test]
290+
fn test_function_starts_with_impl_pattern() {
291+
assert!(function_starts_with(
292+
"_<futures..task_impl..Spawn<T>>::enter::_{{closure}}",
293+
"<futures::"
294+
));
295+
296+
assert!(function_starts_with(
297+
"<futures::task_impl::Spawn<T>>::enter::{{closure}}",
298+
"<futures::"
299+
));
300+
301+
assert!(!function_starts_with(
302+
"futures::task_impl::std::set",
303+
"<futures::"
304+
));
305+
}
253306
}

0 commit comments

Comments
 (0)