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())) { if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
// set correct stack top for current task // set correct stack top for current task
tasks[i].pxTopOfStack = (StackType_t *)frame; 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", 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); 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." print "Esptool is not found! Set proper $IDF_PATH in environment."
sys.exit(2) sys.exit(2)
__version__ = "0.1-dev" __version__ = "0.2-dev"
if os.name == 'nt': if os.name == 'nt':
CLOSE_FDS = False CLOSE_FDS = False
@ -77,12 +77,6 @@ class BinStruct(object):
keys = self.__class__.fields keys = self.__class__.fields
return struct.pack(self.__class__.format, *(self.__dict__[k] for k in keys)) 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): class Elf32FileHeader(BinStruct):
"""ELF32 file header """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)) raise ESPCoreDumpError("%s does not appear to be an Xtensa ELF file. e_machine=%04x" % (self.name, machine))
self.e_type = type self.e_type = type
self.e_machine = machine self.e_machine = machine
self.sections = []
self.program_segments = []
if shnum > 0: if shnum > 0:
self._read_sections(f, shoff, shstrndx) self._read_sections(f, shoff, shstrndx)
else: if phnum > 0:
self.sections = [] self._read_program_segments(f, phoff, phentsize, phnum)
if phnum > 0:
self._read_program_segments(f, phoff, phentsize, phnum)
else:
self.program_segments = []
def _read_sections(self, f, section_header_offs, shstrndx): def _read_sections(self, f, section_header_offs, shstrndx):
"""Reads core dump sections from ELF file """Reads core dump sections from ELF file
@ -373,17 +365,15 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
def read_program_header(offs): def read_program_header(offs):
type,offset,vaddr,_paddr,filesz,_memsz,flags,_align = struct.unpack_from("<LLLLLLLL", seg_table[offs:]) type,offset,vaddr,_paddr,filesz,_memsz,flags,_align = struct.unpack_from("<LLLLLLLL", seg_table[offs:])
return (type,offset,vaddr,filesz,flags) return (type,offset,vaddr,filesz,flags)
all_segments = [read_program_header(offs) for offs in seg_table_offs] prog_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]
# build the real list of ImageSegment by reading actual data for each segment from the ELF file itself # build the real list of ImageSegment by reading actual data for each segment from the ELF file itself
def read_data(offs,size): def read_data(offs,size):
f.seek(offs) f.seek(offs)
return f.read(size) 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] if vaddr != 0]
self.program_segments = prog_segments
def add_program_segment(self, addr, data, type, flags): def add_program_segment(self, addr, data, type, flags):
"""Adds new program segment """Adds new program segment
@ -571,7 +561,7 @@ class ESPCoreDumpLoader(object):
if self.fcore_name: if self.fcore_name:
self.remove_tmp_file(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 """Creates core dump ELF file
""" """
core_off = off core_off = off
@ -622,6 +612,11 @@ class ESPCoreDumpLoader(object):
# add notes # add notes
core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0) 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_type = ESPCoreDumpElfFile.ET_CORE
core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA
@ -748,7 +743,7 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
raise ESPCoreDumpLoaderError("Invalid start magic number!") raise ESPCoreDumpLoaderError("Invalid start magic number!")
return tot_len 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 """Checks flash coredump data integrity and creates ELF file
""" """
data = self.read_data(0, self.ESP32_COREDUMP_FLASH_MAGIC_SZ) 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: if mag2 != self.ESP32_COREDUMP_FLASH_MAGIC_END:
raise ESPCoreDumpLoaderError("Invalid end marker %x" % mag2) 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): class GDBMIOutRecordHandler(object):
@ -856,15 +851,27 @@ class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler):
""" """
TAG = '~' 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): def dbg_corefile(args):
""" Command to load core dump from file or flash and run GDB debug session with it """ Command to load core dump from file or flash and run GDB debug session with it
""" """
global CLOSE_FDS global CLOSE_FDS
loader = None loader = None
rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
if not args.core: if not args.core:
loader = ESPCoreDumpFlashLoader(args.off, port=args.port) 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: if not core_fname:
print "Failed to create corefile!" print "Failed to create corefile!"
loader.cleanup() loader.cleanup()
@ -873,7 +880,7 @@ def dbg_corefile(args):
core_fname = args.core core_fname = args.core
if args.core_format and args.core_format != 'elf': if args.core_format and args.core_format != 'elf':
loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64') 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: if not core_fname:
print "Failed to create corefile!" print "Failed to create corefile!"
loader.cleanup() loader.cleanup()
@ -883,7 +890,8 @@ def dbg_corefile(args):
bufsize = 0, bufsize = 0,
args = [args.gdb, args = [args.gdb,
'--nw', # ignore .gdbinit '--nw', # ignore .gdbinit
'--core=%s' % core_fname, # core file '--core=%s' % core_fname, # core file,
'-ex', rom_sym_cmd,
args.prog], args.prog],
stdin = None, stdout = None, stderr = None, stdin = None, stdout = None, stderr = None,
close_fds = CLOSE_FDS close_fds = CLOSE_FDS
@ -918,16 +926,19 @@ def info_corefile(args):
out_handlers[h].execute(ln) out_handlers[h].execute(ln)
break 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( p = subprocess.Popen(
bufsize = 0, bufsize = 0,
args = [args.gdb, args = gdb_args,
'--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],
stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT,
close_fds = CLOSE_FDS 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) print "GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
p.wait() p.wait()
print "Problem occured! GDB exited, restart it." print "Problem occured! GDB exited, restart it."
p = gdbmi_start(handlers) p = gdbmi_start(handlers, [])
elif handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: 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) print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)
return p return p
loader = None loader = None
rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
if not args.core: if not args.core:
loader = ESPCoreDumpFlashLoader(args.off, port=args.port) 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: if not core_fname:
print "Failed to create corefile!" print "Failed to create corefile!"
loader.cleanup() loader.cleanup()
@ -961,7 +972,7 @@ def info_corefile(args):
core_fname = args.core core_fname = args.core
if args.core_format and args.core_format != 'elf': if args.core_format and args.core_format != 'elf':
loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64') 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: if not core_fname:
print "Failed to create corefile!" print "Failed to create corefile!"
loader.cleanup() loader.cleanup()
@ -1015,7 +1026,7 @@ def info_corefile(args):
handlers = {} handlers = {}
handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False) handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False)
handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
p = gdbmi_start(handlers) p = gdbmi_start(handlers, [rom_sym_cmd])
print "===============================================================" print "==============================================================="
print "==================== ESP32 CORE DUMP START ====================" print "==================== ESP32 CORE DUMP START ===================="
@ -1033,11 +1044,21 @@ def info_corefile(args):
for ms in merged_segs: for ms in merged_segs:
print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3])
for cs in core_segs: 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: if args.print_mem:
print "\n====================== CORE DUMP MEMORY CONTENTS ========================" print "\n====================== CORE DUMP MEMORY CONTENTS ========================"
for cs in core_elf.program_segments: 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)) p = gdbmi_getinfo(p, handlers, "x/%dx 0x%x" % (len(cs.data)/4, cs.addr))
print "\n===================== ESP32 CORE DUMP END =====================" 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('--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('--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('--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_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)
parser_info_coredump = subparsers.add_parser( 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('--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('--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('--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('--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) 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. 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' 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. * --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). * --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". * --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". * --print-mem,-m Print memory dump. Used only with "info_corefile".