# -*- coding: utf-8 -*-
"""
    Custom GDB command for reading ARMv8-A Core L1 Cache contents.

 Copyright 2025 NXP

 NXP Confidential and Proprietary. This software is owned or controlled by NXP
 and may only be used strictly in accordance with the applicable license terms.
 By expressly accepting such terms or by downloading, installing, activating
 and/or otherwise using the software, you are agreeing that you have read, and
 that you agree to comply with and are bound by, such license terms. If you do
 not agree to be bound by the applicable license terms, then you may not retain,
 install, activate or otherwise use the software.
"""

import sys
import gdb  # @UnresolvedImport  # pylint: disable=F0401
import os
import json
import os
from enum import Enum

CURRENT_DIR = "".join([os.path.dirname(os.path.realpath(__file__)), "/"])
sys.path.insert(0, CURRENT_DIR)
sys.path.insert(0, CURRENT_DIR + "/../../utils")

import mem_reg_utils as utils
from errors import GtaLibException, ErrorCode

class CacheType(Enum):
    """
    The available cache types for which the read operation can be performed. 
    This can be extended for future types.
    """
    ICACHE = "icache"
    DCACHE = "dcache"

###########################################################################
##### Cache Memory Interface
###########################################################################
class CacheMemory():
    """
    Cache memory interface that must be implemented in order to be able to read a cache.
    All the cache memory types must implement this.
    """        
    def read(self, context, set, way, begin, end):
        pass

    def get_max_set_no(self):
        pass

    def get_max_way_no(self):
        pass

    def print_data(self):
        pass

###########################################################################
##### Arm V8 ICache Implementation 
###########################################################################
class ArmV8ICACHE(CacheMemory):
    ###########################################################################
    def __init__(self):
        self._MAX_CACHE_SET_NO = 256
        self._MAX_CACHE_WAY_NO = 2
        self._cache_lines = []

    ###########################################################################
    def get_max_set_no(self):
        return self._MAX_CACHE_SET_NO
    
    ###########################################################################
    def get_max_way_no(self):
        return self._MAX_CACHE_WAY_NO
    
    ###########################################################################
    def read(self, context, set, way, begin, end):
        is_range_read = (begin != 0) or (end !=0)

        cache_read_cmd = 'monitor cache read {0} 1 {1} {2} {3} {4}'.format(
            context, set, way, begin, end)
        raw_data = utils.gdb_exec(cache_read_cmd, False, True).unwrap().rstrip()

        # header0 (4 bytes) data0 (5 bytes) data1 (5 bytes) ... data15 (5 bytes)
        cache_line_size = 84 * 2
        if (len(raw_data) % cache_line_size) != 0:
            raise gdb.GdbError("[S32Debugger]Error: Invalid result from GTA")
                
        raw_cache_lines = [raw_data[i:i + cache_line_size] 
                           for i in range(0, len(raw_data), cache_line_size)]
       
        self._cache_lines = []
        way_ovf = False
        way_read = 0
        set_read = begin
        for raw_cache_line in raw_cache_lines:
            header = raw_cache_line[0:8] 
            header_value = int(header, 16)

            # in case of range read we need to know what is the actual set/way read
            if not is_range_read:
                set_read = set
                way_read = way  
            else:
                if way_ovf:
                    set_read = set_read + 1
                    way_read = 0 
                    way_ovf = False
            # Set mode
            set_mode = (header_value >> 29) & 0x3
            if set_mode == 0x0:
                set_mode_value = "A32"
            elif set_mode == 0x1:
                set_mode_value = "T32"
            elif set_mode == 0x2:
                set_mode_value = "A64"
            else:
                set_mode_value = "Invalid"

            # Non-secure
            non_secure = (header_value >> 28) & 0x1
            if non_secure == 0x1:
                security_value = "Not Secure"
            else:
                # In this case: non_secure == 0x0.
                security_value = "Secure"

            # Tag address
            tag_address_value = ((header_value & 0x0FFFFFFF) << 12) | (set_read << 6)

            data_element_len = 5 * 2 # 4 bytes * 2 digits per byte
            line_values = [int(raw_cache_line[i:i + data_element_len], 16)
                           for i in range(8, len(raw_cache_line), data_element_len)]
            self._cache_lines.append(
                {
                    "Tag": tag_address_value,
                    "Mode": set_mode_value,
                    "Secure": security_value,
                    "Set": set_read,
                    "Way": way_read,
                    "Data": {
                        "Length": len(line_values),
                        "CacheLine": line_values,
                    },
                })
            
            ## check if the next way read will be an ovf
            way_read = way_read + 1
            way_ovf = way_read > (self.get_max_way_no()-1)

        return self._cache_lines
    
    ###########################################################################
    def print_data(self):     
        gdb.write(
            "|--------------------|-----------|--------------|-----|-----|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|\n")
        gdb.write(
            "|         Tag        |    Mode   |    Secure    | Set | Way |  00          |  04          |  08          |  0C          |  10          |  14          |  18          |  1C          |  20          |  24          |  28          |  2C          |  30          |  34          |  38          |  3C          |\n")
        gdb.write(
            "|--------------------|-----------|--------------|-----|-----|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|\n")

        CACHE_LINE_INFO = "| 0x{:016X} | {:^9} | {:^12} | {:^3} | {:^3} |"
        for cache_line in self._cache_lines:
            gdb.write(
                CACHE_LINE_INFO.format(
                    cache_line["Tag"],
                    cache_line["Mode"],
                    cache_line["Secure"],
                    str(cache_line["Set"]),
                    str(cache_line["Way"])
                ))
            for cache_data in cache_line["Data"]["CacheLine"]:
                gdb.write(" {:012X} |".format(cache_data))

            gdb.write(os.linesep)
        gdb.write(
            "|--------------------|-----------|--------------|-----|-----|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|\n")
        gdb.write(os.linesep)


