From a7840de9d8f6d99465e65bd0d47be959fc82d255 Mon Sep 17 00:00:00 2001 From: Joseph Tang Date: Thu, 16 Jul 2020 20:49:07 +0800 Subject: [PATCH] core dump update --- components/bt/hli_vectors.S | 3 + components/espcoredump/espcoredump.py | 1196 +++++++++++++---- .../espcoredump/include/esp_core_dump.h | 5 +- .../include_core_dump/esp_core_dump_priv.h | 28 +- components/espcoredump/src/core_dump_common.c | 212 ++- components/espcoredump/src/core_dump_flash.c | 168 ++- components/espcoredump/src/core_dump_port.c | 116 +- 7 files changed, 1393 insertions(+), 335 deletions(-) mode change 100644 => 100755 components/espcoredump/include/esp_core_dump.h mode change 100644 => 100755 components/espcoredump/include_core_dump/esp_core_dump_priv.h mode change 100644 => 100755 components/espcoredump/src/core_dump_common.c mode change 100644 => 100755 components/espcoredump/src/core_dump_flash.c mode change 100644 => 100755 components/espcoredump/src/core_dump_port.c diff --git a/components/bt/hli_vectors.S b/components/bt/hli_vectors.S index 00f61337b6..cfc9112bfa 100644 --- a/components/bt/hli_vectors.S +++ b/components/bt/hli_vectors.S @@ -28,8 +28,11 @@ #define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE) .data + .global _l4_intr_stack + .global _l4_intr_stack_end _l4_intr_stack: .space L4_INTR_STACK_SIZE +_l4_intr_stack_end: _l4_save_ctx: .space REG_SAVE_AREA_SIZE diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 203fe3950f..2deef8d43c 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -5,6 +5,7 @@ from __future__ import print_function from __future__ import unicode_literals from __future__ import division +from hashlib import sha256 import sys try: from builtins import zip @@ -25,6 +26,7 @@ import errno import base64 import binascii import logging +import re idf_path = os.getenv('IDF_PATH') if idf_path: @@ -32,17 +34,56 @@ if idf_path: try: import esptool + 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) -__version__ = "0.3-dev" +__version__ = "0.4-dev" if os.name == 'nt': CLOSE_FDS = False else: CLOSE_FDS = True +INVALID_CAUSE_VALUE = 0xFFFF + +# Exception cause dictionary to get translation of exccause register +# From 4.4.1.5 table 4-64 Exception Causes of Xtensa +# Instruction Set Architecture (ISA) Reference Manual +xtensa_exception_cause_dict = { + 0: ("IllegalInstructionCause", "Illegal instruction"), + 1: ("SyscallCause", "SYSCALL instruction"), + 2: ("InstructionFetchErrorCause", "Processor internal physical address or data error during instruction fetch. (See EXCVADDR for more information)"), + 3: ("LoadStoreErrorCause", "Processor internal physical address or data error during load or store. (See EXCVADDR for more information)"), + 4: ("Level1InterruptCause", "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"), + 5: ("AllocaCause", "MOVSP instruction, if caller`s registers are not in the register file"), + 6: ("IntegerDivideByZeroCause", "QUOS: QUOU, REMS: or REMU divisor operand is zero"), + 8: ("PrivilegedCause", "Attempt to execute a privileged operation when CRING ? 0"), + 9: ("LoadStoreAlignmentCause", "Load or store to an unaligned address. (See EXCVADDR for more information)"), + 12: ("InstrPIFDataErrorCause", "PIF data error during instruction fetch. (See EXCVADDR for more information)"), + 13: ("LoadStorePIFDataErrorCause", "Synchronous PIF data error during LoadStore access. (See EXCVADDR for more information)"), + 14: ("InstrPIFAddrErrorCause", "PIF address error during instruction fetch. (See EXCVADDR for more information)"), + 15: ("LoadStorePIFAddrErrorCause", "Synchronous PIF address error during LoadStore access. (See EXCVADDR for more information)"), + 16: ("InstTLBMissCause", "Error during Instruction TLB refill. (See EXCVADDR for more information)"), + 17: ("InstTLBMultiHitCause", "Multiple instruction TLB entries matched. (See EXCVADDR for more information)"), + 18: ("InstFetchPrivilegeCause", "An instruction fetch referenced a virtual address at a ring level less than CRING. (See EXCVADDR for more information)"), + 20: ("InstFetchProhibitedCause", "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch (EXCVADDR)."), + 24: ("LoadStoreTLBMissCause", "Error during TLB refill for a load or store. (See EXCVADDR for more information)"), + 25: ("LoadStoreTLBMultiHitCause", "Multiple TLB entries matched for a load or store. (See EXCVADDR for more information)"), + 26: ("LoadStorePrivilegeCause", "A load or store referenced a virtual address at a ring level less than CRING. (See EXCVADDR for more information)"), + 28: ("LoadProhibitedCause", "A load referenced a page mapped with an attribute that does not permit loads. (See EXCVADDR for more information)"), + 29: ("StoreProhibitedCause", "A store referenced a page mapped with an attribute that does not permit stores [Region Protection Option or MMU Option]."), + 32: ("Coprocessor0Disabled", "Coprocessor 0 instruction when cp0 disabled"), + 33: ("Coprocessor1Disabled", "Coprocessor 1 instruction when cp1 disabled"), + 34: ("Coprocessor2Disabled", "Coprocessor 2 instruction when cp2 disabled"), + 35: ("Coprocessor3Disabled", "Coprocessor 3 instruction when cp3 disabled"), + 36: ("Coprocessor4Disabled", "Coprocessor 4 instruction when cp4 disabled"), + 37: ("Coprocessor5Disabled", "Coprocessor 5 instruction when cp5 disabled"), + 38: ("Coprocessor6Disabled", "Coprocessor 6 instruction when cp6 disabled"), + 39: ("Coprocessor7Disabled", "Coprocessor 7 instruction when cp7 disabled"), + INVALID_CAUSE_VALUE: ("InvalidCauseRegister", "Invalid EXCCAUSE register value or current task is broken and was skipped")} + class ESPCoreDumpError(RuntimeError): """Core dump runtime error class @@ -143,19 +184,31 @@ class Elf32NoteDesc(object): def __init__(self, name, type, desc): """Constructor for ELF32 note descriptor """ - self.name = bytearray(name, encoding='ascii') + b'\0' + self.name = name self.type = type self.desc = desc def dump(self): """Returns binary representation of ELF32 note descriptor """ - hdr = struct.pack(" 0: self._read_sections(f, shoff, shstrndx) if phnum > 0: @@ -373,7 +460,6 @@ class ESPCoreDumpElfFile(esptool.ELFFile): raise ESPCoreDumpError("No program header table found at offset %04x in ELF file." % seg_table_offs) if len(seg_table) % LEN_SEG_HEADER != 0: logging.warning('Unexpected ELF program header table length %04x is not mod-%02x' % (len(seg_table),LEN_SEG_HEADER)) - # walk through the program segment table and extract all segments seg_table_offs = range(0, len(seg_table), LEN_SEG_HEADER) @@ -387,8 +473,11 @@ class ESPCoreDumpElfFile(esptool.ELFFile): f.seek(offs) return f.read(size) + # read loadable 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.aux_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags) + for (type, offset, vaddr, filesz, flags) in prog_segments if type == ESPCoreDumpElfFile.PT_NOTE and vaddr == 0] def add_program_segment(self, addr, data, type, flags): """Adds new program segment @@ -408,6 +497,26 @@ class ESPCoreDumpElfFile(esptool.ELFFile): # append self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags)) + def add_aux_segment(self, data, type, flags): + """Adds new note segment + """ + self.aux_segments.append(ESPCoreDumpSegment(0, data, type, flags)) + + def write_program_headers(self, f, off, segs): + for seg in segs: + phdr = Elf32ProgramHeader() + phdr.p_type = seg.type + phdr.p_offset = off + phdr.p_vaddr = seg.addr + phdr.p_paddr = phdr.p_vaddr # TODO + phdr.p_filesz = len(seg.data) + phdr.p_memsz = phdr.p_filesz # TODO + phdr.p_flags = seg.flags + phdr.p_align = 0 # TODO + f.write(phdr.dump()) + off += phdr.p_filesz + return off + def dump(self, f): """Write core dump contents to file """ @@ -422,28 +531,21 @@ class ESPCoreDumpElfFile(esptool.ELFFile): ehdr.e_shoff = 0 ehdr.e_flags = 0 ehdr.e_phentsize = Elf32ProgramHeader().sizeof() - ehdr.e_phnum = len(self.program_segments) + ehdr.e_phnum = len(self.program_segments) + len(self.aux_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 - for i in range(len(self.program_segments)): - 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 - f.write(phdr.dump()) - cur_off += phdr.p_filesz + cur_off = self.write_program_headers(f, cur_off, self.program_segments) + cur_off = self.write_program_headers(f, cur_off, self.aux_segments) # write program segments - for i in range(len(self.program_segments)): - f.write(self.program_segments[i].data) + for segment in self.program_segments: + f.write(segment.data) + # write aux program segments + for segment in self.aux_segments: + f.write(segment.data) class ESPCoreDumpLoaderError(ESPCoreDumpError): @@ -455,23 +557,107 @@ class ESPCoreDumpLoaderError(ESPCoreDumpError): super(ESPCoreDumpLoaderError, self).__init__(message) -class ESPCoreDumpLoader(object): +class ESPCoreDumpVersion(object): + """Core dump version class + """ + # This class contains all version-dependent params + ESP_CORE_DUMP_CHIP_ESP32 = 0 + ESP_CORE_DUMP_CHIP_ESP32S2 = 2 + + def __init__(self, version=None): + """Constructor for core dump version + """ + super(ESPCoreDumpVersion, self).__init__() + if version is None: + self.version = 0 + else: + self.set_version(version) + + @staticmethod + def make_dump_ver(maj, min): + return (((maj & 0xFF) << 8) | ((min & 0xFF) << 0)) + + def set_version(self, version): + self.version = version + + @property + def chip_ver(self): + return ((self.version & 0xFFFF0000) >> 16) + + @property + def dump_ver(self): + return (self.version & 0x0000FFFF) + + @property + def major(self): + return ((self.version & 0x0000FF00) >> 8) + + @property + def minor(self): + return (self.version & 0x000000FF) + + +class ESPCoreDumpLoader(ESPCoreDumpVersion): """Core dump loader base class """ - ESP32_COREDUMP_VESION = 2 - ESP32_COREDUMP_HDR_FMT = '<4L' - ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) - ESP32_COREDUMP_TSK_HDR_FMT = '<3L' - ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT) - ESP32_COREDUMP_LOG_HDR_FMT = '<2L' - ESP32_COREDUMP_LOG_HDR_SZ = struct.calcsize(ESP32_COREDUMP_LOG_HDR_FMT) + # "legacy" stands for core dumps v0.1 (before IDF v4.1) + ESP_COREDUMP_VERSION_BIN_V1 = ESPCoreDumpVersion.make_dump_ver(0, 1) + ESP_COREDUMP_VERSION_BIN_V2 = ESPCoreDumpVersion.make_dump_ver(0, 2) + ESP_COREDUMP_VERSION_ELF_CRC32 = ESPCoreDumpVersion.make_dump_ver(1, 0) + ESP_COREDUMP_VERSION_ELF_SHA256 = ESPCoreDumpVersion.make_dump_ver(1, 1) + ESP_CORE_DUMP_INFO_TYPE = 8266 + ESP_CORE_DUMP_TASK_INFO_TYPE = 678 + ESP_CORE_DUMP_EXTRA_INFO_TYPE = 677 + ESP_COREDUMP_CURR_TASK_MARKER = 0xdeadbeef + ESP_COREDUMP_BIN_V1_HDR_FMT = '<4L' + ESP_COREDUMP_BIN_V1_HDR_SZ = struct.calcsize(ESP_COREDUMP_BIN_V1_HDR_FMT) + ESP_COREDUMP_HDR_FMT = '<5L' + ESP_COREDUMP_HDR_SZ = struct.calcsize(ESP_COREDUMP_HDR_FMT) + ESP_COREDUMP_TSK_HDR_FMT = '<3L' + ESP_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP_COREDUMP_TSK_HDR_FMT) + ESP_COREDUMP_MEM_SEG_HDR_FMT = '<2L' + ESP_COREDUMP_MEM_SEG_HDR_SZ = struct.calcsize(ESP_COREDUMP_MEM_SEG_HDR_FMT) + ESP_COREDUMP_NOTE_HDR_FMT = '<3L' + ESP_COREDUMP_NOTE_HDR_SZ = struct.calcsize(ESP_COREDUMP_NOTE_HDR_FMT) + ESP_COREDUMP_CRC_FMT = ' 0x40000000) + + @staticmethod + def stack_is_sane(sp): + """Check stack address if it is correct + """ + return ((sp >= 0x3F800000 and sp < 0x40000000) or + (sp >= ESPCoreDumpLoader.ESP_COREDUMP_FAKE_STACK_START and + sp < ESPCoreDumpLoader.ESP_COREDUMP_FAKE_STACK_LIMIT)) + + @staticmethod + def pc_is_sane(pc): + """Check PC if it is correct + """ + return not(pc < 0x40000000) + + @staticmethod + def correct_pc(pc): + """Corrects PC + """ + if pc & 0xC0000000: + pc = (pc & 0x3FFFFFFF) | 0x40000000 + return pc + + def addr_is_fake(self, addr): + """Check if address is in fake area + """ + return ((addr < 0x3f3fffff and addr >= 0x20000000) or addr >= 0x80000000) def remove_tmp_file(self, fname): """Silently removes temporary file @@ -579,99 +781,197 @@ class ESPCoreDumpLoader(object): if self.fcore_name: self.remove_tmp_file(self.fcore_name) - def create_corefile(self, core_fname=None, off=0, rom_elf=None): + def _extract_elf_corefile(self, core_fname=None, off=0, exe_name=None): + """ Reads the ELF formatted core dump image and parse it + """ + core_off = off + self.set_version(self.hdr['ver']) + if self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_CRC32: + checksum_len = self.ESP_COREDUMP_CRC_SZ + elif self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_SHA256: + checksum_len = self.ESP_COREDUMP_SHA256_SZ + else: + raise ESPCoreDumpLoaderError("Core dump version '%d' is not supported!" % self.dump_ver) + core_elf = ESPCoreDumpElfFile() + data = self.read_data(core_off, self.hdr['tot_len'] - checksum_len - self.ESP_COREDUMP_HDR_SZ) + with open(core_fname, 'w+b') as fce: + try: + fce.write(data) + fce.flush() + fce.seek(0) + core_elf._read_elf_file(fce) + if exe_name: + exe_elf = ESPCoreDumpElfFile(exe_name) + # Read note segments from core file which are belong to tasks (TCB or stack) + for ns in core_elf.aux_segments: + if ns.type != ESPCoreDumpElfFile.PT_NOTE: + continue + note_read = 0 + while note_read < len(ns.data): + note = Elf32NoteDesc("", 0, None) + note_read += note.read(ns.data[note_read:]) + # Check for version info note + if 'ESP_CORE_DUMP_INFO' == note.name and note.type == self.ESP_CORE_DUMP_INFO_TYPE and exe_name: + app_sha256 = binascii.hexlify(exe_elf.sha256()) + n_ver_len = struct.calcsize(" self.ESP32_COREDUMP_VESION: - raise ESPCoreDumpLoaderError("Core dump version '%d' is not supported! Should be up to '%d'." % (coredump_ver, self.ESP32_COREDUMP_VESION)) - tcbsz_aligned = tcbsz - if tcbsz_aligned % 4: - tcbsz_aligned = 4 * (old_div(tcbsz_aligned,4) + 1) - core_off += self.ESP32_COREDUMP_HDR_SZ - core_elf = ESPCoreDumpElfFile() - notes = b'' - for i in range(task_num): - data = self.read_data(core_off, self.ESP32_COREDUMP_TSK_HDR_SZ) - tcb_addr,stack_top,stack_end = struct.unpack_from(self.ESP32_COREDUMP_TSK_HDR_FMT, data) - if stack_end > stack_top: + with open(core_fname, 'w+b') as fce: + tcbsz_aligned = self.hdr['tcbsz'] + if tcbsz_aligned % 4: + tcbsz_aligned = 4 * (old_div(tcbsz_aligned,4) + 1) + core_elf = ESPCoreDumpElfFile() + notes = b'' + core_dump_info_notes = b'' + task_info_notes = b'' + task_status = EspCoreDumpTaskStatus() + for i in range(self.hdr['task_num']): + task_status.index = i + task_status.flags = EspCoreDumpTaskStatus.TASK_STATUS_CORRECT + task_regs = None + extra_regs = None + data = self.read_data(core_off, self.ESP_COREDUMP_TSK_HDR_SZ) + tcb_addr,stack_top,stack_end = struct.unpack_from(self.ESP_COREDUMP_TSK_HDR_FMT, data) stack_len = stack_end - stack_top stack_base = stack_top - else: - stack_len = stack_top - stack_end - stack_base = stack_end + stack_len_aligned = stack_len + if stack_len_aligned % 4: + stack_len_aligned = 4 * (old_div(stack_len_aligned,4) + 1) - stack_len_aligned = stack_len - if stack_len_aligned % 4: - stack_len_aligned = 4 * (old_div(stack_len_aligned,4) + 1) + core_off += self.ESP_COREDUMP_TSK_HDR_SZ + logging.debug("Read TCB %d bytes @ 0x%x" % (tcbsz_aligned, tcb_addr)) + data = self.read_data(core_off, tcbsz_aligned) + task_status.tcb_addr = tcb_addr + try: + if self.tcb_is_sane(tcb_addr, tcbsz_aligned): + if self.hdr['tcbsz'] != tcbsz_aligned: + core_elf.add_program_segment(tcb_addr, data[:self.hdr['tcbsz'] - tcbsz_aligned], + ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + else: + core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + elif tcb_addr and self.addr_is_fake(tcb_addr): + task_status.flags |= EspCoreDumpTaskStatus.TASK_STATUS_TCB_CORRUPTED + except ESPCoreDumpError as e: + logging.warning("Skip TCB %d bytes @ 0x%x. (Reason: %s)" % (tcbsz_aligned, tcb_addr, e)) - core_off += self.ESP32_COREDUMP_TSK_HDR_SZ - logging.info("Read TCB %d bytes @ 0x%x" % (tcbsz_aligned, tcb_addr)) - data = self.read_data(core_off, tcbsz_aligned) - try: - if tcbsz != tcbsz_aligned: - core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], - ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_off += tcbsz_aligned + task_status.stack_start = stack_base + task_status.stack_len = stack_len_aligned + if self.stack_is_sane(stack_base): + logging.debug("Read stack %d bytes @ 0x%x" % (stack_len_aligned, stack_base)) + data = self.read_data(core_off, stack_len_aligned) + if stack_len != stack_len_aligned: + data = data[:stack_len - stack_len_aligned] + try: + core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + except ESPCoreDumpError as e: + logging.warning("Skip task's (%x) stack %d bytes @ 0x%x. (Reason: %s)" % (tcb_addr, stack_len_aligned, stack_base, e)) + core_off += stack_len_aligned + try: + logging.debug("Stack start_end: 0x%x @ 0x%x" % (stack_top, stack_end)) + task_regs,extra_regs = self.get_registers_from_stack(data, stack_end > stack_top) + except Exception as e: + logging.error(e) + return None, None else: - core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) - except ESPCoreDumpError as e: - logging.warning("Skip TCB %d bytes @ 0x%x. (Reason: %s)" % (tcbsz_aligned, tcb_addr, e)) + task_status.flags |= EspCoreDumpTaskStatus.TASK_STATUS_STACK_CORRUPTED + logging.warning("Skip task's (%x) stack %d bytes @ 0x%x. (Reason: invalid address)" % (tcb_addr, stack_len_aligned, stack_base)) - core_off += tcbsz_aligned - logging.info("Read stack %d bytes @ 0x%x" % (stack_len_aligned, stack_base)) - data = self.read_data(core_off, stack_len_aligned) - if stack_len != stack_len_aligned: - data = data[:stack_len - stack_len_aligned] + task_info_notes += Elf32NoteDesc("TASK_INFO", self.ESP_CORE_DUMP_TASK_INFO_TYPE, task_status.dump()).dump() + if task_regs: + prstatus = XtensaPrStatus() + prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task + prstatus.pr_pid = tcb_addr + note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() + notes += note + if extra_regs and ESPCoreDumpElfFile.REG_EXCCAUSE_IDX in extra_regs and len(core_dump_info_notes) == 0: + # actually there will be only one such note - for crashed task + core_dump_info_notes += Elf32NoteDesc("ESP_CORE_DUMP_INFO", self.ESP_CORE_DUMP_INFO_TYPE, struct.pack(" stack_top) - except Exception as e: - print(e) - return None, None - 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() - notes += note - - # read log - data = self.read_data(core_off, self.ESP32_COREDUMP_LOG_HDR_SZ) - core_off += self.ESP32_COREDUMP_LOG_HDR_SZ - log_len, log_start = struct.unpack_from(self.ESP32_COREDUMP_LOG_HDR_FMT, data) - log_saved = self.read_data(core_off, log_len) - core_off += log_len - - # add notes - try: - core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0) - except ESPCoreDumpError as e: - logging.warning("Skip NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(notes), 0, e)) - # add ROM text sections - if rom_elf: - for ps in rom_elf.program_segments: - if ps.flags & ESPCoreDumpSegment.PF_X: + core_elf.add_aux_segment(core_dump_info_notes, ESPCoreDumpElfFile.PT_NOTE, 0) + except ESPCoreDumpError as e: + logging.warning("Skip core dump info NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(core_dump_info_notes), 0, e)) + try: + core_elf.add_aux_segment(task_info_notes, ESPCoreDumpElfFile.PT_NOTE, 0) + except ESPCoreDumpError as e: + logging.warning("Skip failed tasks info NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(task_info_notes), 0, e)) + # add ROM text sections + if rom_elf: + for ps in rom_elf.program_segments: + if (ps.flags & ESPCoreDumpSegment.PF_X) == 0: + continue try: core_elf.add_program_segment(ps.addr, ps.data, ESPCoreDumpElfFile.PT_LOAD, ps.flags) except ESPCoreDumpError as e: logging.warning("Skip ROM segment %d bytes @ 0x%x. (Reason: %s)" % (len(ps.data), ps.addr, e)) - - core_elf.e_type = ESPCoreDumpElfFile.ET_CORE - core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA - if core_fname: - fce = open(core_fname, 'wb') + # dump core ELF + core_elf.e_type = ESPCoreDumpElfFile.ET_CORE + core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA + core_elf.dump(fce) + if self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V2: + try: + log_hdr = self.read_data(core_off, self.ESP_COREDUMP_LOG_HDR_SZ) + core_off += self.ESP_COREDUMP_LOG_HDR_SZ + log_start, log_len = struct.unpack_from(self.ESP_COREDUMP_LOG_HDR_FMT, log_hdr) + log_dat = self.read_data(core_off, log_len) + except: + log_dat = None else: - fhnd,core_fname = tempfile.mkstemp() - fce = os.fdopen(fhnd, 'wb') - core_elf.dump(fce) - fce.close() - return core_fname, log_saved + log_dat = None + return core_fname, log_dat + + def create_corefile(self, core_fname=None, exe_name=None, rom_elf=None, off=0): + """Creates core dump ELF file + """ + data = self.read_data(off, self.ESP_COREDUMP_HDR_SZ) + vals = struct.unpack_from(self.ESP_COREDUMP_HDR_FMT, data) + self.hdr = dict(zip(('tot_len', 'ver', 'task_num', 'tcbsz', 'segs_num'), vals)) + if not core_fname: + fce = tempfile.NamedTemporaryFile(mode='w+b', delete=False) + core_fname = fce.name + self.set_version(self.hdr['ver']) + if self.chip_ver == ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32S2 or self.chip_ver == ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32: + if self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_CRC32 or self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_SHA256: + return self._extract_elf_corefile(core_fname, off + self.ESP_COREDUMP_HDR_SZ, exe_name) + elif self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V2: + return self._extract_bin_corefile(core_fname, rom_elf, off + self.ESP_COREDUMP_HDR_SZ) + elif self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V1: + return self._extract_bin_corefile(core_fname, rom_elf, off + self.ESP_COREDUMP_BIN_V1_HDR_SZ) + raise ESPCoreDumpLoaderError("Core dump version '0x%x' is not supported!" % (self.dump_ver)) + else: + raise ESPCoreDumpLoaderError("Core dump chip '0x%x' is not supported!" % (self.chip_ver)) def read_data(self, off, sz): """Reads data from raw core dump got from flash or UART @@ -693,6 +993,7 @@ class ESPCoreDumpFileLoader(ESPCoreDumpLoader): def _load_coredump(self, path, b64): """Loads core dump from (raw binary or base64-encoded) file """ + logging.debug("Load core dump from '%s'", path) self.fcore_name = None if b64: fhnd,self.fcore_name = tempfile.mkstemp() @@ -721,78 +1022,193 @@ class ESPCoreDumpFileLoader(ESPCoreDumpLoader): class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): """Core dump flash loader class """ - ESP32_COREDUMP_FLASH_CRC_FMT = ' part_size: + logging.error("Incorrect size of core dump image: %d, use partition size instead: %d", self.dump_sz, part_size) + self.dump_sz = part_size + # set actual size of core dump image and read it from flash + tool_args[-2] = str(self.dump_sz) et_out = subprocess.check_output(tool_args) - print(et_out.decode('utf-8')) + if len(et_out): + logging.info(et_out.decode('utf-8')) except subprocess.CalledProcessError as e: logging.error("esptool script execution failed with err %d" % e.returncode) - logging.info("Command ran: '%s'" % e.cmd) - logging.info("Command out:") - logging.info(e.output) + logging.debug("Command ran: '%s'" % e.cmd) + logging.debug("Command out:") + logging.debug(e.output) if self.fcore_name: + f.close() self.remove_tmp_file(self.fcore_name) raise e return f + def _load_coredump(self, off=None): + """Loads core dump from flash using parttool or elftool (if offset is set) + """ + tool_path = None + try: + if off: + tool_path = '' + logging.info("Invoke esptool to read image.") + f = self.invoke_esptool(tool_path=tool_path, off=off) + else: + tool_path = '' + logging.info("Invoke parttool to read image.") + f = self.invoke_parttool(tool_path=tool_path) + except subprocess.CalledProcessError as e: + if len(e.output): + logging.info(e.output) + logging.warning("System path is not set. Try to use predefined path.") + if off: + tool_path = self.get_tool_path(use_esptool=True) + f = self.invoke_esptool(tool_path=tool_path, off=off) + else: + tool_path = self.get_tool_path(use_esptool=False) + f = self.invoke_parttool(tool_path=tool_path) + return f + def _read_core_dump_length(self, f): """Reads core dump length """ - data = f.read(self.ESP32_COREDUMP_FLASH_LEN_SZ) - tot_len, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_LEN_FMT, data) + data = f.read(self.ESP_COREDUMP_FLASH_LEN_SZ) + tot_len, = struct.unpack_from(self.ESP_COREDUMP_FLASH_LEN_FMT, data) return tot_len - def create_corefile(self, core_fname=None, rom_elf=None): + def create_corefile(self, core_fname=None, exe_name=None, rom_elf=None): """Checks flash coredump data integrity and creates ELF file """ - data = self.read_data(self.dump_sz - self.ESP32_COREDUMP_FLASH_CRC_SZ, self.ESP32_COREDUMP_FLASH_CRC_SZ) - dump_crc, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_CRC_FMT, data) - data = self.read_data(0, self.dump_sz - self.ESP32_COREDUMP_FLASH_CRC_SZ) - data_crc = binascii.crc32(data) & 0xffffffff - if dump_crc != data_crc: - raise ESPCoreDumpLoaderError("Invalid core dump CRC %x, should be %x" % (data_crc, dump_crc)) - return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname) + data = self.read_data(0, self.ESP_COREDUMP_HDR_SZ) + self.checksum_len = 0 + _,coredump_ver_data,_,_,_ = struct.unpack_from(self.ESP_COREDUMP_HDR_FMT, data) + self.set_version(coredump_ver_data) + if self.chip_ver != ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32S2 and self.chip_ver != ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32: + raise ESPCoreDumpLoaderError("Invalid core dump chip version: '%s', should be <= '0x%x'" % (self.chip_ver, self.ESP_CORE_DUMP_CHIP_ESP32S2)) + if self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_CRC32 or self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V1 \ + or self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V2: + logging.debug("Dump size = %d, crc off = 0x%x", self.dump_sz, self.dump_sz - self.ESP_COREDUMP_CRC_SZ) + data = self.read_data(self.dump_sz - self.ESP_COREDUMP_CRC_SZ, self.ESP_COREDUMP_CRC_SZ) + dump_crc, = struct.unpack_from(self.ESP_COREDUMP_CRC_FMT, data) + data = self.read_data(0, self.dump_sz - self.ESP_COREDUMP_CRC_SZ) + data_crc = binascii.crc32(data) & 0xffffffff + if dump_crc != data_crc: + raise ESPCoreDumpLoaderError("Invalid core dump CRC %x, should be %x" % (data_crc, dump_crc)) + elif self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_SHA256: + dump_sha256 = self.read_data(self.dump_sz - self.ESP_COREDUMP_SHA256_SZ, self.ESP_COREDUMP_SHA256_SZ) + data = self.read_data(0, self.dump_sz - self.ESP_COREDUMP_SHA256_SZ) + data_sha256 = sha256(data) + data_sha256_str = data_sha256.hexdigest() + dump_sha256_str = binascii.hexlify(dump_sha256).decode('ascii') + if dump_sha256_str != data_sha256_str: + raise ESPCoreDumpLoaderError("Invalid core dump SHA256 '%s', should be '%s'" % (dump_sha256_str, data_sha256_str)) + return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname, exe_name) class GDBMIOutRecordHandler(object): @@ -809,7 +1225,7 @@ class GDBMIOutRecordHandler(object): """Base method to execute GDB/MI output record handler function """ if self.verbose: - print("%s.execute: [[%s]]" % (self.__class__.__name__, ln)) + logging.debug("%s.execute: [[%s]]" % (self.__class__.__name__, ln)) class GDBMIOutStreamHandler(GDBMIOutRecordHandler): @@ -844,26 +1260,25 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): """Constructor for GDB/MI result handler """ super(GDBMIResultHandler, self).__init__(None, verbose) - self.result_class = None - self.result_str = None + self.result_class = '' + self.result_str = '' def _parse_rc(self, ln, rc): """Parses result code """ 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: - logging.error("Invalid result format: '%s'" % ln) + if not ln.startswith(rc_str): + return False + self.result_class = rc + if len(ln) > len(rc_str): + self.result_str = ln[len(rc_str):] + if self.result_str.startswith(','): + self.result_str = self.result_str[1:] else: - self.result_str = '' - return True - return False + logging.error("Invalid result format: '%s'" % ln) + else: + self.result_str = '' + return True def execute(self, ln): """Executes GDB/MI result handler function @@ -882,6 +1297,163 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): logging.error("Unknown GDB/MI result: '%s'" % ln) +class GDBMIThreadListIdsHandler(GDBMIResultHandler): + """GDB/MI thread-list-ids handler class + """ + def __init__(self, verbose=False): + """Constructor for GDB/MI result handler + """ + super(GDBMIThreadListIdsHandler, self).__init__(verbose) + self.threads = [] + self.current_thread = '' + + def execute(self, ln): + """Executes GDB/MI thread-list-ids handler function + """ + GDBMIResultHandler.execute(self, ln) + if self.result_class != self.RC_DONE: + return + # simple parsing method + result = re.search(r'thread-ids\s*=\s*\{([^\{\}]*)\}', self.result_str) + if result: + for tid in re.finditer(r'thread-id="(\d+)"', result.group(1)): + self.threads.append(tid.group(1)) + result = re.search(r'current-thread-id="(\d+)"', self.result_str) + if result: + self.current_thread = result.group(1) + + +class GDBMIThreadSelectHandler(GDBMIResultHandler): + """GDB/MI thread-select handler class + """ + def execute(self, ln): + """Executes GDB/MI thread-select handler function + """ + GDBMIResultHandler.execute(self, ln) + if self.result_class != self.RC_DONE: + return + + +class GDBMIThreadInfoHandler(GDBMIResultHandler): + """GDB/MI thread-info handler class + """ + def __init__(self, verbose=False): + """Constructor for GDB/MI result handler + """ + super(GDBMIThreadInfoHandler, self).__init__(verbose) + self.current = False + self.id = '' + self.target_id = '' + self.details = '' + self.name = '' + self.frame = '' + self.state = '' + self.core = '' + + def execute(self, ln): + """Executes GDB/MI thread-info handler function + """ + GDBMIResultHandler.execute(self, ln) + if self.result_class != self.RC_DONE: + return + # simple parsing method + result = re.search(r'id="(\d+)"', self.result_str) + if result: + self.id = result.group(1) + result = re.search(r'current="\*"', self.result_str) + if result: + self.current = True + result = re.search(r'target-id="([^"]+)"', self.result_str) + if result: + self.target_id = result.group(1) + + +class GDBMIDataEvalHandler(GDBMIResultHandler): + """GDB/MI data-evaluate-expression handler class + """ + def __init__(self, verbose=False): + """Constructor for GDB/MI result handler + """ + super(GDBMIDataEvalHandler, self).__init__(verbose) + self.value = '' + + def execute(self, ln): + """Executes GDB/MI data-evaluate-expression handler function + """ + GDBMIResultHandler.execute(self, ln) + if self.result_class != self.RC_DONE: + return + # simple parsing method + if self.verbose: + logging.debug("GDBMIDataEvalHandler: result '%s'", self.result_str) + res_str = self.result_str.replace(r'\"', '\'') + m = re.search(r'(frame=\{.+\},)?value="(?P.+)"$', res_str) + if m: + if self.verbose: + logging.debug("GDBMIDataEvalHandler: found value = '%s'", m.group('val')) + self.value = m.group('val') + + +class GDBMIDataReadMemoryHandler(GDBMIResultHandler): + """GDB/MI data-read-memory handler class + """ + def __init__(self, verbose=False): + """Constructor for GDB/MI result handler + """ + super(GDBMIDataReadMemoryHandler, self).__init__(verbose) + self.addr = '' + self.nr_bytes = '' + self.total_bytes = '' + self.next_row = '' + self.prev_row = '' + self.next_page = '' + self.prev_page = '' + # list of {'addr': xxx, 'data': [xxx]} + self.memory = [] + + def execute(self, ln): + """Executes GDB/MI data-read-memory handler function + """ + GDBMIResultHandler.execute(self, ln) + if self.result_class != self.RC_DONE: + return + # simple parsing method + result = re.search(r'addr="(0x[0-9a-fA-F]+)"', self.result_str) + if result: + self.addr = result.group(1) + result = re.search(r'nr-bytes="(\d+)"', self.result_str) + if result: + self.nr_bytes = result.group(1) + result = re.search(r'total-bytes="(\d+)"', self.result_str) + if result: + self.total_bytes = result.group(1) + result = re.search(r'next-row="(0x[0-9a-fA-F]+)"', self.result_str) + if result: + self.next_row = result.group(1) + result = re.search(r'prev-row="(0x[0-9a-fA-F]+)"', self.result_str) + if result: + self.prev_row = result.group(1) + result = re.search(r'next-page="(0x[0-9a-fA-F]+)"', self.result_str) + if result: + self.next_page = result.group(1) + result = re.search(r'prev-page="(0x[0-9a-fA-F]+)"', self.result_str) + if result: + self.prev_page = result.group(1) + result = re.search(r'memory=\[\{(.*)\}\]', self.result_str) + if result: + mem_str = '{%s}' % result.group(1) + pos = 0 + r = re.compile(r'addr="(?P0x[0-9a-fA-F]+)",\s*data=\[(?P[^\[\]]*)\]') + while True: + result = r.search(mem_str, pos=pos) + if not result: + break + # make list and dequote values + row_data = [x[1:-1] for x in result.group('data').split(",")] + self.memory.append({'addr': result.group('addr'), 'data': row_data}) + pos = result.end('data') + 1 + + class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler): """GDB/MI console stream handler class """ @@ -908,8 +1480,8 @@ def dbg_corefile(args): 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, log_saved = loader.create_corefile(args.save_core, rom_elf=rom_elf) + loader = ESPCoreDumpFlashLoader(args.off, port=args.port, baud=args.baud) + core_fname, log_saved = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf) if not core_fname: logging.error("Failed to create corefile!") loader.cleanup() @@ -918,7 +1490,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, log_saved = loader.create_corefile(args.save_core, rom_elf=rom_elf) + core_fname, log_saved = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf) if not core_fname: logging.error("Failed to create corefile!") loader.cleanup() @@ -982,10 +1554,10 @@ def info_corefile(args): gdbmi_read2prompt(p.stdout, handlers) return p - def gdbmi_getinfo(p, handlers, gdb_cmd): + def gdbmi_cmd_exec(p, handlers, gdbmi_cmd): for t in handlers: handlers[t].result_class = None - p.stdin.write(bytearray("-interpreter-exec console \"%s\"\n" % gdb_cmd, encoding='utf-8')) + p.stdin.write(bytearray("%s\n" % gdbmi_cmd, encoding='utf-8')) gdbmi_read2prompt(p.stdout, handlers) if not handlers[GDBMIResultHandler.TAG].result_class or handlers[GDBMIResultHandler.TAG].result_class == GDBMIResultHandler.RC_EXIT: logging.error("GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)) @@ -996,11 +1568,113 @@ def info_corefile(args): logging.error("GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str)) return p + def gdbmi_getinfo(p, handlers, gdb_cmd): + return gdbmi_cmd_exec(p, handlers, "-interpreter-exec console \"%s\"" % gdb_cmd) + + def gdbmi_get_thread_ids(p): + handlers = {} + result = GDBMIThreadListIdsHandler(verbose=False) + handlers[GDBMIResultHandler.TAG] = result + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + p = gdbmi_cmd_exec(p, handlers, "-thread-list-ids") + return p,result.threads,result.current_thread + + def gdbmi_switch_thread(p, thr_id): + handlers = {} + result = GDBMIThreadSelectHandler(verbose=False) + handlers[GDBMIResultHandler.TAG] = result + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + return gdbmi_cmd_exec(p, handlers, "-thread-select %s" % thr_id) + + def gdbmi_get_thread_info(p, thr_id=None): + handlers = {} + result = GDBMIThreadInfoHandler(verbose=False) + handlers[GDBMIResultHandler.TAG] = result + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + if thr_id: + cmd = "-thread-info %s" % thr_id + else: + cmd = "-thread-info" + p = gdbmi_cmd_exec(p, handlers, cmd) + return p,result + + def gdbmi_data_evaluate_expression(p, expr): + handlers = {} + result = GDBMIDataEvalHandler(verbose=False) + handlers[GDBMIResultHandler.TAG] = result + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + p = gdbmi_cmd_exec(p, handlers, "-data-evaluate-expression \"%s\"" % expr) + return p,result + + def gdbmi_data_read_memory(p, addr, fmt, sz, nrows, ncols, off=0, aschar=False): + handlers = {} + result = GDBMIDataReadMemoryHandler(verbose=False) + handlers[GDBMIResultHandler.TAG] = result + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + p = gdbmi_cmd_exec(p, handlers, "-data-read-memory -o %d -- %s %s %d %d %d %s" % + (off, addr, fmt, sz, nrows, ncols, "aschar" if aschar else "")) + return p,result + + def gdbmi_freertos_get_task_name(p, tcb_addr): + p,res = gdbmi_data_evaluate_expression(p, "(char*)((TCB_t *)0x%x)->pcTaskName" % tcb_addr) + result = re.match("0x[a-fA-F0-9]+[^']*'([^']*)'", res.value) + if result: + return p,result.group(1) + return p,'' + + def gdb2freertos_thread_id(gdb_thread_id): + return int(gdb_thread_id.replace("process ", ""), 0) + + def gdbmi_get_reg(p, reg): + p,res = gdbmi_data_evaluate_expression(p, "(void *)$%s" % reg) + result = re.search(r'(?P0x[a-fA-F0-9]+)', res.value) + if result: + return p,int(result.group('val'),0) + return p,0 + + def gdbmi_print_backtrace(p, stack_addr=None): + if stack_addr: + p,res = gdbmi_data_read_memory(p, stack_addr, 'x', 4, 1, ESPCoreDumpLoader.XT_STK_FRMSZ) + if len(res.memory) == 0: + logging.error("Cannot read stack @ 0x%x!", stack_addr) + return p + stack = res.memory[0]['data'] + if int(stack[ESPCoreDumpLoader.XT_STK_EXIT], 0): + pc = int(stack[ESPCoreDumpLoader.XT_STK_PC], 0) + ra = int(stack[ESPCoreDumpLoader.XT_STK_AR_START + 0], 0) + sp = int(stack[ESPCoreDumpLoader.XT_STK_AR_START + 1], 0) + else: + pc = int(stack[ESPCoreDumpLoader.XT_SOL_PC], 0) + ra = int(stack[ESPCoreDumpLoader.XT_SOL_AR_START + 0], 0) + sp = int(stack[ESPCoreDumpLoader.XT_SOL_AR_START + 1], 0) + else: + p,sp = gdbmi_get_reg(p, 'sp') + p,pc = gdbmi_get_reg(p, 'pc') + p,ra = gdbmi_get_reg(p, 'a0') + pc = ESPCoreDumpLoader.correct_pc(pc) + bt_str = '0x%x:0x%x' % (pc, sp) + pc = ra + for i in range(100): + psp = sp + if not ESPCoreDumpLoader.stack_is_sane(sp): + break + p,res = gdbmi_data_evaluate_expression(p, "*((uint32_t *) (%d - 0x10 + 4))" % sp) + sp = int(res.value, 0) + pc = ESPCoreDumpLoader.correct_pc(pc) + bt_str += ' 0x%x:0x%x' % (pc - 3, sp) + p,res = gdbmi_data_evaluate_expression(p, "*((uint32_t *) (%d - 0x10))" % psp) + pc = int(res.value, 0) + if not ESPCoreDumpLoader.pc_is_sane(pc): + break + out_str = subprocess.check_output('%s -C -p -i -f -e %s %s' % (args.addr2line, args.prog, bt_str), shell=True) + print(out_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, log_saved = loader.create_corefile(args.save_core, rom_elf=rom_elf) + loader = ESPCoreDumpFlashLoader(args.off, port=args.port, baud=args.baud) + core_fname, log_saved = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf) if not core_fname: logging.error("Failed to create corefile!") loader.cleanup() @@ -1009,12 +1683,12 @@ 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, log_saved = loader.create_corefile(args.save_core, rom_elf=rom_elf) + core_fname, log_saved = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf) if not core_fname: logging.error("Failed to create corefile!") loader.cleanup() return - print("prog %s" % (args.prog)) + exe_elf = ESPCoreDumpElfFile(args.prog) core_elf = ESPCoreDumpElfFile(core_fname) merged_segs = [] @@ -1064,18 +1738,89 @@ def info_corefile(args): handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False) handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) p = gdbmi_start(handlers, [rom_sym_cmd]) - + extra_note = None + task_info = {} + for seg in core_elf.aux_segments: + if seg.type != ESPCoreDumpElfFile.PT_NOTE: + continue + note_read = 0 + while note_read < len(seg.data): + note = Elf32NoteDesc("", 0, None) + note_read += note.read(seg.data[note_read:]) + if note.type == ESPCoreDumpLoader.ESP_CORE_DUMP_EXTRA_INFO_TYPE and 'EXTRA_INFO' in note.name: + extra_note = note + if note.type == ESPCoreDumpLoader.ESP_CORE_DUMP_TASK_INFO_TYPE and 'TASK_INFO' in note.name: + task_info_desc = EspCoreDumpTaskStatus(buf=note.desc) + task_info[task_info_desc.tcb_addr] = task_info_desc print("===============================================================") print("==================== ESP32 CORE DUMP START ====================") handlers[GDBMIResultHandler.TAG].result_class = None handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler + if extra_note: + extra_info = struct.unpack("<%dL" % (len(extra_note.desc) / struct.calcsize("= level) { ets_printf(DRAM_STR(format), esp_log_early_timestamp(), (const char *)TAG, ##__VA_ARGS__); } @@ -33,7 +34,7 @@ #endif #define COREDUMP_MAX_TASK_STACK_SIZE (64*1024) -#define COREDUMP_VERSION 2 +#define COREDUMP_VERSION 0x0002 typedef uint32_t core_dump_crc_t; @@ -69,6 +70,7 @@ typedef struct _core_dump_header_t uint32_t version; // core dump struct version uint32_t tasks_num; // number of tasks uint32_t tcb_sz; // size of TCB + uint32_t mem_segs_num; // number of memory segments } core_dump_header_t; /** core dump task data header */ @@ -79,6 +81,13 @@ typedef struct _core_dump_task_header_t uint32_t stack_end; // stack end address } core_dump_task_header_t; +/** core dump memory segment header */ +typedef struct _core_dump_mem_seg_header_t +{ + uint32_t start; // memory region start address + uint32_t size; // memory region size +} core_dump_mem_seg_header_t; + typedef struct _core_dump_log_header_t { uint32_t len; //bytes @@ -97,14 +106,21 @@ void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg); // Gets RTOS tasks snapshot uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks, - const uint32_t snapshot_size, uint32_t* const tcb_sz); + const uint32_t snapshot_size); // Checks TCB consistency -bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz); +bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz); -bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz); +bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, bool *is_curr_task); bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length); +uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task_snapshot, uint32_t *stk_len); +bool esp_core_dump_check_in_bt_isr(int sp); + +#define esp_core_dump_in_isr_context() xPortInterruptedFromISRContext() +uint32_t esp_core_dump_get_isr_stack_end(void); + +#define COREDUMP_TCB_SIZE sizeof(StaticTask_t) bool esp_core_dump_process_log(core_dump_log_header_t *log); diff --git a/components/espcoredump/src/core_dump_common.c b/components/espcoredump/src/core_dump_common.c old mode 100644 new mode 100755 index c0fad97354..7a35755d2c --- a/components/espcoredump/src/core_dump_common.c +++ b/components/espcoredump/src/core_dump_common.c @@ -22,35 +22,99 @@ const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_commo #if CONFIG_ESP32_ENABLE_COREDUMP +static inline uint32_t esp_core_dump_get_tcb_len(void) +{ + if (COREDUMP_TCB_SIZE % sizeof(uint32_t)) { + return ((COREDUMP_TCB_SIZE / sizeof(uint32_t) + 1) * sizeof(uint32_t)); + } + return COREDUMP_TCB_SIZE; +} + +static inline uint32_t esp_core_dump_get_stack_len(uint32_t stack_start, uint32_t stack_end) +{ + uint32_t len = stack_end - stack_start; + // Take stack padding into account + return (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); +} + +static esp_err_t esp_core_dump_save_task(core_dump_write_config_t *write_cfg, + core_dump_task_header_t *task) +{ + uint32_t stk_paddr, stk_len; + + if (task->tcb_addr == 0) { + ESP_COREDUMP_LOG_PROCESS("Skip task with bad TCB addr!"); + return ESP_OK; // skip + } + ESP_COREDUMP_LOG_PROCESS("Dump task %x", task->tcb_addr); + int err = write_cfg->write(write_cfg->priv, task, sizeof(*task)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err); + return err; + } + // Save TCB + err = write_cfg->write(write_cfg->priv, task->tcb_addr, esp_core_dump_get_tcb_len()); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err); + return err; + } + stk_paddr = esp_core_dump_get_stack(task, &stk_len); + stk_len = esp_core_dump_get_stack_len(stk_paddr, stk_paddr+stk_len); + // Save task stack + err = write_cfg->write(write_cfg->priv, (void *)stk_paddr, stk_len); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err); + return err; + } + return ESP_OK; +} + +static esp_err_t esp_core_dump_save_mem_segment(core_dump_write_config_t* write_cfg, + core_dump_mem_seg_header_t* seg) +{ + esp_err_t err = ESP_FAIL; + + if (!esp_core_dump_mem_seg_is_sane(seg->start, seg->size)) { + ESP_COREDUMP_LOG_PROCESS("Skip invalid memory segment, (%x, %lu)!", + seg->start, seg->size); + return ESP_OK; + } + // Save mem segment header + err = write_cfg->write(write_cfg->priv, (void*)seg, sizeof(core_dump_mem_seg_header_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write memory segment header, error=%d!", err); + return err; + } + // Save memory contents + err = write_cfg->write(write_cfg->priv, (void*)seg->start, seg->size); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu), error=%d!", + seg->start, seg->size, err); + return err; + } + ESP_COREDUMP_LOG_PROCESS("Memory segment (%x, %lu) is saved.", + seg->start, seg->size); + return ESP_OK; +} + static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_t *write_cfg) { esp_err_t err; static core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM]; - uint32_t tcb_sz, task_num, tcb_sz_padded; - bool task_is_valid = false; - uint32_t data_len = 0, i; - union - { - core_dump_header_t hdr; - core_dump_task_header_t task_hdr; - } dump_data; - union { - uint32_t i32[128]; - char str[512]; - } iram_str; + uint32_t task_num, tcb_sz_padded; + bool task_is_valid = false, is_current_task; + uint32_t data_len = 0, i, curr_task_idx = (uint32_t)-1, good_task_idx = (uint32_t)-1; + core_dump_header_t hdr; + core_dump_mem_seg_header_t interrupted_task_stack = {.start = 0, .size = 0}; - task_num = esp_core_dump_get_tasks_snapshot(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM, &tcb_sz); + task_num = esp_core_dump_get_tasks_snapshot(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM); ESP_COREDUMP_LOGI("Found tasks: (%d)!", task_num); - // Take TCB padding into account, actual TCB size will be stored in header - if (tcb_sz % sizeof(uint32_t)) - tcb_sz_padded = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); - else - tcb_sz_padded = tcb_sz; + tcb_sz_padded = esp_core_dump_get_tcb_len(); // Verifies all tasks in the snapshot for (i = 0; i < task_num; i++) { - task_is_valid = esp_core_dump_process_tcb(frame, &tasks[i], tcb_sz); + task_is_valid = esp_core_dump_process_tcb(frame, &tasks[i], &is_current_task); // Check if task tcb is corrupted if (!task_is_valid) { write_cfg->bad_tasks_num++; @@ -58,22 +122,54 @@ static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_ } else { data_len += (tcb_sz_padded + sizeof(core_dump_task_header_t)); } + if (is_current_task) { + curr_task_idx = i; + ESP_COREDUMP_LOGI("Task #%d (TCB:%x) is crashed task.", + i, tasks[i].tcb_addr); + } uint32_t len = 0; task_is_valid = esp_core_dump_process_stack(&tasks[i], &len); if (task_is_valid) { // Increase core dump size by task stack size - data_len += len; + data_len += esp_core_dump_get_stack_len(tasks[i].stack_start, tasks[i].stack_end); + ESP_COREDUMP_LOG_PROCESS("Core dump stack,i = %d, len = %d data_len=%d", i, len, data_len); + good_task_idx = i; } else { // If task tcb is ok but stack is corrupted write_cfg->bad_tasks_num++; + data_len -= (tcb_sz_padded + sizeof(core_dump_task_header_t)); } } + if (curr_task_idx == (uint32_t)-1) { + curr_task_idx = good_task_idx; + } + assert((curr_task_idx != (uint32_t)-1) && "No any good task in the system!"); + XtExcFrame * exc_frame =(XtExcFrame *)frame; + if (esp_core_dump_in_isr_context() || esp_core_dump_check_in_bt_isr(exc_frame->a1)) { + interrupted_task_stack.start = tasks[curr_task_idx].stack_start; + interrupted_task_stack.size = esp_core_dump_get_stack_len(tasks[curr_task_idx].stack_start, tasks[curr_task_idx].stack_end); + // size of the task's stack has been already taken into account, also addresses have also been checked + data_len += sizeof(core_dump_mem_seg_header_t); + tasks[curr_task_idx].stack_start = (uint32_t)frame; + if (esp_core_dump_check_in_bt_isr(exc_frame->a1)) { + extern uint32_t _l4_intr_stack_end; + tasks[curr_task_idx].stack_end = &_l4_intr_stack_end; + } else { + tasks[curr_task_idx].stack_end = esp_core_dump_get_isr_stack_end(); + } + ESP_COREDUMP_LOG_PROCESS("Add ISR stack %lu bytes [%x..%x]", + tasks[curr_task_idx].stack_end - tasks[curr_task_idx].stack_start, + tasks[curr_task_idx].stack_start, tasks[curr_task_idx].stack_end); + // take into account size of the ISR stack + data_len += esp_core_dump_get_stack_len(tasks[curr_task_idx].stack_start, tasks[curr_task_idx].stack_end); + } + core_dump_log_header_t logs = { 0 }; if (esp_core_dump_process_log(&logs) == true) { data_len += (logs.len + sizeof(core_dump_log_header_t)); } - + ESP_COREDUMP_LOGI("LOG dump len = (%d %p)", logs.len, logs.start); // Add core dump header size data_len += sizeof(core_dump_header_t); ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu (%d %d)", data_len, task_num, write_cfg->bad_tasks_num); @@ -95,56 +191,56 @@ static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_ } } // Write header - dump_data.hdr.data_len = data_len; - dump_data.hdr.version = COREDUMP_VERSION; - dump_data.hdr.tasks_num = task_num - write_cfg->bad_tasks_num; - dump_data.hdr.tcb_sz = tcb_sz; - err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_header_t)); + hdr.data_len = data_len; + hdr.version = COREDUMP_VERSION; + hdr.tasks_num = task_num - write_cfg->bad_tasks_num; + hdr.tcb_sz = COREDUMP_TCB_SIZE; + hdr.mem_segs_num = 0; + if (interrupted_task_stack.size > 0) { + hdr.mem_segs_num++; // stack of interrupted task + } + err = write_cfg->write(write_cfg->priv, &hdr, sizeof(hdr)); if (err != ESP_OK) { ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err); return err; } + // save crashed task as the first one + err = esp_core_dump_save_task(write_cfg, &tasks[curr_task_idx]); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to save crashed task #%d (TCB:%x), error=%d!", + curr_task_idx, tasks[curr_task_idx].tcb_addr, err); + return err; + } // Write tasks for (i = 0; i < task_num; i++) { - if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].tcb_addr, tcb_sz)) { - ESP_COREDUMP_LOG_PROCESS("Skip TCB with bad addr %x!", tasks[i].tcb_addr); + if (i == curr_task_idx) continue; - } - ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].tcb_addr); - // Save TCB address, stack base and stack top addr - dump_data.task_hdr.tcb_addr = tasks[i].tcb_addr; - dump_data.task_hdr.stack_start = tasks[i].stack_start; - dump_data.task_hdr.stack_end = tasks[i].stack_end; - err = write_cfg->write(write_cfg->priv, (void*)&dump_data, sizeof(core_dump_task_header_t)); + err = esp_core_dump_save_task(write_cfg, &tasks[i]); if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err); + ESP_COREDUMP_LOGE("Failed to save core dump task #%d (TCB:%x), error=%d!", + i, tasks[i].tcb_addr, err); return err; } - // Save TCB - err = write_cfg->write(write_cfg->priv, tasks[i].tcb_addr, tcb_sz); + } + if (interrupted_task_stack.size > 0) { + err = esp_core_dump_save_mem_segment(write_cfg, &interrupted_task_stack); if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err); + ESP_COREDUMP_LOGE("Failed to save interrupted task stack, error=%d!", err); return err; } - // Save task stack - if (tasks[i].stack_start != 0 && tasks[i].stack_end != 0) { - err = write_cfg->write(write_cfg->priv, (void*)tasks[i].stack_start, - tasks[i].stack_end - tasks[i].stack_start); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err); - return err; - } - } else { - ESP_COREDUMP_LOG_PROCESS("Skip corrupted task %x stack!", tasks[i].tcb_addr); - } } - err = write_cfg->write(write_cfg->priv, (void*)&logs, sizeof(core_dump_log_header_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write LOG Header (%d)!", err); - return err; - } + union { + uint32_t i32[128]; + char str[512]; + } iram_str; + if (logs.len > 0 && logs.start != NULL) { + err = write_cfg->write(write_cfg->priv, (void*)&logs, sizeof(core_dump_log_header_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to save log, error=%d!", err); + return err; + } for (int i = 0; i < logs.len / 512; i++) { for (int j = 0; j < 128; j++) { if (i * 128 + j < logs.len / 4) { @@ -153,12 +249,6 @@ static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_ } err = write_cfg->write(write_cfg->priv, (void*)iram_str.str, 512); } - - // err = write_cfg->write(write_cfg->priv, (void*)logs.start, logs.len); - // if (err != ESP_OK) { - // ESP_COREDUMP_LOGE("Failed to write LOG (%d)!", err); - // return err; - // } } // write end diff --git a/components/espcoredump/src/core_dump_flash.c b/components/espcoredump/src/core_dump_flash.c old mode 100644 new mode 100755 index be6752db9e..32b5e4cb96 --- a/components/espcoredump/src/core_dump_flash.c +++ b/components/espcoredump/src/core_dump_flash.c @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #include +#include "esp_ota_ops.h" #include "rom/crc.h" #include "esp_partition.h" #include "esp_core_dump_priv.h" @@ -19,6 +20,18 @@ const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_flash"; #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#define DUER_COREDUMP_VALID_NUM 3 +#define DUER_COREDUMP_MAX_LEN 64*1024 +#define DUER_CRAB_ELF_SHA256_LEN 64 +#define DUER_CRAB_ELF_APPNAME_LEN 32 + +typedef struct _duer_crab_head_t +{ + uint32_t magic_num; + long crash_time; + char app_name[DUER_CRAB_ELF_APPNAME_LEN]; + char elf_sha256[DUER_CRAB_ELF_SHA256_LEN + 1]; +} __attribute__((__packed__)) duer_crab_head_t; typedef struct _core_dump_write_flash_data_t { @@ -40,18 +53,51 @@ typedef struct _core_dump_flash_config_t core_dump_partition_t partition; // CRC of core dump partition config core_dump_crc_t partition_config_crc; + /*add by zhangjiajie for multiple coredump*/ + uint8_t exist_coredump_num; + /*add end*/ } core_dump_flash_config_t; // core dump flash data static core_dump_flash_config_t s_core_flash_config; +duer_crab_head_t duer_crab_head; static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void) { return crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition)); } +static uint32_t esp_core_dump_read_coredump_num(char* valid_num) { + esp_err_t err; + uint32_t data32 = 0; + uint8_t coredump_num = 0; + + *valid_num = 0; + do { + err = spi_flash_read(s_core_flash_config.partition.start + coredump_num*DUER_COREDUMP_MAX_LEN, &data32, sizeof(uint32_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to read coredump head (%d)!", err); + break; + } + ESP_COREDUMP_LOGI("crab read head data=%x", data32); + if ((data32 & 0xffffff00) != 0xa5a5a500) { + break; + } + if ((data32 & 0xff) > DUER_COREDUMP_VALID_NUM) { + ESP_COREDUMP_LOGE("Coredump num err[%x]", valid_num); + break; + } + coredump_num ++; + } while (coredump_num < DUER_COREDUMP_VALID_NUM); + + *valid_num = coredump_num; + + return ESP_OK; +} + void esp_core_dump_flash_init() { + char valid_num = 0; const esp_partition_t *core_part; ESP_COREDUMP_LOGI("Init core dump to flash"); @@ -64,6 +110,22 @@ void esp_core_dump_flash_init() s_core_flash_config.partition.start = core_part->address; s_core_flash_config.partition.size = core_part->size; s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc(); + + /*add by zhangjiajie for multiple coredump*/ + memset(&duer_crab_head, 0, sizeof(duer_crab_head)); + + /*若只有1个coredump,coredump_num=1 */ + s_core_flash_config.exist_coredump_num = 0; + esp_core_dump_read_coredump_num(&valid_num); + s_core_flash_config.exist_coredump_num = valid_num; + + /*save current sha256,avoid access cache when crash*/ + esp_ota_get_app_elf_sha256(duer_crab_head.elf_sha256, sizeof(duer_crab_head.elf_sha256)); + ESP_COREDUMP_LOGI("crab sha256:%s", duer_crab_head.elf_sha256); + /*save current app name,avoid access cache when crash*/ + esp_crab_get_app_name(duer_crab_head.app_name, sizeof(duer_crab_head.app_name)); + ESP_COREDUMP_LOGI("crab appname:%s", duer_crab_head.app_name); + /*add end*/ } static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size) @@ -110,24 +172,31 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le { esp_err_t err; uint32_t sec_num; + int total_len = 0; core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; // check for available space in partition - if ((*data_len + sizeof(uint32_t)) > s_core_flash_config.partition.size) { - ESP_COREDUMP_LOGE("Not enough space to save core dump!"); + if ((*data_len + sizeof(uint32_t) + sizeof(uint32_t)) > DUER_COREDUMP_MAX_LEN) { + ESP_COREDUMP_LOGE("[%d]Not enough space to save core dump!", *data_len); return ESP_ERR_NO_MEM; } // add space for CRC *data_len += sizeof(core_dump_crc_t); + total_len = *data_len + sizeof(uint32_t); + ESP_COREDUMP_LOGE("total_len: (0x%x)!", total_len); - memset(wr_data, 0, sizeof(*wr_data)); +// memset(wr_data, 0, sizeof(*wr_data)); + /*offset add duer coredump head and sha256*/ + wr_data->off = DUER_COREDUMP_MAX_LEN * s_core_flash_config.exist_coredump_num + sizeof(duer_crab_head); + wr_data->crc = 0; - sec_num = *data_len / SPI_FLASH_SEC_SIZE; - if (*data_len % SPI_FLASH_SEC_SIZE) { + /*add coredump head len*/ + sec_num = total_len / SPI_FLASH_SEC_SIZE; + if (total_len % SPI_FLASH_SEC_SIZE) { sec_num++; } assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size); - err = spi_flash_erase_range(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE); + err = spi_flash_erase_range(s_core_flash_config.partition.start + DUER_COREDUMP_MAX_LEN * s_core_flash_config.exist_coredump_num, DUER_COREDUMP_MAX_LEN); if (err != ESP_OK) { ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err); return err; @@ -153,20 +222,22 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr static esp_err_t esp_core_dump_flash_write_start(void *priv) { + ets_printf(DRAM_STR("================= CORE DUMP START =================\r\n")); return ESP_OK; } static esp_err_t esp_core_dump_flash_write_end(void *priv) { + ets_printf(DRAM_STR("================= CORE DUMP END =================\r\n")); core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; #if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG union { - uint8_t data8[16]; - uint32_t data32[4]; + uint8_t data8[20]; + uint32_t data32[5]; } rom_data; - esp_err_t err = spi_flash_read(s_core_flash_config.partition.start + 0, &rom_data, sizeof(rom_data)); + esp_err_t err = spi_flash_read(s_core_flash_config.partition.start + s_core_flash_config.exist_coredump_num*DUER_COREDUMP_MAX_LEN, &rom_data, sizeof(rom_data)); if (err != ESP_OK) { ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err); return err; @@ -179,9 +250,71 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv) #endif // write core dump CRC ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x", wr_data->crc); - return esp_core_dump_flash_write_word(wr_data, wr_data->crc); + esp_err_t ret = esp_core_dump_flash_write_word(wr_data, wr_data->crc); + if (ESP_OK == ret) { + + duer_crab_head.magic_num = 0xa5a5a500 | (s_core_flash_config.exist_coredump_num + 1); + + ESP_COREDUMP_LOGI("crab magic num:0x%x", duer_crab_head.magic_num); + ESP_COREDUMP_LOGI("crab crash time:%d", duer_crab_head.crash_time); + ESP_COREDUMP_LOGI("crab app name:%s", duer_crab_head.app_name); + ESP_COREDUMP_LOGI("crab sha256:%s", duer_crab_head.elf_sha256); + + ret = spi_flash_write(s_core_flash_config.partition.start + s_core_flash_config.exist_coredump_num*DUER_COREDUMP_MAX_LEN, &duer_crab_head, sizeof(duer_crab_head)); + if (ESP_OK != ret) { + ESP_COREDUMP_LOGE("Failed to write coredump head to flash (%d)!", ret); + return ret; + } + + s_core_flash_config.exist_coredump_num++; + } + + return ret; } +static void baidu_esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { + const static char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i = 0, j = 0, a = 0, b = 0, c = 0; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 0x0F) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 0x3F]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} +static esp_err_t baidu_esp_core_dump_uart_write_data(void * data, uint32_t data_len) +{ + esp_err_t err = ESP_OK; + char buf[64 + 4], *addr = data; + char *end = addr + data_len; + + while (addr < end) { + size_t len = end - addr; + if (len > 48) len = 48; + /* Copy to stack to avoid alignment restrictions. */ + char *tmp = buf + (sizeof(buf) - len); + memcpy(tmp, addr, len); + baidu_esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); + addr += len; + ets_printf(DRAM_STR("%s\r\n"), buf); + } + + return err; +} static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) { esp_err_t err = ESP_OK; @@ -195,6 +328,8 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_ wr_data->off += len; wr_data->crc = crc32_le(wr_data->crc, data, data_len); + baidu_esp_core_dump_uart_write_data(data, data_len); + return err; } @@ -229,6 +364,19 @@ void esp_core_dump_to_flash(XtExcFrame *frame) wr_cfg.write = esp_core_dump_flash_write_data; wr_cfg.priv = &wr_data; + duer_crab_head.crash_time = 0; + + ESP_COREDUMP_LOGI("Exist Coredump num [%d]...", s_core_flash_config.exist_coredump_num); + if (DUER_COREDUMP_VALID_NUM < s_core_flash_config.exist_coredump_num) { + ESP_COREDUMP_LOGE("Coredump is full,exist_coredump_num=[%d]...", s_core_flash_config.exist_coredump_num); + return; + } + /*check that partition can hold next coredump*/ + if (s_core_flash_config.partition.size < (DUER_COREDUMP_MAX_LEN*(1 + s_core_flash_config.exist_coredump_num))) { + ESP_COREDUMP_LOGI("partition can not hold next coredump,num=[%d],flash size=[%d]...", s_core_flash_config.exist_coredump_num, s_core_flash_config.partition.size); + return; + } + ESP_COREDUMP_LOGI("Save core dump to flash..."); esp_core_dump_write((void*)frame, &wr_cfg); ESP_COREDUMP_LOGI("Core dump has been saved to flash."); diff --git a/components/espcoredump/src/core_dump_port.c b/components/espcoredump/src/core_dump_port.c old mode 100644 new mode 100755 index ba6af4f5c2..f509b704aa --- a/components/espcoredump/src/core_dump_port.c +++ b/components/espcoredump/src/core_dump_port.c @@ -15,41 +15,91 @@ #include #include "esp_panic.h" #include "esp_core_dump_priv.h" -#include "esp_core_dump.h" const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port"; #if CONFIG_ESP32_ENABLE_COREDUMP +#define COREDUMP_INVALID_CAUSE_VALUE 0xFFFF +#define COREDUMP_FAKE_STACK_START 0x20000000 +#define COREDUMP_FAKE_STACK_LIMIT 0x30000000 + +extern uint8_t port_IntStack; + +static XtExcFrame s_fake_stack_frame = { + .pc = (UBaseType_t) COREDUMP_FAKE_STACK_START, // task entrypoint fake_ptr + .a0 = (UBaseType_t) 0, // to terminate GDB backtrace + .a1 = (UBaseType_t) (COREDUMP_FAKE_STACK_START + sizeof(XtExcFrame)), // physical top of stack frame + .exit = (UBaseType_t) 0, // user exception exit dispatcher + .ps = (PS_UM | PS_EXCM), + .exccause = (UBaseType_t) COREDUMP_INVALID_CAUSE_VALUE, +}; + +uint32_t s_fake_stacks_num; + +_Static_assert(sizeof(TaskSnapshot_t) == sizeof(core_dump_task_header_t), "FreeRTOS task snapshot binary compatibility issue!"); + +// The function creates small fake stack for task as deep as exception frame size +// It is required for gdb to take task into account but avoid back trace of stack. +// The espcoredump.py script is able to recognize that task is broken +static void *esp_core_dump_get_fake_stack(uint32_t *stk_len) +{ + *stk_len = sizeof(s_fake_stack_frame); + return (uint8_t*)COREDUMP_FAKE_STACK_START + sizeof(s_fake_stack_frame)*s_fake_stacks_num++; +} + static inline bool esp_task_stack_start_is_sane(uint32_t sp) { return esp_ptr_in_dram((const void*)sp) || esp_ptr_external_ram((const void*)sp); } -inline bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz) +static inline bool esp_tcb_addr_is_sane(uint32_t addr) { - //TODO: currently core dump supports TCBs in DRAM only, external SRAM not supported yet - return !(addr < 0x3ffae000UL || (addr + sz) > 0x40000000UL); + return esp_core_dump_mem_seg_is_sane(addr, COREDUMP_TCB_SIZE); +} + +inline bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz) +{ + return (esp_ptr_in_dram((const void*)addr) && esp_ptr_in_dram((const void*)(addr + sz))) || + (esp_ptr_external_ram((const void*)addr) && esp_ptr_external_ram((const void*)(addr + sz))); +} + +inline uint32_t esp_core_dump_get_isr_stack_end(void) +{ + return (uint32_t)((uint8_t *)&port_IntStack + (xPortGetCoreID()+1)*configISR_STACK_SIZE); } uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks, - const uint32_t snapshot_size, uint32_t* const tcb_sz) + const uint32_t snapshot_size) { - uint32_t task_num = (uint32_t)uxTaskGetSnapshotAll((TaskSnapshot_t*)tasks, (UBaseType_t)snapshot_size, (UBaseType_t*)tcb_sz); + UBaseType_t tcb_sz; //unused + uint32_t task_num = (uint32_t)uxTaskGetSnapshotAll((TaskSnapshot_t*)tasks, (UBaseType_t)snapshot_size, &tcb_sz); return task_num; } -bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz) +bool esp_core_dump_check_in_bt_isr(int sp) +{ + extern uint32_t _l4_intr_stack, _l4_intr_stack_end; + return (sp > &_l4_intr_stack && sp < &_l4_intr_stack_end) ? true : false; +} + +bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, bool *is_curr_task) { XtExcFrame *exc_frame = (XtExcFrame*)frame; - if (!esp_tcb_addr_is_sane((uint32_t)task_snaphort->tcb_addr, tcb_sz)) { + *is_curr_task = false; + if (!esp_tcb_addr_is_sane((uint32_t)task_snaphort->tcb_addr)) { ESP_COREDUMP_LOG_PROCESS("Bad TCB addr %x!", task_snaphort->tcb_addr); + task_snaphort->tcb_addr = 0; return false; } if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { - // Set correct stack top for current task - task_snaphort->stack_start = (uint32_t)exc_frame; + *is_curr_task = true; + // Set correct stack top for current task; only modify if we came from the task, + // and not an ISR that crashed. + if (!xPortInterruptedFromISRContext() && !esp_core_dump_check_in_bt_isr(exc_frame->a1)) { + task_snaphort->stack_start = (uint32_t)exc_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 (exc_frame->exit == 0) @@ -77,44 +127,52 @@ bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snapho bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length) { uint32_t len = 0; - bool task_is_valid = false; + len = (uint32_t)task_snaphort->stack_end - (uint32_t)task_snaphort->stack_start; // Check task's stack if (!esp_stack_ptr_is_sane(task_snaphort->stack_start) || !esp_task_stack_start_is_sane((uint32_t)task_snaphort->stack_end) || + (task_snaphort->stack_end < task_snaphort->stack_start) || (len > COREDUMP_MAX_TASK_STACK_SIZE)) { // Check if current task stack corrupted if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { - ESP_COREDUMP_LOG_PROCESS("Crashed task will be skipped!"); + ESP_COREDUMP_LOG_PROCESS("Crashed task stack is corrupted!"); } ESP_COREDUMP_LOG_PROCESS("Corrupted TCB %x: stack len %lu, top %x, end %x!", task_snaphort->tcb_addr, len, task_snaphort->stack_start, task_snaphort->stack_end); - task_snaphort->tcb_addr = 0; // make TCB addr invalid to skip it in dump - task_is_valid = false; - } else { - ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, - task_snaphort->stack_start, task_snaphort->stack_end); - // Take stack padding into account - *length = (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); - task_is_valid = true; + task_snaphort->stack_start = (uint32_t)esp_core_dump_get_fake_stack(&len); + task_snaphort->stack_end = (uint32_t)(task_snaphort->stack_start + len); } - return task_is_valid; + ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, + task_snaphort->stack_start, task_snaphort->stack_end); + *length = len; + return true; } -static log_dump_get_len_t get_len = NULL; -static log_dump_get_ptr_t get_ptr = NULL; -bool esp_log_dump_init(log_dump_get_len_t g_len, log_dump_get_ptr_t g_ptr) +uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task_snapshot, uint32_t *stk_len) { - get_len = g_len; - get_ptr = g_ptr; + *stk_len = task_snapshot->stack_end - task_snapshot->stack_start; + if (task_snapshot->stack_start >= COREDUMP_FAKE_STACK_START && task_snapshot->stack_start < COREDUMP_FAKE_STACK_LIMIT) { + return (uint32_t)&s_fake_stack_frame; + } + return task_snapshot->stack_start; +} + +static uint32_t (*_get_len)(void) = NULL; +static int * (*_get_ptr)(void) = NULL; + +bool esp_log_dump_init(uint32_t (*get_len)(void), int * (*get_ptr)(void)) +{ + _get_len = get_len; + _get_ptr = get_ptr; return true; } bool esp_core_dump_process_log(core_dump_log_header_t *log) { - if (get_len && get_ptr) { - log->len = get_len(); - log->start = get_ptr(); + if (_get_len && _get_ptr) { + log->len = _get_len(); + log->start = _get_ptr(); return true; } else { log->len = 0;