size_report: Ported to native Windows

size_report was assuming Unix-style absolute paths and misbehaving
when paths had a colon ("C:\") in them. Also, refactored and improved
documentation.

Signed-off-by: Sebastian Bøe <sebastian.boe@nordicsemi.no>
This commit is contained in:
Sebastian Bøe 2018-01-22 15:19:15 +01:00 committed by Anas Nashif
commit 4bd904ccae

View file

@ -14,6 +14,7 @@ import subprocess
import json import json
import operator import operator
import platform import platform
from pathlib import Path
parser = OptionParser() parser = OptionParser()
parser.add_option("-d", "--depth", dest="depth", type="int", parser.add_option("-d", "--depth", dest="depth", type="int",
@ -35,34 +36,76 @@ parser.add_option("-n", "--nm", type="string", dest="bin_nm",
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
# Return a dict containing symbol_name: path/to/file/where/it/originates # Return a dict containing {
# for all symbols from the .elf file. Optionnaly strips the path according # symbol_name: {:,path/to/file}/symbol
# }
# for all symbols from the .elf file. Optionaly strips the path according
# to the passed sub-path # to the passed sub-path
def load_symbols_and_paths(bin_nm, elf_file, path_to_strip=""):
def load_symbols_and_paths(bin_nm, elf_file, path_to_strip=None):
symbols_paths = {}
nm_out = subprocess.check_output( nm_out = subprocess.check_output(
[bin_nm, elf_file, "-S", "-l", "--size-sort", "--radix=d"], [bin_nm, elf_file, "-S", "-l", "--size-sort", "--radix=d"],
universal_newlines=True universal_newlines=True
) )
for line in nm_out.splitlines(): for line in nm_out.splitlines():
fields = line.replace('\t', ' ').split(' ') if not line:
# Get rid of trailing empty field # Get rid of trailing empty field
if len(fields) == 1 and fields[0] == '':
continue continue
assert len(fields) >= 4
if len(fields) < 5: symbol, path = parse_symbol_path_pair(line)
path = ":/" + fields[3]
if path:
processed_path = Path(path).relative_to(Path(path_to_strip))
else: else:
path = fields[4].split(':')[0] processed_path = Path(":")
if path_to_strip is not None:
if path_to_strip in path: pathlike_string = processed_path / symbol
path = path.replace(path_to_strip, "") + '/' + fields[3]
else: yield symbol, pathlike_string
path = ":/" + fields[3]
symbols_paths[fields[3]] = path # Return a pair containing either
return symbols_paths #
# (symbol_name, "path/to/file")
# or
# (symbol_name, "")
#
# depending on if the file is found or not
# }
def parse_symbol_path_pair(line):
# Line's output from nm might look like this:
# '536871152 00000012 b gpio_e /absolute/path/gpio.c:247'
#
# We are only trying to extract the symbol and the filename.
#
# In general lines look something like this:
#
# 'number number string symbol[\t<absolute_path>:line]
#
# The file is optional, nm might not find out where a symbol came from.
#
# NB: <absolute_path> looks different on Windows and Linux
# Replace tabs with spaces to easily split up the fields (NB:
# Whitespace in paths is not supported)
line_without_tabs = line.replace('\t', ' ')
fields = line_without_tabs.split()
assert len(fields) >= 4
symbol = fields[3]
file_is_missing = len(fields) == 4
if file_is_missing:
path = ""
else:
path_with_line_number = fields[4]
# Remove the trailing line number, e.g. 'C:\file.c:237'
line_number_index = path_with_line_number.rfind(':')
path = path_with_line_number[:line_number_index]
return (symbol, path)
def get_section_size(f, section_name): def get_section_size(f, section_name):
@ -148,25 +191,24 @@ def generate_target_memory_section(
bin_size = os.stat(bin_file_abs).st_size bin_size = os.stat(bin_file_abs).st_size
# Get the path associated to each symbol # Get the path associated to each symbol
symbols_paths = load_symbols_and_paths(bin_nm, elf_file_abs, source_dir) symbols_paths = dict(load_symbols_and_paths(bin_nm, elf_file_abs, source_dir))
# A set of helper function for building a simple tree with a path-like # A set of helper function for building a simple tree with a path-like
# hierarchy. # hierarchy.
def _insert_one_elem(tree, path, size): def _insert_one_elem(tree, path, size):
splitted_path = path.split('/')
cur = None cur = None
for p in splitted_path: for p in path.parts:
if cur is None: if cur is None:
cur = p cur = p
else: else:
cur = cur + '/' + p cur = cur + os.path.sep + p
if cur in tree: if cur in tree:
tree[cur] += size tree[cur] += size
else: else:
tree[cur] = size tree[cur] = size
def _parent_for_node(e): def _parent_for_node(e):
parent = "root" if len(e.split('/')) == 1 else e.rsplit('/', 1)[0] parent = "root" if len(os.path.sep) == 1 else e.rsplit(os.path.sep, 1)[0]
if e == "root": if e == "root":
parent = None parent = None
return parent return parent
@ -340,7 +382,7 @@ def print_tree(data, total, depth):
bcolors["FAIL"] + "Path", "Size", "%" + bcolors["ENDC"])) bcolors["FAIL"] + "Path", "Size", "%" + bcolors["ENDC"]))
print("'='*110i") print("'='*110i")
for i in sorted(data): for i in sorted(data):
p = i.split("/") p = i.split(os.path.sep)
if depth and len(p) > depth: if depth and len(p) > depth:
continue continue