###########################################################################
##### Arm V8 DCache Implementation 
###########################################################################
class ArmV8DCACHE(CacheMemory):
    ###########################################################################
    def __init__(self):
        self._MAX_CACHE_SET_NO = 128
        self._MAX_CACHE_WAY_NO = 4
        self._cache_lines = []
    
    ###########################################################################
    def get_max_set_no(self):
        return self._MAX_CACHE_SET_NO
    
    ###########################################################################
    def get_max_way_no(self):
        return self._MAX_CACHE_WAY_NO
    
    ###########################################################################
    def read(self, context, set, way, begin, end):
        is_range_read = (begin != 0) or (end !=0)

        cache_read_cmd = 'monitor cache read {0} 0 {1} {2} {3} {4}'.format(
            context, set, way, begin, end)       
        raw_data = utils.gdb_exec(cache_read_cmd, False, True).unwrap().rstrip()
        
        # structure of a dcache line read from CCS
        # header0 (4 bytes) header1 (4 bytes) data0 (4 bytes) data1 (4 bytes) ... data15 (4 bytes)
        cache_line_size = 72 * 2
        if (len(raw_data) % cache_line_size) != 0:
            raise gdb.GdbError("[S32Debugger]Error: Invalid result from GTA")
            
        raw_cache_lines = [raw_data[i:i + cache_line_size] 
            for i in range(0, len(raw_data), cache_line_size)]
                    

        self._cache_lines = []
        way_ovf = False
        way_read = 0
        set_read = begin
        for raw_cache_line in raw_cache_lines:
            header0 = raw_cache_line[0:8]  # header0
            header0_value = int(header0, 16)
            header1 = raw_cache_line[8:16] # header1
            header1_value = int(header1, 16)
            # in case of range read we need to know what is the actual set/way read
            if not is_range_read:
                set_read = set
                way_read = way  
            else:
                if way_ovf:
                    set_read = set_read + 1
                    way_read = 0 
                    way_ovf = False
            # MOESI
            moesi_1 = (header1_value >> 29) & 0x3
            moesi_0 = header0_value & 0x3

            if (moesi_1 & 0x2) != 0x0:
                if (moesi_0 & 0x1) == 0x1:
                    moesi_value = "UniqueDirty"
                else:
                    # In this case: (moesi_0 & 0x1) == 0x0.
                    moesi_value = "UniqueClean"
            elif moesi_1 == 0x1:
                if (moesi_0 & 0x1) == 0x1:
                    moesi_value = "SharedDirty"
                else:
                    # In this case: (moesi_0 & 0x1) == 0x0.
                    moesi_value = "SharedClean"
            else:
                # moesi_1 == 0x0 is the only case remaining, since if moesi_1 is 0x2 or 0x3,
                # the first condition will be true and that branch will be executed.
                moesi_value = "Invalid"

            # Non-secure
            non_secure = (header1_value >> 28) & 0x1

            if non_secure == 0x1:
                security_value = "Not secure"
            else:
                # In this case: non_secure == 0x0
                security_value = "Secure"

            # Tag address
            tag_addr_1 = header1_value & 0x0FFFFFFF
            tag_addr_0 = (header0_value >> 31) & 0x1
            tag_addr = (tag_addr_1 << 1) | tag_addr_0
            tag_addr_value = (tag_addr << 11) + (set_read << 6)

            data_element_len = 4 * 2 # 5 bytes * 2 digits per byte
            line_values = [int(raw_cache_line[i:i + data_element_len], 16)
                           for i in range(16, len(raw_cache_line), data_element_len)]
                                                
            self._cache_lines.append(
                {
                    "Tag": tag_addr_value,
                    "Mode": moesi_value,
                    "Secure": security_value,
                    "Set": set_read,
                    "Way": way_read,
                    "Data": {
                        "Length": len(line_values),
                        "CacheLine": line_values,
                    },
                })
            
            ## check if the next way read will be an ovf 
            way_read = way_read + 1
            way_ovf = way_read > (self.get_max_way_no()-1)

        return self._cache_lines
    
    ###########################################################################
    def print_data(self):
        gdb.write(
            "|--------------------|----------------|--------------|-----|-----|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|\n")
        gdb.write(
            "|         Tag        |     MOESI      |    Secure    | Set | Way |  00        |  04        |  08        |  0C        |  10        |  14        |  18        |  1C        |  20        |  24        |  28        |  2C        |  30        |  34        |  38        |  3C        |\n")
        gdb.write(
            "|--------------------|----------------|--------------|-----|-----|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|\n")

        CACHE_LINE_INFO = "| 0x{:016X} | {:^14} | {:^12} | {:^3} | {:^3} |"
        for cache_line in self._cache_lines:
            gdb.write(
                CACHE_LINE_INFO.format(
                    cache_line["Tag"],
                    cache_line["Mode"],
                    cache_line["Secure"],
                    str(cache_line["Set"]),
                    str(cache_line["Way"])
                ))   
            for cache_data in cache_line["Data"]["CacheLine"]:
                gdb.write(" {:010X} |".format(cache_data))              
            gdb.write(os.linesep)
        gdb.write(
            "|--------------------|----------------|--------------|-----|-----|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|\n")
        gdb.write(os.linesep)

