Skip to content

Instantly share code, notes, and snippets.

@flaviut
Last active April 27, 2024 22:06
Show Gist options
  • Save flaviut/3dbfb937fcfab407e9f2be88728a642d to your computer and use it in GitHub Desktop.
Save flaviut/3dbfb937fcfab407e9f2be88728a642d to your computer and use it in GitHub Desktop.
This tool decodes an ESP32 stack trace into a human-readable stack trace. The original stack trace generally gets printed to the serial console when the esp32 crashes.

This tool decodes an ESP32 stack trace into a human-readable stack trace. The original stack trace generally gets printed to the serial console when the esp32 crashes.

Usage example:

$ python decode.py -a ~/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-addr2line ./.pio/build/esp32-3248S035C/firmware.elf
Backtrace: 0x400eee2e:0x3ffb1eb0 0x400ed765:0x3ffb1ed0 0x400f1317:0x3ffb1ef0 0x400f1576:0x3ffb1f60 0x400ebc80:0x3ffb1f80 0x400ebeed:0x3ffb1fa0 0x400d74cb:0x3ffb1fc0 0x400dcd46:0x3ffb1fe0 0x400dcda2:0x3ffb2000 0x400ebd81:0x3ffb2040 0x400dcd46:0x3ffb2060 0x400dcda2:0x3ffb2080 0x400f1954:0x3ffb20c0 0x4019f9ce:0x3ffb20f0 0x400dcbf7:0x3ffb2110 0x400dcda2:0x3ffb2130 0x400dceda:0x3ffb2170 0x400dd56c:0x3ffb21a0 0x400eea88:0x3ffb21f0 0x400d5252:0x3ffb2220 0x400d7715:0x3ffb2240 0x400d661d:0x3ffb2260 0x4010a32e:0x3ffb2290
lv_tlsf_free at .pio/libdeps/esp32-3248S035C/lvgl/src/misc/lv_tlsf.c:1167
lv_mem_free at .pio/libdeps/esp32-3248S035C/lvgl/src/misc/lv_mem.c:179
allocate_btn_areas_and_controls at .pio/libdeps/esp32-3248S035C/lvgl/src/widgets/lv_btnmatrix.c:878
lv_btnmatrix_set_map at .pio/libdeps/esp32-3248S035C/lvgl/src/widgets/lv_btnmatrix.c:96
lv_keyboard_update_map at .pio/libdeps/esp32-3248S035C/lvgl/src/extra/widgets/keyboard/lv_keyboard.c:397
...
import argparse
import subprocess
import sys
import os
import re
from pathlib import Path
def parse_arguments():
parser = argparse.ArgumentParser(description='Decode stack trace using addr2line.')
parser.add_argument('elf_file', type=str, help='Path to the ELF file.')
parser.add_argument('-a', '--addr2line', type=str, default='addr2line',
help='Path to the addr2line tool (default is addr2line in system path)')
return parser.parse_args()
def common_path_prefix(paths):
"""Find a common prefix to make the paths more human-readable"""
paths_for_common = [p for p in paths if os.path.isabs(p)]
try:
commonpath = os.path.commonpath(paths_for_common)
except:
commonpath = ''
return max([commonpath, os.getcwd()], key=len) + '/'
def decode_stack_trace(elf_file, addr2line_path, stack_trace):
# Extracting the backtrace addresses from the stack trace string
matches = re.findall(r' (0x[0-9a-fA-F]+):(0x[0-9a-fA-F]+)', stack_trace)
if not matches:
print("No addresses found in the stack trace.")
return
decoded_outputs = []
for ip, sp in matches:
cmd = [addr2line_path, '-e', elf_file, '--functions', '--demangle', '--pretty-print', ip]
try:
output = subprocess.run(cmd, capture_output=True, text=True)
decoded_outputs.append(output.stdout.strip())
except Exception as e:
print(f"Error decoding address {ip}: {str(e)}")
if not decoded_outputs:
print("No output received from addr2line.")
return
# Remove common path prefix from file paths
paths = [line.split(' ')[-1] for line in decoded_outputs if ' at ' in line]
for output in decoded_outputs:
print(output.replace(common_path_prefix(paths), ''))
if __name__ == "__main__":
args = parse_arguments()
stack_trace = sys.stdin.read()
decode_stack_trace(args.elf_file, args.addr2line, stack_trace)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment