From 3606141e79d1b145b57691890c7461de39a6a488 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 22 Jul 2026 19:12:39 +0300 Subject: [PATCH] scripts: sof-crash-decode: support debug_stream.py exception format The debug_stream.py tool outputs exception records in a format that differs from the QEMU format the decoder originally targeted: - Register values can be (nil) instead of 0x0, e.g. 'A4 (nil)' - Backtrace entries are on separate lines after 'Backtrace:' rather than all on a single line - Additional registers LCOUNT and THREADPTR are present Update the parser to handle both formats: - Fix (nil) regex: the trailing word boundary anchor failed on the closing parenthesis; split hex and decimal into separate capture groups to avoid the issue - Parse backtrace entries across multiple lines after the Backtrace: header, stopping when a line contains non-backtrace content - Add LCOUNT and THREADPTR to the accepted register list The existing single-line backtrace and QEMU register formats continue to work unchanged. Signed-off-by: Jyri Sarha --- scripts/sof-crash-decode.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/scripts/sof-crash-decode.py b/scripts/sof-crash-decode.py index 2db0222f620d..d0ab97b8466f 100755 --- a/scripts/sof-crash-decode.py +++ b/scripts/sof-crash-decode.py @@ -157,32 +157,44 @@ def parse_crash_log(content): elif re.match(r'^AR\d{2}$', reg): reg = f"AR{int(reg[2:])}" - if re.match(r'^(PC|PR|SP|A\d+|AR\d+|EXCCAUSE|VADDR|LBEG|LEND|SAR|EPC\d+|EPS\d+|PS)$', reg): + if re.match(r'^(PC|PR|SP|A\d+|AR\d+|EXCCAUSE|VADDR|LBEG|LEND|LCOUNT|SAR|EPC\d+|EPS\d+|PS|THREADPTR)$', reg): registers[reg] = val else: # Standard format # regex for registers: we want standalone pairs like PC 0x123 or A0 0x123 or EXCCAUSE 9 - reg_pattern = re.compile(r'\b([A-Z0-9]+)\s+(0x[0-9a-fA-F]+|\d+|(?:nil))\b') + # Also handle (nil) for zero-valued registers like A4 (nil) or THREADPTR (nil) + reg_pattern = re.compile(r'\b([A-Z0-9]+)\s+(?:(0x[0-9a-fA-F]+)\b|(\d+)\b|\(nil\))') for match in reg_pattern.finditer(content): reg = match.group(1) - val_str = match.group(2) - if val_str == "(nil)": - val = 0 - elif val_str.startswith("0x"): - val = int(val_str, 16) + hex_str = match.group(2) + dec_str = match.group(3) + if hex_str: + val = int(hex_str, 16) + elif dec_str: + val = int(dec_str) else: - val = int(val_str) + val = 0 # Keep only known registers or likely candidates - if re.match(r'^(PC|PR|SP|A\d+|AR\d+|EXCCAUSE|VADDR|LBEG|LEND|SAR|EPC\d+|EPS\d+|PS)$', reg): + if re.match(r'^(PC|PR|SP|A\d+|AR\d+|EXCCAUSE|VADDR|LBEG|LEND|LCOUNT|SAR|EPC\d+|EPS\d+|PS|THREADPTR)$', reg): registers[reg] = val - # Backtrace parsing + # Backtrace parsing: support both single-line and multi-line formats + # Single-line: "Backtrace: 0xPC:0xSP 0xPC:0xSP ..." + # Multi-line: "Backtrace:\n0xPC:0xSP\n0xPC:0xSP\n..." bt_idx = content.find("Backtrace:") if bt_idx != -1: - bt_line = content[bt_idx:content.find('\n', bt_idx)] + bt_rest = content[bt_idx + len("Backtrace:"):] bt_pattern = re.compile(r'(0x[0-9a-fA-F]+):(0x[0-9a-fA-F]+)') - for match in bt_pattern.finditer(bt_line): + for match in bt_pattern.finditer(bt_rest): + # Stop if we hit a line that doesn't look like backtrace data + preceding = bt_rest[:match.start()] + # Allow only whitespace/newlines between entries + last_newline = preceding.rfind('\n') + if last_newline != -1: + line_before = preceding[last_newline + 1:match.start()].strip() + if line_before and not bt_pattern.match(line_before): + break pc = int(match.group(1), 16) sp = int(match.group(2), 16) backtraces.append((pc, sp))