From 4bd904ccaec5ebcddfb8d9be6290facbd38beec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Mon, 22 Jan 2018 15:19:15 +0100 Subject: [PATCH] size_report: Ported to native Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- scripts/footprint/size_report | 94 +++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/scripts/footprint/size_report b/scripts/footprint/size_report index 8bdd32a44e5..c2f1db88bd6 100755 --- a/scripts/footprint/size_report +++ b/scripts/footprint/size_report @@ -14,6 +14,7 @@ import subprocess import json import operator import platform +from pathlib import Path parser = OptionParser() 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() -# Return a dict containing symbol_name: path/to/file/where/it/originates -# for all symbols from the .elf file. Optionnaly strips the path according +# Return a dict containing { +# symbol_name: {:,path/to/file}/symbol +# } +# for all symbols from the .elf file. Optionaly strips the path according # to the passed sub-path - - -def load_symbols_and_paths(bin_nm, elf_file, path_to_strip=None): - symbols_paths = {} +def load_symbols_and_paths(bin_nm, elf_file, path_to_strip=""): nm_out = subprocess.check_output( [bin_nm, elf_file, "-S", "-l", "--size-sort", "--radix=d"], universal_newlines=True ) for line in nm_out.splitlines(): - fields = line.replace('\t', ' ').split(' ') - # Get rid of trailing empty field - if len(fields) == 1 and fields[0] == '': + if not line: + # Get rid of trailing empty field continue - assert len(fields) >= 4 - if len(fields) < 5: - path = ":/" + fields[3] + + symbol, path = parse_symbol_path_pair(line) + + if path: + processed_path = Path(path).relative_to(Path(path_to_strip)) else: - path = fields[4].split(':')[0] - if path_to_strip is not None: - if path_to_strip in path: - path = path.replace(path_to_strip, "") + '/' + fields[3] - else: - path = ":/" + fields[3] - symbols_paths[fields[3]] = path - return symbols_paths + processed_path = Path(":") + + pathlike_string = processed_path / symbol + + yield symbol, pathlike_string + +# Return a pair containing either +# +# (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:line] + # + # The file is optional, nm might not find out where a symbol came from. + # + # NB: 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): @@ -148,25 +191,24 @@ def generate_target_memory_section( bin_size = os.stat(bin_file_abs).st_size # 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 # hierarchy. def _insert_one_elem(tree, path, size): - splitted_path = path.split('/') cur = None - for p in splitted_path: + for p in path.parts: if cur is None: cur = p else: - cur = cur + '/' + p + cur = cur + os.path.sep + p if cur in tree: tree[cur] += size else: tree[cur] = size 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": parent = None return parent @@ -340,7 +382,7 @@ def print_tree(data, total, depth): bcolors["FAIL"] + "Path", "Size", "%" + bcolors["ENDC"])) print("'='*110i") for i in sorted(data): - p = i.split("/") + p = i.split(os.path.sep) if depth and len(p) > depth: continue