#!/usr/bin/env python # # ESP32 core dump Utility import sys import os import argparse import subprocess import tempfile import struct import array import errno try: import esptool except ImportError: idf_path = os.getenv('IDF_PATH') if idf_path is None: print "Esptool is not found! Install it or set proper $IDF_PATH in environment." sys.exit(2) sys.path.append('%s/components/esptool_py/esptool' % idf_path) import esptool __version__ = "0.1-dev" ESP32_COREDUMP_HDR_FMT = '<4L' ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED class Struct(object): def __init__(self, buf=None): if buf is None: buf = b'\0' * self.sizeof() fields = struct.unpack(self.__class__.fmt, buf[:self.sizeof()]) self.__dict__.update(zip(self.__class__.fields, fields)) def sizeof(self): return struct.calcsize(self.__class__.fmt) def dump(self): keys = self.__class__.fields if sys.version_info > (3, 0): # Convert strings into bytearrays if this is Python 3 for k in keys: if type(self.__dict__[k]) is str: self.__dict__[k] = bytearray(self.__dict__[k], encoding='ascii') return struct.pack(self.__class__.fmt, *(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(Struct): """ELF32 File header""" fields = ("e_ident", "e_type", "e_machine", "e_version", "e_entry", "e_phoff", "e_shoff", "e_flags", "e_ehsize", "e_phentsize", "e_phnum", "e_shentsize", "e_shnum", "e_shstrndx") fmt = "<16sHHLLLLLHHHHHH" def __init__(self, buf=None): super(Elf32FileHeader, self).__init__(buf) if buf is None: # Fill in sane ELF header for LSB32 self.e_ident = "\x7fELF\1\1\1\0\0\0\0\0\0\0\0\0" self.e_version = ESPCoreDumpFile.EV_CURRENT self.e_ehsize = self.sizeof() class Elf32ProgramHeader(Struct): """ELF32 Program Header""" fields = ("p_type", "p_offset", "p_vaddr", "p_paddr", "p_filesz", "p_memsz", "p_flags", "p_align") fmt = " 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 = [] def _read_sections(self, f, section_header_offs, shstrndx): f.seek(section_header_offs) section_header = f.read() LEN_SEC_HEADER = 0x28 if len(section_header) == 0: raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) if len(section_header) % LEN_SEC_HEADER != 0: print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) # walk through the section header and extract all sections section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER) def read_section_header(offs): name_offs,sec_type,flags,lma,sec_offs,size = struct.unpack_from("= ps.addr and addr < (ps.addr + seg_len): raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len): raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) # append self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags)) # currently dumps only program segments. # dumping sections is not supported yet def dump(self, f): print "dump to '%s'" % f # write ELF header ehdr = Elf32FileHeader() ehdr.e_type = self.e_type ehdr.e_machine = self.e_machine ehdr.e_entry = 0 ehdr.e_phoff = ehdr.sizeof() ehdr.e_shoff = 0 ehdr.e_flags = 0 ehdr.e_phentsize = Elf32ProgramHeader().sizeof() ehdr.e_phnum = len(self.program_segments) ehdr.e_shentsize = 0 ehdr.e_shnum = 0 ehdr.e_shstrndx = self.SHN_UNDEF f.write(ehdr.dump()) # write program header table cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize # print "" % (ehdr.e_ehsize, ehdr.e_phnum, ehdr.e_phentsize) for i in range(len(self.program_segments)): print "dump header for seg '%s'" % self.program_segments[i] phdr = Elf32ProgramHeader() phdr.p_type = self.program_segments[i].type phdr.p_offset = cur_off phdr.p_vaddr = self.program_segments[i].addr phdr.p_paddr = phdr.p_vaddr # TODO phdr.p_filesz = len(self.program_segments[i].data) phdr.p_memsz = phdr.p_filesz # TODO phdr.p_flags = self.program_segments[i].flags phdr.p_align = 0 # TODO # print "header '%s'" % phdr f.write(phdr.dump()) cur_off += phdr.p_filesz # write program segments for i in range(len(self.program_segments)): print "dump seg '%s'" % self.program_segments[i] f.write(self.program_segments[i].data) class ESPCoreDumpError(RuntimeError): """ TBD """ def __init__(self, message): super(ESPCoreDumpError, self).__init__(message) class ESPCoreDumpLoaderError(ESPCoreDumpError): """ TBD """ def __init__(self, message): super(ESPCoreDumpLoaderError, self).__init__(message) class ESPCoreDumpLoader(object): """ TBD """ FLASH_READ_BLOCK_SZ = 0x2000 def __init__(self, off, path=None, chip='esp32', port=None, baud=None): # print "esptool.__file__ %s" % esptool.__file__ if not path: self.path = esptool.__file__ self.path = self.path[:-1] else: self.path = path self.port = port self.baud = baud self.chip = chip self.fcores = [] self.fgdbcore = None self._load_coredump(off) def _load_coredump(self, off): args = [self.path, '-c', self.chip] if self.port: args.extend(['-p', self.port]) if self.baud: args.extend(['-b', str(self.baud)]) read_sz = self.FLASH_READ_BLOCK_SZ read_off = off args.extend(['read_flash', str(read_off), str(read_sz), '']) try: dump_sz = 0 tot_len = 0 while True: fhnd,fname = tempfile.mkstemp() # print "tmpname %s" % fname # os.close(fhnd) args[-1] = fname et_out = subprocess.check_output(args) print et_out # data = os.fdopen(fhnd, 'r').read(sz) self.fcores.append(os.fdopen(fhnd, 'r')) if dump_sz == 0: # read dump length from the first block dump_sz = self._read_core_dump_length(self.fcores[0]) tot_len += read_sz if tot_len >= dump_sz: break read_off += read_sz if dump_sz - tot_len >= self.FLASH_READ_BLOCK_SZ: read_sz = self.FLASH_READ_BLOCK_SZ else: read_sz = dump_sz - tot_len args[-3] = str(read_off) args[-2] = str(read_sz) except subprocess.CalledProcessError as e: print "esptool script execution failed with err %d" % e.returncode print "Command ran: '%s'" % e.cmd print "Command out:" print e.output self.cleanup() return [] def _read_core_dump_length(self, f): global ESP32_COREDUMP_HDR_FMT global ESP32_COREDUMP_FLASH_MAGIC_START print "Read core dump header from '%s'" % f.name data = f.read(4*4) mag1,tot_len,task_num,tcbsz = struct.unpack_from(ESP32_COREDUMP_HDR_FMT, data) if mag1 != ESP32_COREDUMP_FLASH_MAGIC_START: raise ESPCoreDumpLoaderError("Invalid start magic number!") return tot_len def remove_tmp_file(self, fname): try: os.remove(fname) except OSError as e: if e.errno != errno.ENOENT: print "Warning failed to remove temp file '%s'!" % fname def _get_registers_from_stack(self, data, grows_down): # from "gdb/xtensa-tdep.h" # typedef struct # { #0 xtensa_elf_greg_t pc; #1 xtensa_elf_greg_t ps; #2 xtensa_elf_greg_t lbeg; #3 xtensa_elf_greg_t lend; #4 xtensa_elf_greg_t lcount; #5 xtensa_elf_greg_t sar; #6 xtensa_elf_greg_t windowstart; #7 xtensa_elf_greg_t windowbase; #8..63 xtensa_elf_greg_t reserved[8+48]; #64 xtensa_elf_greg_t ar[64]; # } xtensa_elf_gregset_t; REG_PC_IDX=0 REG_PS_IDX=1 REG_LB_IDX=2 REG_LE_IDX=3 REG_LC_IDX=4 REG_SAR_IDX=5 REG_WS_IDX=6 REG_WB_IDX=7 REG_AR_START_IDX=64 REG_AR_NUM=64 # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128, # but gdb complanis when it less then 129 REG_NUM=129 XT_SOL_EXIT=0 XT_SOL_PC=1 XT_SOL_PS=2 XT_SOL_NEXT=3 XT_SOL_AR_START=4 XT_SOL_AR_NUM=4 XT_SOL_FRMSZ=8 XT_STK_EXIT=0 XT_STK_PC=1 XT_STK_PS=2 XT_STK_AR_START=3 XT_STK_AR_NUM=16 XT_STK_SAR=19 XT_STK_EXCCAUSE=20 XT_STK_EXCVADDR=21 XT_STK_LBEG=22 XT_STK_LEND=23 XT_STK_LCOUNT=24 XT_STK_FRMSZ=25 regs = [0] * REG_NUM # TODO: support for growing up stacks if not grows_down: print "Growing up stacks are not supported for now!" return regs # for i in range(REG_NUM): # regs[i] = i # return regs ex_struct = "<%dL" % XT_STK_FRMSZ if len(data) < struct.calcsize(ex_struct): print "Too small stack to keep frame: %d bytes!" % len(data) return regs stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)]) # Stack frame type indicator is always the first item rc = stack[XT_STK_EXIT] if rc != 0: print "EXCSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_STK_PC] regs[REG_PS_IDX] = stack[XT_STK_PS] for i in range(XT_STK_AR_NUM): regs[REG_AR_START_IDX + i] = stack[XT_STK_AR_START + i] regs[REG_SAR_IDX] = stack[XT_STK_SAR] regs[REG_LB_IDX] = stack[XT_STK_LBEG] regs[REG_LE_IDX] = stack[XT_STK_LEND] regs[REG_LC_IDX] = stack[XT_STK_LCOUNT] print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) else: print "SOLSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_SOL_PC] regs[REG_PS_IDX] = stack[XT_SOL_PS] for i in range(XT_SOL_AR_NUM): regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i] nxt = stack[XT_SOL_NEXT] print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) # TODO: remove magic hack with saved PC to get proper value regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000) if regs[REG_PC_IDX] & 0x80000000: regs[REG_PC_IDX] = (regs[REG_PC_IDX] & 0x3fffffff) | 0x40000000; if regs[REG_AR_START_IDX + 0] & 0x80000000: regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000; return regs def cleanup(self): # if self.fgdbcore: # self.fgdbcore.close() # self.remove_tmp_file(self.fgdbcore.name) for f in self.fcores: if f: f.close() self.remove_tmp_file(f.name) def get_corefile_from_flash(self): """ TBD """ global ESP32_COREDUMP_HDR_FMT ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) ESP32_COREDUMP_TSK_HDR_FMT = ' stack_top: stack_len = stack_end - stack_top stack_base = stack_top else: stack_len = stack_top - stack_end stack_base = stack_end print "tcb_addr=%x, stack_top=%x, stack_end=%x, stack_len=%d" % (tcb_addr,stack_top,stack_end,stack_len) stack_len_aligned = stack_len if stack_len_aligned % 4: stack_len_aligned = 4*(stack_len_aligned/4 + 1) core_off += ESP32_COREDUMP_TSK_HDR_SZ print "Read task[%d] TCB" % i data = self.read_flash(core_off, tcbsz_aligned, flash_progress) if tcbsz != tcbsz_aligned: core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) else: core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) # print "tcb=%s" % data core_off += tcbsz_aligned print "Read task[%d] stack %d bytes" % (i,stack_len) data = self.read_flash(core_off, stack_len_aligned, flash_progress) # print "stk=%s" % data if stack_len != stack_len_aligned: data = data[:stack_len - stack_len_aligned] core_elf.add_program_segment(stack_base, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) core_off += stack_len_aligned task_regs = self._get_registers_from_stack(data, stack_end > stack_top) prstatus = XtensaPrStatus() prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task prstatus.pr_pid = i # TODO: use pid assigned by OS note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() print "NOTE_LEN %d" % len(note) notes += note print "Read core dump endmarker" data = self.read_flash(core_off, ESP32_COREDUMP_MAGIC_SZ, flash_progress) mag = struct.unpack_from(ESP32_COREDUMP_MAGIC_FMT, data) print "mag2=%x" % (mag) # add notes core_elf.add_program_segment(0, notes, ESPCoreDumpFile.PT_NOTE, 0) core_elf.e_type = ESPCoreDumpFile.ET_CORE core_elf.e_machine = ESPCoreDumpFile.EM_XTENSA fhnd,fname = tempfile.mkstemp() self.fgdbcore = os.fdopen(fhnd, 'wb') core_elf.dump(self.fgdbcore) return fname ######################### END ########################### def read_flash(self, off, sz, progress=None): # print "read_flash: %x %d" % (off, sz) id = off / self.FLASH_READ_BLOCK_SZ if id >= len(self.fcores): return '' self.fcores[id].seek(off % self.FLASH_READ_BLOCK_SZ) data = self.fcores[id].read(sz) # print "data1: %s" % data return data class GDBMIOutRecordHandler(object): """ TBD """ TAG = '' def __init__(self, f, verbose=False): self.verbose = verbose def execute(self, ln): if self.verbose: print "%s.execute '%s'" % (self.__class__.__name__, ln) class GDBMIOutStreamHandler(GDBMIOutRecordHandler): """ TBD """ def __init__(self, f, verbose=False): super(GDBMIOutStreamHandler, self).__init__(None, verbose) self.func = f def execute(self, ln): GDBMIOutRecordHandler.execute(self, ln) if self.func: # remove TAG / quotes and replace c-string \n with actual NL self.func(ln[1:].strip('"').replace('\\n', '\n').replace('\\t', '\t')) class GDBMIResultHandler(GDBMIOutRecordHandler): """ TBD """ TAG = '^' RC_DONE = 'done' RC_RUNNING = 'running' RC_CONNECTED = 'connected' RC_ERROR = 'error' RC_EXIT = 'exit' def __init__(self, verbose=False): super(GDBMIResultHandler, self).__init__(None, verbose) self.result_class = None self.result_str = None def _parse_rc(self, ln, rc): rc_str = "{0}{1}".format(self.TAG, rc) if ln.startswith(rc_str): self.result_class = rc sl = len(rc_str) if len(ln) > sl: self.result_str = ln[sl:] if self.result_str.startswith(','): self.result_str = self.result_str[1:] else: print "Invalid result format: '%s'" % ln else: self.result_str = '' return True return False def execute(self, ln): GDBMIOutRecordHandler.execute(self, ln) if self._parse_rc(ln, self.RC_DONE): return if self._parse_rc(ln, self.RC_RUNNING): return if self._parse_rc(ln, self.RC_CONNECTED): return if self._parse_rc(ln, self.RC_ERROR): return if self._parse_rc(ln, self.RC_EXIT): return print "Unknown result: '%s'" % ln class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler): """ TBD """ TAG = '~' def dbg_corefile(args): """ TBD """ print "dbg_corefile %s %s %s" % (args.gdb, args.prog, args.core) loader = None if not args.core: loader = ESPCoreDumpLoader(args.off, port=args.port) core_fname = loader.get_corefile_from_flash() loader.fgdbcore.close() # core_fname = 'esp_core.elf' else: core_fname = args.core # print core_fname # return p = subprocess.Popen( bufsize = 0, args = [args.gdb, '--nw', # ignore .gdbinit '--core=%s' % core_fname, # core file args.prog], stdin = None, stdout = None, stderr = None, close_fds = True ) p.wait() if loader: loader.remove_tmp_file(loader.fgdbcore.name) loader.cleanup() print 'Done!' def info_corefile(args): # def info_corefile(args): """ TBD """ print "info_corefile %s %s %s" % (args.gdb, args.prog, args.core) def gdbmi_console_stream_handler(ln): # print ln sys.stdout.write(ln) sys.stdout.flush() def gdbmi_read2prompt(f, out_handlers=None): """ TBD """ while True: ln = f.readline().rstrip(' \n') # print "LINE='{0}'".format(ln) if ln == '(gdb)': break elif len(ln) == 0: break elif out_handlers: for h in out_handlers: if ln.startswith(out_handlers[h].TAG): out_handlers[h].execute(ln) break loader = None if not args.core: loader = ESPCoreDumpLoader(args.off, port=args.port) core_fname = loader.get_corefile_from_flash() loader.fgdbcore.close() else: core_fname = args.core handlers = {} handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False) handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) 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], # ], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = True ) gdbmi_read2prompt(p.stdout, handlers) exe_elf = ESPCoreDumpFile(args.prog) core_elf = ESPCoreDumpFile(core_fname) merged_segs = []#[(s, 0) for s in exe_elf.sections if s.flags & (esptool.ELFSection.SHF_ALLOC | esptool.ELFSection.SHF_WRITE)] for s in exe_elf.sections: merged = False for ps in core_elf.program_segments: if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr: # sec: |XXXXXXXXXX| # seg: |...XXX.............| seg_addr = ps.addr if ps.addr + len(ps.data) <= s.addr + len(s.data): # sec: |XXXXXXXXXX| # seg: |XXXXXXXXXXX...| # merged: |XXXXXXXXXXXXXX| seg_len = len(s.data) + (s.addr - ps.addr) else: # sec: |XXXXXXXXXX| # seg: |XXXXXXXXXXXXXXXXX| # merged: |XXXXXXXXXXXXXXXXX| seg_len = len(ps.data) merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) merged = True elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data): # sec: |XXXXXXXXXX| # seg: |...XXX.............| seg_addr = s.addr if (ps.addr + len(ps.data)) >= (s.addr + len(s.data)): # sec: |XXXXXXXXXX| # seg: |..XXXXXXXXXXX| # merged: |XXXXXXXXXXXXX| seg_len = len(s.data) + (ps.addr + len(ps.data)) - (s.addr + len(s.data)) else: # sec: |XXXXXXXXXX| # seg: |XXXXXX| # merged: |XXXXXXXXXX| seg_len = len(s.data) merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) merged = True if not merged: merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False)) # merged_segs.append(('None', ps.addr, len(ps.data), 'None')) print "===============================================================" print "==================== ESP32 CORE DUMP START ====================" handlers[GDBMIResultHandler.TAG].result_class = None handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler print "\n================== CURRENT THREAD REGISTERS ===================" p.stdin.write("-interpreter-exec console \"info registers\"\n") gdbmi_read2prompt(p.stdout, handlers) if 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 "\n==================== CURRENT THREAD STACK =====================" p.stdin.write("-interpreter-exec console \"bt\"\n") gdbmi_read2prompt(p.stdout, handlers) if 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 "\n======================== THREADS INFO =========================" p.stdin.write("-interpreter-exec console \"info threads\"\n") gdbmi_read2prompt(p.stdout, handlers) if 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 "\n======================= MEMORY REGIONS ========================" print "Name Address Size Attrs" for ms in merged_segs: print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) if args.print_mem: print "\n====================== MEMORY CONTENTS ========================" for ms in merged_segs: # if ms[3].find('W') == -1: if not ms[4]: continue print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) p.stdin.write("-interpreter-exec console \"x/%dx 0x%x\"\n" % (ms[2]/4, ms[1])) gdbmi_read2prompt(p.stdout, handlers) if 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 "\n===================== ESP32 CORE DUMP END =====================" print "===============================================================" p.terminate() p.stdin.close() p.stdout.close() if loader: loader.remove_tmp_file(loader.fgdbcore.name) loader.cleanup() def main(): parser = argparse.ArgumentParser(description='coredumper.py v%s - ESP32 Core Dump Utility' % __version__, prog='coredumper') parser.add_argument('--chip', '-c', help='Target chip type', choices=['auto', 'esp32'], default=os.environ.get('ESPTOOL_CHIP', 'auto')) parser.add_argument( '--port', '-p', help='Serial port device', default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT)) parser.add_argument( '--baud', '-b', help='Serial port baud rate used when flashing/reading', type=int, default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD)) # parser.add_argument( # '--no-stub', # help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", # action='store_true') subparsers = parser.add_subparsers( dest='operation', help='Run coredumper {command} -h for additional help') parser_debug_coredump = subparsers.add_parser( 'dbg_corefile', help='Starts GDB debugging session with specified corefile') parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) 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('prog', help='Path to program\'s ELF binary', type=str) parser_info_coredump = subparsers.add_parser( 'info_corefile', help='Print core dump info from file') parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true') parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) 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('prog', help='Path to program\'s ELF binary', type=str) # internal sanity check - every operation matches a module function of the same name for operation in subparsers.choices.keys(): assert operation in globals(), "%s should be a module function" % operation args = parser.parse_args() print 'coredumper.py v%s' % __version__ # operation function can take 1 arg (args), 2 args (esp, arg) # or be a member function of the ESPLoader class. operation_func = globals()[args.operation] operation_func(args) if __name__ == '__main__': try: main() except ESPCoreDumpError as e: print '\nA fatal error occurred: %s' % e sys.exit(2)