From da4a19fdacd7a859da395e014c9e0fded99aa985 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Thu, 12 May 2022 00:25:39 +0300 Subject: [PATCH] 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 --- tools/sizes.py | 200 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 141 insertions(+), 59 deletions(-) diff --git a/tools/sizes.py b/tools/sizes.py index 7ce4ff1d99..ee7219161a 100755 --- a/tools/sizes.py +++ b/tools/sizes.py @@ -17,74 +17,156 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from __future__ import print_function import argparse import os import subprocess -import sys - -def get_segment_hints(iram): - hints = {} - hints['ICACHE'] = ' - flash instruction cache' - hints['IROM'] = ' - code in flash (default or ICACHE_FLASH_ATTR)' - hints['IRAM'] = ' / ' + str(iram) + ' - code in IRAM (IRAM_ATTR, ISRs...)' - hints['DATA'] = ') - initialized variables (global, static) in RAM/HEAP' - hints['RODATA'] = ') / 81920 - constants (global, static) in RAM/HEAP' - hints['BSS'] = ') - zeroed variables (global, static) in RAM/HEAP' - return hints - -def get_segment_sizes(elf, path): - sizes = {} - sizes['ICACHE'] = 0 - sizes['IROM'] = 0 - sizes['IRAM'] = 0 - sizes['DATA'] = 0 - sizes['RODATA'] = 0 - sizes['BSS'] = 0 - p = subprocess.Popen([path + "/xtensa-lx106-elf-size", '-A', elf], stdout=subprocess.PIPE, universal_newlines=True ) - lines = p.stdout.readlines() - for line in lines: - words = line.split() - if line.startswith('.irom0.text'): - sizes['IROM'] = sizes['IROM'] + int(words[1]) - elif line.startswith('.text'): # Gets .text and .text1 - sizes['IRAM'] = sizes['IRAM'] + int(words[1]) - elif line.startswith('.data'): # Gets .text and .text1 - sizes['DATA'] = sizes['DATA'] + int(words[1]) - elif line.startswith('.rodata'): # Gets .text and .text1 - sizes['RODATA'] = sizes['RODATA'] + int(words[1]) - elif line.startswith('.bss'): # Gets .text and .text1 - sizes['BSS'] = sizes['BSS'] + int(words[1]) + + +def get_segment_sizes(elf, path, mmu): + iram_size = 0 + iheap_size = 0 + icache_size = 32168 + + for line in mmu.split(): + words = line.split("=") + if line.startswith("-DMMU_IRAM_SIZE"): + iram_size = int(words[1], 16) + elif line.startswith("-DMMU_ICACHE_SIZE"): + icache_size = int(words[1], 16) + elif line.startswith("-DMMU_SEC_HEAP_SIZE"): + iheap_size = int(words[1], 16) + + sizes = [ + [ + "Variables and constants in RAM (global, static)", + [ + { + "DATA": 0, + "RODATA": 0, + "BSS": 0, + }, + 80192, + ], + ], + [ + "Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR)", + [ + { + "ICACHE": icache_size, + "IHEAP": iheap_size, + "IRAM": 0, + }, + 65536, + ], + ], + ["Code in flash (default, ICACHE_FLASH_ATTR)", [{"IROM": 0}, 1048576]], + ] + + section_mapping = ( + (".irom0.text", "IROM"), + (".text", "IRAM"), + (".data", "DATA"), + (".rodata", "RODATA"), + (".bss", "BSS"), + ) + + cmd = [os.path.join(path, "xtensa-lx106-elf-size"), "-A", elf] + with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc: + lines = proc.stdout.readlines() + for line in lines: + words = line.split() + for section, target in section_mapping: + if not line.startswith(section): + continue + for group, (segments, total) in sizes: + if target in segments: + segments[target] += int(words[1]) + assert segments[target] <= total + return sizes -def get_mmu_sizes(mmu, sizes): - iram = 0x8000 - sizes['ICACHE'] = 0x8000 - lines = mmu.split(' ') - for line in lines: - words = line.split('=') - if line.startswith('-DMMU_IRAM_SIZE'): - iram = int(words[1], 16) - elif line.startswith('-DMMU_ICACHE_SIZE'): - sizes['ICACHE'] = int(words[1], 16) - return [iram, sizes] + +def percentage(lhs, rhs): + return "{}%".format(int(100.0 * float(lhs) / float(rhs))) + + +HINTS = { + "ICACHE": "reserved space for flash instruction cache", + "IRAM": "code in IRAM", + "IHEAP": "secondary heap space", + "IROM": "code in flash", + "DATA": "initialized variables", + "RODATA": "constants", + "BSS": "zeroed variables", +} + + +def safe_prefix(n, length): + if n == length: + return "`--" + + return "|--" + + +def prefix(n, length): + if n == length: + return "└──" + + return "├──" + + +def filter_segments(segments): + used = 0 + number = 0 + available = [] + + for (segment, size) in segments.items(): + if not size: + continue + used += size + number += 1 + available.append((number, segment, size)) + + return (number, used, available) + def main(): - parser = argparse.ArgumentParser(description='Report the different segment sizes of a compiled ELF file') - parser.add_argument('-e', '--elf', action='store', required=True, help='Path to the Arduino sketch ELF') - parser.add_argument('-p', '--path', action='store', required=True, help='Path to Xtensa toolchain binaries') - parser.add_argument('-i', '--mmu', action='store', required=False, help='MMU build options') + parser = argparse.ArgumentParser( + description="Report the different segment sizes of a compiled ELF file" + ) + parser.add_argument( + "-e", + "--elf", + action="store", + required=True, + help="Path to the Arduino sketch ELF", + ) + parser.add_argument( + "-p", + "--path", + action="store", + required=True, + help="Path to Xtensa toolchain binaries", + ) + parser.add_argument( + "-i", "--mmu", action="store", required=False, help="MMU build options" + ) args = parser.parse_args() - sizes = get_segment_sizes(args.elf, args.path) - [iram, sizes] = get_mmu_sizes(args.mmu, sizes) - hints = get_segment_hints(iram) + sizes = get_segment_sizes(args.elf, args.path, args.mmu) + + for group, (segments, total) in sizes: + number, used, segments = filter_segments(segments) - sys.stderr.write("Executable segment sizes:" + os.linesep) - for k in sizes.keys(): - sys.stderr.write("%-7s: %-5d %s %s" % (k, sizes[k], hints[k], os.linesep)) + print(f". {group:<8}, used {used} / {total} bytes ({percentage(used, total)})") + print("| SEGMENT BYTES DESCRIPTION") + for n, segment, size in segments: + try: + print(f"{prefix(n, number)} ", end="") + except UnicodeEncodeError: + print(f"{safe_prefix(n, number)} ", end="") + print(f"{segment:<8} {size:<8} {HINTS[segment]:<16}") - return 0 -if __name__ == '__main__': - sys.exit(main()) +if __name__ == "__main__": + main()