| #!/usr/bin/python3 |
| #coding:utf-8 |
| # |
| # Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved. |
| # |
| # SPDX-License-Identifier: MIT |
| # |
| |
| |
| from __future__ import print_function |
| |
| import sys |
| import os |
| import argparse |
| |
| parser = argparse.ArgumentParser(description='total size of each object file in an ld linker map.') |
| parser.add_argument('map_file', help="A map file generated by passing -M/--print-map to ld during linking.") |
| parser.add_argument('--combine', action='store_true', |
| help="All object files in an .a archive or in a directory are combined") |
| args = parser.parse_args() |
| |
| class SectionSize(): |
| text = 0 |
| rela_text = 0 |
| data = 0 # Including metadata like import tables |
| bss = 0 |
| customize = 0 |
| heap = 0 |
| stack = 0 |
| def rom(self): |
| return self.text + self.data |
| def ram(self): |
| return self.data + self.bss |
| def total(self): |
| return self.text + self.data + self.bss |
| def add_gcc_section(self, section, size): |
| if section.startswith('.comment'): |
| return |
| if section.startswith('.debug'): |
| return |
| if section.startswith('.ARM.attributes'): |
| return |
| if section.startswith('.text'): |
| self.text += size |
| elif section.startswith('.rela.dyn'): |
| self.rela_text += size |
| elif section.startswith('.bss') or section.startswith('.common') or section.startswith('.sbss'): |
| self.bss += size |
| elif section.startswith('.data') or section.startswith('.rodata'): |
| self.data += size |
| elif section.startswith('.heap'): |
| self.heap += size |
| self.bss += size |
| elif section.startswith('.stack'): |
| self.stack += size |
| self.bss += size |
| else: |
| if (size > 0): |
| print("customer section:%s, size:%d" % (section, size)) |
| self.customize += size |
| def add_xcc_section(self, section, size): |
| if section.startswith('.comment'): |
| return |
| if section.startswith('.debug'): |
| return |
| if section.startswith('.xt.prop')or section.startswith('.xt.lit'): |
| return |
| if section.startswith('.text') or section.endswith('.text') \ |
| or section.startswith('.literal') or section.endswith('.literal') \ |
| or section.startswith('.rodata') or section.endswith('.rodata'): |
| self.text += size |
| elif section.startswith('.rela.dyn'): |
| self.rela_text += size |
| elif section.startswith('.bss') or section.startswith('.common') or section.startswith('.sbss'): |
| self.bss += size |
| elif section.startswith('.data') or section.endswith('.data'): |
| self.data += size |
| elif section.startswith('.heap'): |
| self.heap += size |
| self.bss += size |
| elif section.startswith('.stack'): |
| self.stack += size |
| self.bss += size |
| else: |
| if (size > 0): |
| print("customer section:%s, size:%d" % (section, size)) |
| self.customize += size |
| |
| size_by_source = {} |
| with open(args.map_file) as f: |
| if os.getenv('COMPILER') == "xcc": |
| arch_toolchain = "XCC" |
| toolchain_keyword = "xtensa-elf" |
| is_xtensa = 1 |
| else: |
| arch_toolchain = "GCC" |
| toolchain_keyword = "toolchains" |
| is_xtensa = 0 |
| print("%s toolchain map analyzer" % arch_toolchain) |
| |
| lines = iter(f) |
| for line in lines: |
| if line.strip() == "Linker script and memory map": |
| break |
| |
| current_section = None |
| split_line = None |
| last_addr = 0 |
| last_size = 0 |
| for line in lines: |
| line = line.strip('\n') |
| if split_line: |
| # Glue a line that was split in two back together |
| if line.startswith(' ' * 16): |
| line = split_line + line |
| else: # Shouldn't happen |
| print("Warning: discarding line ", split_line) |
| split_line = None |
| |
| if ('size before relaxing' in line): |
| continue |
| if line.startswith((".", " .", " *fill*")): |
| pieces = line.split(None, 3) # Don't split paths containing spaces |
| |
| if line.startswith("."): |
| # Note: this line might be wrapped, with the size of the section |
| # on the next line, but we ignore the size anyway and will ignore that line |
| current_section = pieces[0] |
| # XCC text section format |
| if (pieces == 4): |
| source = pieces[-1] |
| elif len(pieces) == 1 and len(line) > 14: |
| # ld splits the rest of this line onto the next if the section name is too long |
| split_line = line |
| elif len(pieces) >= 3 and "=" not in pieces and "before" not in pieces: |
| if pieces[0] == "*fill*": |
| # fill use the last source to store the fill align data |
| #source = pieces[0] |
| size = int(pieces[-1], 16) |
| if (pieces[-2] == last_addr): |
| # sub the merged size from the same address for fill data |
| size = size - last_size |
| else: |
| last_size = size |
| last_addr = pieces[-2] |
| else: |
| source = pieces[-1] |
| size = int(pieces[-2], 16) |
| if (pieces[-3] == last_addr): |
| # sub the merged size from the same address for text data |
| size = size - last_size |
| else: |
| last_size = size |
| last_addr = pieces[-3] |
| |
| if args.combine: |
| if '.a(' in source: |
| # path/to/archive.a(object.o) |
| source = source[:source.index('.a(') + 2] |
| elif '.dir' in source: |
| source = source[:source.find(".dir")] |
| elif source.endswith('.o'): |
| if toolchain_keyword in source: |
| source = 'toolchain_obj' |
| else: |
| source = os.path.basename(source) |
| |
| if source not in size_by_source: |
| size_by_source[source] = SectionSize() |
| if is_xtensa == 1: |
| size_by_source[source].add_xcc_section(current_section, size) |
| else: |
| size_by_source[source].add_gcc_section(current_section, size) |
| |
| # Print out summary |
| sources = list(size_by_source.keys()) |
| sources.sort(key = lambda x: size_by_source[x].total()) |
| sumrom = sumram = sumcode = sumdata = sumbss = sumstack = sumheap = sumcustomize = 0 |
| |
| print('---------------------------------------------------------------------------------------------------') |
| col_format = "%-20s\t%-12s\t%-12s\t%-7s\t%-12s\t%-12s\t%-7s" |
| print(col_format % ("module file", "ROM(text+data)", "RAM(data+bss)", ".text", ".data", ".bss", "customize")) |
| for source in sources: |
| size = size_by_source[source] |
| sumcode += size.text |
| sumdata += size.data |
| sumbss += size.bss |
| sumrom += size.rom() |
| sumram += size.ram() |
| sumstack += size.stack |
| sumheap += size.heap |
| sumcustomize += size.customize |
| print(col_format % (os.path.basename(source), size.rom(), size.ram(), size.text, size.data, size.bss, size.customize)) |
| |
| print('---------------------------------------------------------------------------------------------------') |
| col_format = "%-5s\t%-12s\t%-12s\t%-7s\t%-12s\t%-12s\t%-7s\t%-7s\t%-7s" |
| print(col_format % (" ", "ROM(text+data)", "RAM(data+bss)", ".text", ".data", ".bss", "cust", "stack", "heap" )) |
| print(col_format % ("total", sumrom, sumram, sumcode, sumdata, sumbss, sumcustomize, sumstack, sumheap)) |
| print('---------------------------------------------------------------------------------------------------') |