#!/usr/bin/env python3 # # Copyright (c) 2021 Intel Corporation # # SPDX-License-Identifier: Apache-2.0 """ Class for Dictionary-based Logging Database """ import base64 import copy import json from .mipi_syst import gen_syst_xml_file from .utils import extract_one_string_in_section from .utils import find_string_in_mappings ARCHS = { "arc" : { "kconfig": "CONFIG_ARC", }, "arm" : { "kconfig": "CONFIG_ARM", }, "arm64" : { "kconfig": "CONFIG_ARM64", }, "mips" : { "kconfig": "CONFIG_MIPS", }, "sparc" : { "kconfig": "CONFIG_SPARC", }, "x86" : { "kconfig": "CONFIG_X86", }, "nios2" : { "kconfig": "CONFIG_NIOS2", # Small static strings are put into section "datas" # so we need to include them also. # # See include/arch/nios2/linker.ld on .sdata.* # for explanation. "extra_string_section": ['datas'], }, "riscv" : { "kconfig": "CONFIG_RISCV", }, "xtensa" : { "kconfig": "CONFIG_XTENSA", }, } class LogDatabase(): """Class of log database""" # Update this if database format of dictionary based logging # has changed ZEPHYR_DICT_LOG_VER = 2 LITTLE_ENDIAN = True BIG_ENDIAN = False def __init__(self): new_db = {} new_db['version'] = self.ZEPHYR_DICT_LOG_VER new_db['target'] = {} new_db['log_subsys'] = {} new_db['log_subsys']['log_instances'] = {} new_db['build_id'] = None new_db['arch'] = None new_db['kconfigs'] = {} self.database = new_db def get_version(self): """Get Database Version""" return self.database['version'] def get_build_id(self): """Get Build ID""" return self.database['build_id'] def set_build_id(self, build_id): """Set Build ID in Database""" self.database['build_id'] = build_id def get_arch(self): """Get the Target Architecture""" return self.database['arch'] def set_arch(self, arch): """Set the Target Architecture""" self.database['arch'] = arch def get_tgt_bits(self): """Get Target Bitness: 32 or 64""" if 'bits' in self.database['target']: return self.database['target']['bits'] return None def set_tgt_bits(self, bits): """Set Target Bitness: 32 or 64""" self.database['target']['bits'] = bits def is_tgt_64bit(self): """Return True if target is 64-bit, False if 32-bit. None if error.""" if 'bits' not in self.database['target']: return None if self.database['target']['bits'] == 32: return False if self.database['target']['bits'] == 64: return True return None def get_tgt_endianness(self): """ Get Target Endianness. Return True if little endian, False if big. """ if 'little_endianness' in self.database['target']: return self.database['target']['little_endianness'] return None def set_tgt_endianness(self, endianness): """ Set Target Endianness True if little endian, False if big. """ self.database['target']['little_endianness'] = endianness def is_tgt_little_endian(self): """Return True if target is little endian""" if 'little_endianness' not in self.database['target']: return None return self.database['target']['little_endianness'] == self.LITTLE_ENDIAN def get_string_mappings(self): """Get string mappings to database""" return self.database['string_mappings'] def set_string_mappings(self, database): """Add string mappings to database""" self.database['string_mappings'] = database def has_string_mappings(self): """Return True if there are string mappings in database""" if 'string_mappings' in self.database: return True return False def has_string_sections(self): """Return True if there are any static string sections""" if 'sections' not in self.database: return False return len(self.database['sections']) != 0 def __find_string_in_mappings(self, string_ptr): """ Find string pointed by string_ptr in the string mapping list. Return None if not found. """ return find_string_in_mappings(self.database['string_mappings'], string_ptr) def __find_string_in_sections(self, string_ptr): """ Find string pointed by string_ptr in the binary data sections. Return None if not found. """ for _, sect in self.database['sections'].items(): one_str = extract_one_string_in_section(sect, string_ptr) if one_str is not None: return one_str return None def find_string(self, string_ptr): """Find string pointed by string_ptr in the database. Return None if not found.""" one_str = None if self.has_string_mappings(): one_str = self.__find_string_in_mappings(string_ptr) if one_str is None and self.has_string_sections(): one_str = self.__find_string_in_sections(string_ptr) return one_str def add_log_instance(self, source_id, name, level, address): """Add one log instance into database""" self.database['log_subsys']['log_instances'][source_id] = { 'source_id' : source_id, 'name' : name, 'level' : level, 'addr' : address, } def get_log_source_string(self, domain_id, source_id): """Get the source string based on source ID""" # JSON stores key as string, so we need to convert src_id = str(source_id) if src_id in self.database['log_subsys']['log_instances']: return self.database['log_subsys']['log_instances'][src_id]['name'] return f"unknown<{domain_id}:{source_id}>" def add_kconfig(self, name, val): """Add a kconfig name-value pair into database""" self.database['kconfigs'][name] = val def get_kconfigs(self): """Return kconfig name-value pairs""" return self.database['kconfigs'] @staticmethod def read_json_database(db_file_name): """Read database from file and return a LogDatabase object""" try: with open(db_file_name, "r", encoding="iso-8859-1") as db_fd: json_db = json.load(db_fd) except (OSError, json.JSONDecodeError): return None # Decode data in JSON back into binary data if 'sections' in json_db: for _, sect in json_db['sections'].items(): sect['data'] = base64.b64decode(sect['data_b64']) database = LogDatabase() database.database = json_db # JSON encodes the addresses in string mappings as literal strings. # So convert them back to integers, as this is needed for partial # matchings. if database.has_string_mappings(): new_str_map = {} for addr, one_str in database.get_string_mappings().items(): new_str_map[int(addr)] = one_str database.set_string_mappings(new_str_map) return database @staticmethod def write_json_database(db_file_name, database): """Write the database into file""" json_db = copy.deepcopy(database.database) # Make database object into something JSON can dump if 'sections' in json_db: for _, sect in json_db['sections'].items(): encoded = base64.b64encode(sect['data']) sect['data_b64'] = encoded.decode('ascii') del sect['data'] try: with open(db_file_name, "w", encoding="iso-8859-1") as db_fd: db_fd.write(json.dumps(json_db)) except OSError: return False return True @staticmethod def write_syst_database(db_file_name, database): """ Write the database into MIPI Sys-T Collateral XML file """ try: with open(db_file_name, "w", encoding="iso-8859-1") as db_fd: xml = gen_syst_xml_file(database) db_fd.write(xml) except OSError: return False return True