Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 64 additions & 42 deletions scripts/footprint/size_report
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import json

from packaging import version

from colorama import init, Fore
from colorama import init, Fore, Style

from anytree import RenderTree, NodeMixin, findall_by_attr
from anytree.exporter import DictExporter
Expand Down Expand Up @@ -131,8 +131,19 @@ def get_symbols(elf, addr_ranges):
"""
rom_syms = dict()
ram_syms = dict()
all_syms = dict()
unassigned_syms = dict()

def entry(sym, loc, section):
"""
Factor function for a symbol entry.
"""
return {'name': sym.name,
'symbol': sym,
'section': section,
'loc': loc,
'mapped_files': set()}

rom_addr_ranges = addr_ranges['rom']
ram_addr_ranges = addr_ranges['ram']
unassigned_addr_ranges = addr_ranges['unassigned']
Expand All @@ -144,40 +155,42 @@ def get_symbols(elf, addr_ranges):
if get_symbol_size(sym) == 0:
continue

found_sec = False
entry = {'name': sym.name,
'symbol': sym,
'mapped_files': set(),
'section': None}
ram_sym = is_symbol_in_ranges(sym, ram_addr_ranges)
rom_sym = is_symbol_in_ranges(sym, rom_addr_ranges)

# Determine the location(s) for this symbol.
loc = []
if ram_sym:
loc.append('ram')
if rom_sym:
loc.append('rom')

# If symbol is in ROM area?
bound = is_symbol_in_ranges(sym, rom_addr_ranges)
if bound:
if rom_sym:
if sym.name not in rom_syms:
rom_syms[sym.name] = list()
entry['section'] = bound['name']
rom_syms[sym.name].append(entry)
found_sec = True
rom_syms[sym.name].append(entry(sym, loc, rom_sym['name']))

# If symbol is in RAM area?
bound = is_symbol_in_ranges(sym, ram_addr_ranges)
if bound:
if ram_sym:
if sym.name not in ram_syms:
ram_syms[sym.name] = list()
entry['section'] = bound['name']
ram_syms[sym.name].append(entry)
found_sec = True
ram_syms[sym.name].append(entry(sym, loc, ram_sym['name']))

if not found_sec:
# If symbol is in either area add to "all" list.
if ram_sym or rom_sym:
if sym.name not in all_syms:
all_syms[sym.name] = list()
all_syms[sym.name].append(entry(sym, loc, ram_sym['name'] if ram_sym else rom_sym['name']))
else:
bound = is_symbol_in_ranges(sym, unassigned_addr_ranges)
if bound:
entry['section'] = bound['name']
if sym.name not in unassigned_syms:
unassigned_syms[sym.name] = list()
unassigned_syms[sym.name].append(entry)
unassigned_syms[sym.name].append(entry(sym, ["unassigned"], bound['name'] if bound else None))

ret = {'rom': rom_syms,
'ram': ram_syms,
'all': all_syms,
'unassigned': unassigned_syms}
return ret

Expand Down Expand Up @@ -206,6 +219,7 @@ def get_section_ranges(elf):
rom_size = 0
ram_size = 0
unassigned_size = 0
total_size = 0

xip = any(section.get_symbol_by_name('CONFIG_XIP')
for section in elf.iter_sections('SHT_SYMTAB'))
Expand All @@ -223,6 +237,7 @@ def get_section_ranges(elf):
# BSS and noinit sections
ram_addr_ranges.append(bound)
ram_size += size
total_size += size
is_assigned = True
print_section_info(section, "RAM bss section")

Expand All @@ -233,6 +248,7 @@ def get_section_ranges(elf):
# Text section
rom_addr_ranges.append(bound)
rom_size += size
total_size += size
is_assigned = True
print_section_info(section, "ROM txt section")

Expand All @@ -245,13 +261,15 @@ def get_section_ranges(elf):
rom_size += size
ram_addr_ranges.append(bound)
ram_size += size
total_size += size
is_assigned = True
print_section_info(section, "DATA r/w section")

elif (flags & SHF_ALLOC) == SHF_ALLOC:
# Read only data
rom_addr_ranges.append(bound)
rom_size += size
total_size += size
is_assigned = True
print_section_info(section, "ROM r/o section")

Expand All @@ -265,7 +283,9 @@ def get_section_ranges(elf):
'ram': ram_addr_ranges,
'ram_total_size': ram_size,
'unassigned': unassigned_addr_ranges,
'unassigned_total_size': unassigned_size}
'unassigned_total_size': unassigned_size,
'all': ram_addr_ranges + rom_addr_ranges,
'all_total_size': total_size}
return ret


Expand Down Expand Up @@ -592,6 +612,7 @@ class TreeNode(NodeMixin):
self.section = section
if children:
self.children = children
self.loc = []

def __repr__(self):
return self._name
Expand Down Expand Up @@ -635,7 +656,7 @@ def generate_any_tree(symbol_dict, total_size, path_prefix):

# A set of helper function for building a simple tree with a path-like
# hierarchy.
def _insert_one_elem(root, path, size, addr, section):
def _insert_one_elem(root, path, size, addr, section, loc):
cur = None
node = None
parent = root
Expand Down Expand Up @@ -666,6 +687,7 @@ def generate_any_tree(symbol_dict, total_size, path_prefix):
# Don't do it on file- and directory- level parent nodes.
node.address = addr
node.section = section
node.loc = loc
else:
# normally this shouldn't happen; just to detect data or logic errors.
print(f"ERROR: no end node created for {root}, {path}, 0x{addr:08x}+{size}@{section}")
Expand Down Expand Up @@ -704,7 +726,7 @@ def generate_any_tree(symbol_dict, total_size, path_prefix):
else:
dest_node = node_no_paths

_insert_one_elem(dest_node, path, size, addr, section)
_insert_one_elem(dest_node, path, size, addr, section, symbol['loc'])


if node_zephyr_base is not root:
Expand Down Expand Up @@ -748,10 +770,14 @@ def node_sort(items):
return sorted(items, key=lambda item: item._name)


def print_any_tree(root, total_size, depth):
def print_any_tree(root, total_size, depth, header=None):
"""
Print the symbol tree.
"""
if header:
print(f"{Fore.WHITE}{Style.BRIGHT}{header}")
print('-' * 110 + f"{Fore.RESET}{Style.RESET_ALL}")

print('{:98s} {:>7s} {:>7s} {:11s} {:16s}'.format(
Fore.YELLOW + "Path", "Size", "%", " Address", "Section" + Fore.RESET))
print('=' * 138)
Expand Down Expand Up @@ -797,14 +823,16 @@ def parse_args():
help="Output path")
parser.add_argument("-w", "--workspace", default=None,
help="Workspace path (Usually the same as WEST_TOPDIR)")
parser.add_argument("target", choices=['rom', 'ram', 'all'])
parser.add_argument("target", choices=['rom', 'ram', 'all'], nargs="+")
parser.add_argument("-d", "--depth", dest="depth",
type=int, default=None,
help="How deep should we go into the tree",
metavar="DEPTH")
parser.add_argument("-v", "--verbose", action="store_true",
help="Print extra debugging information")
parser.add_argument("--json", help="store results in a JSON file.")
parser.add_argument("--json", help='Store results in the given JSON file ' + \
'(a "{target}" string in the filename will ' + \
'be replaced by "ram", "rom", or "all").')
args = parser.parse_args()


Expand All @@ -820,12 +848,7 @@ def main():
init()

assert os.path.exists(args.kernel), "{0} does not exist.".format(args.kernel)
if args.target == 'ram':
targets = ['ram']
elif args.target == 'rom':
targets = ['rom']
elif args.target == 'all':
targets = ['rom', 'ram']
targets = args.target

elf = ELFFile(open(args.kernel, "rb"))
assert elf.has_dwarf_info(), "ELF file has no DWARF information"
Expand All @@ -834,17 +857,15 @@ def main():
addr_ranges = get_section_ranges(elf)
dwarfinfo = elf.get_dwarf_info()

for t in targets:

symbols = get_symbols(elf, addr_ranges)

for sym in symbols['unassigned'].values():
for sym_entry in sym:
print(f"WARN: Symbol '{sym_entry['name']}' section '{sym_entry['section']}' "
"is not in RAM or ROM.")
symbols = get_symbols(elf, addr_ranges)
for sym in symbols['unassigned'].values():
for sym_entry in sym:
print(f"WARN: Symbol '{sym_entry['name']}' section '{sym_entry['section']}' "
"is not in RAM or ROM.")

for t in targets:
if args.json:
jsonout = args.json
jsonout = args.json.replace('{target}', t)
else:
jsonout = os.path.join(args.output, f'{t}.json')

Expand All @@ -870,7 +891,8 @@ def main():

root = generate_any_tree(symbol_dict, symsize, common_path_prefix)
if not args.quiet:
print_any_tree(root, symsize, args.depth)
header = f"{t.upper()} Report" if len(targets) > 1 else None
print_any_tree(root, symsize, args.depth, header)

exporter = DictExporter(attriter=lambda attrs: [(k.lstrip('_'), v) for k, v in attrs])
data = dict()
Expand Down
Loading