From 2c97c89211cb50fe8a9827e187582337e4d5135d Mon Sep 17 00:00:00 2001 From: Tyson Smith Date: Thu, 26 Oct 2023 11:58:41 -0700 Subject: [PATCH] Use the first stack in a sanitizer log --- grizzly/common/stack_hasher.py | 54 ++++++++++++++--------------- grizzly/common/test_stack_hasher.py | 32 +++++++++++++---- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/grizzly/common/stack_hasher.py b/grizzly/common/stack_hasher.py index 14388f7f..29bf461c 100644 --- a/grizzly/common/stack_hasher.py +++ b/grizzly/common/stack_hasher.py @@ -25,7 +25,7 @@ # These entries pad out the stack and make bucketing more difficult IGNORED_FRAMES = ( "core::panicking::", - "mozglue_static::panic_hook::", + "mozglue_static::panic_hook", "rust_begin_unwind", "RustMozCrash", "std::panicking::", @@ -347,9 +347,11 @@ def from_text(cls, input_text, major_depth=MAJOR_DEPTH, parse_mode=None): input_txt is the data to parse the trace from. """ - frames = [] - prev_line = None - for line in reversed(input_text.split("\n")): + # collection of parsed stacks + stacks = [] + # current stack + current = [] + for line in input_text.split("\n"): line = line.rstrip() if not line: # skip empty lines @@ -369,30 +371,28 @@ def from_text(cls, input_text, major_depth=MAJOR_DEPTH, parse_mode=None): assert frame.mode == parse_mode if frame.stack_line is not None: - stack_line = int(frame.stack_line) - # check if we've found a different stack in the data - if prev_line is not None and prev_line <= stack_line: + # check for new stack + if int(frame.stack_line) == 0 and current: + stacks.append(current) + current = [] + # check for out of order frames + elif ( + current + and current[-1].stack_line + and int(frame.stack_line) <= int(current[-1].stack_line) + ): + LOG.debug("scrambled logs?") break - frames.insert(0, frame) - if stack_line < 1: - break - prev_line = stack_line - else: - frames.insert(0, frame) - - # sanity check - if frames and prev_line is not None: - # assuming the first frame is 0 - if int(frames[0].stack_line) != 0: - LOG.warning("First stack frame is %s not 0", frames[0].stack_line) - if int(frames[-1].stack_line) != len(frames) - 1: - LOG.warning( - "Missing frames? Last frame is %s, expected %d (frames-1)", - frames[-1].stack_line, - len(frames) - 1, - ) - - return cls(frames=frames, major_depth=major_depth) + current.append(frame) + stacks.append(current) + LOG.debug("stacks detected: %d", sum(1 for x in stacks if x)) + + # select stack to use + if parse_mode == Mode.SANITIZER: + current = stacks[0] + else: + current = stacks[-1] + return cls(frames=current, major_depth=major_depth) @property def height_limit(self): diff --git a/grizzly/common/test_stack_hasher.py b/grizzly/common/test_stack_hasher.py index 238a32b6..30db181c 100644 --- a/grizzly/common/test_stack_hasher.py +++ b/grizzly/common/test_stack_hasher.py @@ -125,13 +125,20 @@ def test_stack_07(): def test_stack_08(): - """test creating a Stack by calling from_text() with text containing 2 stacks""" + """test creating a Stack by calling from_text() with text multiple stacks""" input_txt = ( - "" - " #0 0x0bad0bad in bad::frame0(nsA*, nsB*) /aa/a.cpp:12:1\n" - " #1 0x0bad0bad in bad::frame1(mz::d::EE*, int) /aa/a.cpp:12:1\n" + "=================================================================\n" + "==5540==ERROR: AddressSanitizer: ...\n" " #0 0x1badf00d in good::frame0(nsA*, nsB*) /aa/a.cpp:12:1\n" - " #1 0xdeadbeef in good::frame1(mz::d::EE*, int) /aa/a.cpp:12:1\n" + " #1 0xdeadbeef in good::frame1(mz::d::EE*, int) /aa/a.cpp:12:1\n\n" + "0x12876 is located 1024 bytes after 4096-byte region [0x12876,0x12876)\n" + "freed by thread T5 here:\n" + " #0 0x0bad0bad in bad::frame0(nsA*, nsB*) /aa/a.cpp:12:1\n" + " #1 0x0bad0bad in bad::frame1(mz::d::EE*, int) /aa/a.cpp:12:1\n\n" + "0x12876 is located 1024 bytes after 4096-byte region [0x12876,0x12876)\n" + "previously allocated by thread T0 here:\n" + " #0 0x0bad0bad in bad::frame0(nsA*, nsB*) /aa/a.cpp:12:1\n" + " #1 0x0bad0bad in bad::frame1(mz::d::EE*, int) /aa/a.cpp:12:1\n\n" ) stack = Stack.from_text(input_txt) assert len(stack.frames) == 2 @@ -309,7 +316,7 @@ def test_stack_16(): " #2 0x0001233 in stack_1c() test/a.cpp:34\n" ) assert len(stack.frames) == 2 - assert stack.frames[0].function == "stack_1b" + assert stack.frames[0].function == "stack_1a" assert stack.frames[0].mode == Mode.SANITIZER @@ -474,6 +481,19 @@ def test_sanitizer_stackframe_09(): assert frame.mode == Mode.SANITIZER +def test_sanitizer_stackframe_10(): + """test creating a StackFrame from a line with build id""" + frame = StackFrame.from_line( + " #0 0x7f76d25b7fc0 (/usr/lib/x86_64-linux-gnu/dri/swrast_dri.so+0x704fc0) " + "(BuildId: d04a40e4062a8d444ff6f23d4fe768215b2e32c7)" + ) + assert frame.stack_line == "0" + assert frame.function is None + assert frame.location == "swrast_dri.so" + assert frame.offset == "0x704fc0" + assert frame.mode == Mode.SANITIZER + + def test_gdb_stackframe_01(): """test creating a StackFrame from a GDB line with symbols""" frame = StackFrame.from_line(