1#
2# Copyright (c) 2010-2024 Antmicro
3#
4# This file is licensed under the MIT License.
5# Full license text is available in 'licenses/MIT.txt'.
6#
7
8from struct import *
9
10
11class BaseEntry(Struct):
12    def __init__(self, format):
13        super(BaseEntry, self).__init__(format)
14        self.realTime = 0
15        self.virtualTime = 0
16        self.entryType = -1
17
18class MetricsParser:
19    def __init__(self, filePath):
20        self.filePath = filePath
21
22    def get_instructions_entries(self):
23        with open(self.filePath, "rb") as f:
24            cpus, _ = self._parseHeader(f)
25            return cpus, self._parse(f, b'\x00', '<iQ')
26
27    def get_memory_entries(self):
28        with open(self.filePath, "rb") as f:
29            _, _ = self._parseHeader(f)
30            return self._parse(f, b'\x01', 'c')
31
32    def get_peripheral_entries(self):
33        with open(self.filePath, "rb") as f:
34            _, peripherals = self._parseHeader(f)
35            return peripherals, self._parse(f, b'\x02', '<cQ')
36
37    def get_exceptions_entries(self):
38        with open(self.filePath, "rb") as f:
39            _, _ = self._parseHeader(f)
40            return self._parse(f, b'\x03', 'Q')
41
42    def _parse(self, f, entryType, format):
43        startTime = 0
44        entries = []
45        entry = BaseEntry('<qdc')
46        while True:
47            entryHeader = f.read(entry.size)
48            if not entryHeader:
49                break
50            realTime, entry.virtualTime, entry.entryType = entry.unpack(entryHeader)
51            if startTime == 0:
52                startTime = realTime
53            entry.realTime = (realTime - startTime) / 10000
54            if entry.entryType == entryType:
55                result = [entry.realTime, entry.virtualTime]
56                for data in self._read(format, f):
57                    result.append(data)
58                entries.append(result)
59            else:
60                self._ignore(entry.entryType, f)
61        return entries
62
63    def _parseHeader(self, f):
64        cpus = {}
65        peripherals = {}
66        numberOfCpus = self._read('i', f)[0]
67        for x in range(numberOfCpus):
68            cpuId = self._read('i', f)[0]
69            cpuNameLength = self._read('i', f)[0]
70            cpus[cpuId] = self._read('{}s'.format(cpuNameLength), f)[0].decode()
71        numberOfPeripherals = self._read('i', f)[0]
72        for x in range(numberOfPeripherals):
73            peripheralNameLength = self._read('i', f)[0]
74            peripheralName = self._read('{}s'.format(peripheralNameLength), f)[0].decode()
75            peripheralStartAddress, peripheralEndAddress = self._read('2Q', f)
76            peripherals[peripheralName] = [peripheralStartAddress, peripheralEndAddress]
77        return cpus, peripherals
78
79    def _ignore(self, entryType, f):
80        if entryType == b'\x00':
81            self._read('<iQ', f)
82        if entryType == b'\x01':
83            self._read('c', f)
84        if entryType == b'\x02':
85            self._read('<cQ', f)
86        if entryType == b'\x03':
87            self._read('<Q', f)
88
89    def _read(self, format, file):
90        return unpack(format, file.read(calcsize(format)))
91