loader.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. #
  2. # Copyright 2021 Espressif Systems (Shanghai) PTE LTD
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import base64
  17. import binascii
  18. import hashlib
  19. import logging
  20. import os
  21. import subprocess
  22. import sys
  23. import tempfile
  24. from construct import AlignedStruct, Bytes, GreedyRange, Int32ul, Padding, Struct, abs_, this
  25. from . import ESPCoreDumpLoaderError, _ArchMethodsBase, _TargetMethodsBase
  26. from .elf import (TASK_STATUS_CORRECT, TASK_STATUS_TCB_CORRUPTED, ElfFile, ElfSegment, ESPCoreDumpElfFile,
  27. EspTaskStatus, NoteSection)
  28. from .xtensa import _ArchMethodsXtensa, _TargetMethodsESP32
  29. IDF_PATH = os.getenv('IDF_PATH')
  30. PARTTOOL_PY = os.path.join(IDF_PATH, 'components', 'partition_table', 'parttool.py')
  31. ESPTOOL_PY = os.path.join(IDF_PATH, 'components', 'esptool_py', 'esptool', 'esptool.py')
  32. # Following structs are based on source code
  33. # components/espcoredump/include_core_dump/esp_core_dump_priv.h
  34. EspCoreDumpV1Header = Struct(
  35. 'tot_len' / Int32ul,
  36. 'ver' / Int32ul,
  37. 'task_num' / Int32ul,
  38. 'tcbsz' / Int32ul,
  39. )
  40. EspCoreDumpV2Header = Struct(
  41. 'tot_len' / Int32ul,
  42. 'ver' / Int32ul,
  43. 'task_num' / Int32ul,
  44. 'tcbsz' / Int32ul,
  45. 'segs_num' / Int32ul,
  46. )
  47. CRC = Int32ul
  48. SHA256 = Bytes(32)
  49. TaskHeader = Struct(
  50. 'tcb_addr' / Int32ul,
  51. 'stack_top' / Int32ul,
  52. 'stack_end' / Int32ul,
  53. )
  54. MemSegmentHeader = Struct(
  55. 'mem_start' / Int32ul,
  56. 'mem_sz' / Int32ul,
  57. 'data' / Bytes(this.mem_sz),
  58. )
  59. class EspCoreDumpVersion(object):
  60. """Core dump version class
  61. """
  62. # This class contains all version-dependent params
  63. ESP32 = 0
  64. ESP32S2 = 2
  65. XTENSA_CHIPS = [ESP32, ESP32S2]
  66. ESP_COREDUMP_TARGETS = XTENSA_CHIPS
  67. def __init__(self, version=None):
  68. """Constructor for core dump version
  69. """
  70. super(EspCoreDumpVersion, self).__init__()
  71. if version is None:
  72. self.version = 0
  73. else:
  74. self.set_version(version)
  75. @staticmethod
  76. def make_dump_ver(major, minor):
  77. return ((major & 0xFF) << 8) | ((minor & 0xFF) << 0)
  78. def set_version(self, version):
  79. self.version = version
  80. @property
  81. def chip_ver(self):
  82. return (self.version & 0xFFFF0000) >> 16
  83. @property
  84. def dump_ver(self):
  85. return self.version & 0x0000FFFF
  86. @property
  87. def major(self):
  88. return (self.version & 0x0000FF00) >> 8
  89. @property
  90. def minor(self):
  91. return self.version & 0x000000FF
  92. class EspCoreDumpLoader(EspCoreDumpVersion):
  93. # "legacy" stands for core dumps v0.1 (before IDF v4.1)
  94. BIN_V1 = EspCoreDumpVersion.make_dump_ver(0, 1)
  95. BIN_V2 = EspCoreDumpVersion.make_dump_ver(0, 2)
  96. ELF_CRC32 = EspCoreDumpVersion.make_dump_ver(1, 0)
  97. ELF_SHA256 = EspCoreDumpVersion.make_dump_ver(1, 1)
  98. def __init__(self):
  99. super(EspCoreDumpLoader, self).__init__()
  100. self.core_src_file = None
  101. self.core_src_struct = None
  102. self.core_src = None
  103. self.core_elf_file = None
  104. self.header = None
  105. self.header_struct = EspCoreDumpV1Header
  106. self.checksum_struct = CRC
  107. # These two method classes will be assigned in ``reload_coredump``
  108. self.target_method_cls = _TargetMethodsBase
  109. self.arch_method_cls = _ArchMethodsBase
  110. self._temp_files = []
  111. def __del__(self):
  112. if self.core_src_file:
  113. self.core_src_file.close()
  114. if self.core_elf_file:
  115. self.core_elf_file.close()
  116. for f in self._temp_files:
  117. try:
  118. os.remove(f)
  119. except OSError:
  120. pass
  121. def _create_temp_file(self):
  122. t = tempfile.NamedTemporaryFile('wb', delete=False)
  123. self._temp_files.append(t.name)
  124. return t
  125. def _reload_coredump(self):
  126. with open(self.core_src_file.name, 'rb') as fr:
  127. coredump_bytes = fr.read()
  128. _header = EspCoreDumpV1Header.parse(coredump_bytes) # first we use V1 format to get version
  129. self.set_version(_header.ver)
  130. if self.dump_ver == self.ELF_CRC32:
  131. self.checksum_struct = CRC
  132. self.header_struct = EspCoreDumpV2Header
  133. elif self.dump_ver == self.ELF_SHA256:
  134. self.checksum_struct = SHA256
  135. self.header_struct = EspCoreDumpV2Header
  136. elif self.dump_ver == self.BIN_V1:
  137. self.checksum_struct = CRC
  138. self.header_struct = EspCoreDumpV1Header
  139. elif self.dump_ver == self.BIN_V2:
  140. self.checksum_struct = CRC
  141. self.header_struct = EspCoreDumpV2Header
  142. else:
  143. raise ESPCoreDumpLoaderError('Core dump version "0x%x" is not supported!' % self.dump_ver)
  144. self.core_src_struct = Struct(
  145. 'header' / self.header_struct,
  146. 'data' / Bytes(this.header.tot_len - self.header_struct.sizeof() - self.checksum_struct.sizeof()),
  147. 'checksum' / self.checksum_struct,
  148. )
  149. self.core_src = self.core_src_struct.parse(coredump_bytes)
  150. # Reload header if header struct changes after parsing
  151. if self.header_struct != EspCoreDumpV1Header:
  152. self.header = EspCoreDumpV2Header.parse(coredump_bytes)
  153. if self.chip_ver in self.ESP_COREDUMP_TARGETS:
  154. if self.chip_ver == self.ESP32:
  155. self.target_method_cls = _TargetMethodsESP32
  156. if self.chip_ver in self.XTENSA_CHIPS:
  157. self.arch_method_cls = _ArchMethodsXtensa
  158. else:
  159. raise ESPCoreDumpLoaderError('Core dump chip "0x%x" is not supported!' % self.chip_ver)
  160. def _validate_dump_file(self):
  161. if self.chip_ver not in self.ESP_COREDUMP_TARGETS:
  162. raise ESPCoreDumpLoaderError('Invalid core dump chip version: "{}", should be <= "0x{:X}"'
  163. .format(self.chip_ver, self.ESP32S2))
  164. if self.checksum_struct == CRC:
  165. self._crc_validate()
  166. elif self.checksum_struct == SHA256:
  167. self._sha256_validate()
  168. def _crc_validate(self):
  169. data_crc = binascii.crc32(EspCoreDumpV2Header.build(self.core_src.header) + self.core_src.data) & 0xffffffff
  170. if data_crc != self.core_src.checksum:
  171. raise ESPCoreDumpLoaderError('Invalid core dump CRC %x, should be %x' % (data_crc, self.core_src.crc))
  172. def _sha256_validate(self):
  173. data_sha256 = hashlib.sha256(EspCoreDumpV2Header.build(self.core_src.header) + self.core_src.data)
  174. data_sha256_str = data_sha256.hexdigest()
  175. sha256_str = binascii.hexlify(self.core_src.checksum).decode('ascii')
  176. if data_sha256_str != sha256_str:
  177. raise ESPCoreDumpLoaderError('Invalid core dump SHA256 "{}", should be "{}"'
  178. .format(data_sha256_str, sha256_str))
  179. def create_corefile(self, exe_name=None): # type: (str) -> None
  180. """
  181. Creates core dump ELF file
  182. """
  183. self._validate_dump_file()
  184. self.core_elf_file = self._create_temp_file()
  185. if self.dump_ver in [self.ELF_CRC32,
  186. self.ELF_SHA256]:
  187. self._extract_elf_corefile(exe_name)
  188. elif self.dump_ver in [self.BIN_V1,
  189. self.BIN_V2]:
  190. self._extract_bin_corefile()
  191. else:
  192. raise NotImplementedError
  193. def _extract_elf_corefile(self, exe_name=None):
  194. """
  195. Reads the ELF formatted core dump image and parse it
  196. """
  197. self.core_elf_file.write(self.core_src.data)
  198. # Need to be closed before read. Otherwise the result will be wrong
  199. self.core_elf_file.close()
  200. core_elf = ESPCoreDumpElfFile(self.core_elf_file.name)
  201. # Read note segments from core file which are belong to tasks (TCB or stack)
  202. for seg in core_elf.note_segments:
  203. for note_sec in seg.note_secs:
  204. # Check for version info note
  205. if note_sec.name == 'ESP_CORE_DUMP_INFO' \
  206. and note_sec.type == ESPCoreDumpElfFile.PT_INFO \
  207. and exe_name:
  208. exe_elf = ElfFile(exe_name)
  209. app_sha256 = binascii.hexlify(exe_elf.sha256)
  210. coredump_sha256_struct = Struct(
  211. 'ver' / Int32ul,
  212. 'sha256' / Bytes(64) # SHA256 as hex string
  213. )
  214. coredump_sha256 = coredump_sha256_struct.parse(note_sec.desc[:coredump_sha256_struct.sizeof()])
  215. if coredump_sha256.sha256 != app_sha256:
  216. raise ESPCoreDumpLoaderError(
  217. 'Invalid application image for coredump: coredump SHA256({}) != app SHA256({}).'
  218. .format(coredump_sha256, app_sha256))
  219. if coredump_sha256.ver != self.version:
  220. raise ESPCoreDumpLoaderError(
  221. 'Invalid application image for coredump: coredump SHA256 version({}) != app SHA256 version({}).'
  222. .format(coredump_sha256.ver, self.version))
  223. @staticmethod
  224. def _get_aligned_size(size, align_with=4):
  225. if size % align_with:
  226. return align_with * (size // align_with + 1)
  227. return size
  228. @staticmethod
  229. def _build_note_section(name, sec_type, desc):
  230. name = bytearray(name, encoding='ascii') + b'\0'
  231. return NoteSection.build({
  232. 'namesz': len(name),
  233. 'descsz': len(desc),
  234. 'type': sec_type,
  235. 'name': name,
  236. 'desc': desc,
  237. })
  238. def _extract_bin_corefile(self):
  239. """
  240. Creates core dump ELF file
  241. """
  242. tcbsz_aligned = self._get_aligned_size(self.header.tcbsz)
  243. coredump_data_struct = Struct(
  244. 'tasks' / GreedyRange(
  245. AlignedStruct(
  246. 4,
  247. 'task_header' / TaskHeader,
  248. 'tcb' / Bytes(self.header.tcbsz),
  249. 'stack' / Bytes(abs_(this.task_header.stack_top - this.task_header.stack_end)),
  250. )
  251. ),
  252. 'mem_seg_headers' / MemSegmentHeader[self.core_src.header.segs_num]
  253. )
  254. core_elf = ESPCoreDumpElfFile()
  255. notes = b''
  256. core_dump_info_notes = b''
  257. task_info_notes = b''
  258. coredump_data = coredump_data_struct.parse(self.core_src.data)
  259. for i, task in enumerate(coredump_data.tasks):
  260. stack_len_aligned = self._get_aligned_size(abs(task.task_header.stack_top - task.task_header.stack_end))
  261. task_status_kwargs = {
  262. 'task_index': i,
  263. 'task_flags': TASK_STATUS_CORRECT,
  264. 'task_tcb_addr': task.task_header.tcb_addr,
  265. 'task_stack_start': min(task.task_header.stack_top, task.task_header.stack_end),
  266. 'task_stack_len': stack_len_aligned,
  267. 'task_name': Padding(16).build({}) # currently we don't have task_name, keep it as padding
  268. }
  269. # Write TCB
  270. try:
  271. if self.target_method_cls.tcb_is_sane(task.task_header.tcb_addr, tcbsz_aligned):
  272. core_elf.add_segment(task.task_header.tcb_addr,
  273. task.tcb,
  274. ElfFile.PT_LOAD,
  275. ElfSegment.PF_R | ElfSegment.PF_W)
  276. elif task.task_header.tcb_addr and self.target_method_cls.addr_is_fake(task.task_header.tcb_addr):
  277. task_status_kwargs['task_flags'] |= TASK_STATUS_TCB_CORRUPTED
  278. except ESPCoreDumpLoaderError as e:
  279. logging.warning('Skip TCB {} bytes @ 0x{:x}. (Reason: {})'
  280. .format(tcbsz_aligned, task.task_header.tcb_addr, e))
  281. # Write stack
  282. try:
  283. if self.target_method_cls.stack_is_sane(task_status_kwargs['task_stack_start']):
  284. core_elf.add_segment(task_status_kwargs['task_stack_start'],
  285. task.stack,
  286. ElfFile.PT_LOAD,
  287. ElfSegment.PF_R | ElfSegment.PF_W)
  288. elif task_status_kwargs['task_stack_start'] \
  289. and self.target_method_cls.addr_is_fake(task_status_kwargs['task_stack_start']):
  290. task_status_kwargs['task_flags'] |= TASK_STATUS_TCB_CORRUPTED
  291. core_elf.add_segment(task_status_kwargs['task_stack_start'],
  292. task.stack,
  293. ElfFile.PT_LOAD,
  294. ElfSegment.PF_R | ElfSegment.PF_W)
  295. except ESPCoreDumpLoaderError as e:
  296. logging.warning('Skip task\'s ({:x}) stack {} bytes @ 0x{:x}. (Reason: {})'
  297. .format(task_status_kwargs['tcb_addr'],
  298. task_status_kwargs['stack_len_aligned'],
  299. task_status_kwargs['stack_base'],
  300. e))
  301. try:
  302. logging.debug('Stack start_end: 0x{:x} @ 0x{:x}'
  303. .format(task.task_header.stack_top, task.task_header.stack_end))
  304. task_regs, extra_regs = self.arch_method_cls.get_registers_from_stack(
  305. task.stack,
  306. task.task_header.stack_end > task.task_header.stack_top
  307. )
  308. except Exception as e:
  309. raise ESPCoreDumpLoaderError(str(e))
  310. task_info_notes += self._build_note_section('TASK_INFO',
  311. ESPCoreDumpElfFile.PT_TASK_INFO,
  312. EspTaskStatus.build(task_status_kwargs))
  313. notes += self._build_note_section('CORE',
  314. ElfFile.PT_LOAD,
  315. self.arch_method_cls.build_prstatus_data(task.task_header.tcb_addr,
  316. task_regs))
  317. if extra_regs and len(core_dump_info_notes) == 0:
  318. # actually there will be only one such note - for crashed task
  319. core_dump_info_notes += self._build_note_section('ESP_CORE_DUMP_INFO',
  320. ESPCoreDumpElfFile.PT_INFO,
  321. Int32ul.build(self.header.ver))
  322. exc_regs = []
  323. for reg_id in extra_regs:
  324. exc_regs.extend([reg_id, extra_regs[reg_id]])
  325. _regs = [task.task_header.tcb_addr] + exc_regs
  326. core_dump_info_notes += self._build_note_section(
  327. 'EXTRA_INFO',
  328. ESPCoreDumpElfFile.PT_EXTRA_INFO,
  329. Int32ul[1 + len(exc_regs)].build(_regs)
  330. )
  331. if self.dump_ver == self.BIN_V2:
  332. for header in coredump_data.mem_seg_headers:
  333. logging.debug('Read memory segment {} bytes @ 0x{:x}'.format(header.mem_sz, header.mem_start))
  334. core_elf.add_segment(header.mem_start, header.data, ElfFile.PT_LOAD, ElfSegment.PF_R | ElfSegment.PF_W)
  335. # add notes
  336. try:
  337. core_elf.add_segment(0, notes, ElfFile.PT_NOTE, 0)
  338. except ESPCoreDumpLoaderError as e:
  339. logging.warning('Skip NOTES segment {:d} bytes @ 0x{:x}. (Reason: {})'.format(len(notes), 0, e))
  340. # add core dump info notes
  341. try:
  342. core_elf.add_segment(0, core_dump_info_notes, ElfFile.PT_NOTE, 0)
  343. except ESPCoreDumpLoaderError as e:
  344. logging.warning('Skip core dump info NOTES segment {:d} bytes @ 0x{:x}. (Reason: {})'
  345. .format(len(core_dump_info_notes), 0, e))
  346. try:
  347. core_elf.add_segment(0, task_info_notes, ElfFile.PT_NOTE, 0)
  348. except ESPCoreDumpLoaderError as e:
  349. logging.warning('Skip failed tasks info NOTES segment {:d} bytes @ 0x{:x}. (Reason: {})'
  350. .format(len(task_info_notes), 0, e))
  351. # dump core ELF
  352. core_elf.e_type = ElfFile.ET_CORE
  353. core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA
  354. core_elf.dump(self.core_elf_file.name)
  355. class ESPCoreDumpFlashLoader(EspCoreDumpLoader):
  356. ESP_COREDUMP_PART_TABLE_OFF = 0x8000
  357. def __init__(self, offset, target='esp32', port=None, baud=None):
  358. super(ESPCoreDumpFlashLoader, self).__init__()
  359. self.port = port
  360. self.baud = baud
  361. self.target = target
  362. self._get_coredump(offset)
  363. self._reload_coredump()
  364. def _get_coredump(self, off):
  365. """
  366. Loads core dump from flash using parttool or elftool (if offset is set)
  367. """
  368. try:
  369. if off:
  370. logging.info('Invoke esptool to read image.')
  371. self._invoke_esptool(off=off)
  372. else:
  373. logging.info('Invoke parttool to read image.')
  374. self._invoke_parttool()
  375. except subprocess.CalledProcessError as e:
  376. if e.output:
  377. logging.info(e.output)
  378. logging.error('Error during the subprocess execution')
  379. else:
  380. # Need to be closed before read. Otherwise the result will be wrong
  381. self.core_src_file.close()
  382. def _invoke_esptool(self, off=None):
  383. """
  384. Loads core dump from flash using elftool
  385. """
  386. tool_args = [sys.executable, ESPTOOL_PY, '-c', self.target]
  387. if self.port:
  388. tool_args.extend(['-p', self.port])
  389. if self.baud:
  390. tool_args.extend(['-b', str(self.baud)])
  391. self.core_src_file = self._create_temp_file()
  392. try:
  393. (part_offset, part_size) = self._get_core_dump_partition_info()
  394. if not off:
  395. off = part_offset # set default offset if not specified
  396. logging.warning('The core dump image offset is not specified. Use partition offset: %d.', part_offset)
  397. if part_offset != off:
  398. logging.warning('Predefined image offset: %d does not match core dump partition offset: %d', off,
  399. part_offset)
  400. # Here we use V1 format to locate the size
  401. tool_args.extend(['read_flash', str(off), str(EspCoreDumpV1Header.sizeof())])
  402. tool_args.append(self.core_src_file.name)
  403. # read core dump length
  404. et_out = subprocess.check_output(tool_args)
  405. if et_out:
  406. logging.info(et_out.decode('utf-8'))
  407. header = EspCoreDumpV1Header.parse(open(self.core_src_file.name, 'rb').read())
  408. if not header or not 0 < header.tot_len <= part_size:
  409. logging.error('Incorrect size of core dump image: {}, use partition size instead: {}'
  410. .format(header.tot_len, part_size))
  411. coredump_len = part_size
  412. else:
  413. coredump_len = header.tot_len
  414. # set actual size of core dump image and read it from flash
  415. tool_args[-2] = str(coredump_len)
  416. et_out = subprocess.check_output(tool_args)
  417. if et_out:
  418. logging.info(et_out.decode('utf-8'))
  419. except subprocess.CalledProcessError as e:
  420. logging.error('esptool script execution failed with err %d', e.returncode)
  421. logging.debug("Command ran: '%s'", e.cmd)
  422. logging.debug('Command out:')
  423. logging.debug(e.output)
  424. raise e
  425. def _invoke_parttool(self):
  426. """
  427. Loads core dump from flash using parttool
  428. """
  429. tool_args = [sys.executable, PARTTOOL_PY]
  430. if self.port:
  431. tool_args.extend(['--port', self.port])
  432. tool_args.extend(['read_partition', '--partition-type', 'data', '--partition-subtype', 'coredump', '--output'])
  433. self.core_src_file = self._create_temp_file()
  434. try:
  435. tool_args.append(self.core_src_file.name)
  436. # read core dump partition
  437. et_out = subprocess.check_output(tool_args)
  438. if et_out:
  439. logging.info(et_out.decode('utf-8'))
  440. except subprocess.CalledProcessError as e:
  441. logging.error('parttool script execution failed with err %d', e.returncode)
  442. logging.debug("Command ran: '%s'", e.cmd)
  443. logging.debug('Command out:')
  444. logging.debug(e.output)
  445. raise e
  446. def _get_core_dump_partition_info(self, part_off=None):
  447. """
  448. Get core dump partition info using parttool
  449. """
  450. logging.info('Retrieving core dump partition offset and size...')
  451. if not part_off:
  452. part_off = self.ESP_COREDUMP_PART_TABLE_OFF
  453. try:
  454. tool_args = [sys.executable, PARTTOOL_PY, '-q', '--partition-table-offset', str(part_off)]
  455. if self.port:
  456. tool_args.extend(['--port', self.port])
  457. invoke_args = tool_args + ['get_partition_info', '--partition-type', 'data',
  458. '--partition-subtype', 'coredump',
  459. '--info', 'offset', 'size']
  460. res = subprocess.check_output(invoke_args).strip()
  461. (offset_str, size_str) = res.rsplit(b'\n')[-1].split(b' ')
  462. size = int(size_str, 16)
  463. offset = int(offset_str, 16)
  464. logging.info('Core dump partition offset=%d, size=%d', offset, size)
  465. except subprocess.CalledProcessError as e:
  466. logging.error('parttool get partition info failed with err %d', e.returncode)
  467. logging.debug("Command ran: '%s'", e.cmd)
  468. logging.debug('Command out:')
  469. logging.debug(e.output)
  470. logging.error('Check if the coredump partition exists in partition table.')
  471. raise e
  472. return offset, size
  473. class ESPCoreDumpFileLoader(EspCoreDumpLoader):
  474. def __init__(self, path, is_b64=False):
  475. super(ESPCoreDumpFileLoader, self).__init__()
  476. self.is_b64 = is_b64
  477. self._get_coredump(path)
  478. self._reload_coredump()
  479. def _get_coredump(self, path):
  480. """
  481. Loads core dump from (raw binary or base64-encoded) file
  482. """
  483. logging.debug('Load core dump from "%s", %s format', path, 'b64' if self.is_b64 else 'raw')
  484. if not self.is_b64:
  485. self.core_src_file = open(path, mode='rb')
  486. else:
  487. self.core_src_file = self._create_temp_file()
  488. with open(path, 'rb') as fb64:
  489. while True:
  490. line = fb64.readline()
  491. if len(line) == 0:
  492. break
  493. data = base64.standard_b64decode(line.rstrip(b'\r\n'))
  494. self.core_src_file.write(data)
  495. self.core_src_file.flush()
  496. self.core_src_file.seek(0)