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