sentry_backtrace/
process.rs

1use std::borrow::Cow;
2
3use backtrace::Backtrace;
4use sentry_core::ClientOptions;
5
6use crate::trim::{is_well_known_not_in_app, trim_stacktrace};
7use crate::utils::{
8    demangle_symbol, filename, function_starts_with, parse_crate_name, strip_symbol,
9};
10use crate::{Frame, Stacktrace};
11
12/// Processes a `Stacktrace`.
13///
14/// Trims a `Stacktrace` and marks frames as in-app based on the provided
15/// `ClientOptions`.
16pub fn process_event_stacktrace(stacktrace: &mut Stacktrace, options: &ClientOptions) {
17    // automatically trim backtraces
18    if options.trim_backtraces {
19        trim_stacktrace(stacktrace, |frame, _| {
20            if let Some(ref func) = frame.function {
21                options.extra_border_frames.contains(&func.as_str())
22            } else {
23                false
24            }
25        })
26    }
27
28    // automatically prime in_app and set package
29    let mut any_in_app = false;
30    for frame in &mut stacktrace.frames {
31        let func_name = match frame.function {
32            Some(ref func) => func,
33            None => continue,
34        };
35
36        // set package if missing to crate prefix
37        if frame.package.is_none() {
38            frame.package = parse_crate_name(func_name);
39        }
40
41        match frame.in_app {
42            Some(true) => {
43                any_in_app = true;
44                continue;
45            }
46            Some(false) => {
47                continue;
48            }
49            None => {}
50        }
51
52        for m in &options.in_app_include {
53            if function_starts_with(func_name, m) {
54                frame.in_app = Some(true);
55                any_in_app = true;
56                break;
57            }
58        }
59
60        if frame.in_app.is_some() {
61            continue;
62        }
63
64        for m in &options.in_app_exclude {
65            if function_starts_with(func_name, m) {
66                frame.in_app = Some(false);
67                break;
68            }
69        }
70
71        if frame.in_app.is_some() {
72            continue;
73        }
74
75        if is_well_known_not_in_app(func_name) {
76            frame.in_app = Some(false);
77        }
78    }
79
80    if !any_in_app {
81        for frame in &mut stacktrace.frames {
82            if frame.in_app.is_none() {
83                frame.in_app = Some(true);
84            }
85        }
86    }
87}
88
89/// Convert a `backtrace::Backtrace` into a Rust `Stacktrace`
90pub fn backtrace_to_stacktrace(bt: &Backtrace) -> Option<Stacktrace> {
91    let frames = bt
92        .frames()
93        .iter()
94        .flat_map(|frame| {
95            // For each frame, there may be multiple symbols if a function was inlined, so
96            // add an entry for each symbol.
97            let symbols = frame.symbols();
98            symbols
99                .iter()
100                .map(move |sym| {
101                    let abs_path = sym.filename().map(|m| m.to_string_lossy().to_string());
102                    let filename = abs_path.as_ref().map(|p| filename(p).to_string());
103                    let real_symbol = sym
104                        .name()
105                        .map_or(Cow::Borrowed("<unknown>"), |n| Cow::Owned(n.to_string()));
106                    let symbol = strip_symbol(&real_symbol);
107                    let function = demangle_symbol(&symbol);
108                    Frame {
109                        symbol: if symbol != function {
110                            Some(symbol.into())
111                        } else {
112                            None
113                        },
114                        function: Some(function),
115                        instruction_addr: Some(frame.ip().into()),
116                        abs_path,
117                        filename,
118                        lineno: sym.lineno().map(u64::from),
119                        colno: None,
120                        ..Default::default()
121                    }
122
123                    // If there were no symbols at all, make sure to add at least one frame, as we
124                    // may be able to symbolicate it on the server.
125                })
126                .chain(if symbols.is_empty() {
127                    Some(Frame {
128                        instruction_addr: Some(frame.ip().into()),
129                        function: Some("<unknown>".into()),
130                        ..Default::default()
131                    })
132                } else {
133                    None
134                })
135        })
136        .collect();
137    Stacktrace::from_frames_reversed(frames)
138}