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:
27 simple_mem_system.print_dbus_access=true
28 simple_mem_system.print_to_file=true
30 - Via run_helper.sh script: `run_helper.sh ... trace`
31 - Via command line: `bare_etiss_processor ... --simple_mem_system.print_dbus_access=true --simple_mem_system.print_to_file=true`
32- A file "dBusAccess.csv" should have been created
35> ./get_metrics.py ../bin/TARGET_ELF_FILE [-i memsegs.ini]
39DEFAULT_RAM_START = 0x80000
40DEFAULT_RAM_SIZE = 0x80000
41DEFAULT_STACK_SIZE = 0x4000
49 assert self.
min <= self.
max,
"Invalid MemRange"
58 return adr >= self.
min and adr < self.
max
60 def trace(self, adr, mode, pc, sz):
70 raise ValueError(f
"Invalid mode: {mode}")
83 return self.
name +
"\t[not accessed]"
84 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>)"
96 ignoreSections = [
"",
".stack",
".comment",
".riscv.attributes",
".strtab",
".shstrtab"]
98 with open(inFile,
"rb")
as f:
99 e = elffile.ELFFile(f)
101 for s
in e.iter_sections():
102 if s.name.startswith(
".text"):
103 m[
"rom_code"] += s.data_size
104 elif s.name.startswith(
".srodata"):
105 m[
"rom_rodata"] += s.data_size
106 elif s.name.startswith(
".sdata"):
107 m[
"ram_data"] += s.data_size
108 elif s.name ==
".rodata":
109 m[
"rom_rodata"] += s.data_size
110 elif s.name ==
".vectors" or s.name ==
".init_array":
111 m[
"rom_misc"] += s.data_size
112 elif s.name ==
".data":
113 m[
"ram_data"] += s.data_size
114 elif s.name ==
".bss" or s.name ==
".sbss" or s.name ==
".shbss":
115 m[
"ram_zdata"] += s.data_size
116 elif s.name.startswith(
".gcc_except"):
118 elif s.name.startswith(
".sdata2"):
120 elif s.name.startswith(
".debug_"):
122 elif s.name
in ignoreSections:
124 elif isinstance(s, SymbolTableSection):
125 for sym
in s.iter_symbols():
126 if sym.name ==
"_heap_start":
127 heapStart = sym[
"st_value"]
129 print(
"warning: ignored: " + s.name +
" / size: " +
str(s.data_size))
136 return f
"unknown [{unknown_msg}]" if unknown_msg
else "unknown"
137 return humanize.naturalsize(sz) +
" (" + hex(sz) +
")"
140if __name__ ==
"__main__":
141 parser = argparse.ArgumentParser()
142 parser.add_argument(
"elf", metavar=
"ELF", type=str, nargs=1, help=
"The target ELF file")
146 default=
"dBusAccess.csv",
148 help=
"Path to CSV trace file of memory accesses (default: %(default)s)",
151 "--ini",
"-i", default=
"", type=str, help=
"Path to INI file containing simple_mem_system layout (optional)"
153 parser.add_argument(
"--out",
"-o", metavar=
"FILE", type=str, default=
"", help=
"""Output CSV file (default: -)""")
154 args = parser.parse_args()
156 elfFile = args.elf[0]
157 traceFile = args.trace
161 ramStart = DEFAULT_RAM_START
162 ramSize = DEFAULT_RAM_SIZE
163 stackSize = DEFAULT_STACK_SIZE
176 config = configparser.ConfigParser()
179 if "IntConfigurations" not in config:
180 raise RuntimeError(
"Section [IntConfigurations] does not exist in config file " + memCfg)
182 cfg = config[
"IntConfigurations"]
187 ramStart =
int(cfg[
"simple_mem_system.memseg_origin_01"], 0)
188 ramSize =
int(cfg[
"simple_mem_system.memseg_length_01"], 0)
192 raise RuntimeError(
"did not find heap start")
194 print(
"heap starts at: " + hex(heapStart))
197 h =
MemRange(
"Heap", heapStart, ramStart + ramSize - stackSize)
198 s =
MemRange(
"Stack", ramStart + ramSize - stackSize, ramStart + ramSize)
201 trace_available =
False
202 if os.path.exists(traceFile):
203 trace_available =
True
204 with open(traceFile)
as f:
205 reader = csv.reader(f, skipinitialspace=
True, delimiter=
";")
213 if mem.contains(adr):
214 mem.trace(adr, mode, pc, sz)
219 romSize = sum([staticSizes[k]
for k
in staticSizes
if k.startswith(
"rom_")])
220 ramSize = sum([staticSizes[k]
for k
in staticSizes
if k.startswith(
"ram_")])
224 "rom_rodata": staticSizes[
"rom_rodata"],
225 "rom_code": staticSizes[
"rom_code"],
226 "rom_misc": staticSizes[
"rom_misc"],
227 "ram": (ramSize + s.usage() + h.usage())
if trace_available
else ramSize,
228 "ram_data": staticSizes[
"ram_data"],
229 "ram_zdata": staticSizes[
"ram_zdata"],
230 "ram_stack": s.usage()
if trace_available
else None,
231 "ram_heap": h.usage()
if trace_available
else None,
234 print(
"=== Results ===")
235 print(
"ROM usage: " +
printSz(results[
"rom"]))
236 print(
" read-only data: " +
printSz(results[
"rom_rodata"]))
237 print(
" code: " +
printSz(results[
"rom_code"]))
238 print(
" other required: " +
printSz(results[
"rom_misc"]))
242 + (
"" if trace_available
else " [stack and heap usage not included]")
244 print(
" data: " +
printSz(results[
"ram_data"]))
245 print(
" zero-init data: " +
printSz(results[
"ram_zdata"]))
246 print(
" stack: " +
printSz(results[
"ram_stack"], unknown_msg=
"missing trace file"))
247 print(
" heap: " +
printSz(results[
"ram_heap"], unknown_msg=
"missing trace file"))
251 with open(csvFile,
"w")
as f:
252 writer = csv.DictWriter(f, fieldnames=results.keys())
254 writer.writerow(results)
trace(self, adr, mode, pc, sz)
__init__(self, name, min, max)
printSz(sz, unknown_msg="")