8 import elftools.elf.elffile
as elffile
9 from elftools.elf.constants
import SH_FLAGS
10 from elftools.elf.sections
import SymbolTableSection
14 Script to gather metrics on ROM,RAM,Stack and Heap usage.
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
22 > ./get_metrics.py ../bin/TARGET_ELF_FILE [-i memsegs.ini]
26 DEFAULT_RAM_START = 0x80000
27 DEFAULT_RAM_SIZE = 0x80000
28 DEFAULT_STACK_SIZE = 0x4000
36 assert self.
minmin <= self.
maxmax,
"Invalid MemRange"
41 self.
lowlow = 0xFFFFFFFF
45 return adr >= self.
minmin
and adr < self.
maxmax
47 def trace(self, adr, mode, pc, sz):
57 raise ValueError(f
"Invalid mode: {mode}")
66 return self.
highhigh - self.
lowlow
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>)"
83 ignoreSections = [
"",
".stack",
".comment",
".riscv.attributes",
".strtab",
".shstrtab"]
85 with open(inFile,
"rb")
as f:
86 e = elffile.ELFFile(f)
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"):
105 elif s.name.startswith(
".sdata2"):
107 elif s.name.startswith(
".debug_"):
109 elif s.name
in ignoreSections:
111 elif isinstance(s, SymbolTableSection):
112 for sym
in s.iter_symbols():
113 if sym.name ==
"_heap_start":
114 heapStart = sym[
"st_value"]
116 print(
"warning: ignored: " + s.name +
" / size: " +
str(s.data_size))
123 return f
"unknown [{unknown_msg}]" if unknown_msg
else "unknown"
124 return humanize.naturalsize(sz) +
" (" + hex(sz) +
")"
127 if __name__ ==
"__main__":
128 parser = argparse.ArgumentParser()
129 parser.add_argument(
"elf", metavar=
"ELF", type=str, nargs=1, help=
"The target ELF file")
133 default=
"dBusAccess.csv",
135 help=
"Path to CSV trace file of memory accesses (default: %(default)s)",
138 "--ini",
"-i", default=
"", type=str, help=
"Path to INI file containing simple_mem_system layout (optional)"
140 parser.add_argument(
"--out",
"-o", metavar=
"FILE", type=str, default=
"", help=
"""Output CSV file (default: -)""")
141 args = parser.parse_args()
143 elfFile = args.elf[0]
144 traceFile = args.trace
148 ramStart = DEFAULT_RAM_START
149 ramSize = DEFAULT_RAM_SIZE
150 stackSize = DEFAULT_STACK_SIZE
163 config = configparser.ConfigParser()
166 if "IntConfigurations" not in config:
167 raise RuntimeError(
"Section [IntConfigurations] does not exist in config file " + memCfg)
169 cfg = config[
"IntConfigurations"]
174 ramStart =
int(cfg[
"simple_mem_system.memseg_origin_01"], 0)
175 ramSize =
int(cfg[
"simple_mem_system.memseg_length_01"], 0)
179 raise RuntimeError(
"did not find heap start")
181 print(
"heap starts at: " + hex(heapStart))
184 h =
MemRange(
"Heap", heapStart, ramStart + ramSize - stackSize)
185 s =
MemRange(
"Stack", ramStart + ramSize - stackSize, ramStart + ramSize)
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=
";")
200 if mem.contains(adr):
201 mem.trace(adr, mode, pc, sz)
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_")])
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,
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"]))
229 + (
"" if trace_available
else " [stack and heap usage not included]")
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"))
238 with open(csvFile,
"w")
as f:
239 writer = csv.DictWriter(f, fieldnames=results.keys())
241 writer.writerow(results)
def __init__(self, name, min, max)
def trace(self, adr, mode, pc, sz)
def printSz(sz, unknown_msg="")