1#!/usr/bin/env python3 2# 3# Copyright (c) 2021 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7""" 8Class for Dictionary-based Logging Database 9""" 10 11import base64 12import copy 13import json 14 15from .mipi_syst import gen_syst_xml_file 16from .utils import extract_one_string_in_section, find_string_in_mappings 17 18ARCHS = { 19 "arc" : { 20 "kconfig": "CONFIG_ARC", 21 }, 22 "arm" : { 23 "kconfig": "CONFIG_ARM", 24 }, 25 "arm64" : { 26 "kconfig": "CONFIG_ARM64", 27 }, 28 "mips" : { 29 "kconfig": "CONFIG_MIPS", 30 }, 31 "sparc" : { 32 "kconfig": "CONFIG_SPARC", 33 }, 34 "x86" : { 35 "kconfig": "CONFIG_X86", 36 }, 37 "posix" : { 38 "kconfig": "CONFIG_ARCH_POSIX", 39 }, 40 "riscv32e" : { 41 "kconfig": "CONFIG_RISCV_ISA_RV32E", 42 }, 43 "riscv" : { 44 "kconfig": "CONFIG_RISCV", 45 }, 46 "rx" : { 47 "kconfig": "CONFIG_RX", 48 }, 49 "xtensa" : { 50 "kconfig": "CONFIG_XTENSA", 51 }, 52} 53 54 55class LogDatabase: 56 """Class of log database""" 57 # Update this if database format of dictionary based logging 58 # has changed 59 ZEPHYR_DICT_LOG_VER = 3 60 61 LITTLE_ENDIAN = True 62 BIG_ENDIAN = False 63 64 def __init__(self): 65 new_db = {} 66 67 new_db['version'] = self.ZEPHYR_DICT_LOG_VER 68 new_db['target'] = {} 69 new_db['log_subsys'] = {} 70 new_db['log_subsys']['log_instances'] = {} 71 new_db['build_id'] = None 72 new_db['arch'] = None 73 new_db['kconfigs'] = {} 74 75 self.database = new_db 76 77 78 def get_version(self): 79 """Get Database Version""" 80 return self.database['version'] 81 82 83 def get_build_id(self): 84 """Get Build ID""" 85 return self.database['build_id'] 86 87 88 def set_build_id(self, build_id): 89 """Set Build ID in Database""" 90 self.database['build_id'] = build_id 91 92 93 def get_arch(self): 94 """Get the Target Architecture""" 95 return self.database['arch'] 96 97 98 def set_arch(self, arch): 99 """Set the Target Architecture""" 100 self.database['arch'] = arch 101 102 103 def get_tgt_bits(self): 104 """Get Target Bitness: 32 or 64""" 105 if 'bits' in self.database['target']: 106 return self.database['target']['bits'] 107 108 return None 109 110 111 def set_tgt_bits(self, bits): 112 """Set Target Bitness: 32 or 64""" 113 self.database['target']['bits'] = bits 114 115 116 def is_tgt_64bit(self): 117 """Return True if target is 64-bit, False if 32-bit. 118 None if error.""" 119 if 'bits' not in self.database['target']: 120 return None 121 122 if self.database['target']['bits'] == 32: 123 return False 124 125 if self.database['target']['bits'] == 64: 126 return True 127 128 return None 129 130 131 def get_tgt_endianness(self): 132 """ 133 Get Target Endianness. 134 135 Return True if little endian, False if big. 136 """ 137 if 'little_endianness' in self.database['target']: 138 return self.database['target']['little_endianness'] 139 140 return None 141 142 143 def set_tgt_endianness(self, endianness): 144 """ 145 Set Target Endianness 146 147 True if little endian, False if big. 148 """ 149 self.database['target']['little_endianness'] = endianness 150 151 152 def is_tgt_little_endian(self): 153 """Return True if target is little endian""" 154 if 'little_endianness' not in self.database['target']: 155 return None 156 157 return self.database['target']['little_endianness'] == self.LITTLE_ENDIAN 158 159 160 def get_string_mappings(self): 161 """Get string mappings to database""" 162 return self.database['string_mappings'] 163 164 165 def set_string_mappings(self, database): 166 """Add string mappings to database""" 167 self.database['string_mappings'] = database 168 169 170 def has_string_mappings(self): 171 """Return True if there are string mappings in database""" 172 return 'string_mappings' in self.database 173 174 175 def has_string_sections(self): 176 """Return True if there are any static string sections""" 177 if 'sections' not in self.database: 178 return False 179 180 return len(self.database['sections']) != 0 181 182 183 def __find_string_in_mappings(self, string_ptr): 184 """ 185 Find string pointed by string_ptr in the string mapping 186 list. Return None if not found. 187 """ 188 return find_string_in_mappings(self.database['string_mappings'], string_ptr) 189 190 191 def __find_string_in_sections(self, string_ptr): 192 """ 193 Find string pointed by string_ptr in the binary data 194 sections. Return None if not found. 195 """ 196 for _, sect in self.database['sections'].items(): 197 one_str = extract_one_string_in_section(sect, string_ptr) 198 199 if one_str is not None: 200 return one_str 201 202 return None 203 204 205 def find_string(self, string_ptr): 206 """Find string pointed by string_ptr in the database. 207 Return None if not found.""" 208 one_str = None 209 210 if self.has_string_mappings(): 211 one_str = self.__find_string_in_mappings(string_ptr) 212 213 if one_str is None and self.has_string_sections(): 214 one_str = self.__find_string_in_sections(string_ptr) 215 216 return one_str 217 218 219 def add_log_instance(self, source_id, name, level, address): 220 """Add one log instance into database""" 221 self.database['log_subsys']['log_instances'][source_id] = { 222 'source_id' : source_id, 223 'name' : name, 224 'level' : level, 225 'addr' : address, 226 } 227 228 229 def get_log_source_string(self, domain_id, source_id): 230 """Get the source string based on source ID""" 231 # JSON stores key as string, so we need to convert 232 src_id = str(source_id) 233 234 if src_id in self.database['log_subsys']['log_instances']: 235 return self.database['log_subsys']['log_instances'][src_id]['name'] 236 237 return f"unknown<{domain_id}:{source_id}>" 238 239 240 def add_kconfig(self, name, val): 241 """Add a kconfig name-value pair into database""" 242 self.database['kconfigs'][name] = val 243 244 245 def get_kconfigs(self): 246 """Return kconfig name-value pairs""" 247 return self.database['kconfigs'] 248 249 250 @staticmethod 251 def read_json_database(db_file_name): 252 """Read database from file and return a LogDatabase object""" 253 try: 254 with open(db_file_name, encoding="iso-8859-1") as db_fd: 255 json_db = json.load(db_fd) 256 except (OSError, json.JSONDecodeError): 257 return None 258 259 # Decode data in JSON back into binary data 260 if 'sections' in json_db: 261 for _, sect in json_db['sections'].items(): 262 sect['data'] = base64.b64decode(sect['data_b64']) 263 264 database = LogDatabase() 265 database.database = json_db 266 267 # JSON encodes the addresses in string mappings as literal strings. 268 # So convert them back to integers, as this is needed for partial 269 # matchings. 270 if database.has_string_mappings(): 271 new_str_map = {} 272 273 for addr, one_str in database.get_string_mappings().items(): 274 new_str_map[int(addr)] = one_str 275 276 database.set_string_mappings(new_str_map) 277 278 return database 279 280 281 @staticmethod 282 def write_json_database(db_file_name, database): 283 """Write the database into file""" 284 json_db = copy.deepcopy(database.database) 285 286 # Make database object into something JSON can dump 287 if 'sections' in json_db: 288 for _, sect in json_db['sections'].items(): 289 encoded = base64.b64encode(sect['data']) 290 sect['data_b64'] = encoded.decode('ascii') 291 del sect['data'] 292 293 try: 294 with open(db_file_name, "w", encoding="iso-8859-1") as db_fd: 295 db_fd.write(json.dumps(json_db)) 296 except OSError: 297 return False 298 299 return True 300 301 @staticmethod 302 def write_syst_database(db_file_name, database): 303 """ 304 Write the database into MIPI Sys-T Collateral XML file 305 """ 306 307 try: 308 with open(db_file_name, "w", encoding="iso-8859-1") as db_fd: 309 xml = gen_syst_xml_file(database) 310 db_fd.write(xml) 311 except OSError: 312 return False 313 314 return True 315