Skip to content

Commit da4a19f

Browse files
authored
Table output for segment size script (#8551)
* Table output for segment size script Also include maximum aka total for every segment key Re-format the file and clean-up the resulting data dict * revert to line output * used, percentage * unicodes * shorter desc, headers
1 parent 8d5dda0 commit da4a19f

File tree

1 file changed

+141
-59
lines changed

1 file changed

+141
-59
lines changed

tools/sizes.py

+141-59
Original file line numberDiff line numberDiff line change
@@ -17,74 +17,156 @@
1717
# You should have received a copy of the GNU General Public License
1818
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1919

20-
from __future__ import print_function
2120
import argparse
2221
import os
2322
import subprocess
24-
import sys
25-
26-
def get_segment_hints(iram):
27-
hints = {}
28-
hints['ICACHE'] = ' - flash instruction cache'
29-
hints['IROM'] = ' - code in flash (default or ICACHE_FLASH_ATTR)'
30-
hints['IRAM'] = ' / ' + str(iram) + ' - code in IRAM (IRAM_ATTR, ISRs...)'
31-
hints['DATA'] = ') - initialized variables (global, static) in RAM/HEAP'
32-
hints['RODATA'] = ') / 81920 - constants (global, static) in RAM/HEAP'
33-
hints['BSS'] = ') - zeroed variables (global, static) in RAM/HEAP'
34-
return hints
35-
36-
def get_segment_sizes(elf, path):
37-
sizes = {}
38-
sizes['ICACHE'] = 0
39-
sizes['IROM'] = 0
40-
sizes['IRAM'] = 0
41-
sizes['DATA'] = 0
42-
sizes['RODATA'] = 0
43-
sizes['BSS'] = 0
44-
p = subprocess.Popen([path + "/xtensa-lx106-elf-size", '-A', elf], stdout=subprocess.PIPE, universal_newlines=True )
45-
lines = p.stdout.readlines()
46-
for line in lines:
47-
words = line.split()
48-
if line.startswith('.irom0.text'):
49-
sizes['IROM'] = sizes['IROM'] + int(words[1])
50-
elif line.startswith('.text'): # Gets .text and .text1
51-
sizes['IRAM'] = sizes['IRAM'] + int(words[1])
52-
elif line.startswith('.data'): # Gets .text and .text1
53-
sizes['DATA'] = sizes['DATA'] + int(words[1])
54-
elif line.startswith('.rodata'): # Gets .text and .text1
55-
sizes['RODATA'] = sizes['RODATA'] + int(words[1])
56-
elif line.startswith('.bss'): # Gets .text and .text1
57-
sizes['BSS'] = sizes['BSS'] + int(words[1])
23+
24+
25+
def get_segment_sizes(elf, path, mmu):
26+
iram_size = 0
27+
iheap_size = 0
28+
icache_size = 32168
29+
30+
for line in mmu.split():
31+
words = line.split("=")
32+
if line.startswith("-DMMU_IRAM_SIZE"):
33+
iram_size = int(words[1], 16)
34+
elif line.startswith("-DMMU_ICACHE_SIZE"):
35+
icache_size = int(words[1], 16)
36+
elif line.startswith("-DMMU_SEC_HEAP_SIZE"):
37+
iheap_size = int(words[1], 16)
38+
39+
sizes = [
40+
[
41+
"Variables and constants in RAM (global, static)",
42+
[
43+
{
44+
"DATA": 0,
45+
"RODATA": 0,
46+
"BSS": 0,
47+
},
48+
80192,
49+
],
50+
],
51+
[
52+
"Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR)",
53+
[
54+
{
55+
"ICACHE": icache_size,
56+
"IHEAP": iheap_size,
57+
"IRAM": 0,
58+
},
59+
65536,
60+
],
61+
],
62+
["Code in flash (default, ICACHE_FLASH_ATTR)", [{"IROM": 0}, 1048576]],
63+
]
64+
65+
section_mapping = (
66+
(".irom0.text", "IROM"),
67+
(".text", "IRAM"),
68+
(".data", "DATA"),
69+
(".rodata", "RODATA"),
70+
(".bss", "BSS"),
71+
)
72+
73+
cmd = [os.path.join(path, "xtensa-lx106-elf-size"), "-A", elf]
74+
with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc:
75+
lines = proc.stdout.readlines()
76+
for line in lines:
77+
words = line.split()
78+
for section, target in section_mapping:
79+
if not line.startswith(section):
80+
continue
81+
for group, (segments, total) in sizes:
82+
if target in segments:
83+
segments[target] += int(words[1])
84+
assert segments[target] <= total
85+
5886
return sizes
5987

60-
def get_mmu_sizes(mmu, sizes):
61-
iram = 0x8000
62-
sizes['ICACHE'] = 0x8000
63-
lines = mmu.split(' ')
64-
for line in lines:
65-
words = line.split('=')
66-
if line.startswith('-DMMU_IRAM_SIZE'):
67-
iram = int(words[1], 16)
68-
elif line.startswith('-DMMU_ICACHE_SIZE'):
69-
sizes['ICACHE'] = int(words[1], 16)
70-
return [iram, sizes]
88+
89+
def percentage(lhs, rhs):
90+
return "{}%".format(int(100.0 * float(lhs) / float(rhs)))
91+
92+
93+
HINTS = {
94+
"ICACHE": "reserved space for flash instruction cache",
95+
"IRAM": "code in IRAM",
96+
"IHEAP": "secondary heap space",
97+
"IROM": "code in flash",
98+
"DATA": "initialized variables",
99+
"RODATA": "constants",
100+
"BSS": "zeroed variables",
101+
}
102+
103+
104+
def safe_prefix(n, length):
105+
if n == length:
106+
return "`--"
107+
108+
return "|--"
109+
110+
111+
def prefix(n, length):
112+
if n == length:
113+
return "└──"
114+
115+
return "├──"
116+
117+
118+
def filter_segments(segments):
119+
used = 0
120+
number = 0
121+
available = []
122+
123+
for (segment, size) in segments.items():
124+
if not size:
125+
continue
126+
used += size
127+
number += 1
128+
available.append((number, segment, size))
129+
130+
return (number, used, available)
131+
71132

72133
def main():
73-
parser = argparse.ArgumentParser(description='Report the different segment sizes of a compiled ELF file')
74-
parser.add_argument('-e', '--elf', action='store', required=True, help='Path to the Arduino sketch ELF')
75-
parser.add_argument('-p', '--path', action='store', required=True, help='Path to Xtensa toolchain binaries')
76-
parser.add_argument('-i', '--mmu', action='store', required=False, help='MMU build options')
134+
parser = argparse.ArgumentParser(
135+
description="Report the different segment sizes of a compiled ELF file"
136+
)
137+
parser.add_argument(
138+
"-e",
139+
"--elf",
140+
action="store",
141+
required=True,
142+
help="Path to the Arduino sketch ELF",
143+
)
144+
parser.add_argument(
145+
"-p",
146+
"--path",
147+
action="store",
148+
required=True,
149+
help="Path to Xtensa toolchain binaries",
150+
)
151+
parser.add_argument(
152+
"-i", "--mmu", action="store", required=False, help="MMU build options"
153+
)
77154

78155
args = parser.parse_args()
79-
sizes = get_segment_sizes(args.elf, args.path)
80-
[iram, sizes] = get_mmu_sizes(args.mmu, sizes)
81-
hints = get_segment_hints(iram)
156+
sizes = get_segment_sizes(args.elf, args.path, args.mmu)
157+
158+
for group, (segments, total) in sizes:
159+
number, used, segments = filter_segments(segments)
82160

83-
sys.stderr.write("Executable segment sizes:" + os.linesep)
84-
for k in sizes.keys():
85-
sys.stderr.write("%-7s: %-5d %s %s" % (k, sizes[k], hints[k], os.linesep))
161+
print(f". {group:<8}, used {used} / {total} bytes ({percentage(used, total)})")
162+
print("| SEGMENT BYTES DESCRIPTION")
163+
for n, segment, size in segments:
164+
try:
165+
print(f"{prefix(n, number)} ", end="")
166+
except UnicodeEncodeError:
167+
print(f"{safe_prefix(n, number)} ", end="")
168+
print(f"{segment:<8} {size:<8} {HINTS[segment]:<16}")
86169

87-
return 0
88170

89-
if __name__ == '__main__':
90-
sys.exit(main())
171+
if __name__ == "__main__":
172+
main()

0 commit comments

Comments
 (0)