Merge branch 'feature/add_rom_elf' into 'master'

Support for ROM functions in core dump backtraces

See merge request idf/esp-idf!2672
This commit is contained in:
Angus Gratton 2018-08-01 15:21:03 +08:00
commit 7457054c1f
3 changed files with 75 additions and 38 deletions

View file

@ -127,6 +127,10 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
// set correct stack top for current task
tasks[i].pxTopOfStack = (StackType_t *)frame;
// This field is not initialized for crashed task, but stack frame has the structure of interrupt one,
// so make workaround to allow espcoredump to parse it properly.
if (frame->exit == 0)
frame->exit = -1;
ESP_COREDUMP_LOG_PROCESS("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x",
frame->exit, frame->pc, frame->ps, frame->a0, frame->a1);
}

View file

@ -22,7 +22,7 @@ except ImportError:
print "Esptool is not found! Set proper $IDF_PATH in environment."
sys.exit(2)
__version__ = "0.1-dev"
__version__ = "0.2-dev"
if os.name == 'nt':
CLOSE_FDS = False
@ -77,12 +77,6 @@ class BinStruct(object):
keys = self.__class__.fields
return struct.pack(self.__class__.format, *(self.__dict__[k] for k in keys))
# def __str__(self):
# keys = self.__class__.fields
# return (self.__class__.__name__ + "({" +
# ", ".join("%s:%r" % (k, self.__dict__[k]) for k in keys) +
# "})")
class Elf32FileHeader(BinStruct):
"""ELF32 file header
@ -304,14 +298,12 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
raise ESPCoreDumpError("%s does not appear to be an Xtensa ELF file. e_machine=%04x" % (self.name, machine))
self.e_type = type
self.e_machine = machine
self.sections = []
self.program_segments = []
if shnum > 0:
self._read_sections(f, shoff, shstrndx)
else:
self.sections = []
if phnum > 0:
self._read_program_segments(f, phoff, phentsize, phnum)
else:
self.program_segments = []
if phnum > 0:
self._read_program_segments(f, phoff, phentsize, phnum)
def _read_sections(self, f, section_header_offs, shstrndx):
"""Reads core dump sections from ELF file
@ -373,17 +365,15 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
def read_program_header(offs):
type,offset,vaddr,_paddr,filesz,_memsz,flags,_align = struct.unpack_from("<LLLLLLLL", seg_table[offs:])
return (type,offset,vaddr,filesz,flags)
all_segments = [read_program_header(offs) for offs in seg_table_offs]
prog_segments = [s for s in all_segments if s[0] == self.PT_LOAD]
prog_segments = [read_program_header(offs) for offs in seg_table_offs]
# build the real list of ImageSegment by reading actual data for each segment from the ELF file itself
def read_data(offs,size):
f.seek(offs)
return f.read(size)
prog_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags) for (type, offset, vaddr, filesz,flags) in prog_segments
self.program_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags) for (type, offset, vaddr, filesz,flags) in prog_segments
if vaddr != 0]
self.program_segments = prog_segments
def add_program_segment(self, addr, data, type, flags):
"""Adds new program segment
@ -571,7 +561,7 @@ class ESPCoreDumpLoader(object):
if self.fcore_name:
self.remove_tmp_file(self.fcore_name)
def create_corefile(self, core_fname=None, off=0):
def create_corefile(self, core_fname=None, off=0, rom_elf=None):
"""Creates core dump ELF file
"""
core_off = off
@ -622,6 +612,11 @@ class ESPCoreDumpLoader(object):
# add notes
core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0)
# add ROM text sections
if rom_elf:
for ps in rom_elf.program_segments:
if ps.flags & ESPCoreDumpSegment.PF_X:
core_elf.add_program_segment(ps.addr, ps.data, ESPCoreDumpElfFile.PT_LOAD, ps.flags)
core_elf.e_type = ESPCoreDumpElfFile.ET_CORE
core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA
@ -748,7 +743,7 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
raise ESPCoreDumpLoaderError("Invalid start magic number!")
return tot_len
def create_corefile(self, core_fname=None):
def create_corefile(self, core_fname=None, rom_elf=None):
"""Checks flash coredump data integrity and creates ELF file
"""
data = self.read_data(0, self.ESP32_COREDUMP_FLASH_MAGIC_SZ)
@ -761,7 +756,7 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
if mag2 != self.ESP32_COREDUMP_FLASH_MAGIC_END:
raise ESPCoreDumpLoaderError("Invalid end marker %x" % mag2)
return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname, off=self.ESP32_COREDUMP_FLASH_MAGIC_SZ)
return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname, off=self.ESP32_COREDUMP_FLASH_MAGIC_SZ, rom_elf=rom_elf)
class GDBMIOutRecordHandler(object):
@ -856,15 +851,27 @@ class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler):
"""
TAG = '~'
def load_aux_elf(elf_path):
""" Loads auxilary ELF file and composes GDB command to read its symbols
"""
elf = None
sym_cmd = ''
if os.path.exists(elf_path):
elf = ESPCoreDumpElfFile(elf_path)
for s in elf.sections:
if s.name == '.text':
sym_cmd = 'add-symbol-file %s 0x%x' % (elf_path, s.addr)
return (elf, sym_cmd)
def dbg_corefile(args):
""" Command to load core dump from file or flash and run GDB debug session with it
"""
global CLOSE_FDS
loader = None
rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
if not args.core:
loader = ESPCoreDumpFlashLoader(args.off, port=args.port)
core_fname = loader.create_corefile(args.save_core)
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print "Failed to create corefile!"
loader.cleanup()
@ -873,7 +880,7 @@ def dbg_corefile(args):
core_fname = args.core
if args.core_format and args.core_format != 'elf':
loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
core_fname = loader.create_corefile(args.save_core)
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print "Failed to create corefile!"
loader.cleanup()
@ -883,7 +890,8 @@ def dbg_corefile(args):
bufsize = 0,
args = [args.gdb,
'--nw', # ignore .gdbinit
'--core=%s' % core_fname, # core file
'--core=%s' % core_fname, # core file,
'-ex', rom_sym_cmd,
args.prog],
stdin = None, stdout = None, stderr = None,
close_fds = CLOSE_FDS
@ -918,16 +926,19 @@ def info_corefile(args):
out_handlers[h].execute(ln)
break
def gdbmi_start(handlers):
def gdbmi_start(handlers, gdb_cmds):
gdb_args = [args.gdb,
'--quiet', # inhibit dumping info at start-up
'--nx', # inhibit window interface
'--nw', # ignore .gdbinit
'--interpreter=mi2', # use GDB/MI v2
'--core=%s' % core_fname] # core file
for c in gdb_cmds:
gdb_args += ['-ex', c]
gdb_args.append(args.prog)
p = subprocess.Popen(
bufsize = 0,
args = [args.gdb,
'--quiet', # inhibit dumping info at start-up
'--nx', # inhibit window interface
'--nw', # ignore .gdbinit
'--interpreter=mi2', # use GDB/MI v2
'--core=%s' % core_fname, # core file
args.prog],
args = gdb_args,
stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT,
close_fds = CLOSE_FDS
)
@ -943,16 +954,16 @@ def info_corefile(args):
print "GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
p.wait()
print "Problem occured! GDB exited, restart it."
p = gdbmi_start(handlers)
p = gdbmi_start(handlers, [])
elif handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
return p
loader = None
rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
if not args.core:
loader = ESPCoreDumpFlashLoader(args.off, port=args.port)
core_fname = loader.create_corefile(args.save_core)
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print "Failed to create corefile!"
loader.cleanup()
@ -961,7 +972,7 @@ def info_corefile(args):
core_fname = args.core
if args.core_format and args.core_format != 'elf':
loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
core_fname = loader.create_corefile(args.save_core)
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print "Failed to create corefile!"
loader.cleanup()
@ -1015,7 +1026,7 @@ def info_corefile(args):
handlers = {}
handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False)
handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
p = gdbmi_start(handlers)
p = gdbmi_start(handlers, [rom_sym_cmd])
print "==============================================================="
print "==================== ESP32 CORE DUMP START ===================="
@ -1033,11 +1044,21 @@ def info_corefile(args):
for ms in merged_segs:
print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3])
for cs in core_segs:
print ".coredump.tasks 0x%x 0x%x %s" % (cs.addr, len(cs.data), cs.attr_str())
# core dump exec segments are from ROM, other are belong to tasks (TCB or stack)
if cs.flags & ESPCoreDumpSegment.PF_X:
seg_name = 'rom.text'
else:
seg_name = 'tasks.data'
print ".coredump.%s 0x%x 0x%x %s" % (seg_name, cs.addr, len(cs.data), cs.attr_str())
if args.print_mem:
print "\n====================== CORE DUMP MEMORY CONTENTS ========================"
for cs in core_elf.program_segments:
print ".coredump.tasks 0x%x 0x%x %s" % (cs.addr, len(cs.data), cs.attr_str())
# core dump exec segments are from ROM, other are belong to tasks (TCB or stack)
if cs.flags & ESPCoreDumpSegment.PF_X:
seg_name = 'rom.text'
else:
seg_name = 'tasks.data'
print ".coredump.%s 0x%x 0x%x %s" % (seg_name, cs.addr, len(cs.data), cs.attr_str())
p = gdbmi_getinfo(p, handlers, "x/%dx 0x%x" % (len(cs.data)/4, cs.addr))
print "\n===================== ESP32 CORE DUMP END ====================="
@ -1086,6 +1107,7 @@ def main():
parser_debug_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000)
parser_debug_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c"', type=str)
parser_debug_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)
parser_info_coredump = subparsers.add_parser(
@ -1096,6 +1118,7 @@ def main():
parser_info_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
parser_info_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000)
parser_info_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. Does not work with "-c"', type=str)
parser_info_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true')
parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)

