Skip to main content

foundations_sentry/
backtrace.rs

1//! Address-only stacktrace capture for server-side symbolication.
2
3use sentry_core::protocol::{Event, Stacktrace};
4use sentry_core::{ClientOptions, Integration};
5
6/// Attaches an unresolved current-thread stacktrace to events without a stacktrace.
7///
8/// This respects [`ClientOptions::attach_stacktrace`] and preserves stacktraces already
9/// attached to the event, any exception, or any thread. It captures instruction addresses
10/// without loading debug information or resolving symbols.
11///
12/// Install this instead of [`sentry_backtrace::AttachStacktraceIntegration`], with
13/// [`ClientOptions::default_integrations`] disabled. Server-side symbolication additionally
14/// requires loaded-image metadata from `sentry_debug_images::DebugImagesIntegration` and
15/// access to matching debug files.
16///
17/// # Panics
18///
19/// Client initialization panics if [`sentry_backtrace::AttachStacktraceIntegration`] is
20/// also installed, since it could resolve symbols before this integration runs.
21#[derive(Debug, Default)]
22pub struct UnresolvedStacktraceIntegration;
23
24impl Integration for UnresolvedStacktraceIntegration {
25    fn name(&self) -> &'static str {
26        "attach-unresolved-stacktrace"
27    }
28
29    fn setup(&self, options: &mut ClientOptions) {
30        assert!(
31            !options.integrations.iter().any(|integration| {
32                integration
33                    .as_ref()
34                    .as_any()
35                    .is::<sentry_backtrace::AttachStacktraceIntegration>()
36            }),
37            "UnresolvedStacktraceIntegration must replace sentry_backtrace::AttachStacktraceIntegration"
38        );
39    }
40
41    fn process_event(
42        &self,
43        mut event: Event<'static>,
44        options: &ClientOptions,
45    ) -> Option<Event<'static>> {
46        if !options.attach_stacktrace
47            || event.stacktrace.is_some()
48            || event
49                .exception
50                .iter()
51                .any(|exception| exception.stacktrace.is_some())
52            || event
53                .threads
54                .iter()
55                .any(|thread| thread.stacktrace.is_some())
56        {
57            return Some(event);
58        }
59        if let Some(stacktrace) = unresolved_stacktrace() {
60            let mut thread = sentry_backtrace::current_thread(false);
61            thread.stacktrace = Some(stacktrace);
62            event.threads.values.push(thread);
63        }
64        Some(event)
65    }
66}
67
68pub(crate) fn unresolved_stacktrace() -> Option<Stacktrace> {
69    sentry_backtrace::backtrace_to_stacktrace(&backtrace::Backtrace::new_unresolved())
70}