blob: 2d9b70529063f002b6d715a6b9c82fc198c88d4b [file] [log] [blame]
xiaohu.huangffae1712022-11-01 18:04:01 +08001#!/usr/bin/python3
2#coding:utf-8
3#
4# Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved.
5#
6# SPDX-License-Identifier: MIT
7#
8
9
10from __future__ import print_function
11
12import sys
13import os
14import argparse
15
16parser = argparse.ArgumentParser(description='total size of each object file in an ld linker map.')
17parser.add_argument('map_file', help="A map file generated by passing -M/--print-map to ld during linking.")
18parser.add_argument('--combine', action='store_true',
19 help="All object files in an .a archive or in a directory are combined")
20args = parser.parse_args()
21
22class SectionSize():
23 text = 0
24 rela_text = 0
25 data = 0 # Including metadata like import tables
26 bss = 0
27 customize = 0
28 heap = 0
29 stack = 0
30 def rom(self):
31 return self.text + self.data
32 def ram(self):
33 return self.data + self.bss
34 def total(self):
35 return self.text + self.data + self.bss
36 def add_section(self, section, size):
37 if section.startswith('.comment'):
38 return
39 if section.startswith('.debug'):
40 return
41 if section.startswith('.ARM.attributes'):
42 return
43 if section.startswith('.text'):
44 self.text += size
45 elif section.startswith('.rela.dyn'):
46 self.rela_text += size
47 elif section.startswith('.bss') or section.startswith('.common') or section.startswith('.sbss'):
48 self.bss += size
49 elif section.startswith('.data') or section.startswith('.rodata'):
50 self.data += size
51 elif section.startswith('.heap'):
52 self.heap += size
53 self.bss += size
54 elif section.startswith('.stack'):
55 self.stack += size
56 self.bss += size
57 else:
58 if (size > 0):
59 print("customer section:%s, size:%d" % (section, size))
60 self.customize += size
61
62size_by_source = {}
63base = 0x2067a
64with open(args.map_file) as f:
65 lines = iter(f)
66 for line in lines:
67 if line.strip() == "Linker script and memory map":
68 break
69
70 current_section = None
71 split_line = None
72 last_addr = 0
73 last_size = 0
74 for line in lines:
75 line = line.strip('\n')
76 if split_line:
77 # Glue a line that was split in two back together
78 if line.startswith(' ' * 16):
79 line = split_line + line
80 else: # Shouldn't happen
81 print("Warning: discarding line ", split_line)
82 split_line = None
83
84 if ('size before relaxing' in line):
85 continue
86 if line.startswith((".", " .", " *fill*")):
87 pieces = line.split(None, 3) # Don't split paths containing spaces
88
89 if line.startswith("."):
90 # Note: this line might be wrapped, with the size of the section
91 # on the next line, but we ignore the size anyway and will ignore that line
92 current_section = pieces[0]
93 elif len(pieces) == 1 and len(line) > 14:
94 # ld splits the rest of this line onto the next if the section name is too long
95 split_line = line
96 elif len(pieces) >= 3 and "=" not in pieces and "before" not in pieces:
97 if pieces[0] == "*fill*":
98 # fill use the last source to store the fill align data
99 #source = pieces[0]
100 size = int(pieces[-1], 16)
101 if (pieces[-2] == last_addr):
102 # sub the merged size from the same address for fill data
103 size = size - last_size
104 else:
105 last_size = size
106 last_addr = pieces[-2]
107 else:
108 source = pieces[-1]
109 size = int(pieces[-2], 16)
110 if (pieces[-3] == last_addr):
111 # sub the merged size from the same address for text data
112 size = size - last_size
113 else:
114 last_size = size
115 last_addr = pieces[-3]
116
117 if args.combine:
118 if '.a(' in source:
119 # path/to/archive.a(object.o)
120 source = source[:source.index('.a(') + 2]
121 elif '.dir' in source:
122 source = source[:source.find(".dir")]
123 elif source.endswith('.o'):
124 if 'toolchains' in source:
125 source = 'toolchain_obj'
126 else:
127 source = os.path.basename(source)
128
129 if source not in size_by_source:
130 size_by_source[source] = SectionSize()
131 size_by_source[source].add_section(current_section, size)
132
133# Print out summary
134sources = list(size_by_source.keys())
135sources.sort(key = lambda x: size_by_source[x].total())
136sumrom = sumram = sumcode = sumdata = sumbss = sumstack = sumheap = sumcustomize = 0
137
138print('---------------------------------------------------------------------------------------------------')
139col_format = "%-20s\t%-12s\t%-12s\t%-7s\t%-12s\t%-12s\t%-7s"
140print(col_format % ("module file", "ROM(text+data)", "RAM(data+bss)", ".text", ".data", ".bss", "customize"))
141for source in sources:
142 size = size_by_source[source]
143 sumcode += size.text
144 sumdata += size.data
145 sumbss += size.bss
146 sumrom += size.rom()
147 sumram += size.ram()
148 sumstack += size.stack
149 sumheap += size.heap
150 sumcustomize += size.customize
151 print(col_format % (os.path.basename(source), size.rom(), size.ram(), size.text, size.data, size.bss, size.customize))
152
153print('---------------------------------------------------------------------------------------------------')
154col_format = "%-5s\t%-12s\t%-12s\t%-7s\t%-12s\t%-12s\t%-7s\t%-7s\t%-7s"
155print(col_format % (" ", "ROM(text+data)", "RAM(data+bss)", ".text", ".data", ".bss", "cust", "stack", "heap" ))
156print(col_format % ("total", sumrom, sumram, sumcode, sumdata, sumbss, sumcustomize, sumstack, sumheap))
157print('---------------------------------------------------------------------------------------------------')