View file

@ -65,6 +65,15 @@ Base64-encoded body of core dump will be between the following header and footer
The `CORE DUMP START` and `CORE DUMP END` lines must not be included in core dump text file.
ROM Functions in Backtraces
---------------------------
It is possible situation that at the moment of crash some tasks or/and crashed task itself have one or more ROM functions in their callstacks.
Since ROM is not part of the program ELF it will be impossible for GDB to parse such callstacks, because it tries to analyse functions' prologues to acomplish that.
In that case callstack printing will be broken with error message at the first ROM function.
To overcome this issue you can use ROM ELF provided by Espressif (https://dl.espressif.com/dl/esp32_rom.elf) and pass it to 'espcoredump.py'.
Running 'espcoredump.py'
------------------------------------
@ -85,4 +94,5 @@ Generic command syntax:
* --core-format,-t CORE_FORMAT. Specifies that file passed with "-c" is an ELF ("elf"), dumped raw binary ("raw") or base64-encoded ("b64") format.
* --off,-o OFF. Ofsset of coredump partition in flash (type "make partition_table" to see it).
* --save-core,-s SAVE_CORE. Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c".
* --rom-elf,-r ROM_ELF. Path to ROM ELF file to use (if skipped "esp32_rom.elf" is used).
* --print-mem,-m Print memory dump. Used only with "info_corefile".