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