mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-31 13:09:38 +00:00 
			
		
		
		
	feat(coredump): add esp32s2 and esp32c3 support
This commit is contained in:
		| @@ -1,5 +1,5 @@ | |||||||
| # | # | ||||||
| # Copyright 2021 Espressif Systems (Shanghai) PTE LTD | # Copyright 2021 Espressif Systems (Shanghai) CO., LTD | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| # you may not use this file except in compliance with the License. | # you may not use this file except in compliance with the License. | ||||||
| @@ -16,7 +16,22 @@ | |||||||
|  |  | ||||||
| __version__ = '0.4-dev' | __version__ = '0.4-dev' | ||||||
|  |  | ||||||
|  | import abc | ||||||
|  | import os | ||||||
| from abc import abstractmethod | from abc import abstractmethod | ||||||
|  | from importlib import import_module | ||||||
|  |  | ||||||
|  | from future.utils import with_metaclass | ||||||
|  |  | ||||||
|  | try: | ||||||
|  |     from typing import Optional, Tuple | ||||||
|  | except ImportError: | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  | IDF_PATH = os.path.normpath(os.getenv('IDF_PATH', '.')) | ||||||
|  | XTENSA_TARGETS = ['esp32', 'esp32s2'] | ||||||
|  | RISCV_TARGETS = ['esp32c3'] | ||||||
|  | SUPPORTED_TARGETS = XTENSA_TARGETS + RISCV_TARGETS | ||||||
|  |  | ||||||
|  |  | ||||||
| class ESPCoreDumpError(RuntimeError): | class ESPCoreDumpError(RuntimeError): | ||||||
| @@ -27,42 +42,84 @@ class ESPCoreDumpLoaderError(ESPCoreDumpError): | |||||||
|     pass |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
| class _TargetMethodsBase(object): | class BaseArchMethodsMixin(with_metaclass(abc.ABCMeta)):  # type: ignore | ||||||
|     @staticmethod |  | ||||||
|     @abstractmethod |  | ||||||
|     def tcb_is_sane(tcb_addr, tcb_size): |  | ||||||
|         """ |  | ||||||
|         Check tcb address if it is correct |  | ||||||
|         """ |  | ||||||
|         return False |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     @abstractmethod |  | ||||||
|     def stack_is_sane(sp): |  | ||||||
|         """ |  | ||||||
|         Check stack address if it is correct |  | ||||||
|         """ |  | ||||||
|         return False |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     @abstractmethod |  | ||||||
|     def addr_is_fake(addr): |  | ||||||
|         """ |  | ||||||
|         Check if address is in fake area |  | ||||||
|         """ |  | ||||||
|         return False |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class _ArchMethodsBase(object): |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     @abstractmethod |     @abstractmethod | ||||||
|     def get_registers_from_stack(data, grows_down): |     def get_registers_from_stack(data, grows_down): | ||||||
|  |         # type: (bytes, bool) -> Tuple[list[int], Optional[dict[int, int]]] | ||||||
|         """ |         """ | ||||||
|         Returns list of registers (in GDB format) from stack frame |         Parse stack data, growing up stacks are not supported for now. | ||||||
|  |         :param data: stack data | ||||||
|  |         :param grows_down: stack grow direction | ||||||
|  |         :return: return tuple (regs, exception_regs) | ||||||
|         """ |         """ | ||||||
|         return [], {} |         pass | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     @abstractmethod |     @abstractmethod | ||||||
|     def build_prstatus_data(tcb_addr, task_regs): |     def build_prstatus_data(tcb_addr, task_regs):  # type: (int, list[int]) -> str | ||||||
|         return b'' |         """ | ||||||
|  |         Build PrStatus note section | ||||||
|  |         :param tcb_addr: tcb addr | ||||||
|  |         :param task_regs: registers | ||||||
|  |         :return: str | ||||||
|  |         """ | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BaseTargetMethods(with_metaclass(abc.ABCMeta, BaseArchMethodsMixin)):  # type: ignore | ||||||
|  |     UNKNOWN = 'unknown' | ||||||
|  |     TARGET = UNKNOWN | ||||||
|  |  | ||||||
|  |     COREDUMP_FAKE_STACK_START = 0x20000000 | ||||||
|  |     COREDUMP_FAKE_STACK_LIMIT = 0x30000000 | ||||||
|  |     COREDUMP_MAX_TASK_STACK_SIZE = 64 * 1024 | ||||||
|  |  | ||||||
|  |     def __init__(self):  # type: () -> None | ||||||
|  |         if self.TARGET == self.UNKNOWN: | ||||||
|  |             raise ValueError('Please use the derived child-class with valid TARGET') | ||||||
|  |  | ||||||
|  |         self._set_attr_from_soc_header() | ||||||
|  |  | ||||||
|  |     def _set_attr_from_soc_header(self):  # type: () -> None | ||||||
|  |         module = import_module('corefile.soc_headers.{}'.format(self.TARGET)) | ||||||
|  |         for k, v in module.__dict__.items(): | ||||||
|  |             if k.startswith('SOC_'): | ||||||
|  |                 setattr(self, k, v) | ||||||
|  |  | ||||||
|  |     def _esp_ptr_in_dram(self, addr):  # type: (int) -> bool | ||||||
|  |         return self.SOC_DRAM_LOW <= addr < self.SOC_DRAM_HIGH  # type: ignore | ||||||
|  |  | ||||||
|  |     def _esp_ptr_in_iram(self, addr):  # type: (int) -> bool | ||||||
|  |         return self.SOC_IRAM_LOW <= addr < self.SOC_IRAM_HIGH  # type: ignore | ||||||
|  |  | ||||||
|  |     def _esp_ptr_in_rtc_slow(self, addr):  # type: (int) -> bool | ||||||
|  |         return self.SOC_RTC_DATA_LOW <= addr < self.SOC_RTC_DATA_HIGH  # type: ignore | ||||||
|  |  | ||||||
|  |     def _esp_ptr_in_rtc_dram_fast(self, addr):  # type: (int) -> bool | ||||||
|  |         return self.SOC_RTC_DRAM_LOW <= addr < self.SOC_RTC_DRAM_HIGH  # type: ignore | ||||||
|  |  | ||||||
|  |     def tcb_is_sane(self, tcb_addr, tcb_size):  # type: (int, int) -> bool | ||||||
|  |         for func in [self._esp_ptr_in_dram, | ||||||
|  |                      self._esp_ptr_in_iram, | ||||||
|  |                      self._esp_ptr_in_rtc_slow, | ||||||
|  |                      self._esp_ptr_in_rtc_dram_fast]: | ||||||
|  |             res = func(tcb_addr) and func(tcb_addr + tcb_size - 1) | ||||||
|  |             if res: | ||||||
|  |                 return True | ||||||
|  |         return False | ||||||
|  |  | ||||||
|  |     def _esp_stack_ptr_in_dram(self, addr):  # type: (int) -> bool | ||||||
|  |         return not (addr < self.SOC_DRAM_LOW + 0x10 | ||||||
|  |                     or addr > self.SOC_DRAM_HIGH - 0x10 | ||||||
|  |                     or (addr & 0xF) != 0) | ||||||
|  |  | ||||||
|  |     def stack_is_sane(self, stack_start, stack_end):  # type: (int, int) -> bool | ||||||
|  |         return (self._esp_stack_ptr_in_dram(stack_start) | ||||||
|  |                 and self._esp_ptr_in_dram(stack_end) | ||||||
|  |                 and stack_start < stack_end | ||||||
|  |                 and (stack_end - stack_start) < self.COREDUMP_MAX_TASK_STACK_SIZE) | ||||||
|  |  | ||||||
|  |     def addr_is_fake(self, addr):  # type: (int) -> bool | ||||||
|  |         return (self.COREDUMP_FAKE_STACK_START <= addr < self.COREDUMP_FAKE_STACK_LIMIT | ||||||
|  |                 or addr > 2 ** 31 - 1) | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								components/espcoredump/corefile/_parse_soc_header.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								components/espcoredump/corefile/_parse_soc_header.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | """ | ||||||
|  | This file is used to generate soc header constants into sub-package soc_headers | ||||||
|  | """ | ||||||
|  | import os | ||||||
|  | from ast import literal_eval | ||||||
|  |  | ||||||
|  | from . import IDF_PATH, SUPPORTED_TARGETS | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def main():  # type: () -> None | ||||||
|  |     constants = [ | ||||||
|  |         'SOC_DRAM_LOW', | ||||||
|  |         'SOC_DRAM_HIGH', | ||||||
|  |         'SOC_IRAM_LOW', | ||||||
|  |         'SOC_IRAM_HIGH', | ||||||
|  |         'SOC_RTC_DATA_LOW', | ||||||
|  |         'SOC_RTC_DATA_HIGH', | ||||||
|  |         'SOC_RTC_DRAM_LOW', | ||||||
|  |         'SOC_RTC_DRAM_HIGH', | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     for target in SUPPORTED_TARGETS: | ||||||
|  |         target_constants = {} | ||||||
|  |         soc_header_fp = os.path.join(IDF_PATH, 'components/soc/{}/include/soc/soc.h'.format(target)) | ||||||
|  |         module_fp = os.path.join(IDF_PATH, 'components', 'espcoredump', 'corefile', 'soc_headers', | ||||||
|  |                                  '{}.py'.format(target)) | ||||||
|  |  | ||||||
|  |         with open(soc_header_fp) as fr: | ||||||
|  |             for line in fr.readlines(): | ||||||
|  |                 for attr in constants: | ||||||
|  |                     if '#define {}'.format(attr) in line: | ||||||
|  |                         target_constants[attr] = literal_eval(line.strip().split()[-1]) | ||||||
|  |  | ||||||
|  |         for attr in constants: | ||||||
|  |             if attr not in target_constants: | ||||||
|  |                 raise ValueError('ERROR: Attr {} is missing in {}'.format(attr, soc_header_fp)) | ||||||
|  |  | ||||||
|  |         with open(module_fp, 'w') as fw: | ||||||
|  |             for k, v in target_constants.items(): | ||||||
|  |                 fw.write('{} = {}\n'.format(k, hex(v))) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main() | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| # | # | ||||||
| # Copyright 2021 Espressif Systems (Shanghai) PTE LTD | # Copyright 2021 Espressif Systems (Shanghai) CO., LTD | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| # you may not use this file except in compliance with the License. | # you may not use this file except in compliance with the License. | ||||||
| @@ -17,8 +17,13 @@ | |||||||
| import hashlib | import hashlib | ||||||
| import os | import os | ||||||
|  |  | ||||||
| from construct import (AlignedStruct, Bytes, Const, GreedyRange, Int16ul, Int32ul, Padding, Pointer, Sequence, Struct, | from construct import (AlignedStruct, Bytes, Const, Container, GreedyRange, Int16ul, Int32ul, Padding, Pointer, | ||||||
|                        this) |                        Sequence, Struct, this) | ||||||
|  |  | ||||||
|  | try: | ||||||
|  |     from typing import Optional | ||||||
|  | except ImportError: | ||||||
|  |     pass | ||||||
|  |  | ||||||
| # Following structs are based on spec | # Following structs are based on spec | ||||||
| # https://refspecs.linuxfoundation.org/elf/elf.pdf | # https://refspecs.linuxfoundation.org/elf/elf.pdf | ||||||
| @@ -110,12 +115,13 @@ class ElfFile(object): | |||||||
|     EV_CURRENT = 0x01 |     EV_CURRENT = 0x01 | ||||||
|  |  | ||||||
|     def __init__(self, elf_path=None, e_type=None, e_machine=None): |     def __init__(self, elf_path=None, e_type=None, e_machine=None): | ||||||
|  |         # type: (Optional[str], Optional[int], Optional[int]) -> None | ||||||
|         self.e_type = e_type |         self.e_type = e_type | ||||||
|         self.e_machine = e_machine |         self.e_machine = e_machine | ||||||
|  |  | ||||||
|         self._struct = None  # construct Struct |         self._struct = None  # type: Optional[Struct] | ||||||
|         self._model = None  # construct Container |         self._model = None  # type: Optional[Container] | ||||||
|         self._section_names = []  # type: list[str] |         self._section_names = {}  # type: dict[int, str] | ||||||
|  |  | ||||||
|         self.sections = []  # type: list[ElfSection] |         self.sections = []  # type: list[ElfSection] | ||||||
|         self.load_segments = []  # type: list[ElfSegment] |         self.load_segments = []  # type: list[ElfSegment] | ||||||
| @@ -171,7 +177,7 @@ class ElfFile(object): | |||||||
|                 name += c |                 name += c | ||||||
|         return res |         return res | ||||||
|  |  | ||||||
|     def _generate_struct_from_headers(self, header_tables): |     def _generate_struct_from_headers(self, header_tables):  # type: (Container) -> Struct | ||||||
|         """ |         """ | ||||||
|         Generate ``construct`` Struct for this file |         Generate ``construct`` Struct for this file | ||||||
|         :param header_tables: contains elf_header, program_headers, section_headers |         :param header_tables: contains elf_header, program_headers, section_headers | ||||||
| @@ -219,12 +225,12 @@ class ElfFile(object): | |||||||
|         return Struct(*args) |         return Struct(*args) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def sha256(self): |     def sha256(self):  # type: () -> bytes | ||||||
|         """ |         """ | ||||||
|         :return: SHA256 hash of the input ELF file |         :return: SHA256 hash of the input ELF file | ||||||
|         """ |         """ | ||||||
|         sha256 = hashlib.sha256() |         sha256 = hashlib.sha256() | ||||||
|         sha256.update(self._struct.build(self._model)) |         sha256.update(self._struct.build(self._model))  # type: ignore | ||||||
|         return sha256.digest() |         return sha256.digest() | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -234,13 +240,13 @@ class ElfSection(object): | |||||||
|     SHF_EXECINSTR = 0x04 |     SHF_EXECINSTR = 0x04 | ||||||
|     SHF_MASKPROC = 0xf0000000 |     SHF_MASKPROC = 0xf0000000 | ||||||
|  |  | ||||||
|     def __init__(self, name, addr, data, flags): |     def __init__(self, name, addr, data, flags):  # type: (str, int, bytes, int) -> None | ||||||
|         self.name = name |         self.name = name | ||||||
|         self.addr = addr |         self.addr = addr | ||||||
|         self.data = data |         self.data = data | ||||||
|         self.flags = flags |         self.flags = flags | ||||||
|  |  | ||||||
|     def attr_str(self): |     def attr_str(self):  # type: () -> str | ||||||
|         if self.flags & self.SHF_MASKPROC: |         if self.flags & self.SHF_MASKPROC: | ||||||
|             return 'MS' |             return 'MS' | ||||||
|  |  | ||||||
| @@ -250,7 +256,7 @@ class ElfSection(object): | |||||||
|         res += 'A' if self.flags & self.SHF_ALLOC else ' ' |         res += 'A' if self.flags & self.SHF_ALLOC else ' ' | ||||||
|         return res |         return res | ||||||
|  |  | ||||||
|     def __repr__(self): |     def __repr__(self):  # type: () -> str | ||||||
|         return '{:>32} [Addr] 0x{:>08X}, [Size] 0x{:>08X} {:>4}' \ |         return '{:>32} [Addr] 0x{:>08X}, [Size] 0x{:>08X} {:>4}' \ | ||||||
|             .format(self.name, self.addr, len(self.data), self.attr_str()) |             .format(self.name, self.addr, len(self.data), self.attr_str()) | ||||||
|  |  | ||||||
| @@ -260,13 +266,13 @@ class ElfSegment(object): | |||||||
|     PF_W = 0x02 |     PF_W = 0x02 | ||||||
|     PF_R = 0x04 |     PF_R = 0x04 | ||||||
|  |  | ||||||
|     def __init__(self, addr, data, flags): |     def __init__(self, addr, data, flags):  # type: (int, bytes, int) -> None | ||||||
|         self.addr = addr |         self.addr = addr | ||||||
|         self.data = data |         self.data = data | ||||||
|         self.flags = flags |         self.flags = flags | ||||||
|         self.type = ElfFile.PT_LOAD |         self.type = ElfFile.PT_LOAD | ||||||
|  |  | ||||||
|     def attr_str(self): |     def attr_str(self):  # type: () -> str | ||||||
|         res = '' |         res = '' | ||||||
|         res += 'R' if self.flags & self.PF_R else ' ' |         res += 'R' if self.flags & self.PF_R else ' ' | ||||||
|         res += 'W' if self.flags & self.PF_W else ' ' |         res += 'W' if self.flags & self.PF_W else ' ' | ||||||
| @@ -274,22 +280,22 @@ class ElfSegment(object): | |||||||
|         return res |         return res | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _type_str(): |     def _type_str():  # type: () -> str | ||||||
|         return 'LOAD' |         return 'LOAD' | ||||||
|  |  | ||||||
|     def __repr__(self): |     def __repr__(self):  # type: () -> str | ||||||
|         return '{:>8} Addr 0x{:>08X}, Size 0x{:>08X} Flags {:4}' \ |         return '{:>8} Addr 0x{:>08X}, Size 0x{:>08X} Flags {:4}' \ | ||||||
|             .format(self._type_str(), self.addr, len(self.data), self.attr_str()) |             .format(self._type_str(), self.addr, len(self.data), self.attr_str()) | ||||||
|  |  | ||||||
|  |  | ||||||
| class ElfNoteSegment(ElfSegment): | class ElfNoteSegment(ElfSegment): | ||||||
|     def __init__(self, addr, data, flags): |     def __init__(self, addr, data, flags):  # type: (int, bytes, int) -> None | ||||||
|         super(ElfNoteSegment, self).__init__(addr, data, flags) |         super(ElfNoteSegment, self).__init__(addr, data, flags) | ||||||
|         self.type = ElfFile.PT_NOTE |         self.type = ElfFile.PT_NOTE | ||||||
|         self.note_secs = NoteSections.parse(self.data) |         self.note_secs = NoteSections.parse(self.data) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _type_str(): |     def _type_str():  # type: () -> str | ||||||
|         return 'NOTE' |         return 'NOTE' | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -316,13 +322,15 @@ class ESPCoreDumpElfFile(ElfFile): | |||||||
|  |  | ||||||
|     # ELF file machine type |     # ELF file machine type | ||||||
|     EM_XTENSA = 0x5E |     EM_XTENSA = 0x5E | ||||||
|  |     EM_RISCV = 0xF3 | ||||||
|  |  | ||||||
|     def __init__(self, elf_path=None, e_type=None, e_machine=None): |     def __init__(self, elf_path=None, e_type=None, e_machine=None): | ||||||
|  |         # type: (Optional[str], Optional[int], Optional[int]) -> None | ||||||
|         _e_type = e_type or self.ET_CORE |         _e_type = e_type or self.ET_CORE | ||||||
|         _e_machine = e_machine or self.EM_XTENSA |         _e_machine = e_machine or self.EM_XTENSA | ||||||
|         super(ESPCoreDumpElfFile, self).__init__(elf_path, _e_type, _e_machine) |         super(ESPCoreDumpElfFile, self).__init__(elf_path, _e_type, _e_machine) | ||||||
|  |  | ||||||
|     def add_segment(self, addr, data, seg_type, flags): |     def add_segment(self, addr, data, seg_type, flags):  # type: (int, bytes, int, int) -> None | ||||||
|         if seg_type != self.PT_NOTE: |         if seg_type != self.PT_NOTE: | ||||||
|             self.load_segments.append(ElfSegment(addr, data, flags)) |             self.load_segments.append(ElfSegment(addr, data, flags)) | ||||||
|         else: |         else: | ||||||
| @@ -352,7 +360,7 @@ class ESPCoreDumpElfFile(ElfFile): | |||||||
|         }) |         }) | ||||||
|  |  | ||||||
|         offset = ElfHeader.sizeof() + (len(self.load_segments) + len(self.note_segments)) * ProgramHeader.sizeof() |         offset = ElfHeader.sizeof() + (len(self.load_segments) + len(self.note_segments)) * ProgramHeader.sizeof() | ||||||
|         _segments = self.load_segments + self.note_segments |         _segments = self.load_segments + self.note_segments  # type: ignore | ||||||
|         for seg in _segments: |         for seg in _segments: | ||||||
|             res += ProgramHeader.build({ |             res += ProgramHeader.build({ | ||||||
|                 'p_type': seg.type, |                 'p_type': seg.type, | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| # | # | ||||||
| # Copyright 2021 Espressif Systems (Shanghai) PTE LTD | # Copyright 2021 Espressif Systems (Shanghai) CO., LTD | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| # you may not use this file except in compliance with the License. | # you may not use this file except in compliance with the License. | ||||||
| @@ -18,19 +18,14 @@ import logging | |||||||
| import re | import re | ||||||
| import time | import time | ||||||
|  |  | ||||||
| from . import ESPCoreDumpError |  | ||||||
|  |  | ||||||
| try: |  | ||||||
|     import typing |  | ||||||
| except ImportError: |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| from pygdbmi.gdbcontroller import DEFAULT_GDB_TIMEOUT_SEC, GdbController | from pygdbmi.gdbcontroller import DEFAULT_GDB_TIMEOUT_SEC, GdbController | ||||||
|  |  | ||||||
|  | from . import ESPCoreDumpError | ||||||
|  |  | ||||||
|  |  | ||||||
| class EspGDB(object): | class EspGDB(object): | ||||||
|     def __init__(self, gdb_path, gdb_cmds, core_filename, prog_filename, timeout_sec=DEFAULT_GDB_TIMEOUT_SEC): |     def __init__(self, gdb_path, gdb_cmds, core_filename, prog_filename, timeout_sec=DEFAULT_GDB_TIMEOUT_SEC): | ||||||
|         # type: (str, typing.List[str], str, str, int) -> None |  | ||||||
|         """ |         """ | ||||||
|         Start GDB and initialize a GdbController instance |         Start GDB and initialize a GdbController instance | ||||||
|         """ |         """ | ||||||
| @@ -59,7 +54,7 @@ class EspGDB(object): | |||||||
|  |  | ||||||
|     def _gdbmi_run_cmd_get_responses(self, cmd, resp_message, resp_type, multiple=True, |     def _gdbmi_run_cmd_get_responses(self, cmd, resp_message, resp_type, multiple=True, | ||||||
|                                      done_message=None, done_type=None): |                                      done_message=None, done_type=None): | ||||||
|         # type: (str, typing.Optional[str], str, bool, typing.Optional[str], typing.Optional[str]) -> list |  | ||||||
|         self.p.write(cmd, read_response=False) |         self.p.write(cmd, read_response=False) | ||||||
|         t_end = time.time() + self.timeout |         t_end = time.time() + self.timeout | ||||||
|         filtered_response_list = [] |         filtered_response_list = [] | ||||||
| @@ -80,15 +75,15 @@ class EspGDB(object): | |||||||
|         return filtered_response_list |         return filtered_response_list | ||||||
|  |  | ||||||
|     def _gdbmi_run_cmd_get_one_response(self, cmd, resp_message, resp_type): |     def _gdbmi_run_cmd_get_one_response(self, cmd, resp_message, resp_type): | ||||||
|         # type: ( str, typing.Optional[str], str) -> dict |  | ||||||
|         return self._gdbmi_run_cmd_get_responses(cmd, resp_message, resp_type, multiple=False)[0] |         return self._gdbmi_run_cmd_get_responses(cmd, resp_message, resp_type, multiple=False)[0] | ||||||
|  |  | ||||||
|     def _gdbmi_data_evaluate_expression(self, expr):  # type: (str) -> str |     def _gdbmi_data_evaluate_expression(self, expr): | ||||||
|         """ Get the value of an expression, similar to the 'print' command """ |         """ Get the value of an expression, similar to the 'print' command """ | ||||||
|         return self._gdbmi_run_cmd_get_one_response("-data-evaluate-expression \"%s\"" % expr, |         return self._gdbmi_run_cmd_get_one_response("-data-evaluate-expression \"%s\"" % expr, | ||||||
|                                                     'done', 'result')['payload']['value'] |                                                     'done', 'result')['payload']['value'] | ||||||
|  |  | ||||||
|     def get_freertos_task_name(self, tcb_addr):  # type: (int) -> str |     def get_freertos_task_name(self, tcb_addr): | ||||||
|         """ Get FreeRTOS task name given the TCB address """ |         """ Get FreeRTOS task name given the TCB address """ | ||||||
|         try: |         try: | ||||||
|             val = self._gdbmi_data_evaluate_expression('(char*)((TCB_t *)0x%x)->pcTaskName' % tcb_addr) |             val = self._gdbmi_data_evaluate_expression('(char*)((TCB_t *)0x%x)->pcTaskName' % tcb_addr) | ||||||
| @@ -102,7 +97,7 @@ class EspGDB(object): | |||||||
|             return result.group(1) |             return result.group(1) | ||||||
|         return '' |         return '' | ||||||
|  |  | ||||||
|     def run_cmd(self, gdb_cmd):  # type: (str) -> str |     def run_cmd(self, gdb_cmd): | ||||||
|         """ Execute a generic GDB console command via MI2 |         """ Execute a generic GDB console command via MI2 | ||||||
|         """ |         """ | ||||||
|         filtered_responses = self._gdbmi_run_cmd_get_responses(cmd="-interpreter-exec console \"%s\"" % gdb_cmd, |         filtered_responses = self._gdbmi_run_cmd_get_responses(cmd="-interpreter-exec console \"%s\"" % gdb_cmd, | ||||||
| @@ -113,14 +108,14 @@ class EspGDB(object): | |||||||
|             .replace('\\t', '\t') \ |             .replace('\\t', '\t') \ | ||||||
|             .rstrip('\n') |             .rstrip('\n') | ||||||
|  |  | ||||||
|     def get_thread_info(self):  # type: () -> (typing.List[dict], str) |     def get_thread_info(self): | ||||||
|         """ Get information about all threads known to GDB, and the current thread ID """ |         """ Get information about all threads known to GDB, and the current thread ID """ | ||||||
|         result = self._gdbmi_run_cmd_get_one_response('-thread-info', 'done', 'result')['payload'] |         result = self._gdbmi_run_cmd_get_one_response('-thread-info', 'done', 'result')['payload'] | ||||||
|         current_thread_id = result['current-thread-id'] |         current_thread_id = result['current-thread-id'] | ||||||
|         threads = result['threads'] |         threads = result['threads'] | ||||||
|         return threads, current_thread_id |         return threads, current_thread_id | ||||||
|  |  | ||||||
|     def switch_thread(self, thr_id):  # type: (int) -> None |     def switch_thread(self, thr_id): | ||||||
|         """ Tell GDB to switch to a specific thread, given its ID """ |         """ Tell GDB to switch to a specific thread, given its ID """ | ||||||
|         self._gdbmi_run_cmd_get_one_response('-thread-select %s' % thr_id, 'done', 'result') |         self._gdbmi_run_cmd_get_one_response('-thread-select %s' % thr_id, 'done', 'result') | ||||||
|  |  | ||||||
| @@ -129,6 +124,6 @@ class EspGDB(object): | |||||||
|         return list(filter(lambda rsp: rsp['message'] == resp_message and rsp['type'] == resp_type, responses)) |         return list(filter(lambda rsp: rsp['message'] == resp_message and rsp['type'] == resp_type, responses)) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def gdb2freertos_thread_id(gdb_target_id):  # type: (str) -> int |     def gdb2freertos_thread_id(gdb_target_id): | ||||||
|         """ Convert GDB 'target ID' to the FreeRTOS TCB address """ |         """ Convert GDB 'target ID' to the FreeRTOS TCB address """ | ||||||
|         return int(gdb_target_id.replace('process ', ''), 0) |         return int(gdb_target_id.replace('process ', ''), 0) | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| # | # | ||||||
| # Copyright 2021 Espressif Systems (Shanghai) PTE LTD | # Copyright 2021 Espressif Systems (Shanghai) CO., LTD | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| # you may not use this file except in compliance with the License. | # you may not use this file except in compliance with the License. | ||||||
| @@ -25,12 +25,18 @@ import tempfile | |||||||
|  |  | ||||||
| from construct import AlignedStruct, Bytes, GreedyRange, Int32ul, Padding, Struct, abs_, this | from construct import AlignedStruct, Bytes, GreedyRange, Int32ul, Padding, Struct, abs_, this | ||||||
|  |  | ||||||
| from . import ESPCoreDumpLoaderError, _ArchMethodsBase, _TargetMethodsBase | from . import ESPCoreDumpLoaderError | ||||||
| from .elf import (TASK_STATUS_CORRECT, TASK_STATUS_TCB_CORRUPTED, ElfFile, ElfSegment, ESPCoreDumpElfFile, | from .elf import (TASK_STATUS_CORRECT, TASK_STATUS_TCB_CORRUPTED, ElfFile, ElfSegment, ESPCoreDumpElfFile, | ||||||
|                   EspTaskStatus, NoteSection) |                   EspTaskStatus, NoteSection) | ||||||
| from .xtensa import _ArchMethodsXtensa, _TargetMethodsESP32 | from .riscv import Esp32c3Methods | ||||||
|  | from .xtensa import Esp32Methods, Esp32S2Methods | ||||||
|  |  | ||||||
| IDF_PATH = os.getenv('IDF_PATH') | try: | ||||||
|  |     from typing import Optional, Tuple | ||||||
|  | except ImportError: | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  | IDF_PATH = os.getenv('IDF_PATH', '') | ||||||
| PARTTOOL_PY = os.path.join(IDF_PATH, 'components', 'partition_table', 'parttool.py') | PARTTOOL_PY = os.path.join(IDF_PATH, 'components', 'partition_table', 'parttool.py') | ||||||
| ESPTOOL_PY = os.path.join(IDF_PATH, 'components', 'esptool_py', 'esptool', 'esptool.py') | ESPTOOL_PY = os.path.join(IDF_PATH, 'components', 'esptool_py', 'esptool', 'esptool.py') | ||||||
|  |  | ||||||
| @@ -74,12 +80,14 @@ class EspCoreDumpVersion(object): | |||||||
|     # This class contains all version-dependent params |     # This class contains all version-dependent params | ||||||
|     ESP32 = 0 |     ESP32 = 0 | ||||||
|     ESP32S2 = 2 |     ESP32S2 = 2 | ||||||
|  |  | ||||||
|     XTENSA_CHIPS = [ESP32, ESP32S2] |     XTENSA_CHIPS = [ESP32, ESP32S2] | ||||||
|  |  | ||||||
|     ESP_COREDUMP_TARGETS = XTENSA_CHIPS |     ESP32C3 = 5 | ||||||
|  |     RISCV_CHIPS = [ESP32C3] | ||||||
|  |  | ||||||
|     def __init__(self, version=None): |     COREDUMP_SUPPORTED_TARGETS = XTENSA_CHIPS + RISCV_CHIPS | ||||||
|  |  | ||||||
|  |     def __init__(self, version=None):  # type: (int) -> None | ||||||
|         """Constructor for core dump version |         """Constructor for core dump version | ||||||
|         """ |         """ | ||||||
|         super(EspCoreDumpVersion, self).__init__() |         super(EspCoreDumpVersion, self).__init__() | ||||||
| @@ -89,26 +97,26 @@ class EspCoreDumpVersion(object): | |||||||
|             self.set_version(version) |             self.set_version(version) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def make_dump_ver(major, minor): |     def make_dump_ver(major, minor):  # type: (int, int) -> int | ||||||
|         return ((major & 0xFF) << 8) | ((minor & 0xFF) << 0) |         return ((major & 0xFF) << 8) | ((minor & 0xFF) << 0) | ||||||
|  |  | ||||||
|     def set_version(self, version): |     def set_version(self, version):  # type: (int) -> None | ||||||
|         self.version = version |         self.version = version | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def chip_ver(self): |     def chip_ver(self):  # type: () -> int | ||||||
|         return (self.version & 0xFFFF0000) >> 16 |         return (self.version & 0xFFFF0000) >> 16 | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def dump_ver(self): |     def dump_ver(self):  # type: () -> int | ||||||
|         return self.version & 0x0000FFFF |         return self.version & 0x0000FFFF | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def major(self): |     def major(self):  # type: () -> int | ||||||
|         return (self.version & 0x0000FF00) >> 8 |         return (self.version & 0x0000FF00) >> 8 | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def minor(self): |     def minor(self):  # type: () -> int | ||||||
|         return self.version & 0x000000FF |         return self.version & 0x000000FF | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -119,42 +127,37 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|     ELF_CRC32 = EspCoreDumpVersion.make_dump_ver(1, 0) |     ELF_CRC32 = EspCoreDumpVersion.make_dump_ver(1, 0) | ||||||
|     ELF_SHA256 = EspCoreDumpVersion.make_dump_ver(1, 1) |     ELF_SHA256 = EspCoreDumpVersion.make_dump_ver(1, 1) | ||||||
|  |  | ||||||
|     def __init__(self): |     def __init__(self):  # type: () -> None | ||||||
|         super(EspCoreDumpLoader, self).__init__() |         super(EspCoreDumpLoader, self).__init__() | ||||||
|         self.core_src_file = None |         self.core_src_file = None  # type: Optional[str] | ||||||
|         self.core_src_struct = None |         self.core_src_struct = None | ||||||
|         self.core_src = None |         self.core_src = None | ||||||
|  |  | ||||||
|         self.core_elf_file = None |         self.core_elf_file = None  # type: Optional[str] | ||||||
|  |  | ||||||
|         self.header = None |         self.header = None | ||||||
|         self.header_struct = EspCoreDumpV1Header |         self.header_struct = EspCoreDumpV1Header | ||||||
|         self.checksum_struct = CRC |         self.checksum_struct = CRC | ||||||
|  |  | ||||||
|         # These two method classes will be assigned in ``reload_coredump`` |         # target classes will be assigned in ``_reload_coredump`` | ||||||
|         self.target_method_cls = _TargetMethodsBase |         self.target_methods = Esp32Methods() | ||||||
|         self.arch_method_cls = _ArchMethodsBase |  | ||||||
|  |  | ||||||
|         self._temp_files = [] |         self.temp_files = []  # type: list[str] | ||||||
|  |  | ||||||
|     def __del__(self): |     def _create_temp_file(self):  # type: () -> str | ||||||
|         if self.core_src_file: |  | ||||||
|             self.core_src_file.close() |  | ||||||
|         if self.core_elf_file: |  | ||||||
|             self.core_elf_file.close() |  | ||||||
|         for f in self._temp_files: |  | ||||||
|             try: |  | ||||||
|                 os.remove(f) |  | ||||||
|             except OSError: |  | ||||||
|                 pass |  | ||||||
|  |  | ||||||
|     def _create_temp_file(self): |  | ||||||
|         t = tempfile.NamedTemporaryFile('wb', delete=False) |         t = tempfile.NamedTemporaryFile('wb', delete=False) | ||||||
|         self._temp_files.append(t.name) |         # Here we close this at first to make sure the read/write is wrapped in context manager | ||||||
|         return t |         # Otherwise the result will be wrong if you read while open in another session | ||||||
|  |         t.close() | ||||||
|  |         self.temp_files.append(t.name) | ||||||
|  |         return t.name | ||||||
|  |  | ||||||
|     def _reload_coredump(self): |     def _load_core_src(self):  # type: () -> str | ||||||
|         with open(self.core_src_file.name, 'rb') as fr: |         """ | ||||||
|  |         Write core elf into ``self.core_src``, | ||||||
|  |         Return the target str by reading core elf | ||||||
|  |         """ | ||||||
|  |         with open(self.core_src_file, 'rb') as fr:  # type: ignore | ||||||
|             coredump_bytes = fr.read() |             coredump_bytes = fr.read() | ||||||
|  |  | ||||||
|         _header = EspCoreDumpV1Header.parse(coredump_bytes)  # first we use V1 format to get version |         _header = EspCoreDumpV1Header.parse(coredump_bytes)  # first we use V1 format to get version | ||||||
| @@ -179,23 +182,28 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|             'data' / Bytes(this.header.tot_len - self.header_struct.sizeof() - self.checksum_struct.sizeof()), |             'data' / Bytes(this.header.tot_len - self.header_struct.sizeof() - self.checksum_struct.sizeof()), | ||||||
|             'checksum' / self.checksum_struct, |             'checksum' / self.checksum_struct, | ||||||
|         ) |         ) | ||||||
|         self.core_src = self.core_src_struct.parse(coredump_bytes) |         self.core_src = self.core_src_struct.parse(coredump_bytes)  # type: ignore | ||||||
|  |  | ||||||
|         # Reload header if header struct changes after parsing |         # Reload header if header struct changes after parsing | ||||||
|         if self.header_struct != EspCoreDumpV1Header: |         if self.header_struct != EspCoreDumpV1Header: | ||||||
|             self.header = EspCoreDumpV2Header.parse(coredump_bytes) |             self.header = EspCoreDumpV2Header.parse(coredump_bytes) | ||||||
|  |  | ||||||
|         if self.chip_ver in self.ESP_COREDUMP_TARGETS: |         if self.chip_ver in self.COREDUMP_SUPPORTED_TARGETS: | ||||||
|             if self.chip_ver == self.ESP32: |             if self.chip_ver == self.ESP32: | ||||||
|                 self.target_method_cls = _TargetMethodsESP32 |                 self.target_methods = Esp32Methods()  # type: ignore | ||||||
|  |             elif self.chip_ver == self.ESP32S2: | ||||||
|             if self.chip_ver in self.XTENSA_CHIPS: |                 self.target_methods = Esp32S2Methods()  # type: ignore | ||||||
|                 self.arch_method_cls = _ArchMethodsXtensa |             elif self.chip_ver == self.ESP32C3: | ||||||
|  |                 self.target_methods = Esp32c3Methods()  # type: ignore | ||||||
|  |             else: | ||||||
|  |                 raise NotImplementedError | ||||||
|         else: |         else: | ||||||
|             raise ESPCoreDumpLoaderError('Core dump chip "0x%x" is not supported!' % self.chip_ver) |             raise ESPCoreDumpLoaderError('Core dump chip "0x%x" is not supported!' % self.chip_ver) | ||||||
|  |  | ||||||
|     def _validate_dump_file(self): |         return self.target_methods.TARGET  # type: ignore | ||||||
|         if self.chip_ver not in self.ESP_COREDUMP_TARGETS: |  | ||||||
|  |     def _validate_dump_file(self):  # type: () -> None | ||||||
|  |         if self.chip_ver not in self.COREDUMP_SUPPORTED_TARGETS: | ||||||
|             raise ESPCoreDumpLoaderError('Invalid core dump chip version: "{}", should be <= "0x{:X}"' |             raise ESPCoreDumpLoaderError('Invalid core dump chip version: "{}", should be <= "0x{:X}"' | ||||||
|                                          .format(self.chip_ver, self.ESP32S2)) |                                          .format(self.chip_ver, self.ESP32S2)) | ||||||
|  |  | ||||||
| @@ -204,20 +212,24 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|         elif self.checksum_struct == SHA256: |         elif self.checksum_struct == SHA256: | ||||||
|             self._sha256_validate() |             self._sha256_validate() | ||||||
|  |  | ||||||
|     def _crc_validate(self): |     def _crc_validate(self):  # type: () -> None | ||||||
|         data_crc = binascii.crc32(EspCoreDumpV2Header.build(self.core_src.header) + self.core_src.data) & 0xffffffff |         data_crc = binascii.crc32( | ||||||
|         if data_crc != self.core_src.checksum: |             EspCoreDumpV2Header.build(self.core_src.header) + self.core_src.data) & 0xffffffff  # type: ignore | ||||||
|             raise ESPCoreDumpLoaderError('Invalid core dump CRC %x, should be %x' % (data_crc, self.core_src.crc)) |         if data_crc != self.core_src.checksum:  # type: ignore | ||||||
|  |             raise ESPCoreDumpLoaderError( | ||||||
|  |                 'Invalid core dump CRC %x, should be %x' % (data_crc, self.core_src.crc))  # type: ignore | ||||||
|  |  | ||||||
|     def _sha256_validate(self): |     def _sha256_validate(self):  # type: () -> None | ||||||
|         data_sha256 = hashlib.sha256(EspCoreDumpV2Header.build(self.core_src.header) + self.core_src.data) |         data_sha256 = hashlib.sha256( | ||||||
|  |             EspCoreDumpV2Header.build(self.core_src.header) + self.core_src.data)  # type: ignore | ||||||
|         data_sha256_str = data_sha256.hexdigest() |         data_sha256_str = data_sha256.hexdigest() | ||||||
|         sha256_str = binascii.hexlify(self.core_src.checksum).decode('ascii') |         sha256_str = binascii.hexlify(self.core_src.checksum).decode('ascii')  # type: ignore | ||||||
|         if data_sha256_str != sha256_str: |         if data_sha256_str != sha256_str: | ||||||
|             raise ESPCoreDumpLoaderError('Invalid core dump SHA256 "{}", should be "{}"' |             raise ESPCoreDumpLoaderError('Invalid core dump SHA256 "{}", should be "{}"' | ||||||
|                                          .format(data_sha256_str, sha256_str)) |                                          .format(data_sha256_str, sha256_str)) | ||||||
|  |  | ||||||
|     def create_corefile(self, exe_name=None):  # type: (str) -> None |     def create_corefile(self, exe_name=None, e_machine=ESPCoreDumpElfFile.EM_XTENSA): | ||||||
|  |         # type: (Optional[str], int) -> None | ||||||
|         """ |         """ | ||||||
|         Creates core dump ELF file |         Creates core dump ELF file | ||||||
|         """ |         """ | ||||||
| @@ -226,22 +238,21 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|  |  | ||||||
|         if self.dump_ver in [self.ELF_CRC32, |         if self.dump_ver in [self.ELF_CRC32, | ||||||
|                              self.ELF_SHA256]: |                              self.ELF_SHA256]: | ||||||
|             self._extract_elf_corefile(exe_name) |             self._extract_elf_corefile(exe_name, e_machine) | ||||||
|         elif self.dump_ver in [self.BIN_V1, |         elif self.dump_ver in [self.BIN_V1, | ||||||
|                                self.BIN_V2]: |                                self.BIN_V2]: | ||||||
|             self._extract_bin_corefile() |             self._extract_bin_corefile(e_machine) | ||||||
|         else: |         else: | ||||||
|             raise NotImplementedError |             raise NotImplementedError | ||||||
|  |  | ||||||
|     def _extract_elf_corefile(self, exe_name=None): |     def _extract_elf_corefile(self, exe_name=None, e_machine=ESPCoreDumpElfFile.EM_XTENSA):  # type: (str, int) -> None | ||||||
|         """ |         """ | ||||||
|         Reads the ELF formatted core dump image and parse it |         Reads the ELF formatted core dump image and parse it | ||||||
|         """ |         """ | ||||||
|         self.core_elf_file.write(self.core_src.data) |         with open(self.core_elf_file, 'wb') as fw:  # type: ignore | ||||||
|         # Need to be closed before read. Otherwise the result will be wrong |             fw.write(self.core_src.data)  # type: ignore | ||||||
|         self.core_elf_file.close() |  | ||||||
|  |  | ||||||
|         core_elf = ESPCoreDumpElfFile(self.core_elf_file.name) |         core_elf = ESPCoreDumpElfFile(self.core_elf_file, e_machine=e_machine)  # type: ignore | ||||||
|  |  | ||||||
|         # Read note segments from core file which are belong to tasks (TCB or stack) |         # Read note segments from core file which are belong to tasks (TCB or stack) | ||||||
|         for seg in core_elf.note_segments: |         for seg in core_elf.note_segments: | ||||||
| @@ -259,7 +270,7 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|                     coredump_sha256 = coredump_sha256_struct.parse(note_sec.desc[:coredump_sha256_struct.sizeof()]) |                     coredump_sha256 = coredump_sha256_struct.parse(note_sec.desc[:coredump_sha256_struct.sizeof()]) | ||||||
|                     if coredump_sha256.sha256 != app_sha256: |                     if coredump_sha256.sha256 != app_sha256: | ||||||
|                         raise ESPCoreDumpLoaderError( |                         raise ESPCoreDumpLoaderError( | ||||||
|                             'Invalid application image for coredump: coredump SHA256({}) != app SHA256({}).' |                             'Invalid application image for coredump: coredump SHA256({!r}) != app SHA256({!r}).' | ||||||
|                             .format(coredump_sha256, app_sha256)) |                             .format(coredump_sha256, app_sha256)) | ||||||
|                     if coredump_sha256.ver != self.version: |                     if coredump_sha256.ver != self.version: | ||||||
|                         raise ESPCoreDumpLoaderError( |                         raise ESPCoreDumpLoaderError( | ||||||
| @@ -267,46 +278,43 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|                             .format(coredump_sha256.ver, self.version)) |                             .format(coredump_sha256.ver, self.version)) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _get_aligned_size(size, align_with=4): |     def _get_aligned_size(size, align_with=4):  # type: (int, int) -> int | ||||||
|         if size % align_with: |         if size % align_with: | ||||||
|             return align_with * (size // align_with + 1) |             return align_with * (size // align_with + 1) | ||||||
|         return size |         return size | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _build_note_section(name, sec_type, desc): |     def _build_note_section(name, sec_type, desc):  # type: (str, int, str) -> bytes | ||||||
|         name = bytearray(name, encoding='ascii') + b'\0' |         b_name = bytearray(name, encoding='ascii') + b'\0' | ||||||
|         return NoteSection.build({ |         return NoteSection.build({  # type: ignore | ||||||
|             'namesz': len(name), |             'namesz': len(b_name), | ||||||
|             'descsz': len(desc), |             'descsz': len(desc), | ||||||
|             'type': sec_type, |             'type': sec_type, | ||||||
|             'name': name, |             'name': b_name, | ||||||
|             'desc': desc, |             'desc': desc, | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
|     def _extract_bin_corefile(self): |     def _extract_bin_corefile(self, e_machine=ESPCoreDumpElfFile.EM_XTENSA):  # type: (int) -> None | ||||||
|         """ |         """ | ||||||
|         Creates core dump ELF file |         Creates core dump ELF file | ||||||
|         """ |         """ | ||||||
|         tcbsz_aligned = self._get_aligned_size(self.header.tcbsz) |  | ||||||
|  |  | ||||||
|         coredump_data_struct = Struct( |         coredump_data_struct = Struct( | ||||||
|             'tasks' / GreedyRange( |             'tasks' / GreedyRange( | ||||||
|                 AlignedStruct( |                 AlignedStruct( | ||||||
|                     4, |                     4, | ||||||
|                     'task_header' / TaskHeader, |                     'task_header' / TaskHeader, | ||||||
|                     'tcb' / Bytes(self.header.tcbsz), |                     'tcb' / Bytes(self.header.tcbsz),  # type: ignore | ||||||
|                     'stack' / Bytes(abs_(this.task_header.stack_top - this.task_header.stack_end)), |                     'stack' / Bytes(abs_(this.task_header.stack_top - this.task_header.stack_end)),  # type: ignore | ||||||
|                 ) |                 ) | ||||||
|             ), |             ), | ||||||
|             'mem_seg_headers' / MemSegmentHeader[self.core_src.header.segs_num] |             'mem_seg_headers' / MemSegmentHeader[self.core_src.header.segs_num]  # type: ignore | ||||||
|         ) |         ) | ||||||
|  |         core_elf = ESPCoreDumpElfFile(e_machine=e_machine) | ||||||
|         core_elf = ESPCoreDumpElfFile() |  | ||||||
|         notes = b'' |         notes = b'' | ||||||
|         core_dump_info_notes = b'' |         core_dump_info_notes = b'' | ||||||
|         task_info_notes = b'' |         task_info_notes = b'' | ||||||
|  |  | ||||||
|         coredump_data = coredump_data_struct.parse(self.core_src.data) |         coredump_data = coredump_data_struct.parse(self.core_src.data)  # type: ignore | ||||||
|         for i, task in enumerate(coredump_data.tasks): |         for i, task in enumerate(coredump_data.tasks): | ||||||
|             stack_len_aligned = self._get_aligned_size(abs(task.task_header.stack_top - task.task_header.stack_end)) |             stack_len_aligned = self._get_aligned_size(abs(task.task_header.stack_top - task.task_header.stack_end)) | ||||||
|             task_status_kwargs = { |             task_status_kwargs = { | ||||||
| @@ -314,32 +322,34 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|                 'task_flags': TASK_STATUS_CORRECT, |                 'task_flags': TASK_STATUS_CORRECT, | ||||||
|                 'task_tcb_addr': task.task_header.tcb_addr, |                 'task_tcb_addr': task.task_header.tcb_addr, | ||||||
|                 'task_stack_start': min(task.task_header.stack_top, task.task_header.stack_end), |                 'task_stack_start': min(task.task_header.stack_top, task.task_header.stack_end), | ||||||
|  |                 'task_stack_end': max(task.task_header.stack_top, task.task_header.stack_end), | ||||||
|                 'task_stack_len': stack_len_aligned, |                 'task_stack_len': stack_len_aligned, | ||||||
|                 'task_name': Padding(16).build({})  # currently we don't have task_name, keep it as padding |                 'task_name': Padding(16).build({})  # currently we don't have task_name, keep it as padding | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             # Write TCB |             # Write TCB | ||||||
|             try: |             try: | ||||||
|                 if self.target_method_cls.tcb_is_sane(task.task_header.tcb_addr, tcbsz_aligned): |                 if self.target_methods.tcb_is_sane(task.task_header.tcb_addr, self.header.tcbsz):  # type: ignore | ||||||
|                     core_elf.add_segment(task.task_header.tcb_addr, |                     core_elf.add_segment(task.task_header.tcb_addr, | ||||||
|                                          task.tcb, |                                          task.tcb, | ||||||
|                                          ElfFile.PT_LOAD, |                                          ElfFile.PT_LOAD, | ||||||
|                                          ElfSegment.PF_R | ElfSegment.PF_W) |                                          ElfSegment.PF_R | ElfSegment.PF_W) | ||||||
|                 elif task.task_header.tcb_addr and self.target_method_cls.addr_is_fake(task.task_header.tcb_addr): |                 elif task.task_header.tcb_addr and self.target_methods.addr_is_fake(task.task_header.tcb_addr): | ||||||
|                     task_status_kwargs['task_flags'] |= TASK_STATUS_TCB_CORRUPTED |                     task_status_kwargs['task_flags'] |= TASK_STATUS_TCB_CORRUPTED | ||||||
|             except ESPCoreDumpLoaderError as e: |             except ESPCoreDumpLoaderError as e: | ||||||
|                 logging.warning('Skip TCB {} bytes @ 0x{:x}. (Reason: {})' |                 logging.warning('Skip TCB {} bytes @ 0x{:x}. (Reason: {})' | ||||||
|                                 .format(tcbsz_aligned, task.task_header.tcb_addr, e)) |                                 .format(self.header.tcbsz, task.task_header.tcb_addr, e))  # type: ignore | ||||||
|  |  | ||||||
|             # Write stack |             # Write stack | ||||||
|             try: |             try: | ||||||
|                 if self.target_method_cls.stack_is_sane(task_status_kwargs['task_stack_start']): |                 if self.target_methods.stack_is_sane(task_status_kwargs['task_stack_start'], | ||||||
|  |                                                      task_status_kwargs['task_stack_end']): | ||||||
|                     core_elf.add_segment(task_status_kwargs['task_stack_start'], |                     core_elf.add_segment(task_status_kwargs['task_stack_start'], | ||||||
|                                          task.stack, |                                          task.stack, | ||||||
|                                          ElfFile.PT_LOAD, |                                          ElfFile.PT_LOAD, | ||||||
|                                          ElfSegment.PF_R | ElfSegment.PF_W) |                                          ElfSegment.PF_R | ElfSegment.PF_W) | ||||||
|                 elif task_status_kwargs['task_stack_start'] \ |                 elif (task_status_kwargs['task_stack_start'] | ||||||
|                         and self.target_method_cls.addr_is_fake(task_status_kwargs['task_stack_start']): |                       and self.target_methods.addr_is_fake(task_status_kwargs['task_stack_start'])): | ||||||
|                     task_status_kwargs['task_flags'] |= TASK_STATUS_TCB_CORRUPTED |                     task_status_kwargs['task_flags'] |= TASK_STATUS_TCB_CORRUPTED | ||||||
|                     core_elf.add_segment(task_status_kwargs['task_stack_start'], |                     core_elf.add_segment(task_status_kwargs['task_stack_start'], | ||||||
|                                          task.stack, |                                          task.stack, | ||||||
| @@ -355,7 +365,7 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|             try: |             try: | ||||||
|                 logging.debug('Stack start_end: 0x{:x} @ 0x{:x}' |                 logging.debug('Stack start_end: 0x{:x} @ 0x{:x}' | ||||||
|                               .format(task.task_header.stack_top, task.task_header.stack_end)) |                               .format(task.task_header.stack_top, task.task_header.stack_end)) | ||||||
|                 task_regs, extra_regs = self.arch_method_cls.get_registers_from_stack( |                 task_regs, extra_regs = self.target_methods.get_registers_from_stack( | ||||||
|                     task.stack, |                     task.stack, | ||||||
|                     task.task_header.stack_end > task.task_header.stack_top |                     task.task_header.stack_end > task.task_header.stack_top | ||||||
|                 ) |                 ) | ||||||
| @@ -367,23 +377,24 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|                                                         EspTaskStatus.build(task_status_kwargs)) |                                                         EspTaskStatus.build(task_status_kwargs)) | ||||||
|             notes += self._build_note_section('CORE', |             notes += self._build_note_section('CORE', | ||||||
|                                               ElfFile.PT_LOAD, |                                               ElfFile.PT_LOAD, | ||||||
|                                               self.arch_method_cls.build_prstatus_data(task.task_header.tcb_addr, |                                               self.target_methods.build_prstatus_data(task.task_header.tcb_addr, | ||||||
|                                                                                        task_regs)) |                                                                                       task_regs)) | ||||||
|  |  | ||||||
|             if extra_regs and len(core_dump_info_notes) == 0: |             if len(core_dump_info_notes) == 0:  # the first task is the crashed task | ||||||
|                 # actually there will be only one such note - for crashed task |  | ||||||
|                 core_dump_info_notes += self._build_note_section('ESP_CORE_DUMP_INFO', |                 core_dump_info_notes += self._build_note_section('ESP_CORE_DUMP_INFO', | ||||||
|                                                                  ESPCoreDumpElfFile.PT_INFO, |                                                                  ESPCoreDumpElfFile.PT_INFO, | ||||||
|                                                                  Int32ul.build(self.header.ver)) |                                                                  Int32ul.build(self.header.ver))  # type: ignore | ||||||
|  |                 _regs = [task.task_header.tcb_addr] | ||||||
|  |  | ||||||
|  |                 # For xtensa, we need to put the exception registers into the extra info as well | ||||||
|  |                 if e_machine == ESPCoreDumpElfFile.EM_XTENSA and extra_regs: | ||||||
|  |                     for reg_id in extra_regs: | ||||||
|  |                         _regs.extend([reg_id, extra_regs[reg_id]]) | ||||||
|  |  | ||||||
|                 exc_regs = [] |  | ||||||
|                 for reg_id in extra_regs: |  | ||||||
|                     exc_regs.extend([reg_id, extra_regs[reg_id]]) |  | ||||||
|                 _regs = [task.task_header.tcb_addr] + exc_regs |  | ||||||
|                 core_dump_info_notes += self._build_note_section( |                 core_dump_info_notes += self._build_note_section( | ||||||
|                     'EXTRA_INFO', |                     'EXTRA_INFO', | ||||||
|                     ESPCoreDumpElfFile.PT_EXTRA_INFO, |                     ESPCoreDumpElfFile.PT_EXTRA_INFO, | ||||||
|                     Int32ul[1 + len(exc_regs)].build(_regs) |                     Int32ul[len(_regs)].build(_regs) | ||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|         if self.dump_ver == self.BIN_V2: |         if self.dump_ver == self.BIN_V2: | ||||||
| @@ -409,30 +420,29 @@ class EspCoreDumpLoader(EspCoreDumpVersion): | |||||||
|                             .format(len(task_info_notes), 0, e)) |                             .format(len(task_info_notes), 0, e)) | ||||||
|         # dump core ELF |         # dump core ELF | ||||||
|         core_elf.e_type = ElfFile.ET_CORE |         core_elf.e_type = ElfFile.ET_CORE | ||||||
|         core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA |         core_elf.dump(self.core_elf_file)  # type: ignore | ||||||
|         core_elf.dump(self.core_elf_file.name) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | ||||||
|     ESP_COREDUMP_PART_TABLE_OFF = 0x8000 |     ESP_COREDUMP_PART_TABLE_OFF = 0x8000 | ||||||
|  |  | ||||||
|     def __init__(self, offset, target='esp32', port=None, baud=None): |     def __init__(self, offset, target=None, port=None, baud=None): | ||||||
|  |         # type: (int, Optional[str], Optional[str], Optional[int]) -> None | ||||||
|         super(ESPCoreDumpFlashLoader, self).__init__() |         super(ESPCoreDumpFlashLoader, self).__init__() | ||||||
|         self.port = port |         self.port = port | ||||||
|         self.baud = baud |         self.baud = baud | ||||||
|         self.target = target |  | ||||||
|  |  | ||||||
|         self._get_coredump(offset) |         self._get_core_src(offset, target) | ||||||
|         self._reload_coredump() |         self.target = self._load_core_src() | ||||||
|  |  | ||||||
|     def _get_coredump(self, off): |     def _get_core_src(self, off, target=None):  # type: (int, Optional[str]) -> None | ||||||
|         """ |         """ | ||||||
|         Loads core dump from flash using parttool or elftool (if offset is set) |         Loads core dump from flash using parttool or elftool (if offset is set) | ||||||
|         """ |         """ | ||||||
|         try: |         try: | ||||||
|             if off: |             if off: | ||||||
|                 logging.info('Invoke esptool to read image.') |                 logging.info('Invoke esptool to read image.') | ||||||
|                 self._invoke_esptool(off=off) |                 self._invoke_esptool(off=off, target=target) | ||||||
|             else: |             else: | ||||||
|                 logging.info('Invoke parttool to read image.') |                 logging.info('Invoke parttool to read image.') | ||||||
|                 self._invoke_parttool() |                 self._invoke_parttool() | ||||||
| @@ -440,15 +450,14 @@ class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | |||||||
|             if e.output: |             if e.output: | ||||||
|                 logging.info(e.output) |                 logging.info(e.output) | ||||||
|             logging.error('Error during the subprocess execution') |             logging.error('Error during the subprocess execution') | ||||||
|         else: |  | ||||||
|             # Need to be closed before read. Otherwise the result will be wrong |  | ||||||
|             self.core_src_file.close() |  | ||||||
|  |  | ||||||
|     def _invoke_esptool(self, off=None): |     def _invoke_esptool(self, off=None, target=None):  # type: (Optional[int], Optional[str]) -> None | ||||||
|         """ |         """ | ||||||
|         Loads core dump from flash using elftool |         Loads core dump from flash using elftool | ||||||
|         """ |         """ | ||||||
|         tool_args = [sys.executable, ESPTOOL_PY, '-c', self.target] |         if target is None: | ||||||
|  |             target = 'auto' | ||||||
|  |         tool_args = [sys.executable, ESPTOOL_PY, '-c', target] | ||||||
|         if self.port: |         if self.port: | ||||||
|             tool_args.extend(['-p', self.port]) |             tool_args.extend(['-p', self.port]) | ||||||
|         if self.baud: |         if self.baud: | ||||||
| @@ -466,14 +475,14 @@ class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | |||||||
|  |  | ||||||
|             # Here we use V1 format to locate the size |             # Here we use V1 format to locate the size | ||||||
|             tool_args.extend(['read_flash', str(off), str(EspCoreDumpV1Header.sizeof())]) |             tool_args.extend(['read_flash', str(off), str(EspCoreDumpV1Header.sizeof())]) | ||||||
|             tool_args.append(self.core_src_file.name) |             tool_args.append(self.core_src_file)  # type: ignore | ||||||
|  |  | ||||||
|             # read core dump length |             # read core dump length | ||||||
|             et_out = subprocess.check_output(tool_args) |             et_out = subprocess.check_output(tool_args) | ||||||
|             if et_out: |             if et_out: | ||||||
|                 logging.info(et_out.decode('utf-8')) |                 logging.info(et_out.decode('utf-8')) | ||||||
|  |  | ||||||
|             header = EspCoreDumpV1Header.parse(open(self.core_src_file.name, 'rb').read()) |             header = EspCoreDumpV1Header.parse(open(self.core_src_file, 'rb').read())  # type: ignore | ||||||
|             if not header or not 0 < header.tot_len <= part_size: |             if not header or not 0 < header.tot_len <= part_size: | ||||||
|                 logging.error('Incorrect size of core dump image: {}, use partition size instead: {}' |                 logging.error('Incorrect size of core dump image: {}, use partition size instead: {}' | ||||||
|                               .format(header.tot_len, part_size)) |                               .format(header.tot_len, part_size)) | ||||||
| @@ -492,7 +501,7 @@ class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | |||||||
|             logging.debug(e.output) |             logging.debug(e.output) | ||||||
|             raise e |             raise e | ||||||
|  |  | ||||||
|     def _invoke_parttool(self): |     def _invoke_parttool(self):  # type: () -> None | ||||||
|         """ |         """ | ||||||
|         Loads core dump from flash using parttool |         Loads core dump from flash using parttool | ||||||
|         """ |         """ | ||||||
| @@ -503,7 +512,7 @@ class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | |||||||
|  |  | ||||||
|         self.core_src_file = self._create_temp_file() |         self.core_src_file = self._create_temp_file() | ||||||
|         try: |         try: | ||||||
|             tool_args.append(self.core_src_file.name) |             tool_args.append(self.core_src_file)  # type: ignore | ||||||
|             # read core dump partition |             # read core dump partition | ||||||
|             et_out = subprocess.check_output(tool_args) |             et_out = subprocess.check_output(tool_args) | ||||||
|             if et_out: |             if et_out: | ||||||
| @@ -515,7 +524,7 @@ class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | |||||||
|             logging.debug(e.output) |             logging.debug(e.output) | ||||||
|             raise e |             raise e | ||||||
|  |  | ||||||
|     def _get_core_dump_partition_info(self, part_off=None): |     def _get_core_dump_partition_info(self, part_off=None):  # type: (Optional[int]) -> Tuple[int, int] | ||||||
|         """ |         """ | ||||||
|         Get core dump partition info using parttool |         Get core dump partition info using parttool | ||||||
|         """ |         """ | ||||||
| @@ -545,28 +554,27 @@ class ESPCoreDumpFlashLoader(EspCoreDumpLoader): | |||||||
|  |  | ||||||
|  |  | ||||||
| class ESPCoreDumpFileLoader(EspCoreDumpLoader): | class ESPCoreDumpFileLoader(EspCoreDumpLoader): | ||||||
|     def __init__(self, path, is_b64=False): |     def __init__(self, path, is_b64=False):  # type: (str, bool) -> None | ||||||
|         super(ESPCoreDumpFileLoader, self).__init__() |         super(ESPCoreDumpFileLoader, self).__init__() | ||||||
|         self.is_b64 = is_b64 |         self.is_b64 = is_b64 | ||||||
|  |  | ||||||
|         self._get_coredump(path) |         self._get_core_src(path) | ||||||
|         self._reload_coredump() |         self.target = self._load_core_src() | ||||||
|  |  | ||||||
|     def _get_coredump(self, path): |     def _get_core_src(self, path):  # type: (str) -> None | ||||||
|         """ |         """ | ||||||
|         Loads core dump from (raw binary or base64-encoded) file |         Loads core dump from (raw binary or base64-encoded) file | ||||||
|         """ |         """ | ||||||
|         logging.debug('Load core dump from "%s", %s format', path, 'b64' if self.is_b64 else 'raw') |         logging.debug('Load core dump from "%s", %s format', path, 'b64' if self.is_b64 else 'raw') | ||||||
|         if not self.is_b64: |         if not self.is_b64: | ||||||
|             self.core_src_file = open(path, mode='rb') |             self.core_src_file = path | ||||||
|         else: |         else: | ||||||
|             self.core_src_file = self._create_temp_file() |             self.core_src_file = self._create_temp_file() | ||||||
|             with open(path, 'rb') as fb64: |             with open(self.core_src_file, 'wb') as fw: | ||||||
|                 while True: |                 with open(path, 'rb') as fb64: | ||||||
|                     line = fb64.readline() |                     while True: | ||||||
|                     if len(line) == 0: |                         line = fb64.readline() | ||||||
|                         break |                         if len(line) == 0: | ||||||
|                     data = base64.standard_b64decode(line.rstrip(b'\r\n')) |                             break | ||||||
|                     self.core_src_file.write(data) |                         data = base64.standard_b64decode(line.rstrip(b'\r\n')) | ||||||
|                 self.core_src_file.flush() |                         fw.write(data)  # type: ignore | ||||||
|                 self.core_src_file.seek(0) |  | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								components/espcoredump/corefile/riscv.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								components/espcoredump/corefile/riscv.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | # | ||||||
|  | # Copyright 2021 Espressif Systems (Shanghai) CO., LTD | ||||||
|  | # | ||||||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | # you may not use this file except in compliance with the License. | ||||||
|  | # You may obtain a copy of the License at | ||||||
|  | # | ||||||
|  | #     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | # Unless required by applicable law or agreed to in writing, software | ||||||
|  | # distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | # See the License for the specific language governing permissions and | ||||||
|  | # limitations under the License. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | from construct import Int16ul, Int32ul, Padding, Struct | ||||||
|  |  | ||||||
|  | from . import BaseArchMethodsMixin, BaseTargetMethods, ESPCoreDumpLoaderError | ||||||
|  |  | ||||||
|  | try: | ||||||
|  |     from typing import Any, Optional, Tuple | ||||||
|  | except ImportError: | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  | RISCV_GP_REGS_COUNT = 32 | ||||||
|  | PRSTATUS_SIZE = 204 | ||||||
|  | PRSTATUS_OFFSET_PR_CURSIG = 12 | ||||||
|  | PRSTATUS_OFFSET_PR_PID = 24 | ||||||
|  | PRSTATUS_OFFSET_PR_REG = 72 | ||||||
|  | ELF_GREGSET_T_SIZE = 128 | ||||||
|  |  | ||||||
|  | PrStruct = Struct( | ||||||
|  |     Padding(PRSTATUS_OFFSET_PR_CURSIG), | ||||||
|  |     'pr_cursig' / Int16ul, | ||||||
|  |     Padding(PRSTATUS_OFFSET_PR_PID - PRSTATUS_OFFSET_PR_CURSIG - Int16ul.sizeof()), | ||||||
|  |     'pr_pid' / Int32ul, | ||||||
|  |     Padding(PRSTATUS_OFFSET_PR_REG - PRSTATUS_OFFSET_PR_PID - Int32ul.sizeof()), | ||||||
|  |     'regs' / Int32ul[RISCV_GP_REGS_COUNT], | ||||||
|  |     Padding(PRSTATUS_SIZE - PRSTATUS_OFFSET_PR_REG - ELF_GREGSET_T_SIZE) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class RiscvMethodsMixin(BaseArchMethodsMixin): | ||||||
|  |     @staticmethod | ||||||
|  |     def get_registers_from_stack(data, grows_down): | ||||||
|  |         # type: (bytes, bool) -> Tuple[list[int], Optional[dict[int, int]]] | ||||||
|  |         regs = Int32ul[RISCV_GP_REGS_COUNT].parse(data) | ||||||
|  |         if not grows_down: | ||||||
|  |             raise ESPCoreDumpLoaderError('Growing up stacks are not supported for now!') | ||||||
|  |         return regs, None | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def build_prstatus_data(tcb_addr, task_regs):  # type: (int, list[int]) -> Any | ||||||
|  |         return PrStruct.build({ | ||||||
|  |             'pr_cursig': 0, | ||||||
|  |             'pr_pid': tcb_addr, | ||||||
|  |             'regs': task_regs, | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Esp32c3Methods(BaseTargetMethods, RiscvMethodsMixin): | ||||||
|  |     TARGET = 'esp32c3' | ||||||
							
								
								
									
										8
									
								
								components/espcoredump/corefile/soc_headers/esp32.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								components/espcoredump/corefile/soc_headers/esp32.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | SOC_DRAM_LOW = 0x3ffae000 | ||||||
|  | SOC_DRAM_HIGH = 0x40000000 | ||||||
|  | SOC_IRAM_LOW = 0x40080000 | ||||||
|  | SOC_IRAM_HIGH = 0x400a0000 | ||||||
|  | SOC_RTC_DRAM_LOW = 0x3ff80000 | ||||||
|  | SOC_RTC_DRAM_HIGH = 0x3ff82000 | ||||||
|  | SOC_RTC_DATA_LOW = 0x50000000 | ||||||
|  | SOC_RTC_DATA_HIGH = 0x50002000 | ||||||
							
								
								
									
										8
									
								
								components/espcoredump/corefile/soc_headers/esp32c3.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								components/espcoredump/corefile/soc_headers/esp32c3.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | SOC_IRAM_LOW = 0x4037c000 | ||||||
|  | SOC_IRAM_HIGH = 0x403e0000 | ||||||
|  | SOC_DRAM_LOW = 0x3fc80000 | ||||||
|  | SOC_DRAM_HIGH = 0x3fce0000 | ||||||
|  | SOC_RTC_DRAM_LOW = 0x50000000 | ||||||
|  | SOC_RTC_DRAM_HIGH = 0x50002000 | ||||||
|  | SOC_RTC_DATA_LOW = 0x50000000 | ||||||
|  | SOC_RTC_DATA_HIGH = 0x50002000 | ||||||
							
								
								
									
										8
									
								
								components/espcoredump/corefile/soc_headers/esp32s2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								components/espcoredump/corefile/soc_headers/esp32s2.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | SOC_IRAM_LOW = 0x40020000 | ||||||
|  | SOC_IRAM_HIGH = 0x40070000 | ||||||
|  | SOC_DRAM_LOW = 0x3ffb0000 | ||||||
|  | SOC_DRAM_HIGH = 0x40000000 | ||||||
|  | SOC_RTC_DRAM_LOW = 0x3ff9e000 | ||||||
|  | SOC_RTC_DRAM_HIGH = 0x3ffa0000 | ||||||
|  | SOC_RTC_DATA_LOW = 0x50000000 | ||||||
|  | SOC_RTC_DATA_HIGH = 0x50002000 | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| # | # | ||||||
| # Copyright 2021 Espressif Systems (Shanghai) PTE LTD | # Copyright 2021 Espressif Systems (Shanghai) CO., LTD | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| # you may not use this file except in compliance with the License. | # you may not use this file except in compliance with the License. | ||||||
| @@ -16,12 +16,16 @@ | |||||||
|  |  | ||||||
| from construct import Int16ul, Int32ul, Int64ul, Struct | from construct import Int16ul, Int32ul, Int64ul, Struct | ||||||
|  |  | ||||||
| from . import ESPCoreDumpLoaderError, _ArchMethodsBase, _TargetMethodsBase | from . import BaseArchMethodsMixin, BaseTargetMethods, ESPCoreDumpLoaderError | ||||||
|  |  | ||||||
|  | try: | ||||||
|  |     from typing import Any, Optional, Tuple | ||||||
|  | except ImportError: | ||||||
|  |     pass | ||||||
|  |  | ||||||
| INVALID_CAUSE_VALUE = 0xFFFF | INVALID_CAUSE_VALUE = 0xFFFF | ||||||
| XCHAL_EXCCAUSE_NUM = 64 | XCHAL_EXCCAUSE_NUM = 64 | ||||||
|  |  | ||||||
|  |  | ||||||
| # Exception cause dictionary to get translation of exccause register | # Exception cause dictionary to get translation of exccause register | ||||||
| # From 4.4.1.5 table 4-64 Exception Causes of Xtensa | # From 4.4.1.5 table 4-64 Exception Causes of Xtensa | ||||||
| # Instruction Set Architecture (ISA) Reference Manual | # Instruction Set Architecture (ISA) Reference Manual | ||||||
| @@ -81,7 +85,7 @@ XTENSA_EXCEPTION_CAUSE_DICT = { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| class XtensaRegisters(object): | class ExceptionRegisters(object): | ||||||
|     # extra regs IDs used in EXTRA_INFO note |     # extra regs IDs used in EXTRA_INFO note | ||||||
|     EXCCAUSE_IDX = 0 |     EXCCAUSE_IDX = 0 | ||||||
|     EXCVADDR_IDX = 1 |     EXCVADDR_IDX = 1 | ||||||
| @@ -100,14 +104,14 @@ class XtensaRegisters(object): | |||||||
|     EPS7_IDX = 199 |     EPS7_IDX = 199 | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def registers(self): |     def registers(self):  # type: () -> dict[str, int] | ||||||
|         return {k: v for k, v in self.__class__.__dict__.items() |         return {k: v for k, v in self.__class__.__dict__.items() | ||||||
|                 if not k.startswith('__') and isinstance(v, int)} |                 if not k.startswith('__') and isinstance(v, int)} | ||||||
|  |  | ||||||
|  |  | ||||||
| # Following structs are based on source code | # Following structs are based on source code | ||||||
| # IDF_PATH/components/espcoredump/src/core_dump_port.c | # IDF_PATH/components/espcoredump/src/core_dump_port.c | ||||||
| XtensaPrStatus = Struct( | PrStatus = Struct( | ||||||
|     'si_signo' / Int32ul, |     'si_signo' / Int32ul, | ||||||
|     'si_code' / Int32ul, |     'si_code' / Int32ul, | ||||||
|     'si_errno' / Int32ul, |     'si_errno' / Int32ul, | ||||||
| @@ -126,28 +130,28 @@ XtensaPrStatus = Struct( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def print_exc_regs_info(extra_info): | def print_exc_regs_info(extra_info):  # type: (list[int]) -> None | ||||||
|     """ |     """ | ||||||
|     Print the register info by parsing extra_info |     Print the register info by parsing extra_info | ||||||
|     :param extra_info: extra info data str |     :param extra_info: extra info data str | ||||||
|     :return: None |     :return: None | ||||||
|     """ |     """ | ||||||
|     exccause = extra_info[1 + 2 * XtensaRegisters.EXCCAUSE_IDX + 1] |     exccause = extra_info[1 + 2 * ExceptionRegisters.EXCCAUSE_IDX + 1] | ||||||
|     exccause_str = XTENSA_EXCEPTION_CAUSE_DICT.get(exccause) |     exccause_str = XTENSA_EXCEPTION_CAUSE_DICT.get(exccause) | ||||||
|     if not exccause_str: |     if not exccause_str: | ||||||
|         exccause_str = ('Invalid EXCCAUSE code', 'Invalid EXCAUSE description or not found.') |         exccause_str = ('Invalid EXCCAUSE code', 'Invalid EXCAUSE description or not found.') | ||||||
|     print('exccause       0x%x (%s)' % (exccause, exccause_str[0])) |     print('exccause       0x%x (%s)' % (exccause, exccause_str[0])) | ||||||
|     print('excvaddr       0x%x' % extra_info[1 + 2 * XtensaRegisters.EXCVADDR_IDX + 1]) |     print('excvaddr       0x%x' % extra_info[1 + 2 * ExceptionRegisters.EXCVADDR_IDX + 1]) | ||||||
|  |  | ||||||
|     # skip crashed_task_tcb, exccause, and excvaddr |     # skip crashed_task_tcb, exccause, and excvaddr | ||||||
|     for i in range(5, len(extra_info), 2): |     for i in range(5, len(extra_info), 2): | ||||||
|         if (extra_info[i] >= XtensaRegisters.EPC1_IDX and extra_info[i] <= XtensaRegisters.EPC7_IDX): |         if (extra_info[i] >= ExceptionRegisters.EPC1_IDX and extra_info[i] <= ExceptionRegisters.EPC7_IDX): | ||||||
|             print('epc%d           0x%x' % ((extra_info[i] - XtensaRegisters.EPC1_IDX + 1), extra_info[i + 1])) |             print('epc%d           0x%x' % ((extra_info[i] - ExceptionRegisters.EPC1_IDX + 1), extra_info[i + 1])) | ||||||
|  |  | ||||||
|     # skip crashed_task_tcb, exccause, and excvaddr |     # skip crashed_task_tcb, exccause, and excvaddr | ||||||
|     for i in range(5, len(extra_info), 2): |     for i in range(5, len(extra_info), 2): | ||||||
|         if (extra_info[i] >= XtensaRegisters.EPS2_IDX and extra_info[i] <= XtensaRegisters.EPS7_IDX): |         if (extra_info[i] >= ExceptionRegisters.EPS2_IDX and extra_info[i] <= ExceptionRegisters.EPS7_IDX): | ||||||
|             print('eps%d           0x%x' % ((extra_info[i] - XtensaRegisters.EPS2_IDX + 2), extra_info[i + 1])) |             print('eps%d           0x%x' % ((extra_info[i] - ExceptionRegisters.EPS2_IDX + 2), extra_info[i + 1])) | ||||||
|  |  | ||||||
|  |  | ||||||
| # from "gdb/xtensa-tdep.h" | # from "gdb/xtensa-tdep.h" | ||||||
| @@ -200,24 +204,11 @@ XT_STK_LCOUNT = 24 | |||||||
| XT_STK_FRMSZ = 25 | XT_STK_FRMSZ = 25 | ||||||
|  |  | ||||||
|  |  | ||||||
| class _TargetMethodsESP32(_TargetMethodsBase): | class XtensaMethodsMixin(BaseArchMethodsMixin): | ||||||
|     @staticmethod |  | ||||||
|     def tcb_is_sane(tcb_addr, tcb_size): |  | ||||||
|         return not (tcb_addr < 0x3ffae000 or (tcb_addr + tcb_size) > 0x40000000) |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     def stack_is_sane(sp): |  | ||||||
|         return not (sp < 0x3ffae010 or sp > 0x3fffffff) |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     def addr_is_fake(addr): |  | ||||||
|         return (0x20000000 <= addr < 0x3f3fffff) or addr >= 0x80000000 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class _ArchMethodsXtensa(_ArchMethodsBase): |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def get_registers_from_stack(data, grows_down): |     def get_registers_from_stack(data, grows_down): | ||||||
|         extra_regs = {v: 0 for v in XtensaRegisters().registers.values()} |         # type: (bytes, bool) -> Tuple[list[int], Optional[dict[int, int]]] | ||||||
|  |         extra_regs = {v: 0 for v in ExceptionRegisters().registers.values()} | ||||||
|         regs = [0] * REG_NUM |         regs = [0] * REG_NUM | ||||||
|         # TODO: support for growing up stacks |         # TODO: support for growing up stacks | ||||||
|         if not grows_down: |         if not grows_down: | ||||||
| @@ -245,10 +236,10 @@ class _ArchMethodsXtensa(_ArchMethodsBase): | |||||||
|             if regs[REG_PS_IDX] & (1 << 5): |             if regs[REG_PS_IDX] & (1 << 5): | ||||||
|                 regs[REG_PS_IDX] &= ~(1 << 4) |                 regs[REG_PS_IDX] &= ~(1 << 4) | ||||||
|             if stack[XT_STK_EXCCAUSE] in XTENSA_EXCEPTION_CAUSE_DICT: |             if stack[XT_STK_EXCCAUSE] in XTENSA_EXCEPTION_CAUSE_DICT: | ||||||
|                 extra_regs[XtensaRegisters.EXCCAUSE_IDX] = stack[XT_STK_EXCCAUSE] |                 extra_regs[ExceptionRegisters.EXCCAUSE_IDX] = stack[XT_STK_EXCCAUSE] | ||||||
|             else: |             else: | ||||||
|                 extra_regs[XtensaRegisters.EXCCAUSE_IDX] = INVALID_CAUSE_VALUE |                 extra_regs[ExceptionRegisters.EXCCAUSE_IDX] = INVALID_CAUSE_VALUE | ||||||
|             extra_regs[XtensaRegisters.EXCVADDR_IDX] = stack[XT_STK_EXCVADDR] |             extra_regs[ExceptionRegisters.EXCVADDR_IDX] = stack[XT_STK_EXCVADDR] | ||||||
|         else: |         else: | ||||||
|             regs[REG_PC_IDX] = stack[XT_SOL_PC] |             regs[REG_PC_IDX] = stack[XT_SOL_PC] | ||||||
|             regs[REG_PS_IDX] = stack[XT_SOL_PS] |             regs[REG_PS_IDX] = stack[XT_SOL_PS] | ||||||
| @@ -258,8 +249,8 @@ class _ArchMethodsXtensa(_ArchMethodsBase): | |||||||
|         return regs, extra_regs |         return regs, extra_regs | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def build_prstatus_data(tcb_addr, task_regs): |     def build_prstatus_data(tcb_addr, task_regs):  # type: (int, list[int]) -> Any | ||||||
|         return XtensaPrStatus.build({ |         return PrStatus.build({ | ||||||
|             'si_signo': 0, |             'si_signo': 0, | ||||||
|             'si_code': 0, |             'si_code': 0, | ||||||
|             'si_errno': 0, |             'si_errno': 0, | ||||||
| @@ -276,3 +267,11 @@ class _ArchMethodsXtensa(_ArchMethodsBase): | |||||||
|             'pr_cutime': 0, |             'pr_cutime': 0, | ||||||
|             'pr_cstime': 0, |             'pr_cstime': 0, | ||||||
|         }) + Int32ul[len(task_regs)].build(task_regs) |         }) + Int32ul[len(task_regs)].build(task_regs) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Esp32Methods(BaseTargetMethods, XtensaMethodsMixin): | ||||||
|  |     TARGET = 'esp32' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Esp32S2Methods(BaseTargetMethods, XtensaMethodsMixin): | ||||||
|  |     TARGET = 'esp32s2' | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| #!/usr/bin/env python | #!/usr/bin/env python | ||||||
| # | # | ||||||
| # ESP32 core dump Utility | # ESP-IDF Core Dump Utility | ||||||
|  |  | ||||||
| import argparse | import argparse | ||||||
| import logging | import logging | ||||||
| @@ -10,7 +10,7 @@ import sys | |||||||
| from shutil import copyfile | from shutil import copyfile | ||||||
|  |  | ||||||
| from construct import GreedyRange, Int32ul, Struct | from construct import GreedyRange, Int32ul, Struct | ||||||
| from corefile import __version__, xtensa | from corefile import RISCV_TARGETS, SUPPORTED_TARGETS, XTENSA_TARGETS, __version__, xtensa | ||||||
| from corefile.elf import TASK_STATUS_CORRECT, ElfFile, ElfSegment, ESPCoreDumpElfFile, EspTaskStatus | from corefile.elf import TASK_STATUS_CORRECT, ElfFile, ElfSegment, ESPCoreDumpElfFile, EspTaskStatus | ||||||
| from corefile.gdb import EspGDB | from corefile.gdb import EspGDB | ||||||
| from corefile.loader import ESPCoreDumpFileLoader, ESPCoreDumpFlashLoader | from corefile.loader import ESPCoreDumpFileLoader, ESPCoreDumpFlashLoader | ||||||
| @@ -28,32 +28,40 @@ except ImportError: | |||||||
|     sys.stderr.write('esptool is not found!\n') |     sys.stderr.write('esptool is not found!\n') | ||||||
|     sys.exit(2) |     sys.exit(2) | ||||||
|  |  | ||||||
|  | try: | ||||||
|  |     from typing import Optional, Tuple | ||||||
|  | except ImportError: | ||||||
|  |     pass | ||||||
|  |  | ||||||
| if os.name == 'nt': | if os.name == 'nt': | ||||||
|     CLOSE_FDS = False |     CLOSE_FDS = False | ||||||
| else: | else: | ||||||
|     CLOSE_FDS = True |     CLOSE_FDS = True | ||||||
|  |  | ||||||
|  |  | ||||||
| def load_aux_elf(elf_path):  # type: (str) -> (ElfFile, str) | def load_aux_elf(elf_path):  # type: (str) -> str | ||||||
|     """ |     """ | ||||||
|     Loads auxiliary ELF file and composes GDB command to read its symbols. |     Loads auxiliary ELF file and composes GDB command to read its symbols. | ||||||
|     """ |     """ | ||||||
|     elf = None |  | ||||||
|     sym_cmd = '' |     sym_cmd = '' | ||||||
|     if os.path.exists(elf_path): |     if os.path.exists(elf_path): | ||||||
|         elf = ElfFile(elf_path) |         elf = ElfFile(elf_path) | ||||||
|         for s in elf.sections: |         for s in elf.sections: | ||||||
|             if s.name == '.text': |             if s.name == '.text': | ||||||
|                 sym_cmd = 'add-symbol-file %s 0x%x' % (elf_path, s.addr) |                 sym_cmd = 'add-symbol-file %s 0x%x' % (elf_path, s.addr) | ||||||
|     return elf, sym_cmd |     return sym_cmd | ||||||
|  |  | ||||||
|  |  | ||||||
| def core_prepare(): | def get_core_dump_elf(e_machine=ESPCoreDumpFileLoader.ESP32): | ||||||
|  |     # type: (int) -> Tuple[str, Optional[str], Optional[list[str]]] | ||||||
|     loader = None |     loader = None | ||||||
|     core_filename = None |     core_filename = None | ||||||
|  |     target = None | ||||||
|  |     temp_files = None | ||||||
|  |  | ||||||
|     if not args.core: |     if not args.core: | ||||||
|         # Core file not specified, try to read core dump from flash. |         # Core file not specified, try to read core dump from flash. | ||||||
|         loader = ESPCoreDumpFlashLoader(args.off, port=args.port, baud=args.baud) |         loader = ESPCoreDumpFlashLoader(args.off, args.chip, port=args.port, baud=args.baud) | ||||||
|     elif args.core_format != 'elf': |     elif args.core_format != 'elf': | ||||||
|         # Core file specified, but not yet in ELF format. Convert it from raw or base64 into ELF. |         # Core file specified, but not yet in ELF format. Convert it from raw or base64 into ELF. | ||||||
|         loader = ESPCoreDumpFileLoader(args.core, args.core_format == 'b64') |         loader = ESPCoreDumpFileLoader(args.core, args.core_format == 'b64') | ||||||
| @@ -63,51 +71,86 @@ def core_prepare(): | |||||||
|  |  | ||||||
|     # Load/convert the core file |     # Load/convert the core file | ||||||
|     if loader: |     if loader: | ||||||
|         loader.create_corefile(exe_name=args.prog) |         loader.create_corefile(exe_name=args.prog, e_machine=e_machine) | ||||||
|         core_filename = loader.core_elf_file.name |         core_filename = loader.core_elf_file | ||||||
|         if args.save_core: |         if args.save_core: | ||||||
|             # We got asked to save the core file, make a copy |             # We got asked to save the core file, make a copy | ||||||
|             copyfile(loader.core_elf_file.name, args.save_core) |             copyfile(loader.core_elf_file, args.save_core) | ||||||
|  |         target = loader.target | ||||||
|  |         temp_files = loader.temp_files | ||||||
|  |  | ||||||
|     return core_filename, loader |     return core_filename, target, temp_files  # type: ignore | ||||||
|  |  | ||||||
|  |  | ||||||
| def dbg_corefile(): | def get_target():  # type: () -> str | ||||||
|  |     if args.chip != 'auto': | ||||||
|  |         return args.chip  # type: ignore | ||||||
|  |  | ||||||
|  |     inst = esptool.ESPLoader.detect_chip(args.port, args.baud) | ||||||
|  |     return inst.CHIP_NAME.lower().replace('-', '')  # type: ignore | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_gdb_path(target=None):  # type: (Optional[str]) -> str | ||||||
|  |     if args.gdb: | ||||||
|  |         return args.gdb  # type: ignore | ||||||
|  |  | ||||||
|  |     if target is None: | ||||||
|  |         target = get_target() | ||||||
|  |  | ||||||
|  |     if target in XTENSA_TARGETS: | ||||||
|  |         # For some reason, xtensa-esp32s2-elf-gdb will report some issue. | ||||||
|  |         # Use xtensa-esp32-elf-gdb instead. | ||||||
|  |         return 'xtensa-esp32-elf-gdb' | ||||||
|  |     if target in RISCV_TARGETS: | ||||||
|  |         return 'riscv32-esp-elf-gdb' | ||||||
|  |     raise ValueError('Invalid value: {}. For now we only support {}'.format(target, SUPPORTED_TARGETS)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_rom_elf_path(target=None):  # type: (Optional[str]) -> str | ||||||
|  |     if args.rom_elf: | ||||||
|  |         return args.rom_elf  # type: ignore | ||||||
|  |  | ||||||
|  |     if target is None: | ||||||
|  |         target = get_target() | ||||||
|  |  | ||||||
|  |     return '{}_rom.elf'.format(target) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def dbg_corefile():  # type: () -> Optional[list[str]] | ||||||
|     """ |     """ | ||||||
|     Command to load core dump from file or flash and run GDB debug session with it |     Command to load core dump from file or flash and run GDB debug session with it | ||||||
|     """ |     """ | ||||||
|     rom_elf, rom_sym_cmd = load_aux_elf(args.rom_elf) |     exe_elf = ESPCoreDumpElfFile(args.prog) | ||||||
|     core_filename, loader = core_prepare() |     core_elf_path, target, temp_files = get_core_dump_elf(e_machine=exe_elf.e_machine) | ||||||
|  |  | ||||||
|  |     rom_elf_path = get_rom_elf_path(target) | ||||||
|  |     rom_sym_cmd = load_aux_elf(rom_elf_path) | ||||||
|  |  | ||||||
|  |     gdb_tool = get_gdb_path(target) | ||||||
|     p = subprocess.Popen(bufsize=0, |     p = subprocess.Popen(bufsize=0, | ||||||
|                          args=[args.gdb, |                          args=[gdb_tool, | ||||||
|                                '--nw',  # ignore .gdbinit |                                '--nw',  # ignore .gdbinit | ||||||
|                                '--core=%s' % core_filename,  # core file, |                                '--core=%s' % core_elf_path,  # core file, | ||||||
|                                '-ex', rom_sym_cmd, |                                '-ex', rom_sym_cmd, | ||||||
|                                args.prog], |                                args.prog], | ||||||
|                          stdin=None, stdout=None, stderr=None, |                          stdin=None, stdout=None, stderr=None, | ||||||
|                          close_fds=CLOSE_FDS) |                          close_fds=CLOSE_FDS) | ||||||
|     p.wait() |     p.wait() | ||||||
|     print('Done!') |     print('Done!') | ||||||
|  |     return temp_files | ||||||
|  |  | ||||||
|  |  | ||||||
| def info_corefile(): | def info_corefile():  # type: () -> Optional[list[str]] | ||||||
|     """ |     """ | ||||||
|     Command to load core dump from file or flash and print it's data in user friendly form |     Command to load core dump from file or flash and print it's data in user friendly form | ||||||
|     """ |     """ | ||||||
|     core_filename, loader = core_prepare() |     exe_elf = ESPCoreDumpElfFile(args.prog) | ||||||
|  |     core_elf_path, target, temp_files = get_core_dump_elf(e_machine=exe_elf.e_machine) | ||||||
|     exe_elf = ElfFile(args.prog) |     core_elf = ESPCoreDumpElfFile(core_elf_path) | ||||||
|     core_elf = ESPCoreDumpElfFile(core_filename) |  | ||||||
|  |  | ||||||
|     if exe_elf.e_machine != core_elf.e_machine: |     if exe_elf.e_machine != core_elf.e_machine: | ||||||
|         raise ValueError('The arch should be the same between core elf and exe elf') |         raise ValueError('The arch should be the same between core elf and exe elf') | ||||||
|  |  | ||||||
|     if core_elf.e_machine == ESPCoreDumpElfFile.EM_XTENSA: |  | ||||||
|         exception_registers_info = xtensa.print_exc_regs_info |  | ||||||
|     else: |  | ||||||
|         raise NotImplementedError |  | ||||||
|  |  | ||||||
|     extra_note = None |     extra_note = None | ||||||
|     task_info = [] |     task_info = [] | ||||||
|     for seg in core_elf.note_segments: |     for seg in core_elf.note_segments: | ||||||
| @@ -119,8 +162,11 @@ def info_corefile(): | |||||||
|                 task_info.append(task_info_struct) |                 task_info.append(task_info_struct) | ||||||
|     print('===============================================================') |     print('===============================================================') | ||||||
|     print('==================== ESP32 CORE DUMP START ====================') |     print('==================== ESP32 CORE DUMP START ====================') | ||||||
|     rom_elf, rom_sym_cmd = load_aux_elf(args.rom_elf) |     rom_elf_path = get_rom_elf_path(target) | ||||||
|     gdb = EspGDB(args.gdb, [rom_sym_cmd], core_filename, args.prog, timeout_sec=args.gdb_timeout_sec) |     rom_sym_cmd = load_aux_elf(rom_elf_path) | ||||||
|  |  | ||||||
|  |     gdb_tool = get_gdb_path(target) | ||||||
|  |     gdb = EspGDB(gdb_tool, [rom_sym_cmd], core_elf_path, args.prog, timeout_sec=args.gdb_timeout_sec) | ||||||
|  |  | ||||||
|     extra_info = None |     extra_info = None | ||||||
|     if extra_note: |     if extra_note: | ||||||
| @@ -132,10 +178,12 @@ def info_corefile(): | |||||||
|             task_name = gdb.get_freertos_task_name(marker) |             task_name = gdb.get_freertos_task_name(marker) | ||||||
|             print("\nCrashed task handle: 0x%x, name: '%s', GDB name: 'process %d'" % (marker, task_name, marker)) |             print("\nCrashed task handle: 0x%x, name: '%s', GDB name: 'process %d'" % (marker, task_name, marker)) | ||||||
|     print('\n================== CURRENT THREAD REGISTERS ===================') |     print('\n================== CURRENT THREAD REGISTERS ===================') | ||||||
|     if extra_note and extra_info: |     # Only xtensa have exception registers | ||||||
|         exception_registers_info(extra_info) |     if exe_elf.e_machine == ESPCoreDumpElfFile.EM_XTENSA: | ||||||
|     else: |         if extra_note and extra_info: | ||||||
|         print('Exception registers have not been found!') |             xtensa.print_exc_regs_info(extra_info) | ||||||
|  |         else: | ||||||
|  |             print('Exception registers have not been found!') | ||||||
|     print(gdb.run_cmd('info registers')) |     print(gdb.run_cmd('info registers')) | ||||||
|     print('\n==================== CURRENT THREAD STACK =====================') |     print('\n==================== CURRENT THREAD STACK =====================') | ||||||
|     print(gdb.run_cmd('bt')) |     print(gdb.run_cmd('bt')) | ||||||
| @@ -235,10 +283,14 @@ def info_corefile(): | |||||||
|  |  | ||||||
|     del gdb |     del gdb | ||||||
|     print('Done!') |     print('Done!') | ||||||
|  |     return temp_files | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     parser = argparse.ArgumentParser(description='espcoredump.py v%s - ESP32 Core Dump Utility' % __version__) |     parser = argparse.ArgumentParser(description='espcoredump.py v%s - ESP32 Core Dump Utility' % __version__) | ||||||
|  |     parser.add_argument('--chip', default=os.environ.get('ESPTOOL_CHIP', 'auto'), | ||||||
|  |                         choices=['auto'] + SUPPORTED_TARGETS, | ||||||
|  |                         help='Target chip type') | ||||||
|     parser.add_argument('--port', '-p', default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT), |     parser.add_argument('--port', '-p', default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT), | ||||||
|                         help='Serial port device') |                         help='Serial port device') | ||||||
|     parser.add_argument('--baud', '-b', type=int, |     parser.add_argument('--baud', '-b', type=int, | ||||||
| @@ -250,20 +302,20 @@ if __name__ == '__main__': | |||||||
|     common_args = argparse.ArgumentParser(add_help=False) |     common_args = argparse.ArgumentParser(add_help=False) | ||||||
|     common_args.add_argument('--debug', '-d', type=int, default=3, |     common_args.add_argument('--debug', '-d', type=int, default=3, | ||||||
|                              help='Log level (0..3)') |                              help='Log level (0..3)') | ||||||
|     common_args.add_argument('--gdb', '-g', default='xtensa-esp32-elf-gdb', |     common_args.add_argument('--gdb', '-g', | ||||||
|                              help='Path to gdb') |                              help='Path to gdb') | ||||||
|     common_args.add_argument('--core', '-c', |     common_args.add_argument('--core', '-c', | ||||||
|                              help='Path to core dump file (if skipped core dump will be read from flash)') |                              help='Path to core dump file (if skipped core dump will be read from flash)') | ||||||
|     common_args.add_argument('--core-format', '-t', choices=['b64', 'elf', 'raw'], default='elf', |     common_args.add_argument('--core-format', '-t', choices=['b64', 'elf', 'raw'], default='elf', | ||||||
|                              help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), ' |                              help='File specified with "-c" is an ELF ("elf"), ' | ||||||
|                                   'raw (raw) or base64-encoded (b64) binary') |                                   'raw (raw) or base64-encoded (b64) binary') | ||||||
|     common_args.add_argument('--off', '-o', type=int, |     common_args.add_argument('--off', '-o', type=int, | ||||||
|                              help='Offset of coredump partition in flash (type "make partition_table" to see).') |                              help='Offset of coredump partition in flash (type "make partition_table" to see).') | ||||||
|     common_args.add_argument('--save-core', '-s', |     common_args.add_argument('--save-core', '-s', | ||||||
|                              help='Save core to file. Otherwise temporary core file will be deleted. ' |                              help='Save core to file. Otherwise temporary core file will be deleted. ' | ||||||
|                                   'Does not work with "-c"', ) |                                   'Does not work with "-c"', ) | ||||||
|     common_args.add_argument('--rom-elf', '-r', default='esp32_rom.elf', |     common_args.add_argument('--rom-elf', '-r', | ||||||
|                              help='Path to ROM ELF file.') |                              help='Path to ROM ELF file. Will use "<target>_rom.elf" if not specified') | ||||||
|     common_args.add_argument('prog', help='Path to program\'s ELF binary') |     common_args.add_argument('prog', help='Path to program\'s ELF binary') | ||||||
|  |  | ||||||
|     operations = parser.add_subparsers(dest='operation') |     operations = parser.add_subparsers(dest='operation') | ||||||
| @@ -291,9 +343,18 @@ if __name__ == '__main__': | |||||||
|     logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level) |     logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level) | ||||||
|  |  | ||||||
|     print('espcoredump.py v%s' % __version__) |     print('espcoredump.py v%s' % __version__) | ||||||
|     if args.operation == 'info_corefile': |     temp_core_files = None | ||||||
|         info_corefile() |     try: | ||||||
|     elif args.operation == 'dbg_corefile': |         if args.operation == 'info_corefile': | ||||||
|         dbg_corefile() |             temp_core_files = info_corefile() | ||||||
|     else: |         elif args.operation == 'dbg_corefile': | ||||||
|         raise ValueError('Please specify action, should be info_corefile or dbg_corefile') |             temp_core_files = dbg_corefile() | ||||||
|  |         else: | ||||||
|  |             raise ValueError('Please specify action, should be info_corefile or dbg_corefile') | ||||||
|  |     finally: | ||||||
|  |         if temp_core_files: | ||||||
|  |             for f in temp_core_files: | ||||||
|  |                 try: | ||||||
|  |                     os.remove(f) | ||||||
|  |                 except OSError: | ||||||
|  |                     pass | ||||||
|   | |||||||
| @@ -2,9 +2,9 @@ | |||||||
|  |  | ||||||
| { coverage debug sys \ | { coverage debug sys \ | ||||||
|     && coverage erase \ |     && coverage erase \ | ||||||
|     && coverage run -a --source=corefile ../espcoredump.py --gdb-timeout-sec 5 info_corefile -m -t b64 -c coredump.b64 -s core.elf test.elf &> output \ |     && coverage run -a --source=corefile ../espcoredump.py --chip esp32 --gdb-timeout-sec 5 info_corefile -m -t b64 -c coredump.b64 -s core.elf test.elf &> output \ | ||||||
|     && diff expected_output output \ |     && diff expected_output output \ | ||||||
|     && coverage run -a --source=corefile ../espcoredump.py --gdb-timeout-sec 5 info_corefile -m -t elf -c core.elf test.elf &> output2 \ |     && coverage run -a --source=corefile ../espcoredump.py --chip esp32 --gdb-timeout-sec 5 info_corefile -m -t elf -c core.elf test.elf &> output2 \ | ||||||
|     && diff expected_output output2 \ |     && diff expected_output output2 \ | ||||||
|     && coverage run -a --source=corefile ./test_espcoredump.py \ |     && coverage run -a --source=corefile ./test_espcoredump.py \ | ||||||
|     && coverage report ../corefile/elf.py ../corefile/gdb.py ../corefile/loader.py ../corefile/xtensa.py ../espcoredump.py \ |     && coverage report ../corefile/elf.py ../corefile/gdb.py ../corefile/loader.py ../corefile/xtensa.py ../espcoredump.py \ | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								tools/ci/mypy_ignore_list.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tools/ci/mypy_ignore_list.txt
									
									
									
									
									
										Normal file
									
								
							
		Reference in New Issue
	
	Block a user
	 Fu Hanxi
					Fu Hanxi