14import elftools.elf.elffile
as elffile
15from elftools.elf.constants
import SH_FLAGS
16from elftools.elf.sections
import SymbolTableSection
20Script to gather metrics on ROM,RAM,Stack and Heap usage.
22To produce the memory trace:
23- Compile etissvp_lib with SC_MEM_WRITE_TRACE defined (simple: uncomment at top of mem.cpp)
24- Invoke the run.sh script with the "nodmi" option
25- A file "pulpino_soc.dmem_memtrace.csv" should have been created
28> ./get_metrics.py ../bin/TARGET_ELF_FILE [-i memsegs.ini]
32DEFAULT_RAM_START = 0x80000
33DEFAULT_RAM_SIZE = 0x80000
34DEFAULT_STACK_SIZE = 0x4000
42 assert self.
min <= self.
max,
"Invalid MemRange"
51 return adr >= self.
min and adr < self.
max
53 def trace(self, adr, mode, pc, sz):
63 raise ValueError(f
"Invalid mode: {mode}")
76 return self.
name +
"\t[not accessed]"
77 return self.
name + 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>)"
89 ignoreSections = [
"",
".stack",
".comment",
".riscv.attributes",
".strtab",
".shstrtab"]
91 with open(inFile,
"rb")
as f:
92 e = elffile.ELFFile(f)
94 for s
in e.iter_sections():
95 if s.name.startswith(
".text"):
96 m[
"rom_code"] += s.data_size
97 elif s.name.startswith(
".srodata"):
98 m[
"rom_rodata"] += s.data_size
99 elif s.name.startswith(
".sdata"):
100 m[
"ram_data"] += s.data_size
101 elif s.name ==
".rodata":
102 m[
"rom_rodata"] += s.data_size
103 elif s.name ==
".vectors" or s.name ==
".init_array":
104 m[
"rom_misc"] += s.data_size
105 elif s.name ==
".data":
106 m[
"ram_data"] += s.data_size
107 elif s.name ==
".bss" or s.name ==
".sbss" or s.name ==
".shbss":
108 m[
"ram_zdata"] += s.data_size
109 elif s.name.startswith(
".gcc_except"):
111 elif s.name.startswith(
".sdata2"):
113 elif s.name.startswith(
".debug_"):
115 elif s.name
in ignoreSections:
117 elif isinstance(s, SymbolTableSection):
118 for sym
in s.iter_symbols():
119 if sym.name ==
"_heap_start":
120 heapStart = sym[
"st_value"]
122 print(
"warning: ignored: " + s.name +
" / size: " +
str(s.data_size))
129 return f
"unknown [{unknown_msg}]" if unknown_msg
else "unknown"
130 return humanize.naturalsize(sz) +
" (" + hex(sz) +
")"
133if __name__ ==
"__main__":
134 parser = argparse.ArgumentParser()
135 parser.add_argument(
"elf", metavar=
"ELF", type=str, nargs=1, help=
"The target ELF file")
139 default=
"dBusAccess.csv",
141 help=
"Path to CSV trace file of memory accesses (default: %(default)s)",
144 "--ini",
"-i", default=
"", type=str, help=
"Path to INI file containing simple_mem_system layout (optional)"
146 parser.add_argument(
"--out",
"-o", metavar=
"FILE", type=str, default=
"", help=
"""Output CSV file (default: -)""")
147 args = parser.parse_args()
149 elfFile = args.elf[0]
150 traceFile = args.trace
154 ramStart = DEFAULT_RAM_START
155 ramSize = DEFAULT_RAM_SIZE
156 stackSize = DEFAULT_STACK_SIZE
169 config = configparser.ConfigParser()
172 if "IntConfigurations" not in config:
173 raise RuntimeError(
"Section [IntConfigurations] does not exist in config file " + memCfg)
175 cfg = config[
"IntConfigurations"]
180 ramStart =
int(cfg[
"simple_mem_system.memseg_origin_01"], 0)
181 ramSize =
int(cfg[
"simple_mem_system.memseg_length_01"], 0)
185 raise RuntimeError(
"did not find heap start")
187 print(
"heap starts at: " + hex(heapStart))
190 h =
MemRange(
"Heap", heapStart, ramStart + ramSize - stackSize)
191 s =
MemRange(
"Stack", ramStart + ramSize - stackSize, ramStart + ramSize)
194 trace_available =
False
195 if os.path.exists(traceFile):
196 trace_available =
True
197 with open(traceFile)
as f:
198 reader = csv.reader(f, skipinitialspace=
True, delimiter=
";")
206 if mem.contains(adr):
207 mem.trace(adr, mode, pc, sz)
212 romSize = sum([staticSizes[k]
for k
in staticSizes
if k.startswith(
"rom_")])
213 ramSize = sum([staticSizes[k]
for k
in staticSizes
if k.startswith(
"ram_")])
217 "rom_rodata": staticSizes[
"rom_rodata"],
218 "rom_code": staticSizes[
"rom_code"],
219 "rom_misc": staticSizes[
"rom_misc"],
220 "ram": (ramSize + s.usage() + h.usage())
if trace_available
else ramSize,
221 "ram_data": staticSizes[
"ram_data"],
222 "ram_zdata": staticSizes[
"ram_zdata"],
223 "ram_stack": s.usage()
if trace_available
else None,
224 "ram_heap": h.usage()
if trace_available
else None,
227 print(
"=== Results ===")
228 print(
"ROM usage: " +
printSz(results[
"rom"]))
229 print(
" read-only data: " +
printSz(results[
"rom_rodata"]))
230 print(
" code: " +
printSz(results[
"rom_code"]))
231 print(
" other required: " +
printSz(results[
"rom_misc"]))
235 + (
"" if trace_available
else " [stack and heap usage not included]")
237 print(
" data: " +
printSz(results[
"ram_data"]))
238 print(
" zero-init data: " +
printSz(results[
"ram_zdata"]))
239 print(
" stack: " +
printSz(results[
"ram_stack"], unknown_msg=
"missing trace file"))
240 print(
" heap: " +
printSz(results[
"ram_heap"], unknown_msg=
"missing trace file"))
244 with open(csvFile,
"w")
as f:
245 writer = csv.DictWriter(f, fieldnames=results.keys())
247 writer.writerow(results)
trace(self, adr, mode, pc, sz)
__init__(self, name, min, max)
printSz(sz, unknown_msg="")