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