168 lines
4.5 KiB
Python
168 lines
4.5 KiB
Python
|
#!/usr/bin/env python3
|
||
|
#
|
||
|
# Copyright (c) 2020 Intel Corporation
|
||
|
#
|
||
|
# SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
import logging
|
||
|
import struct
|
||
|
|
||
|
|
||
|
# Note: keep sync with C code
|
||
|
Z_COREDUMP_HDR_ID = b'ZE'
|
||
|
Z_COREDUMP_HDR_VER = 1
|
||
|
LOG_HDR_STRUCT = "<ccHHBBI"
|
||
|
LOG_HDR_SIZE = struct.calcsize(LOG_HDR_STRUCT)
|
||
|
|
||
|
Z_COREDUMP_ARCH_HDR_ID = b'A'
|
||
|
LOG_ARCH_HDR_STRUCT = "<cHH"
|
||
|
LOG_ARCH_HDR_SIZE = struct.calcsize(LOG_ARCH_HDR_STRUCT)
|
||
|
|
||
|
Z_COREDUMP_MEM_HDR_ID = b'M'
|
||
|
Z_COREDUMP_MEM_HDR_VER = 1
|
||
|
LOG_MEM_HDR_STRUCT = "<cH"
|
||
|
LOG_MEM_HDR_SIZE = struct.calcsize(LOG_MEM_HDR_STRUCT)
|
||
|
|
||
|
|
||
|
logger = logging.getLogger("parser")
|
||
|
|
||
|
|
||
|
def reason_string(reason):
|
||
|
# Keep sync with "enum k_fatal_error_reason"
|
||
|
ret = "(Unknown)"
|
||
|
|
||
|
if reason == 0:
|
||
|
ret = "K_ERR_CPU_EXCEPTION"
|
||
|
elif reason == 1:
|
||
|
ret = "K_ERR_SPURIOUS_IRQ"
|
||
|
elif reason == 2:
|
||
|
ret = "K_ERR_STACK_CHK_FAIL"
|
||
|
elif reason == 3:
|
||
|
ret = "K_ERR_KERNEL_OOPS"
|
||
|
elif reason == 4:
|
||
|
ret = "K_ERR_KERNEL_PANIC"
|
||
|
|
||
|
return ret
|
||
|
|
||
|
|
||
|
class CoredumpLogFile:
|
||
|
"""
|
||
|
Process the binary coredump file for register block
|
||
|
and memory blocks.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, logfile):
|
||
|
self.logfile = logfile
|
||
|
self.fd = None
|
||
|
|
||
|
self.log_hdr = None
|
||
|
self.arch_data = list()
|
||
|
self.memory_regions = list()
|
||
|
|
||
|
def open(self):
|
||
|
self.fd = open(self.logfile, "rb")
|
||
|
|
||
|
def close(self):
|
||
|
self.fd.close()
|
||
|
|
||
|
def get_arch_data(self):
|
||
|
return self.arch_data
|
||
|
|
||
|
def get_memory_regions(self):
|
||
|
return self.memory_regions
|
||
|
|
||
|
def parse_arch_section(self):
|
||
|
hdr = self.fd.read(LOG_ARCH_HDR_SIZE)
|
||
|
_, hdr_ver, num_bytes = struct.unpack(LOG_ARCH_HDR_STRUCT, hdr)
|
||
|
|
||
|
arch_data = self.fd.read(num_bytes)
|
||
|
|
||
|
self.arch_data = {"hdr_ver" : hdr_ver, "data" : arch_data}
|
||
|
|
||
|
return True
|
||
|
|
||
|
def parse_memory_section(self):
|
||
|
hdr = self.fd.read(LOG_MEM_HDR_SIZE)
|
||
|
_, hdr_ver = struct.unpack(LOG_MEM_HDR_STRUCT, hdr)
|
||
|
|
||
|
if hdr_ver != Z_COREDUMP_MEM_HDR_VER:
|
||
|
logger.error(f"Memory block version: {hdr_ver}, expected {Z_COREDUMP_MEM_HDR_VER}!")
|
||
|
return False
|
||
|
|
||
|
# Figure out how to read the start and end addresses
|
||
|
ptr_fmt = None
|
||
|
if self.log_hdr["ptr_size"] == 64:
|
||
|
ptr_fmt = "QQ"
|
||
|
elif self.log_hdr["ptr_size"] == 32:
|
||
|
ptr_fmt = "II"
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
data = self.fd.read(struct.calcsize(ptr_fmt))
|
||
|
saddr, eaddr = struct.unpack(ptr_fmt, data)
|
||
|
|
||
|
size = eaddr - saddr
|
||
|
|
||
|
data = self.fd.read(size)
|
||
|
|
||
|
mem = {"start": saddr, "end": eaddr, "data": data}
|
||
|
self.memory_regions.append(mem)
|
||
|
|
||
|
logger.info("Memory: 0x%x to 0x%x of size %d" %
|
||
|
(saddr, eaddr, size))
|
||
|
|
||
|
return True
|
||
|
|
||
|
def parse(self):
|
||
|
if self.fd is None:
|
||
|
self.open()
|
||
|
|
||
|
hdr = self.fd.read(LOG_HDR_SIZE)
|
||
|
id1, id2, hdr_ver, tgt_code, ptr_size, flags, reason = struct.unpack(LOG_HDR_STRUCT, hdr)
|
||
|
|
||
|
if (id1 + id2) != Z_COREDUMP_HDR_ID:
|
||
|
# ID in header does not match
|
||
|
logger.error("Log header ID not found...")
|
||
|
return False
|
||
|
|
||
|
if hdr_ver != Z_COREDUMP_HDR_VER:
|
||
|
logger.error(f"Log version: {hdr_ver}, expected: {Z_COREDUMP_HDR_VER}!")
|
||
|
return False
|
||
|
|
||
|
ptr_size = 2 ** ptr_size
|
||
|
|
||
|
self.log_hdr = {
|
||
|
"hdr_version": hdr_ver,
|
||
|
"tgt_code": tgt_code,
|
||
|
"ptr_size": ptr_size,
|
||
|
"flags": flags,
|
||
|
"reason": reason,
|
||
|
}
|
||
|
|
||
|
logger.info("Reason: {0}".format(reason_string(reason)))
|
||
|
logger.info(f"Pointer size {ptr_size}")
|
||
|
|
||
|
del id1, id2, hdr_ver, tgt_code, ptr_size, flags, reason
|
||
|
|
||
|
while True:
|
||
|
section_id = self.fd.read(1)
|
||
|
if not section_id:
|
||
|
# no more data to read
|
||
|
break
|
||
|
|
||
|
self.fd.seek(-1, 1) # go back 1 byte
|
||
|
if section_id == Z_COREDUMP_ARCH_HDR_ID:
|
||
|
if not self.parse_arch_section():
|
||
|
logger.error("Cannot parse architecture section")
|
||
|
return False
|
||
|
elif section_id == Z_COREDUMP_MEM_HDR_ID:
|
||
|
if not self.parse_memory_section():
|
||
|
logger.error("Cannot parse memory section")
|
||
|
return False
|
||
|
else:
|
||
|
# Unknown section in log file
|
||
|
logger.error(f"Unknown section in log file with ID {section_id}")
|
||
|
return False
|
||
|
|
||
|
return True
|