###########################################################################
def cache_factory(cache_type):
    if cache_type == CacheType.DCACHE:
        return ArmV8DCACHE()
    elif cache_type == CacheType.ICACHE:
        return ArmV8ICACHE()
    else:
        raise ValueError("Unknown cache type")

###########################################################################
##### Read L1 Cache contents for ARMv8-A core.
###########################################################################
class CacheRead(gdb.Command):
    """
    Read ARMv8-A core L1 Cache contents.
    usage: cache_read type [-c/-context] \" \" [-s/-set] 0 [-w/-way] 0 [-b/-begin] 0 [-e/-end] 0
        type            -   i/icache or d/dcache
        [-js/-JSON]     -   forces the format of the command's output text to be JSON
        [-c/-context]   -   GTA (GDB server) debug context :ccs:<soc>[:core]
                            If present, the context can be a core context;
                            if not, the current context is used.
                            e.g. ":ccs:S32G2XX:CortexA53#0"
        [-s/-set]       -   The Cache set index where the Cache Line will be read.
                            [0..255] in case of icache
                            [0..127] in case of dcache
        [-w/-way]       -   The Cache way to read where the Cache Line will be read.
                            [0..1] in case of icache
                            [0..3] in case of dcache
        [-b/-begin]     -   The beginning set where we start our read. Must be smaller
                            than the ending set. Must not be used in conjunction with
                            set or way parameters.
        [-e/-end]       -   The end set where we end our read. Must be larger
                            than the starting set. Must not be used in conjunction
                            with set or way parameters.


        E.g.: cache_read -c :ccs:S32G2XX:CortexA53#0 -s 2 -w 3 dcache
                                                                      Selected WAY 3
                                                                            |
                                                                            v
                                                                    #################
                          WAY 0           WAY 1           WAY 2     #     WAY 3     #
                                                                    #               #
            Index       Tag Data        Tag Data        Tag Data    #   Tag Data    #
                        ---------       ---------       ---------   #   ---------   #
            0           |   |   |       |   |   |       |   |   |   #   |   |   |   #
            1           |   |   |       |   |   |       |   |   |   #   |   |   |   #
        ###############################################################################
        #   2           |   |   |       |   |   |       |   |   |   #   |123|456|   # #   <-- Selected SET 2
        ###############################################################################
            3           |   |   |       |   |   |       |   |   |   #   |   |   |   #
            ...         |   |   |       |   |   |       |   |   |   #   |   |   |   #
            254         |   |   |       |   |   |       |   |   |   #   |   |   |   #
            255         |   |   |       |   |   |       |   |   |   #   |   |   |   #
                        ---------       ---------       ---------   #   ---------   #
                                                                    #################

        In the example above we selected from the dcache set 2 and way 3 to read the Cache Line containing the TAG 123 and DATA 456.

        Every register and bit shift, that is not explained in the comments, for cache read operation, can be found in the Cortex A53 TRM (Technical Reference Manual).

        Other valid examples:
            cache_read -c :ccs:S32G2XX:CortexA53#0 -s 0 -w 0 dcache
            cache_read icache -s 0 -c :ccs:S32G2XX:CortexA53#0 -w 0
            cache_read -b 0 -e 100 dcache
            cache_read -b 0 -e 100 icache
            cache_read -s 0 -w 0 icache
            cache_read -c :ccs:S32G2XX:CortexA53#0 dcache
            cache_read -c :ccs:S32G2XX:CortexA53#0 icache
            cache_read dcache
            cache_read icache
            etc.
    """

    TYPE = "type"
    CONTEXT = "-c"
    SET = "-s"
    WAY = "-w"
    BEGIN = "-b"
    END = "-e"

    ###########################################################################
    def __init__(self):
        """
        Constructor
        """
        super(CacheRead, self).__init__("cache_read", gdb.COMMAND_USER)
      
        # List of cache lines that have been read in the current invoke,
        # used for printing JSON objects
        self._stored_cache_lines = []
        
        self._context = ""
        self._print_in_console = True
        self._arg_dictionary = {}
    
    ###########################################################################
    def invoke(self, args, from_tty):
        """Called when the command is invoked"""
        self.dont_repeat()
        argv = gdb.string_to_argv(args)
        self._parse_arguments(argv)
        self._execute_command()
        gdb.flush()

    ###########################################################################
    def _parse_arguments(self, args):
        # reset argument Collection and printing.        
        self._arg_dictionary = {}
        self._print_in_console = True
     
        # no arguments added
        if not args:
            raise gdb.GdbError("[S32Debugger]Error: invalid number of parameters")

        #######################################################################
        # Parse the mandatory arguments
        # Parse context.
        if "-c" in args or "-context" in args:
            if "-c" in args:
                context_location = args.index("-c")
            else:
                # In this case: "-context" is in args.
                context_location = args.index("-context")
            context_string = args[context_location + 1]            
            if ":ccs:" not in context_string:
                raise gdb.GdbError(
                    '[S32Debugger]Error: Context error. '
                    'Make sure the context is completed using this pattern: ":ccs:<soc>[:core]"'
                )        
            self._arg_dictionary.update({self.CONTEXT: context_string})
        else:
            # Get the current context as no context was provided.
            self._arg_dictionary.update(
                {
                    self.CONTEXT: (
                        utils.gdb_exec("monitor ctx get current", False, True)
                        .unwrap()
                        .rstrip()
                    )
                }
            )
        #Context used to read the cache
        self._context = self._arg_dictionary[self.CONTEXT]
        if "A53" not in self._context:
                raise gdb.GdbError(
                    "[S32Debugger]Error: core cache read is not supported. "
                    "Only A53 cores are supported.")   

        #######################################################################
        # Cache type
        is_icache_arg = any(type in args for type in ["i", "icache"])
        is_dcache_arg = any(type in args for type in ["d", "dcache"])      
        if not is_dcache_arg and not is_icache_arg:
            raise gdb.GdbError(
                "[S32Debugger]Error: Cache type must be either icache or dcache.")

        
        # Only one cache type must be present at a time.
        if is_dcache_arg and is_icache_arg:
            raise gdb.GdbError(
                "[S32Debugger]Error: Cache type must be either icache or dcache. "
                "Not both at the same time.")

        if is_icache_arg:
            self._arg_dictionary.update({self.TYPE: CacheType.ICACHE})
        elif is_dcache_arg:
            self._arg_dictionary.update({self.TYPE: CacheType.DCACHE})
        #actual memory for which the read is executed            
        self._current_cache = cache_factory(self._arg_dictionary[self.TYPE])

        #######################################################################
        # The format of the command output text, can be directly in JSON format 
        # or in a tabular way
        if "-js" in args or "-JSON" in args:
            self._print_in_console = False

        #######################################################################
        # Parse set.
        is_set_arg = any(s in args for s in ["-s", "-set"])
        is_way_arg = any(w in args for w in ["-w", "-way"])
        is_begin_arg = any(w in args for w in ["-b", "-begin"])
        is_end_arg = any(e in args for e in ["-e", "-end"])
                                    
        if is_set_arg:
            # Check for exclusion with range parameters.
            if is_begin_arg or is_end_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: -s/-set and -w/-way can't be used in"
                    " conjunction with -b/-begin and -e/-end.")

            # Check if way parameter exists.
            if not is_way_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: Only set parameter was provided with no way parameter."
                    " In this case a way parameter must be provided."
                )

            if "-s" in args:
                set_location = args.index("-s")
            else:
                # In this case: "-set" is in args.
                set_location = args.index("-set")

            set_string = args[set_location + 1]
            self._check_if_integer_value(set_string, "Set")
            set = int(set_string)

            if set > (self._current_cache.get_max_set_no() - 1):
                raise gdb.GdbError(
                    "[S32Debugger]Error: the maximum number of sets is {}. The interval is [0..{}]".format(
                        self._current_cache.get_max_set_no(),
                        self._current_cache.get_max_set_no() - 1,
                    )
                )
            self._arg_dictionary.update({self.SET: set})

        # Parse way.
        if is_way_arg:
            # Check for exclusion with range parameters.
            if is_begin_arg or is_end_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: -s/-set and -w/-way can't be "
                    "used in conjunction with -b/-begin and -e/-end."
                )

            # Check if set parameter exists.
            if not is_set_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: Only way parameter was provided with no set parameter."
                    " In this case a set parameter must be provided.")

            if "-w" in args:
                way_location = args.index("-w")
            else:
                # In this case: "-way" is in args.
                way_location = args.index("-way")
            way_string = args[way_location + 1]
            self._check_if_integer_value(way_string, "Way")
            way = int(way_string)

            if way > (self._current_cache.get_max_way_no() - 1):
                raise gdb.GdbError(
                    "[S32Debugger]Error: the maximum number of ways is {}. The interval is [0..{}]".format(
                        self._current_cache.get_max_way_no(),
                        self._current_cache.get_max_way_no() - 1,
                    )
                )
            self._arg_dictionary.update({self.WAY: way})

        #######################################################################
        # Parse the beginning set of the range.
        if is_begin_arg:            
            # Check for exclusion with line parameters.
            if is_set_arg or is_way_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: -b/-begin and -e/-end can't be used in"
                    " conjunction with -s/-set and -w/-way."
                )

            # Check if end parameter exists.
            if not is_end_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: Only begin parameter was provided with no end parameter. "
                    "In this case an end parameter must be provided."
                )

            if "-b" in args:
                begin_location = args.index("-b")
            else:
                # In this case: "-begin" is in args.
                begin_location = args.index("-begin")
            begin_string = args[begin_location + 1]
            self._check_if_integer_value(begin_string, "Begin")
            begin = int(begin_string)

            if begin > (self._current_cache.get_max_set_no() - 1):
                raise gdb.GdbError(
                    "[S32Debugger]Error: the maximum number of sets is {}. The interval is [0..{}]".format(
                        self._current_cache.get_max_set_no(),
                        self._current_cache.get_max_set_no() - 1,
                    )
                )
            self._arg_dictionary.update({self.BEGIN: begin})

        # Parse the end set of the range.
        if is_end_arg:
            # Check for exclusion with line parameters.
            if is_set_arg or is_way_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: -b/-begin and -e/-end can't be used in"
                    " conjunction with -s/-set and -w/-way."
                )

            # Check if begin parameter exists.
            if not is_begin_arg:
                raise gdb.GdbError(
                    "[S32Debugger]Error: Only end parameter was provided with no begin parameter. "
                    "In this case a begin parameter must be provided."
                )

            if "-e" in args:
                end_location = args.index("-e")
            else:
                # In this case: "-end" in in args.
                end_location = args.index("-end")

            end_string = args[end_location + 1]
            self._check_if_integer_value(end_string, "End")
            end = int(end_string)

            begin = self._arg_dictionary[self.BEGIN]
            if begin > end:
                raise gdb.GdbError(""
                "[S32Debugger]Error: begin parameter must be smaller than end parameter. "
                "They can be equal if only one set is to be read."
                )

            if end > (self._current_cache.get_max_set_no() - 1):
                raise gdb.GdbError(
                    "[S32Debugger]Error: the maximum number of sets is {}. "
                    "The interval is [0..{}]".format(
                        self._current_cache.get_max_set_no(),
                        self._current_cache.get_max_set_no() - 1,
                    )
                )
            self._arg_dictionary.update({self.END: end})
    
    ###########################################################################
    def _execute_command(self):
        # Update the mandatory param values.
        context, set, way, begin, end = self._get_read_arguments()

        self._stored_cache_lines = self._current_cache.read(
            context, set, way, begin, end)

        if self._print_in_console:
            self._print_cache_data()
        else:            
            self._dump_json_data()
    
    ###########################################################################
    def _get_read_arguments(self):
        is_set_defined = self.SET in self._arg_dictionary is not None
        is_way_defined = self.WAY in self._arg_dictionary is not None
        is_begin_defined = self.BEGIN in self._arg_dictionary is not None
        is_end_defined = self.END in self._arg_dictionary is not None

        if is_set_defined:
            set = self._arg_dictionary[self.SET]
        else:
            set = 0
            
        # Update the way param.
        if is_way_defined:
            way = self._arg_dictionary[self.WAY]
        else:
            way = 0

        # Update the begin param.
        if is_begin_defined:
            begin = self._arg_dictionary[self.BEGIN]
        else:
            begin = 0            

        # Update the end param.
        if is_end_defined:
            end = self._arg_dictionary[self.END]
        else:
            end = 0

        # in case there is no param defined.. read all the cache
        if (not is_set_defined) and (not is_way_defined) and (not is_begin_defined) and(not is_end_defined):
            set = 0
            way = 0
            begin = 0
            end = self._current_cache.get_max_set_no() - 1

        return self._context, set, way, begin, end
    
    ###########################################################################
    def _print_cache_data(self):
        self._current_cache.print_data()
    
    ###########################################################################
    def _check_if_integer_value(self, input_string, parameter_type):
        try:
            int(input_string)
        except ValueError:
            raise gdb.GdbError(
                'Error: {} needs a numeric input, "{}" is not correct.'.format(
                    parameter_type, input_string
                )
            )
    
    ###########################################################################
    def _dump_json_data(self):
        """
        Dumps the whole contents of the ICACHE / DCACHE depending on the way the read was invoked into JSON format.
        """
        gdb.write(json.dumps(self._stored_cache_lines) + os.linesep)
        self._stored_cache_lines = []