1#!/usr/bin/env python3 2# 3# Copyright (c) 2020 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7import logging 8import struct 9 10# Note: keep sync with C code 11COREDUMP_HDR_ID = b'ZE' 12COREDUMP_HDR_VER = 2 13LOG_HDR_STRUCT = "<ccHHBBI" 14LOG_HDR_SIZE = struct.calcsize(LOG_HDR_STRUCT) 15 16COREDUMP_ARCH_HDR_ID = b'A' 17LOG_ARCH_HDR_STRUCT = "<cHH" 18LOG_ARCH_HDR_SIZE = struct.calcsize(LOG_ARCH_HDR_STRUCT) 19 20COREDUMP_THREADS_META_HDR_ID = b'T' 21LOG_THREADS_META_HDR_STRUCT = "<cHH" 22LOG_THREADS_META_HDR_SIZE = struct.calcsize(LOG_THREADS_META_HDR_STRUCT) 23 24COREDUMP_MEM_HDR_ID = b'M' 25COREDUMP_MEM_HDR_VER = 1 26LOG_MEM_HDR_STRUCT = "<cH" 27LOG_MEM_HDR_SIZE = struct.calcsize(LOG_MEM_HDR_STRUCT) 28 29 30logger = logging.getLogger("parser") 31 32 33def reason_string(reason): 34 # Keep sync with "enum k_fatal_error_reason" 35 ret = "(Unknown)" 36 37 if reason == 0: 38 ret = "K_ERR_CPU_EXCEPTION" 39 elif reason == 1: 40 ret = "K_ERR_SPURIOUS_IRQ" 41 elif reason == 2: 42 ret = "K_ERR_STACK_CHK_FAIL" 43 elif reason == 3: 44 ret = "K_ERR_KERNEL_OOPS" 45 elif reason == 4: 46 ret = "K_ERR_KERNEL_PANIC" 47 48 return ret 49 50 51class CoredumpLogFile: 52 """ 53 Process the binary coredump file for register block 54 and memory blocks. 55 """ 56 57 def __init__(self, logfile): 58 self.logfile = logfile 59 self.fd = None 60 61 self.log_hdr = None 62 self.arch_data = list() 63 self.memory_regions = list() 64 self.threads_metadata = {"hdr_ver": None, "data": None} 65 66 def open(self): 67 self.fd = open(self.logfile, "rb") 68 69 def close(self): 70 self.fd.close() 71 72 def get_arch_data(self): 73 return self.arch_data 74 75 def get_memory_regions(self): 76 return self.memory_regions 77 78 def get_threads_metadata(self): 79 return self.threads_metadata 80 81 def parse_arch_section(self): 82 hdr = self.fd.read(LOG_ARCH_HDR_SIZE) 83 _, hdr_ver, num_bytes = struct.unpack(LOG_ARCH_HDR_STRUCT, hdr) 84 85 arch_data = self.fd.read(num_bytes) 86 87 self.arch_data = {"hdr_ver": hdr_ver, "data": arch_data} 88 89 return True 90 91 def parse_threads_metadata_section(self): 92 hdr = self.fd.read(LOG_THREADS_META_HDR_SIZE) 93 _, hdr_ver, num_bytes = struct.unpack(LOG_THREADS_META_HDR_STRUCT, hdr) 94 95 data = self.fd.read(num_bytes) 96 97 self.threads_metadata = {"hdr_ver": hdr_ver, "data": data} 98 99 return True 100 101 def parse_memory_section(self): 102 hdr = self.fd.read(LOG_MEM_HDR_SIZE) 103 _, hdr_ver = struct.unpack(LOG_MEM_HDR_STRUCT, hdr) 104 105 if hdr_ver != COREDUMP_MEM_HDR_VER: 106 logger.error(f"Memory block version: {hdr_ver}, expected {COREDUMP_MEM_HDR_VER}!") 107 return False 108 109 # Figure out how to read the start and end addresses 110 ptr_fmt = None 111 if self.log_hdr["ptr_size"] == 64: 112 ptr_fmt = "QQ" 113 elif self.log_hdr["ptr_size"] == 32: 114 ptr_fmt = "II" 115 else: 116 return False 117 118 data = self.fd.read(struct.calcsize(ptr_fmt)) 119 saddr, eaddr = struct.unpack(ptr_fmt, data) 120 121 size = eaddr - saddr 122 123 data = self.fd.read(size) 124 125 mem = {"start": saddr, "end": eaddr, "data": data} 126 self.memory_regions.append(mem) 127 128 logger.info(f"Memory: 0x{saddr:x} to 0x{eaddr:x} of size {size:d}") 129 130 return True 131 132 def parse(self): 133 if self.fd is None: 134 self.open() 135 136 hdr = self.fd.read(LOG_HDR_SIZE) 137 id1, id2, hdr_ver, tgt_code, ptr_size, flags, reason = struct.unpack(LOG_HDR_STRUCT, hdr) 138 139 if (id1 + id2) != COREDUMP_HDR_ID: 140 # ID in header does not match 141 logger.error("Log header ID not found...") 142 return False 143 144 if hdr_ver > COREDUMP_HDR_VER: 145 logger.error(f"Log version: {hdr_ver}, expected: {COREDUMP_HDR_VER}!") 146 return False 147 148 ptr_size = 2**ptr_size 149 150 self.log_hdr = { 151 "hdr_version": hdr_ver, 152 "tgt_code": tgt_code, 153 "ptr_size": ptr_size, 154 "flags": flags, 155 "reason": reason, 156 } 157 158 logger.info(f"Reason: {reason_string(reason)}") 159 logger.info(f"Pointer size {ptr_size}") 160 161 del id1, id2, hdr_ver, tgt_code, ptr_size, flags, reason 162 163 while True: 164 section_id = self.fd.read(1) 165 if not section_id: 166 # no more data to read 167 break 168 169 self.fd.seek(-1, 1) # go back 1 byte 170 if section_id == COREDUMP_ARCH_HDR_ID: 171 if not self.parse_arch_section(): 172 logger.error("Cannot parse architecture section") 173 return False 174 elif section_id == COREDUMP_THREADS_META_HDR_ID: 175 if not self.parse_threads_metadata_section(): 176 logger.error("Cannot parse threads metadata section") 177 return False 178 elif section_id == COREDUMP_MEM_HDR_ID: 179 if not self.parse_memory_section(): 180 logger.error("Cannot parse memory section") 181 return False 182 else: 183 # Unknown section in log file 184 logger.error(f"Unknown section in log file with ID {section_id}") 185 return False 186 187 return True 188