ETISS 0.8.0
Extendable Translating Instruction Set Simulator (version 0.8.0)
get_metrics.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import os
4 import csv
5 import argparse
6 import configparser
7 import humanize
8 import elftools.elf.elffile as elffile
9 from elftools.elf.constants import SH_FLAGS
10 from elftools.elf.sections import SymbolTableSection
11 
12 
13 """
14 Script to gather metrics on ROM,RAM,Stack and Heap usage.
15 
16 To produce the memory trace:
17 - Compile etissvp_lib with SC_MEM_WRITE_TRACE defined (simple: uncomment at top of mem.cpp)
18 - Invoke the run.sh script with the "nodmi" option
19 - A file "pulpino_soc.dmem_memtrace.csv" should have been created
20 
21 Then run this script:
22 > ./get_metrics.py ../bin/TARGET_ELF_FILE [-i memsegs.ini]
23 """
24 
25 # Feel free to overwrite these defaults for your needs
26 DEFAULT_RAM_START = 0x80000
27 DEFAULT_RAM_SIZE = 0x80000
28 DEFAULT_STACK_SIZE = 0x4000
29 
30 
31 class MemRange:
32  def __init__(self, name, min, max):
33  self.namename = name
34  self.minmin = min
35  self.maxmax = max
36  assert self.minmin <= self.maxmax, "Invalid MemRange"
37  self.num_readsnum_reads = 0
38  self.num_writesnum_writes = 0
39  self.read_bytesread_bytes = 0
40  self.written_byteswritten_bytes = 0
41  self.lowlow = 0xFFFFFFFF
42  self.highhigh = 0
43 
44  def contains(self, adr):
45  return adr >= self.minmin and adr < self.maxmax
46 
47  def trace(self, adr, mode, pc, sz):
48  self.lowlow = min(adr, self.lowlow)
49  self.highhigh = max(adr, self.highhigh)
50  if mode == "r":
51  self.num_readsnum_reads += 1
52  self.read_bytesread_bytes += sz
53  elif mode == "w":
54  self.num_writesnum_writes += 1
55  self.written_byteswritten_bytes += sz
56  else:
57  raise ValueError(f"Invalid mode: {mode}")
58 
59  @property
60  def count(self):
61  return self.num_readsnum_reads + self.num_writesnum_writes
62 
63  def usage(self):
64  if self.lowlow > self.highhigh:
65  return 0
66  return self.highhigh - self.lowlow
67 
68  def stats(self):
69  if self.lowlow > self.highhigh:
70  return self.namename + "\t[not accessed]"
71  return self.namename + f"\t[0x{self.low:x}-0x{self.high:x}] \t({self.count} times, reads: {self.num_reads} <{self.read_bytes}B>, writes: {self.num_writes} <{self.written_bytes}B>)"
72 
73 
74 def parseElf(inFile):
75  m = {}
76  m["rom_rodata"] = 0
77  m["rom_code"] = 0
78  m["rom_misc"] = 0
79  m["ram_data"] = 0
80  m["ram_zdata"] = 0
81  heapStart = None
82 
83  ignoreSections = ["", ".stack", ".comment", ".riscv.attributes", ".strtab", ".shstrtab"]
84 
85  with open(inFile, "rb") as f:
86  e = elffile.ELFFile(f)
87 
88  for s in e.iter_sections():
89  if s.name.startswith(".text"):
90  m["rom_code"] += s.data_size
91  elif s.name.startswith(".srodata"):
92  m["rom_rodata"] += s.data_size
93  elif s.name.startswith(".sdata"):
94  m["ram_data"] += s.data_size
95  elif s.name == ".rodata":
96  m["rom_rodata"] += s.data_size
97  elif s.name == ".vectors" or s.name == ".init_array":
98  m["rom_misc"] += s.data_size
99  elif s.name == ".data":
100  m["ram_data"] += s.data_size
101  elif s.name == ".bss" or s.name == ".sbss" or s.name == ".shbss":
102  m["ram_zdata"] += s.data_size
103  elif s.name.startswith(".gcc_except"):
104  pass
105  elif s.name.startswith(".sdata2"):
106  pass
107  elif s.name.startswith(".debug_"):
108  pass
109  elif s.name in ignoreSections:
110  pass
111  elif isinstance(s, SymbolTableSection):
112  for sym in s.iter_symbols():
113  if sym.name == "_heap_start":
114  heapStart = sym["st_value"]
115  else:
116  print("warning: ignored: " + s.name + " / size: " + str(s.data_size))
117 
118  return m, heapStart
119 
120 
121 def printSz(sz, unknown_msg=""):
122  if sz is None:
123  return f"unknown [{unknown_msg}]" if unknown_msg else "unknown"
124  return humanize.naturalsize(sz) + " (" + hex(sz) + ")"
125 
126 
127 if __name__ == "__main__":
128  parser = argparse.ArgumentParser()
129  parser.add_argument("elf", metavar="ELF", type=str, nargs=1, help="The target ELF file")
130  parser.add_argument(
131  "--trace",
132  "-t",
133  default="dBusAccess.csv",
134  type=str,
135  help="Path to CSV trace file of memory accesses (default: %(default)s)",
136  )
137  parser.add_argument(
138  "--ini", "-i", default="", type=str, help="Path to INI file containing simple_mem_system layout (optional)"
139  )
140  parser.add_argument("--out", "-o", metavar="FILE", type=str, default="", help="""Output CSV file (default: -)""")
141  args = parser.parse_args()
142 
143  elfFile = args.elf[0]
144  traceFile = args.trace
145  memIni = args.ini
146  csvFile = args.out
147 
148  ramStart = DEFAULT_RAM_START
149  ramSize = DEFAULT_RAM_SIZE
150  stackSize = DEFAULT_STACK_SIZE
151 
152  if len(memIni) > 0:
153  # Overwrite the default memory layout by parsing the specified INI file
154  #
155  # Example configuration file `memsegs.ini`:
156  #
157  # [IntConfigurations]
158  # simple_mem_system.memseg_origin_00=0x0
159  # simple_mem_system.memseg_length_00=0x100000
160  # simple_mem_system.memseg_origin_01=0x100000
161  # simple_mem_system.memseg_length_01=0x5000000
162  #
163  config = configparser.ConfigParser()
164  config.read(memIni)
165 
166  if "IntConfigurations" not in config:
167  raise RuntimeError("Section [IntConfigurations] does not exist in config file " + memCfg)
168 
169  cfg = config["IntConfigurations"]
170 
171  # ROM Start/Size is currently not used
172  # romStart = cfg["simple_mem_system.memseg_origin_00"]
173  # romSize = cfg["simple_mem_system.memseg_length_00"]
174  ramStart = int(cfg["simple_mem_system.memseg_origin_01"], 0)
175  ramSize = int(cfg["simple_mem_system.memseg_length_01"], 0)
176 
177  staticSizes, heapStart = parseElf(elfFile)
178  if not heapStart:
179  raise RuntimeError("did not find heap start")
180 
181  print("heap starts at: " + hex(heapStart))
182 
183  d = MemRange("Data", ramStart, heapStart)
184  h = MemRange("Heap", heapStart, ramStart + ramSize - stackSize)
185  s = MemRange("Stack", ramStart + ramSize - stackSize, ramStart + ramSize)
186  mems = [d, h, s]
187 
188  trace_available = False
189  if os.path.exists(traceFile):
190  trace_available = True
191  with open(traceFile) as f:
192  reader = csv.reader(f, skipinitialspace=True, delimiter=";")
193  for r in reader:
194  # ts = int(r[0])
195  pc = int(r[1], 16)
196  mode = r[2] # r/w
197  adr = int(r[3], 16)
198  sz = int(r[4], 16)
199  for mem in mems:
200  if mem.contains(adr):
201  mem.trace(adr, mode, pc, sz)
202 
203  for mem in mems:
204  print(mem.stats())
205 
206  romSize = sum([staticSizes[k] for k in staticSizes if k.startswith("rom_")])
207  ramSize = sum([staticSizes[k] for k in staticSizes if k.startswith("ram_")])
208 
209  results = {
210  "rom": romSize,
211  "rom_rodata": staticSizes["rom_rodata"],
212  "rom_code": staticSizes["rom_code"],
213  "rom_misc": staticSizes["rom_misc"],
214  "ram": (ramSize + s.usage() + h.usage()) if trace_available else ramSize,
215  "ram_data": staticSizes["ram_data"],
216  "ram_zdata": staticSizes["ram_zdata"],
217  "ram_stack": s.usage() if trace_available else None,
218  "ram_heap": h.usage() if trace_available else None,
219  }
220 
221  print("=== Results ===")
222  print("ROM usage: " + printSz(results["rom"]))
223  print(" read-only data: " + printSz(results["rom_rodata"]))
224  print(" code: " + printSz(results["rom_code"]))
225  print(" other required: " + printSz(results["rom_misc"]))
226  print(
227  "RAM usage: "
228  + printSz(results["ram"])
229  + ("" if trace_available else " [stack and heap usage not included]")
230  )
231  print(" data: " + printSz(results["ram_data"]))
232  print(" zero-init data: " + printSz(results["ram_zdata"]))
233  print(" stack: " + printSz(results["ram_stack"], unknown_msg="missing trace file"))
234  print(" heap: " + printSz(results["ram_heap"], unknown_msg="missing trace file"))
235 
236  # Write metrics to file
237  if csvFile:
238  with open(csvFile, "w") as f:
239  writer = csv.DictWriter(f, fieldnames=results.keys())
240  writer.writeheader()
241  writer.writerow(results)
__device__ int
def __init__(self, name, min, max)
Definition: get_metrics.py:32
def trace(self, adr, mode, pc, sz)
Definition: get_metrics.py:47
def contains(self, adr)
Definition: get_metrics.py:44
def parseElf(inFile)
Definition: get_metrics.py:74
def printSz(sz, unknown_msg="")
Definition: get_metrics.py:121