espcoredump.py 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. #!/usr/bin/env python
  2. #
  3. # ESP32 core dump Utility
  4. from __future__ import print_function
  5. from __future__ import unicode_literals
  6. from __future__ import division
  7. import sys
  8. try:
  9. from builtins import zip
  10. from builtins import str
  11. from builtins import range
  12. from past.utils import old_div
  13. from builtins import object
  14. except ImportError:
  15. print('Import has failed probably because of the missing "future" package. Please install all the packages for '
  16. 'interpreter {} from the $IDF_PATH/requirements.txt file.'.format(sys.executable))
  17. sys.exit(1)
  18. import os
  19. import argparse
  20. import subprocess
  21. import tempfile
  22. import struct
  23. import errno
  24. import base64
  25. import binascii
  26. import logging
  27. idf_path = os.getenv('IDF_PATH')
  28. if idf_path:
  29. sys.path.insert(0, os.path.join(idf_path, 'components', 'esptool_py', 'esptool'))
  30. try:
  31. import esptool
  32. except ImportError:
  33. print("Esptool is not found! Set proper $IDF_PATH in environment.")
  34. sys.exit(2)
  35. __version__ = "0.3-dev"
  36. if os.name == 'nt':
  37. CLOSE_FDS = False
  38. else:
  39. CLOSE_FDS = True
  40. class ESPCoreDumpError(RuntimeError):
  41. """Core dump runtime error class
  42. """
  43. def __init__(self, message):
  44. """Constructor for core dump error
  45. """
  46. super(ESPCoreDumpError, self).__init__(message)
  47. class BinStruct(object):
  48. """Binary structure representation
  49. Subclasses must specify actual structure layout using 'fields' and 'format' members.
  50. For example, the following subclass represents structure with two fields:
  51. f1 of size 2 bytes and 4 bytes f2. Little endian.
  52. class SomeStruct(BinStruct):
  53. fields = ("f1",
  54. "f2")
  55. format = "<HL"
  56. Then subclass can be used to initialize fields of underlaying structure and convert it to binary representation:
  57. f = open('some_struct.bin', 'wb')
  58. s = SomeStruct()
  59. s.f1 = 1
  60. s.f2 = 10
  61. f.write(s.dump())
  62. f.close()
  63. """
  64. def __init__(self, buf=None):
  65. """Base constructor for binary structure objects
  66. """
  67. if buf is None:
  68. buf = b'\0' * self.sizeof()
  69. fields = struct.unpack(self.__class__.format, buf[:self.sizeof()])
  70. self.__dict__.update(zip(self.__class__.fields, fields))
  71. def sizeof(self):
  72. """Returns the size of the structure represented by specific subclass
  73. """
  74. return struct.calcsize(self.__class__.format)
  75. def dump(self):
  76. """Returns binary representation of structure
  77. """
  78. keys = self.__class__.fields
  79. return struct.pack(self.__class__.format, *(self.__dict__[k] for k in keys))
  80. class Elf32FileHeader(BinStruct):
  81. """ELF32 file header
  82. """
  83. fields = ("e_ident",
  84. "e_type",
  85. "e_machine",
  86. "e_version",
  87. "e_entry",
  88. "e_phoff",
  89. "e_shoff",
  90. "e_flags",
  91. "e_ehsize",
  92. "e_phentsize",
  93. "e_phnum",
  94. "e_shentsize",
  95. "e_shnum",
  96. "e_shstrndx")
  97. format = "<16sHHLLLLLHHHHHH"
  98. def __init__(self, buf=None):
  99. """Constructor for ELF32 file header structure
  100. """
  101. super(Elf32FileHeader, self).__init__(buf)
  102. if buf is None:
  103. # Fill in sane ELF header for LSB32
  104. self.e_ident = b"\x7fELF\1\1\1\0\0\0\0\0\0\0\0\0"
  105. self.e_version = ESPCoreDumpElfFile.EV_CURRENT
  106. self.e_ehsize = self.sizeof()
  107. class Elf32ProgramHeader(BinStruct):
  108. """ELF32 program header
  109. """
  110. fields = ("p_type",
  111. "p_offset",
  112. "p_vaddr",
  113. "p_paddr",
  114. "p_filesz",
  115. "p_memsz",
  116. "p_flags",
  117. "p_align")
  118. format = "<LLLLLLLL"
  119. class Elf32NoteDesc(object):
  120. """ELF32 note descriptor
  121. """
  122. def __init__(self, name, type, desc):
  123. """Constructor for ELF32 note descriptor
  124. """
  125. self.name = bytearray(name, encoding='ascii') + b'\0'
  126. self.type = type
  127. self.desc = desc
  128. def dump(self):
  129. """Returns binary representation of ELF32 note descriptor
  130. """
  131. hdr = struct.pack("<LLL", len(self.name), len(self.desc), self.type)
  132. # pad for 4 byte alignment
  133. name = self.name + ((4 - len(self.name)) % 4) * b'\0'
  134. desc = self.desc + ((4 - len(self.desc)) % 4) * b'\0'
  135. return hdr + name + desc
  136. class XtensaPrStatus(BinStruct):
  137. """Xtensa program status structure"""
  138. fields = ("si_signo", "si_code", "si_errno",
  139. "pr_cursig",
  140. "pr_pad0",
  141. "pr_sigpend",
  142. "pr_sighold",
  143. "pr_pid",
  144. "pr_ppid",
  145. "pr_pgrp",
  146. "pr_sid",
  147. "pr_utime",
  148. "pr_stime",
  149. "pr_cutime",
  150. "pr_cstime")
  151. format = "<3LHHLLLLLLQQQQ"
  152. class ESPCoreDumpSegment(esptool.ImageSegment):
  153. """ Wrapper class for a program segment in core ELF file, has a segment
  154. type and flags as well as the common properties of an ImageSegment.
  155. """
  156. # segment flags
  157. PF_X = 0x1 # Execute
  158. PF_W = 0x2 # Write
  159. PF_R = 0x4 # Read
  160. def __init__(self, addr, data, type, flags):
  161. """Constructor for program segment
  162. """
  163. super(ESPCoreDumpSegment, self).__init__(addr, data)
  164. self.flags = flags
  165. self.type = type
  166. def __repr__(self):
  167. """Returns string representation of program segment
  168. """
  169. return "%s %s %s" % (self.type, self.attr_str(), super(ESPCoreDumpSegment, self).__repr__())
  170. def attr_str(self):
  171. """Returns string representation of program segment attributes
  172. """
  173. str = ''
  174. if self.flags & self.PF_R:
  175. str += 'R'
  176. else:
  177. str += ' '
  178. if self.flags & self.PF_W:
  179. str += 'W'
  180. else:
  181. str += ' '
  182. if self.flags & self.PF_X:
  183. str += 'X'
  184. else:
  185. str += ' '
  186. return str
  187. class ESPCoreDumpSection(esptool.ELFSection):
  188. """ Wrapper class for a section in core ELF file, has a section
  189. flags as well as the common properties of an esptool.ELFSection.
  190. """
  191. # section flags
  192. SHF_WRITE = 0x1
  193. SHF_ALLOC = 0x2
  194. SHF_EXECINSTR = 0x4
  195. def __init__(self, name, addr, data, flags):
  196. """Constructor for section
  197. """
  198. super(ESPCoreDumpSection, self).__init__(name, addr, data)
  199. self.flags = flags
  200. def __repr__(self):
  201. """Returns string representation of section
  202. """
  203. return "%s %s" % (super(ESPCoreDumpSection, self).__repr__(), self.attr_str())
  204. def attr_str(self):
  205. """Returns string representation of section attributes
  206. """
  207. str = "R"
  208. if self.flags & self.SHF_WRITE:
  209. str += 'W'
  210. else:
  211. str += ' '
  212. if self.flags & self.SHF_EXECINSTR:
  213. str += 'X'
  214. else:
  215. str += ' '
  216. if self.flags & self.SHF_ALLOC:
  217. str += 'A'
  218. else:
  219. str += ' '
  220. return str
  221. class ESPCoreDumpElfFile(esptool.ELFFile):
  222. """ Wrapper class for core dump ELF file
  223. """
  224. # ELF file type
  225. ET_NONE = 0x0 # No file type
  226. ET_REL = 0x1 # Relocatable file
  227. ET_EXEC = 0x2 # Executable file
  228. ET_DYN = 0x3 # Shared object file
  229. ET_CORE = 0x4 # Core file
  230. # ELF file version
  231. EV_NONE = 0x0
  232. EV_CURRENT = 0x1
  233. # ELF file machine type
  234. EM_NONE = 0x0
  235. EM_XTENSA = 0x5E
  236. # section types
  237. SEC_TYPE_PROGBITS = 0x01
  238. SEC_TYPE_STRTAB = 0x03
  239. # special section index
  240. SHN_UNDEF = 0x0
  241. # program segment types
  242. PT_NULL = 0x0
  243. PT_LOAD = 0x1
  244. PT_DYNAMIC = 0x2
  245. PT_INTERP = 0x3
  246. PT_NOTE = 0x4
  247. PT_SHLIB = 0x5
  248. PT_PHDR = 0x6
  249. def __init__(self, name=None):
  250. """Constructor for core dump ELF file
  251. """
  252. if name:
  253. super(ESPCoreDumpElfFile, self).__init__(name)
  254. else:
  255. self.sections = []
  256. self.program_segments = []
  257. self.e_type = self.ET_NONE
  258. self.e_machine = self.EM_NONE
  259. def _read_elf_file(self, f):
  260. """Reads core dump from ELF file
  261. """
  262. # read the ELF file header
  263. LEN_FILE_HEADER = 0x34
  264. try:
  265. (ident,type,machine,_version,
  266. self.entrypoint,phoff,shoff,_flags,
  267. _ehsize, phentsize,phnum,_shentsize,
  268. shnum,shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER))
  269. except struct.error as e:
  270. raise ESPCoreDumpError("Failed to read a valid ELF header from %s: %s" % (self.name, e))
  271. if bytearray([ident[0]]) != b'\x7f' or ident[1:4] != b'ELF':
  272. raise ESPCoreDumpError("%s has invalid ELF magic header" % self.name)
  273. if machine != self.EM_XTENSA:
  274. raise ESPCoreDumpError("%s does not appear to be an Xtensa ELF file. e_machine=%04x" % (self.name, machine))
  275. self.e_type = type
  276. self.e_machine = machine
  277. self.sections = []
  278. self.program_segments = []
  279. if shnum > 0:
  280. self._read_sections(f, shoff, shstrndx)
  281. if phnum > 0:
  282. self._read_program_segments(f, phoff, phentsize, phnum)
  283. def _read_sections(self, f, section_header_offs, shstrndx):
  284. """Reads core dump sections from ELF file
  285. """
  286. f.seek(section_header_offs)
  287. section_header = f.read()
  288. LEN_SEC_HEADER = 0x28
  289. if len(section_header) == 0:
  290. raise ESPCoreDumpError("No section header found at offset %04x in ELF file." % section_header_offs)
  291. if len(section_header) % LEN_SEC_HEADER != 0:
  292. logging.warning('Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER))
  293. # walk through the section header and extract all sections
  294. section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER)
  295. def read_section_header(offs):
  296. name_offs,sec_type,flags,lma,sec_offs,size = struct.unpack_from("<LLLLLL", section_header[offs:])
  297. return (name_offs, sec_type, flags, lma, size, sec_offs)
  298. all_sections = [read_section_header(offs) for offs in section_header_offsets]
  299. prog_sections = [s for s in all_sections if s[1] == esptool.ELFFile.SEC_TYPE_PROGBITS]
  300. # search for the string table section
  301. if not shstrndx * LEN_SEC_HEADER in section_header_offsets:
  302. raise ESPCoreDumpError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
  303. _,sec_type,_,_,sec_size,sec_offs = read_section_header(shstrndx * LEN_SEC_HEADER)
  304. if sec_type != esptool.ELFFile.SEC_TYPE_STRTAB:
  305. logging.warning('ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
  306. f.seek(sec_offs)
  307. string_table = f.read(sec_size)
  308. # build the real list of ELFSections by reading the actual section names from the
  309. # string table section, and actual data for each section from the ELF file itself
  310. def lookup_string(offs):
  311. raw = string_table[offs:]
  312. return raw[:raw.index(b'\x00')]
  313. def read_data(offs,size):
  314. f.seek(offs)
  315. return f.read(size)
  316. prog_sections = [ESPCoreDumpSection(lookup_string(n_offs), lma, read_data(offs, size), flags)
  317. for (n_offs, _type, flags, lma, size, offs) in prog_sections if lma != 0]
  318. self.sections = prog_sections
  319. def _read_program_segments(self, f, seg_table_offs, entsz, num):
  320. """Reads core dump program segments from ELF file
  321. """
  322. f.seek(seg_table_offs)
  323. seg_table = f.read(entsz * num)
  324. LEN_SEG_HEADER = 0x20
  325. if len(seg_table) == 0:
  326. raise ESPCoreDumpError("No program header table found at offset %04x in ELF file." % seg_table_offs)
  327. if len(seg_table) % LEN_SEG_HEADER != 0:
  328. logging.warning('Unexpected ELF program header table length %04x is not mod-%02x' % (len(seg_table),LEN_SEG_HEADER))
  329. # walk through the program segment table and extract all segments
  330. seg_table_offs = range(0, len(seg_table), LEN_SEG_HEADER)
  331. def read_program_header(offs):
  332. type,offset,vaddr,_paddr,filesz,_memsz,flags,_align = struct.unpack_from("<LLLLLLLL", seg_table[offs:])
  333. return (type,offset,vaddr,filesz,flags)
  334. prog_segments = [read_program_header(offs) for offs in seg_table_offs]
  335. # build the real list of ImageSegment by reading actual data for each segment from the ELF file itself
  336. def read_data(offs,size):
  337. f.seek(offs)
  338. return f.read(size)
  339. self.program_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags)
  340. for (type, offset, vaddr, filesz,flags) in prog_segments if vaddr != 0]
  341. def add_program_segment(self, addr, data, type, flags):
  342. """Adds new program segment
  343. """
  344. # TODO: currently merging with existing segments is not supported
  345. data_sz = len(data)
  346. # check for overlapping and merge if needed
  347. if addr != 0 and data_sz != 0:
  348. for ps in self.program_segments:
  349. seg_len = len(ps.data)
  350. if addr >= ps.addr and addr < (ps.addr + seg_len):
  351. raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." %
  352. (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1))
  353. if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len):
  354. raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." %
  355. (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1))
  356. # append
  357. self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags))
  358. def dump(self, f):
  359. """Write core dump contents to file
  360. """
  361. # TODO: currently dumps only program segments.
  362. # dumping sections is not supported yet
  363. # write ELF header
  364. ehdr = Elf32FileHeader()
  365. ehdr.e_type = self.e_type
  366. ehdr.e_machine = self.e_machine
  367. ehdr.e_entry = 0
  368. ehdr.e_phoff = ehdr.sizeof()
  369. ehdr.e_shoff = 0
  370. ehdr.e_flags = 0
  371. ehdr.e_phentsize = Elf32ProgramHeader().sizeof()
  372. ehdr.e_phnum = len(self.program_segments)
  373. ehdr.e_shentsize = 0
  374. ehdr.e_shnum = 0
  375. ehdr.e_shstrndx = self.SHN_UNDEF
  376. f.write(ehdr.dump())
  377. # write program header table
  378. cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize
  379. for i in range(len(self.program_segments)):
  380. phdr = Elf32ProgramHeader()
  381. phdr.p_type = self.program_segments[i].type
  382. phdr.p_offset = cur_off
  383. phdr.p_vaddr = self.program_segments[i].addr
  384. phdr.p_paddr = phdr.p_vaddr # TODO
  385. phdr.p_filesz = len(self.program_segments[i].data)
  386. phdr.p_memsz = phdr.p_filesz # TODO
  387. phdr.p_flags = self.program_segments[i].flags
  388. phdr.p_align = 0 # TODO
  389. f.write(phdr.dump())
  390. cur_off += phdr.p_filesz
  391. # write program segments
  392. for i in range(len(self.program_segments)):
  393. f.write(self.program_segments[i].data)
  394. class ESPCoreDumpLoaderError(ESPCoreDumpError):
  395. """Core dump loader error class
  396. """
  397. def __init__(self, message):
  398. """Constructor for core dump loader error
  399. """
  400. super(ESPCoreDumpLoaderError, self).__init__(message)
  401. class ESPCoreDumpLoader(object):
  402. """Core dump loader base class
  403. """
  404. ESP32_COREDUMP_VESION = 1
  405. ESP32_COREDUMP_HDR_FMT = '<4L'
  406. ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT)
  407. ESP32_COREDUMP_TSK_HDR_FMT = '<3L'
  408. ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT)
  409. def __init__(self):
  410. """Base constructor for core dump loader
  411. """
  412. self.fcore = None
  413. def _get_registers_from_stack(self, data, grows_down):
  414. """Returns list of registers (in GDB format) from xtensa stack frame
  415. """
  416. # from "gdb/xtensa-tdep.h"
  417. # typedef struct
  418. # {
  419. # 0 xtensa_elf_greg_t pc;
  420. # 1 xtensa_elf_greg_t ps;
  421. # 2 xtensa_elf_greg_t lbeg;
  422. # 3 xtensa_elf_greg_t lend;
  423. # 4 xtensa_elf_greg_t lcount;
  424. # 5 xtensa_elf_greg_t sar;
  425. # 6 xtensa_elf_greg_t windowstart;
  426. # 7 xtensa_elf_greg_t windowbase;
  427. # 8..63 xtensa_elf_greg_t reserved[8+48];
  428. # 64 xtensa_elf_greg_t ar[64];
  429. # } xtensa_elf_gregset_t;
  430. REG_PC_IDX = 0
  431. REG_PS_IDX = 1
  432. REG_LB_IDX = 2
  433. REG_LE_IDX = 3
  434. REG_LC_IDX = 4
  435. REG_SAR_IDX = 5
  436. # REG_WS_IDX = 6
  437. # REG_WB_IDX = 7
  438. REG_AR_START_IDX = 64
  439. # REG_AR_NUM = 64
  440. # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128,
  441. # but gdb complanis when it less then 129
  442. REG_NUM = 129
  443. # XT_SOL_EXIT = 0
  444. XT_SOL_PC = 1
  445. XT_SOL_PS = 2
  446. # XT_SOL_NEXT = 3
  447. XT_SOL_AR_START = 4
  448. XT_SOL_AR_NUM = 4
  449. # XT_SOL_FRMSZ = 8
  450. XT_STK_EXIT = 0
  451. XT_STK_PC = 1
  452. XT_STK_PS = 2
  453. XT_STK_AR_START = 3
  454. XT_STK_AR_NUM = 16
  455. XT_STK_SAR = 19
  456. # XT_STK_EXCCAUSE = 20
  457. # XT_STK_EXCVADDR = 21
  458. XT_STK_LBEG = 22
  459. XT_STK_LEND = 23
  460. XT_STK_LCOUNT = 24
  461. XT_STK_FRMSZ = 25
  462. regs = [0] * REG_NUM
  463. # TODO: support for growing up stacks
  464. if not grows_down:
  465. raise ESPCoreDumpLoaderError("Growing up stacks are not supported for now!")
  466. ex_struct = "<%dL" % XT_STK_FRMSZ
  467. if len(data) < struct.calcsize(ex_struct):
  468. raise ESPCoreDumpLoaderError("Too small stack to keep frame: %d bytes!" % len(data))
  469. stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)])
  470. # Stack frame type indicator is always the first item
  471. rc = stack[XT_STK_EXIT]
  472. if rc != 0:
  473. regs[REG_PC_IDX] = stack[XT_STK_PC]
  474. regs[REG_PS_IDX] = stack[XT_STK_PS]
  475. for i in range(XT_STK_AR_NUM):
  476. regs[REG_AR_START_IDX + i] = stack[XT_STK_AR_START + i]
  477. regs[REG_SAR_IDX] = stack[XT_STK_SAR]
  478. regs[REG_LB_IDX] = stack[XT_STK_LBEG]
  479. regs[REG_LE_IDX] = stack[XT_STK_LEND]
  480. regs[REG_LC_IDX] = stack[XT_STK_LCOUNT]
  481. # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set
  482. # and GDB can not unwind callstack properly (it implies not windowed call0)
  483. if regs[REG_PS_IDX] & (1 << 5):
  484. regs[REG_PS_IDX] &= ~(1 << 4)
  485. else:
  486. regs[REG_PC_IDX] = stack[XT_SOL_PC]
  487. regs[REG_PS_IDX] = stack[XT_SOL_PS]
  488. for i in range(XT_SOL_AR_NUM):
  489. regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i]
  490. # nxt = stack[XT_SOL_NEXT]
  491. # TODO: remove magic hack with saved PC to get proper value
  492. regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000)
  493. if regs[REG_PC_IDX] & 0x80000000:
  494. regs[REG_PC_IDX] = (regs[REG_PC_IDX] & 0x3fffffff) | 0x40000000
  495. if regs[REG_AR_START_IDX + 0] & 0x80000000:
  496. regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000
  497. return regs
  498. def remove_tmp_file(self, fname):
  499. """Silently removes temporary file
  500. """
  501. try:
  502. os.remove(fname)
  503. except OSError as e:
  504. if e.errno != errno.ENOENT:
  505. logging.warning("Failed to remove temp file '%s' (%d)!" % (fname, e.errno))
  506. def cleanup(self):
  507. """Cleans up loader resources
  508. """
  509. if self.fcore:
  510. self.fcore.close()
  511. if self.fcore_name:
  512. self.remove_tmp_file(self.fcore_name)
  513. def create_corefile(self, core_fname=None, off=0, rom_elf=None):
  514. """Creates core dump ELF file
  515. """
  516. core_off = off
  517. data = self.read_data(core_off, self.ESP32_COREDUMP_HDR_SZ)
  518. tot_len,coredump_ver,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_HDR_FMT, data)
  519. if coredump_ver > self.ESP32_COREDUMP_VESION:
  520. raise ESPCoreDumpLoaderError("Core dump version '%d' is not supported! Should be up to '%d'." % (coredump_ver, self.ESP32_COREDUMP_VESION))
  521. tcbsz_aligned = tcbsz
  522. if tcbsz_aligned % 4:
  523. tcbsz_aligned = 4 * (old_div(tcbsz_aligned,4) + 1)
  524. core_off += self.ESP32_COREDUMP_HDR_SZ
  525. core_elf = ESPCoreDumpElfFile()
  526. notes = b''
  527. for i in range(task_num):
  528. data = self.read_data(core_off, self.ESP32_COREDUMP_TSK_HDR_SZ)
  529. tcb_addr,stack_top,stack_end = struct.unpack_from(self.ESP32_COREDUMP_TSK_HDR_FMT, data)
  530. if stack_end > stack_top:
  531. stack_len = stack_end - stack_top
  532. stack_base = stack_top
  533. else:
  534. stack_len = stack_top - stack_end
  535. stack_base = stack_end
  536. stack_len_aligned = stack_len
  537. if stack_len_aligned % 4:
  538. stack_len_aligned = 4 * (old_div(stack_len_aligned,4) + 1)
  539. core_off += self.ESP32_COREDUMP_TSK_HDR_SZ
  540. logging.info("Read TCB %d bytes @ 0x%x" % (tcbsz_aligned, tcb_addr))
  541. data = self.read_data(core_off, tcbsz_aligned)
  542. try:
  543. if tcbsz != tcbsz_aligned:
  544. core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned],
  545. ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  546. else:
  547. core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  548. except ESPCoreDumpError as e:
  549. logging.warning("Skip TCB %d bytes @ 0x%x. (Reason: %s)" % (tcbsz_aligned, tcb_addr, e))
  550. core_off += tcbsz_aligned
  551. logging.info("Read stack %d bytes @ 0x%x" % (stack_len_aligned, stack_base))
  552. data = self.read_data(core_off, stack_len_aligned)
  553. if stack_len != stack_len_aligned:
  554. data = data[:stack_len - stack_len_aligned]
  555. try:
  556. core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  557. except ESPCoreDumpError as e:
  558. logging.warning("Skip task's (%x) stack %d bytes @ 0x%x. (Reason: %s)" % (tcb_addr, stack_len_aligned, stack_base, e))
  559. core_off += stack_len_aligned
  560. try:
  561. logging.info("Stack start_end: 0x%x @ 0x%x" % (stack_top, stack_end))
  562. task_regs = self._get_registers_from_stack(data, stack_end > stack_top)
  563. except Exception as e:
  564. print(e)
  565. return None
  566. prstatus = XtensaPrStatus()
  567. prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task
  568. prstatus.pr_pid = i # TODO: use pid assigned by OS
  569. note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump()
  570. notes += note
  571. # add notes
  572. try:
  573. core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0)
  574. except ESPCoreDumpError as e:
  575. logging.warning("Skip NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(notes), 0, e))
  576. # add ROM text sections
  577. if rom_elf:
  578. for ps in rom_elf.program_segments:
  579. if ps.flags & ESPCoreDumpSegment.PF_X:
  580. try:
  581. core_elf.add_program_segment(ps.addr, ps.data, ESPCoreDumpElfFile.PT_LOAD, ps.flags)
  582. except ESPCoreDumpError as e:
  583. logging.warning("Skip ROM segment %d bytes @ 0x%x. (Reason: %s)" % (len(ps.data), ps.addr, e))
  584. core_elf.e_type = ESPCoreDumpElfFile.ET_CORE
  585. core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA
  586. if core_fname:
  587. fce = open(core_fname, 'wb')
  588. else:
  589. fhnd,core_fname = tempfile.mkstemp()
  590. fce = os.fdopen(fhnd, 'wb')
  591. core_elf.dump(fce)
  592. fce.close()
  593. return core_fname
  594. def read_data(self, off, sz):
  595. """Reads data from raw core dump got from flash or UART
  596. """
  597. self.fcore.seek(off)
  598. data = self.fcore.read(sz)
  599. return data
  600. class ESPCoreDumpFileLoader(ESPCoreDumpLoader):
  601. """Core dump file loader class
  602. """
  603. def __init__(self, path, b64=False):
  604. """Constructor for core dump file loader
  605. """
  606. super(ESPCoreDumpFileLoader, self).__init__()
  607. self.fcore = self._load_coredump(path, b64)
  608. def _load_coredump(self, path, b64):
  609. """Loads core dump from (raw binary or base64-encoded) file
  610. """
  611. self.fcore_name = None
  612. if b64:
  613. fhnd,self.fcore_name = tempfile.mkstemp()
  614. fcore = os.fdopen(fhnd, 'wb')
  615. fb64 = open(path, 'rb')
  616. try:
  617. while True:
  618. line = fb64.readline()
  619. if len(line) == 0:
  620. break
  621. data = base64.standard_b64decode(line.rstrip(b'\r\n'))
  622. fcore.write(data)
  623. fcore.close()
  624. fcore = open(self.fcore_name, 'rb')
  625. except Exception as e:
  626. if self.fcore_name:
  627. self.remove_tmp_file(self.fcore_name)
  628. raise e
  629. finally:
  630. fb64.close()
  631. else:
  632. fcore = open(path, 'rb')
  633. return fcore
  634. class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
  635. """Core dump flash loader class
  636. """
  637. ESP32_COREDUMP_FLASH_CRC_FMT = '<L'
  638. ESP32_COREDUMP_FLASH_CRC_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_CRC_FMT)
  639. ESP32_COREDUMP_FLASH_LEN_FMT = '<L'
  640. ESP32_COREDUMP_FLASH_LEN_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_LEN_FMT)
  641. def __init__(self, off, tool_path=None, chip='esp32', port=None, baud=None):
  642. """Constructor for core dump flash loader
  643. """
  644. super(ESPCoreDumpFlashLoader, self).__init__()
  645. if not tool_path:
  646. self.path = esptool.__file__
  647. _,e = os.path.splitext(self.path)
  648. if e == '.pyc':
  649. self.path = self.path[:-1]
  650. else:
  651. self.path = tool_path
  652. self.port = port
  653. self.baud = baud
  654. self.chip = chip
  655. self.dump_sz = 0
  656. self.fcore = self._load_coredump(off)
  657. def _load_coredump(self, off):
  658. """Loads core dump from flash
  659. """
  660. tool_args = [sys.executable, self.path, '-c', self.chip]
  661. if self.port:
  662. tool_args.extend(['-p', self.port])
  663. if self.baud:
  664. tool_args.extend(['-b', str(self.baud)])
  665. tool_args.extend(['read_flash', str(off), str(self.ESP32_COREDUMP_FLASH_LEN_SZ), ''])
  666. self.fcore_name = None
  667. try:
  668. fhnd,self.fcore_name = tempfile.mkstemp()
  669. tool_args[-1] = self.fcore_name
  670. # read core dump length
  671. et_out = subprocess.check_output(tool_args)
  672. print(et_out.decode('utf-8'))
  673. f = os.fdopen(fhnd, 'rb')
  674. self.dump_sz = self._read_core_dump_length(f)
  675. # read core dump
  676. tool_args[-2] = str(self. dump_sz)
  677. et_out = subprocess.check_output(tool_args)
  678. print(et_out.decode('utf-8'))
  679. except subprocess.CalledProcessError as e:
  680. logging.error("esptool script execution failed with err %d" % e.returncode)
  681. logging.info("Command ran: '%s'" % e.cmd)
  682. logging.info("Command out:")
  683. logging.info(e.output)
  684. if self.fcore_name:
  685. self.remove_tmp_file(self.fcore_name)
  686. raise e
  687. return f
  688. def _read_core_dump_length(self, f):
  689. """Reads core dump length
  690. """
  691. data = f.read(self.ESP32_COREDUMP_FLASH_LEN_SZ)
  692. tot_len, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_LEN_FMT, data)
  693. return tot_len
  694. def create_corefile(self, core_fname=None, rom_elf=None):
  695. """Checks flash coredump data integrity and creates ELF file
  696. """
  697. data = self.read_data(self.dump_sz - self.ESP32_COREDUMP_FLASH_CRC_SZ, self.ESP32_COREDUMP_FLASH_CRC_SZ)
  698. dump_crc, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_CRC_FMT, data)
  699. data = self.read_data(0, self.dump_sz - self.ESP32_COREDUMP_FLASH_CRC_SZ)
  700. data_crc = binascii.crc32(data) & 0xffffffff
  701. if dump_crc != data_crc:
  702. raise ESPCoreDumpLoaderError("Invalid core dump CRC %x, should be %x" % (data_crc, dump_crc))
  703. return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname)
  704. class GDBMIOutRecordHandler(object):
  705. """GDB/MI output record handler base class
  706. """
  707. TAG = ''
  708. def __init__(self, f, verbose=False):
  709. """Base constructor for GDB/MI output record handler
  710. """
  711. self.verbose = verbose
  712. def execute(self, ln):
  713. """Base method to execute GDB/MI output record handler function
  714. """
  715. if self.verbose:
  716. print("%s.execute: [[%s]]" % (self.__class__.__name__, ln))
  717. class GDBMIOutStreamHandler(GDBMIOutRecordHandler):
  718. """GDB/MI output stream handler class
  719. """
  720. def __init__(self, f, verbose=False):
  721. """Constructor for GDB/MI output stream handler
  722. """
  723. super(GDBMIOutStreamHandler, self).__init__(None, verbose)
  724. self.func = f
  725. def execute(self, ln):
  726. """Executes GDB/MI output stream handler function
  727. """
  728. GDBMIOutRecordHandler.execute(self, ln)
  729. if self.func:
  730. # remove TAG / quotes and replace c-string \n with actual NL
  731. self.func(ln[1:].strip('"').replace('\\n', '\n').replace('\\t', '\t'))
  732. class GDBMIResultHandler(GDBMIOutRecordHandler):
  733. """GDB/MI result handler class
  734. """
  735. TAG = '^'
  736. RC_DONE = 'done'
  737. RC_RUNNING = 'running'
  738. RC_CONNECTED = 'connected'
  739. RC_ERROR = 'error'
  740. RC_EXIT = 'exit'
  741. def __init__(self, verbose=False):
  742. """Constructor for GDB/MI result handler
  743. """
  744. super(GDBMIResultHandler, self).__init__(None, verbose)
  745. self.result_class = None
  746. self.result_str = None
  747. def _parse_rc(self, ln, rc):
  748. """Parses result code
  749. """
  750. rc_str = "{0}{1}".format(self.TAG, rc)
  751. if ln.startswith(rc_str):
  752. self.result_class = rc
  753. sl = len(rc_str)
  754. if len(ln) > sl:
  755. self.result_str = ln[sl:]
  756. if self.result_str.startswith(','):
  757. self.result_str = self.result_str[1:]
  758. else:
  759. logging.error("Invalid result format: '%s'" % ln)
  760. else:
  761. self.result_str = ''
  762. return True
  763. return False
  764. def execute(self, ln):
  765. """Executes GDB/MI result handler function
  766. """
  767. GDBMIOutRecordHandler.execute(self, ln)
  768. if self._parse_rc(ln, self.RC_DONE):
  769. return
  770. if self._parse_rc(ln, self.RC_RUNNING):
  771. return
  772. if self._parse_rc(ln, self.RC_CONNECTED):
  773. return
  774. if self._parse_rc(ln, self.RC_ERROR):
  775. return
  776. if self._parse_rc(ln, self.RC_EXIT):
  777. return
  778. logging.error("Unknown GDB/MI result: '%s'" % ln)
  779. class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler):
  780. """GDB/MI console stream handler class
  781. """
  782. TAG = '~'
  783. def load_aux_elf(elf_path):
  784. """ Loads auxilary ELF file and composes GDB command to read its symbols
  785. """
  786. elf = None
  787. sym_cmd = ''
  788. if os.path.exists(elf_path):
  789. elf = ESPCoreDumpElfFile(elf_path)
  790. for s in elf.sections:
  791. if s.name == '.text':
  792. sym_cmd = 'add-symbol-file %s 0x%x' % (elf_path, s.addr)
  793. return (elf, sym_cmd)
  794. def dbg_corefile(args):
  795. """ Command to load core dump from file or flash and run GDB debug session with it
  796. """
  797. global CLOSE_FDS
  798. loader = None
  799. rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
  800. if not args.core:
  801. loader = ESPCoreDumpFlashLoader(args.off, port=args.port)
  802. core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
  803. if not core_fname:
  804. logging.error("Failed to create corefile!")
  805. loader.cleanup()
  806. return
  807. else:
  808. core_fname = args.core
  809. if args.core_format and args.core_format != 'elf':
  810. loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
  811. core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
  812. if not core_fname:
  813. logging.error("Failed to create corefile!")
  814. loader.cleanup()
  815. return
  816. p = subprocess.Popen(bufsize=0,
  817. args=[args.gdb,
  818. '--nw', # ignore .gdbinit
  819. '--core=%s' % core_fname, # core file,
  820. '-ex', rom_sym_cmd,
  821. args.prog
  822. ],
  823. stdin=None, stdout=None, stderr=None,
  824. close_fds=CLOSE_FDS
  825. )
  826. p.wait()
  827. if loader:
  828. if not args.core and not args.save_core:
  829. loader.remove_tmp_file(core_fname)
  830. loader.cleanup()
  831. print('Done!')
  832. def info_corefile(args):
  833. """ Command to load core dump from file or flash and print it's data in user friendly form
  834. """
  835. global CLOSE_FDS
  836. def gdbmi_console_stream_handler(ln):
  837. sys.stdout.write(ln)
  838. sys.stdout.flush()
  839. def gdbmi_read2prompt(f, out_handlers=None):
  840. while True:
  841. ln = f.readline().decode('utf-8').rstrip(' \r\n')
  842. if ln == '(gdb)':
  843. break
  844. elif len(ln) == 0:
  845. break
  846. elif out_handlers:
  847. for h in out_handlers:
  848. if ln.startswith(out_handlers[h].TAG):
  849. out_handlers[h].execute(ln)
  850. break
  851. def gdbmi_start(handlers, gdb_cmds):
  852. gdb_args = [args.gdb,
  853. '--quiet', # inhibit dumping info at start-up
  854. '--nx', # inhibit window interface
  855. '--nw', # ignore .gdbinit
  856. '--interpreter=mi2', # use GDB/MI v2
  857. '--core=%s' % core_fname] # core file
  858. for c in gdb_cmds:
  859. gdb_args += ['-ex', c]
  860. gdb_args.append(args.prog)
  861. p = subprocess.Popen(bufsize=0,
  862. args=gdb_args,
  863. stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
  864. close_fds=CLOSE_FDS)
  865. gdbmi_read2prompt(p.stdout, handlers)
  866. return p
  867. def gdbmi_getinfo(p, handlers, gdb_cmd):
  868. for t in handlers:
  869. handlers[t].result_class = None
  870. p.stdin.write(bytearray("-interpreter-exec console \"%s\"\n" % gdb_cmd, encoding='utf-8'))
  871. gdbmi_read2prompt(p.stdout, handlers)
  872. if not handlers[GDBMIResultHandler.TAG].result_class or handlers[GDBMIResultHandler.TAG].result_class == GDBMIResultHandler.RC_EXIT:
  873. logging.error("GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
  874. p.wait()
  875. logging.error("Problem occured! GDB exited, restart it.")
  876. p = gdbmi_start(handlers, [])
  877. elif handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
  878. logging.error("GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
  879. return p
  880. loader = None
  881. rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
  882. if not args.core:
  883. loader = ESPCoreDumpFlashLoader(args.off, port=args.port)
  884. core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
  885. if not core_fname:
  886. logging.error("Failed to create corefile!")
  887. loader.cleanup()
  888. return
  889. else:
  890. core_fname = args.core
  891. if args.core_format and args.core_format != 'elf':
  892. loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
  893. core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
  894. if not core_fname:
  895. logging.error("Failed to create corefile!")
  896. loader.cleanup()
  897. return
  898. exe_elf = ESPCoreDumpElfFile(args.prog)
  899. core_elf = ESPCoreDumpElfFile(core_fname)
  900. merged_segs = []
  901. core_segs = core_elf.program_segments
  902. for s in exe_elf.sections:
  903. merged = False
  904. for ps in core_segs:
  905. if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr:
  906. # sec: |XXXXXXXXXX|
  907. # seg: |...XXX.............|
  908. seg_addr = ps.addr
  909. if ps.addr + len(ps.data) <= s.addr + len(s.data):
  910. # sec: |XXXXXXXXXX|
  911. # seg: |XXXXXXXXXXX...|
  912. # merged: |XXXXXXXXXXXXXX|
  913. seg_len = len(s.data) + (s.addr - ps.addr)
  914. else:
  915. # sec: |XXXXXXXXXX|
  916. # seg: |XXXXXXXXXXXXXXXXX|
  917. # merged: |XXXXXXXXXXXXXXXXX|
  918. seg_len = len(ps.data)
  919. merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True))
  920. core_segs.remove(ps)
  921. merged = True
  922. elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data):
  923. # sec: |XXXXXXXXXX|
  924. # seg: |...XXX.............|
  925. seg_addr = s.addr
  926. if (ps.addr + len(ps.data)) >= (s.addr + len(s.data)):
  927. # sec: |XXXXXXXXXX|
  928. # seg: |..XXXXXXXXXXX|
  929. # merged: |XXXXXXXXXXXXX|
  930. seg_len = len(s.data) + (ps.addr + len(ps.data)) - (s.addr + len(s.data))
  931. else:
  932. # sec: |XXXXXXXXXX|
  933. # seg: |XXXXXX|
  934. # merged: |XXXXXXXXXX|
  935. seg_len = len(s.data)
  936. merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True))
  937. core_segs.remove(ps)
  938. merged = True
  939. if not merged:
  940. merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False))
  941. handlers = {}
  942. handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False)
  943. handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
  944. p = gdbmi_start(handlers, [rom_sym_cmd])
  945. print("===============================================================")
  946. print("==================== ESP32 CORE DUMP START ====================")
  947. handlers[GDBMIResultHandler.TAG].result_class = None
  948. handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler
  949. print("\n================== CURRENT THREAD REGISTERS ===================")
  950. p = gdbmi_getinfo(p, handlers, "info registers")
  951. print("\n==================== CURRENT THREAD STACK =====================")
  952. p = gdbmi_getinfo(p, handlers, "bt")
  953. print("\n======================== THREADS INFO =========================")
  954. p = gdbmi_getinfo(p, handlers, "info threads")
  955. print("\n======================= ALL MEMORY REGIONS ========================")
  956. print("Name Address Size Attrs")
  957. for ms in merged_segs:
  958. print("%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]))
  959. for cs in core_segs:
  960. # core dump exec segments are from ROM, other are belong to tasks (TCB or stack)
  961. if cs.flags & ESPCoreDumpSegment.PF_X:
  962. seg_name = 'rom.text'
  963. else:
  964. seg_name = 'tasks.data'
  965. print(".coredump.%s 0x%x 0x%x %s" % (seg_name, cs.addr, len(cs.data), cs.attr_str()))
  966. if args.print_mem:
  967. print("\n====================== CORE DUMP MEMORY CONTENTS ========================")
  968. for cs in core_elf.program_segments:
  969. # core dump exec segments are from ROM, other are belong to tasks (TCB or stack)
  970. if cs.flags & ESPCoreDumpSegment.PF_X:
  971. seg_name = 'rom.text'
  972. else:
  973. seg_name = 'tasks.data'
  974. print(".coredump.%s 0x%x 0x%x %s" % (seg_name, cs.addr, len(cs.data), cs.attr_str()))
  975. p = gdbmi_getinfo(p, handlers, "x/%dx 0x%x" % (old_div(len(cs.data),4), cs.addr))
  976. print("\n===================== ESP32 CORE DUMP END =====================")
  977. print("===============================================================")
  978. p.stdin.write(b'q\n')
  979. p.wait()
  980. p.stdin.close()
  981. p.stdout.close()
  982. if loader:
  983. if not args.core and not args.save_core:
  984. loader.remove_tmp_file(core_fname)
  985. loader.cleanup()
  986. print('Done!')
  987. def main():
  988. parser = argparse.ArgumentParser(description='espcoredump.py v%s - ESP32 Core Dump Utility' % __version__, prog='espcoredump')
  989. parser.add_argument('--chip', '-c',
  990. help='Target chip type',
  991. choices=['auto', 'esp32'],
  992. default=os.environ.get('ESPTOOL_CHIP', 'auto'))
  993. parser.add_argument(
  994. '--port', '-p',
  995. help='Serial port device',
  996. default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT))
  997. parser.add_argument(
  998. '--baud', '-b',
  999. help='Serial port baud rate used when flashing/reading',
  1000. type=int,
  1001. default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD))
  1002. subparsers = parser.add_subparsers(
  1003. dest='operation',
  1004. help='Run coredumper {command} -h for additional help')
  1005. parser_debug_coredump = subparsers.add_parser(
  1006. 'dbg_corefile',
  1007. help='Starts GDB debugging session with specified corefile')
  1008. parser_debug_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=2)
  1009. parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
  1010. parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
  1011. parser_debug_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), '
  1012. 'raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
  1013. parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash '
  1014. '(type "make partition_table" to see).', type=int, default=0x110000)
  1015. parser_debug_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. '
  1016. 'Ignored with "-c"', type=str)
  1017. parser_debug_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
  1018. parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)
  1019. parser_info_coredump = subparsers.add_parser(
  1020. 'info_corefile',
  1021. help='Print core dump info from file')
  1022. parser_info_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=0)
  1023. parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
  1024. parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
  1025. parser_info_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), '
  1026. 'raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
  1027. parser_info_coredump.add_argument('--off', '-o', help='Offset of coredump partition in flash (type '
  1028. '"make partition_table" to see).', type=int, default=0x110000)
  1029. parser_info_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. '
  1030. 'Does not work with "-c"', type=str)
  1031. parser_info_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
  1032. parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true')
  1033. parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)
  1034. # internal sanity check - every operation matches a module function of the same name
  1035. for operation in subparsers.choices:
  1036. assert operation in globals(), "%s should be a module function" % operation
  1037. args = parser.parse_args()
  1038. log_level = logging.CRITICAL
  1039. if args.debug == 0:
  1040. log_level = logging.CRITICAL
  1041. elif args.debug == 1:
  1042. log_level = logging.ERROR
  1043. elif args.debug == 2:
  1044. log_level = logging.WARNING
  1045. elif args.debug == 3:
  1046. log_level = logging.INFO
  1047. else:
  1048. log_level = logging.DEBUG
  1049. # logging.warning('Watch out!') # will print a message to the console
  1050. # logging.info('I told you so') # will not print anything
  1051. logging.basicConfig(format='%(levelname)s:%(message)s', level=log_level)
  1052. print('espcoredump.py v%s' % __version__)
  1053. operation_func = globals()[args.operation]
  1054. operation_func(args)
  1055. if __name__ == '__main__':
  1056. try:
  1057. main()
  1058. except ESPCoreDumpError as e:
  1059. print('\nA fatal error occurred: %s' % e)
  1060. sys.exit(2)