Skip to content

Commit 0673c20

Browse files
authored
enhanced stack trace decoder for ESP8266 plus minor fixes on the ESP32 side (mostly for consistency). This also updates gdb_hooks to include the register dump into the portion the user is to copy in to the stack trace decoder (#3013)
plus minor fixes on the ESP32 side (mostly for consistency). This also updates gdb_hooks to include the register dump into the portion the user is to copy in to the stack trace decoder - decoder now includes the register dump, showing the different memory areas color coded to allow quick distinction of code/data addresses - if possible shows a symbol / code line decode of addresses in the register file - shows c/c++ context from the source file - shows assembly context (only after the offending instruction) - provides the same color coded addresses in the hex stack trace - shows a list of functions on the stack - shows a list of data addresses / labels on the stack The output, when used interactively, uses a pager and the input now requires to mark the end of the input by ^D (what was necessary to not get mixed lines in the output) I did change the gdb_hooks.cpp file to - define the FSTRING values in a central place but use them in both the `gdbstub_exception_handler_flash()` and `dumpExceptionInfo()` functions. This might not be the most elegant way, but with the `=======` lines only wrapping around the actual stack dump, the guidance is confusing for the user and they would probably not copy the register file
1 parent 8a452d3 commit 0673c20

File tree

3 files changed

+590
-47
lines changed

3 files changed

+590
-47
lines changed

Sming/Arch/Esp32/Tools/decode-stacktrace.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ class Colors:
3131
GRAY = "\033[90m"
3232
LIGHT_BLUE = "\033[94m" # Labels
3333

34+
CANARY_VALUES = {0xDEADBEEF, 0xA5A5A5A5, 0xFEFEFEFE, 0xABABABAB, 0x55AA55AA, 0xAA55AA55}
35+
3436
def getAddrColor(val):
3537
"""Return ANSI color code based on memory region."""
3638
# Stack Canary / Poison values
37-
if val in [0xDEADBEEF, 0xA5A5A5A5, 0xFEFEFEFE, 0xABABABAB]:
39+
if val in CANARY_VALUES:
3840
return Colors.RED
3941

4042
# Based on typical ESP32 memory map
@@ -50,6 +52,16 @@ def getAddrColor(val):
5052
else:
5153
return Colors.RESET
5254

55+
def colorizeHex(line):
56+
"""Colorize 0x-prefixed hex values in a line using getAddrColor."""
57+
def replacer(m):
58+
s = m.group(0)
59+
try:
60+
return f"{getAddrColor(int(s, 16))}{s}{Colors.RESET}"
61+
except Exception:
62+
return s
63+
return re.sub(r'0x[0-9a-fA-F]+', replacer, line)
64+
5365
def getElfSha256(filepath):
5466
"""Calculate SHA256 of the ELF file to match against dump info."""
5567
try:
@@ -273,7 +285,7 @@ def disassemble(self, addr, count=6):
273285
try:
274286
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)
275287
if res.returncode == 0:
276-
print(f"\n {Colors.BOLD}Disassembly around {Colors.GREEN}0x{start:x}{Colors.RESET}:")
288+
print(f"\n {Colors.BOLD}Disassembly around {getAddrColor(start)}0x{start:x}{Colors.RESET}:")
277289
lines = res.stdout.splitlines()
278290
for line in lines:
279291
# Typical objdump line: "400d3f28: 00413603 l32u a2,4(sp)"
@@ -405,7 +417,7 @@ def flushRegisters(self):
405417
if name in ["MEPC", "PC", "EPC"] and res and " at " in res:
406418
# Extract File:Line
407419
# Format: func at file:line
408-
m = re.search(r' at (.*):(\d+)', res)
420+
m = re.search(r' at (.*?):(\d+)', res)
409421
if m:
410422
sourceContextCandidate = (m.group(1), int(m.group(2)))
411423

@@ -437,7 +449,17 @@ def flushRegisters(self):
437449
self.displaySourceContext(sourceContextCandidate[0], sourceContextCandidate[1])
438450

439451
if targetDisasmAddr:
440-
self.disassemble(targetDisasmAddr)
452+
if 0x40000000 <= targetDisasmAddr < 0x50000000:
453+
self.disassemble(targetDisasmAddr)
454+
else:
455+
if 0x30000000 <= targetDisasmAddr < 0x40000000:
456+
region = "Data (DRAM/DROM)"
457+
elif 0x50000000 <= targetDisasmAddr < 0x60000000:
458+
region = "External SPI RAM"
459+
else:
460+
region = "unknown region"
461+
print(f"\n {Colors.RED}PC/EPC (0x{targetDisasmAddr:08x}) is in {region} — execution jumped to non-code memory.{Colors.RESET}")
462+
print(f" {Colors.RED}No code can be disassembled here. Likely a wild pointer or stack corruption.{Colors.RESET}")
441463

442464
print("") # clean separation
443465
self.registerBuffer = []
@@ -474,7 +496,7 @@ def colorReplacer(match):
474496
addrColored = f"{getAddrColor(val)}{addrStr}{Colors.RESET}"
475497

