]> granicus.if.org Git - esp-idf/commitdiff
coredump: Adds support for ROM ELF file to 'espcoredump' script
authorAlexey Gerenkov <alexey@espressif.com>
Fri, 29 Jun 2018 12:45:31 +0000 (15:45 +0300)
committerAlexey Gerenkov <alexey@espressif.com>
Thu, 5 Jul 2018 19:20:20 +0000 (22:20 +0300)
components/espcoredump/espcoredump.py
docs/en/api-guides/core_dump.rst

index a1091a006a31b62ca24c57c10b5eb3d0a51a638a..321941fd1c455ecc10b7f0c73d1208519400736a 100755 (executable)
@@ -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)
 
index 84eec8297657998911baec493aeb064a74cbdb1f..db738943148f4599461b82725b110232302fe530 100644 (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".