#!/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 .utils import extract_string_from_section


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 binary format of dictionary based logging
    # has changed
    ZEPHYR_DICT_LOG_VER = 1

    LITTLE_ENDIAN = True
    BIG_ENDIAN = False

    def __init__(self):
        new_db = dict()

        new_db['version'] = self.ZEPHYR_DICT_LOG_VER
        new_db['target'] = dict()
        new_db['sections'] = dict()
        new_db['log_subsys'] = dict()
        new_db['log_subsys']['log_instances'] = dict()
        new_db['build_id'] = None
        new_db['arch'] = None
        new_db['kconfigs'] = dict()

        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 add_string_section(self, name, sect_dict):
        """Add a static string section to the collection"""
        self.database['sections'][name] = sect_dict


    def has_string_sections(self):
        """Return True if there are any static string sections"""
        return len(self.database['sections']) != 0


    def find_string(self, string_ptr):
        """Find string pointed by string_ptr from any static string section.
        Return None if not found."""
        for _, sect in self.database['sections'].items():
            one_str = extract_string_from_section(sect, string_ptr)

            if one_str is not None:
                return one_str

        return None


    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") as db_fd:
                json_db = json.load(db_fd)
        except (OSError, json.JSONDecodeError):
            return None

        # Decode data in JSON back into binary data
        for _, sect in json_db['sections'].items():
            sect['data'] = base64.b64decode(sect['data_b64'])

        database = LogDatabase()
        database.database = json_db

        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
        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") as db_fd:
                db_fd.write(json.dumps(json_db))
        except OSError:
            return False

        return True

#!/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 .utils import extract_string_from_section


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 binary format of dictionary based logging
    # has changed
    ZEPHYR_DICT_LOG_VER = 1

    LITTLE_ENDIAN = True
    BIG_ENDIAN = False

    def __init__(self):
        new_db = dict()

        new_db['version'] = self.ZEPHYR_DICT_LOG_VER
        new_db['target'] = dict()
        new_db['sections'] = dict()
        new_db['log_subsys'] = dict()
        new_db['log_subsys']['log_instances'] = dict()
        new_db['build_id'] = None
        new_db['arch'] = None
        new_db['kconfigs'] = dict()

        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 add_string_section(self, name, sect_dict):
        """Add a static string section to the collection"""
        self.database['sections'][name] = sect_dict


    def has_string_sections(self):
        """Return True if there are any static string sections"""
        return len(self.database['sections']) != 0


    def find_string(self, string_ptr):
        """Find string pointed by string_ptr from any static string section.
        Return None if not found."""
        for _, sect in self.database['sections'].items():
            one_str = extract_string_from_section(sect, string_ptr)

            if one_str is not None:
                return one_str

        return None


    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") as db_fd:
                json_db = json.load(db_fd)
        except (OSError, json.JSONDecodeError):
            return None

        # Decode data in JSON back into binary data
        for _, sect in json_db['sections'].items():
            sect['data'] = base64.b64decode(sect['data_b64'])

        database = LogDatabase()
        database.database = json_db

        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
        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") as db_fd:
                db_fd.write(json.dumps(json_db))
        except OSError:
            return False

        return True

#!/usr/bin/env python3 # # Copyright (c) 2021 Intel Corporation # # SPDX-License-Identifier: Apache-2.0 Class for Dictionary-based Logging Database # 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. Class of log database # Update this if binary format of dictionary based logging # has changed Get Database Version Get Build ID Set Build ID in Database Get the Target Architecture Set the Target Architecture Get Target Bitness: 32 or 64 Set Target Bitness: 32 or 64 Return True if target is 64-bit, False if 32-bit.         None if error. Get Target Endianness.          Return True if little endian, False if big. Set Target Endianness          True if little endian, False if big. Return True if target is little endian Add a static string section to the collection Return True if there are any static string sections Find string pointed by string_ptr from any static string section.         Return None if not found. Add one log instance into database Get the source string based on source ID # JSON stores key as string, so we need to convert Add a kconfig name-value pair into database Return kconfig name-value pairs Read database from file and return a LogDatabase object # Decode data in JSON back into binary data Write the database into file # Make database object into something JSON can dump