476498
# Check for canary/poison
477-
if val in [0xDEADBEEF, 0xA5A5A5A5, 0xFEFEFEFE, 0xABABABAB]:
499+
if val in CANARY_VALUES:
478500
# Commit current frame
479501
if frameFuncs:
480502
funcsFound.append((currentFrame, frameFuncs))
@@ -614,7 +636,7 @@ def processLine(self, line):
614636
# If we just entered IN_REGISTERS from IDLE above, we already printed the header.
615637
# But if we are continuing:
616638
if not isRegStart and not self.interactive: # Don't reprint header if we handled it in IDLE
617-
print(rawLine, end='')
639+
print(colorizeHex(rawLine), end='')
618640

619641
# Parse registers: "Name : 0xVal"
620642
# PC : 0x400d1f28 PS : 0x00060830
@@ -651,6 +673,13 @@ def main():
651673

652674
elfFile = sys.argv[1]
653675

676+
# Architecture guard
677+
arch = os.environ.get('SMING_ARCH', '')
678+
if arch and arch.lower() != 'esp32':
679+
print(f"Error: SMING_ARCH='{arch}' but this is the ESP32 decoder.", file=sys.stderr)
680+
print(f"Use the {arch} decoder instead (Sming/Arch/{arch}/Tools/decode-stacktrace.py).", file=sys.stderr)
681+
sys.exit(1)
682+
654683
# Tool Selection
655684
soc = os.environ.get('SMING_SOC', 'esp32').lower()
656685
if soc in ['esp32s2', 'esp32s3']:

Sming/Arch/Esp8266/Components/gdbstub/appcode/gdb_hooks.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,19 @@ static PGM_P const exceptionNames[] PROGMEM = {
3434
SYSTEM_EXCEPTION_MAP(XX)
3535
#undef XX
3636
};
37-
37+
3838
// The asm stub saves the Xtensa registers here when an exception is raised
3939
GdbstubSavedRegisters gdbstub_savedRegs;
4040

41+
DEFINE_PSTR_LOCAL(instructions_pstr, "To decode the stack dump call from command line:\r\n"
42+
" make decode-stacktrace\r\n"
43+
"and copy & paste the text enclosed in '===='.\r\n");
44+
DEFINE_PSTR_LOCAL(separatorLine_pstr, "\n================================================================\r\n");
45+
4146
void debug_print_stack(uint32_t start, uint32_t end)
4247
{
4348
m_puts(_F("\r\n"
4449
"Stack dump:\r\n"));
45-
PSTR_ARRAY(instructions, "To decode the stack dump call from command line:\r\n"
46-
" make decode-stacktrace\r\n"
47-
"and copy & paste the text enclosed in '===='.\r\n");
48-
PSTR_ARRAY(separatorLine, "\n================================================================\r\n");
49-
m_puts(instructions);
50-
m_puts(separatorLine);
5150
for(uint32_t addr = start; addr < end; addr += 0x10) {
5251
uint32_t* values = (uint32_t*)addr;
5352
// rough indicator: stack frames usually have SP saved as the second word
@@ -59,13 +58,14 @@ void debug_print_stack(uint32_t start, uint32_t end)
5958
system_soft_wdt_feed();
6059
wdt_feed();
6160
}
62-
m_puts(separatorLine);
63-
m_puts(instructions);
61+
6462
}
6563

6664
void debug_crash_callback([[maybe_unused]] const rst_info* rst_info, [[maybe_unused]] uint32_t stack,
6765
[[maybe_unused]] uint32_t stack_end)
6866
{
67+
LOAD_PSTR(instructions, instructions_pstr);
68+
LOAD_PSTR(separatorLine, separatorLine_pstr);
6969
#ifdef ENABLE_GDB
7070
gdbFlushUserData();
7171
if(gdb_state.attached) {
@@ -74,6 +74,9 @@ void debug_crash_callback([[maybe_unused]] const rst_info* rst_info, [[maybe_unu
7474
#endif
7575

7676
#if defined(ENABLE_GDB) || ENABLE_CRASH_DUMP
77+
m_puts(instructions);
78+
m_puts(separatorLine);
79+
7780
switch(rst_info->reason) {
7881
case REASON_EXCEPTION_RST:
7982
m_printf(_F("\r\n"
@@ -100,14 +103,20 @@ void debug_crash_callback([[maybe_unused]] const rst_info* rst_info, [[maybe_unu
100103
#elif ENABLE_CRASH_DUMP
101104
debug_print_stack(stack, stack_end);
102105
#endif
103-
106+
m_puts(separatorLine);
107+
m_puts(instructions);
104108
#endif // defined(ENABLE_GDB) || ENABLE_CRASH_DUMP
105109
}
106110

107111
#ifdef HOOK_SYSTEM_EXCEPTIONS
108112

109113
void dumpExceptionInfo()
110114
{
115+
LOAD_PSTR(instructions, instructions_pstr);
116+
LOAD_PSTR(separatorLine, separatorLine_pstr);
117+
m_puts(instructions);
118+
m_puts(separatorLine);
119+
111120
auto& reg = gdbstub_savedRegs;
112121

113122
m_printf(_F("\r\n"
@@ -138,6 +147,8 @@ void dumpExceptionInfo()
138147
if(gdb_present() != eGDB_Attached) {
139148
debug_print_stack(reg.a[1], 0x3fffffb0);
140149
}
150+
m_puts(separatorLine);
151+
m_puts(instructions);
141152
}
142153

143154
// Main exception handler code

0 commit comments

Comments
 (0)