diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c82ce04a..b1ab9ba6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Temp files are now always created in the configured cache directory. ([#2028](https://github.com/getsentry/symbolicator/pull/2028)) - Filter bogus entry point frames after `__clone` and `__clone3` frames. ([#2040](https://github.com/getsentry/symbolicator/pull/2040)) +- Filter nullish outermost frames from stack traces. ([#2041](https://github.com/getsentry/symbolicator/pull/2041)) ## 26.8.0 diff --git a/crates/symbolicator-native/src/symbolication/symbolicate.rs b/crates/symbolicator-native/src/symbolication/symbolicate.rs index dc63b1742..166c1951f 100644 --- a/crates/symbolicator-native/src/symbolication/symbolicate.rs +++ b/crates/symbolicator-native/src/symbolication/symbolicate.rs @@ -14,8 +14,8 @@ use crate::caches::il2cpp::Il2cppService; use crate::caches::ppdb_caches::PortablePdbCacheActor; use crate::caches::symcaches::SymCacheActor; use crate::interface::{ - AdjustInstructionAddr, CompleteStacktrace, CompletedSymbolicationResponse, FrameStatus, - FrameTrust, RawFrame, RawStacktrace, Registers, Signal, SymbolicateStacktraces, + AddrMode, AdjustInstructionAddr, CompleteStacktrace, CompletedSymbolicationResponse, + FrameStatus, FrameTrust, RawFrame, RawStacktrace, Registers, Signal, SymbolicateStacktraces, SymbolicatedFrame, }; use crate::memory::MemoryAccess; @@ -239,6 +239,9 @@ fn symbolicate_stacktrace( continue; } + // Whether this frame is the last (outermost) frame. + let is_outermost_frame = unsymbolicated_frames_iter.peek().is_none(); + // Glibc inserts an explicit `DW_CFA_undefined: RIP` DWARF rule to say that frames like // `_start` have no return address. // See https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/start.S;h=1b3e36826b8a477474cee24d1c931429fbdf6d8f;hb=HEAD#l59 @@ -250,7 +253,7 @@ fn symbolicate_stacktrace( // * this is the last frame to symbolicate (via peek) // * the previous symbolicated frame is a well known frame like `_start` if status == FrameStatus::UnknownImage - && unsymbolicated_frames_iter.peek().is_none() + && is_outermost_frame && symbolicated_frames .last() .is_some_and(is_likely_glibc_undefined_rip_frame) @@ -258,6 +261,22 @@ fn symbolicate_stacktrace( continue; } + // Stack traces unwound from a minidump already consider an address from the first page + // practically null and use it as a marker to indicate that stack unwinding is complete. + // + // Stack traces which are unwound on the client side, may still have a frame at the + // end of the stack trace which is null-ish -> remove it here. + let is_nullish = + frame.addr_mode == AddrMode::Abs && frame.instruction_addr.0 < 4096; + if status == FrameStatus::UnknownImage + && is_outermost_frame + && is_nullish + // Check for any frames to avoid completely empty stack traces + && !symbolicated_frames.is_empty() + { + continue; + } + metrics.unsymbolicated_frames += 1; match frame.trust { FrameTrust::Scan => {