From 471d99c68fab68ebbcbc285b6db138735323be63 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 23 Jan 2018 17:26:48 -0600 Subject: [PATCH] Added pretty bar printing for compile output Looks like this: Building project mbed-os-prettyoutput (ARCH_PRO, GCC_ARM) Scan: . Scan: env Scan: mbed Scan: FEATURE_LWIP Text 70.5KB Data 2.72KB BSS 7.43KB ROM 73.2KB RAM 10.1KB ROM [||||||| ] 73.2KB/512KB RAM [|||||||||||||||| ] 10.1KB/32KB Image: BUILD/ARCH_PRO/GCC_ARM/mbed-os-prettyoutput.bin If you build a target without a cmsis-pack it looks like this: Building project mbed-os-prettyoutput (ARM_BEETLE_SOC, GCC_ARM) Scan: . Scan: env Scan: mbed Scan: FEATURE_BLE Text 99KB Data 2.84KB BSS 13KB ROM 102KB RAM 15.8KB Image: BUILD/ARM_BEETLE_SOC/GCC_ARM/mbed-os-prettyoutput.bin And the old behaviour of displaying the memap table can be brought back by passing the --stats-depth parameter --- tools/build_api.py | 18 +++++++---- tools/make.py | 2 +- tools/memap.py | 74 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 10 deletions(-) diff --git a/tools/build_api.py b/tools/build_api.py index 1ed6653081..23a5259bd4 100644 --- a/tools/build_api.py +++ b/tools/build_api.py @@ -27,6 +27,7 @@ from os import linesep, remove, makedirs from time import time from intelhex import IntelHex from json import load, dump +from tools.arm_pack_manager import Cache from tools.utils import mkdir, run_cmd, run_cmd_ext, NotSupportedException,\ ToolException, InvalidReleaseTargetException, intelhex_offset @@ -547,19 +548,24 @@ def build_project(src_paths, build_path, target, toolchain_name, memap_instance = getattr(toolchain, 'memap_instance', None) memap_table = '' if memap_instance: - # Write output to stdout in text (pretty table) format - memap_table = memap_instance.generate_output('table', stats_depth) - + real_stats_depth = stats_depth if stats_depth is None else 2 + memap_table = memap_instance.generate_output('table', real_stats_depth) if not silent: - print memap_table + if not stats_depth: + memap_bars = memap_instance.generate_output('bars', + real_stats_depth, None, + getattr(toolchain.target, 'device_name', None)) + print memap_bars + else: + print memap_table # Write output to file in JSON format map_out = join(build_path, name + "_map.json") - memap_instance.generate_output('json', stats_depth, map_out) + memap_instance.generate_output('json', real_stats_depth, map_out) # Write output to file in CSV format for the CI map_csv = join(build_path, name + "_map.csv") - memap_instance.generate_output('csv-ci', stats_depth, map_csv) + memap_instance.generate_output('csv-ci', real_stats_depth, map_csv) resources.detect_duplicates(toolchain) diff --git a/tools/make.py b/tools/make.py index 060a6bc990..9822244a17 100644 --- a/tools/make.py +++ b/tools/make.py @@ -116,7 +116,7 @@ if __name__ == '__main__': "--stats-depth", type=int, dest="stats_depth", - default=2, + default=None, help="Depth level for static memory report") # Local run diff --git a/tools/memap.py b/tools/memap.py index b5c1b68814..e8b3141250 100644 --- a/tools/memap.py +++ b/tools/memap.py @@ -9,9 +9,11 @@ from os.path import basename, dirname, join, relpath, commonprefix import re import csv import json +import math from argparse import ArgumentParser from copy import deepcopy from prettytable import PrettyTable +from tools.arm_pack_manager import Cache from utils import argparse_filestring_type, \ argparse_lowercase_hyphen_type, argparse_uppercase_type @@ -506,7 +508,7 @@ class MemapParser(object): export_formats = ["json", "csv-ci", "table"] - def generate_output(self, export_format, depth, file_output=None): + def generate_output(self, export_format, depth, file_output=None, *args): """ Generates summary of memory map data Positional arguments: @@ -531,8 +533,9 @@ class MemapParser(object): to_call = {'json': self.generate_json, 'csv-ci': self.generate_csv, - 'table': self.generate_table}[export_format] - output = to_call(file_desc) + 'table': self.generate_table, + 'bars': self.generate_bars}[export_format] + output = to_call(file_desc, *args) if file_desc is not stdout: file_desc.close() @@ -616,6 +619,71 @@ class MemapParser(object): return output + def generate_bars(self, file_desc, device_name=None): + """ Generates nice looking bars that represent the memory consumption + + Returns: string containing nice looking bars + """ + + # TODO add tty detection, and width detection probably + WIDTH = 72 + try: + # NOTE this only works on linux + import sys, fcntl, termios, struct + height, width, _, _ = struct.unpack('HHHH', + fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, + struct.pack('HHHH', 0, 0, 0, 0))) + WIDTH = min(width, WIDTH) + except Exception: + pass + + text = self.subtotal['.text'] + data = self.subtotal['.data'] + bss = self.subtotal['.bss'] + rom_used = self.mem_summary['total_flash'] + ram_used = self.mem_summary['static_ram'] + + # No device_name = no cmsis-pack = we don't know the memory layout + if device_name is not None: + try: + cache = Cache(False, False) + cmsis_part = cache.index[device_name] + rom_avail = int(cmsis_part['memory']['IROM1']['size'], 0) + ram_avail = int(cmsis_part['memory']['IRAM1']['size'], 0) + except KeyError: + # If we don't have the expected regions, fall back to no device_name + device_name = None + + PREFIXES = ['', 'K', 'M', 'G', 'T', 'P', 'E'] + def unit(n, u='B', p=3): + if n == 0: + return '0' + u + + scale = math.floor(math.log(n, 1024)) + return '{1:.{0}g}{2}{3}'.format(p, n/(1024**scale), PREFIXES[int(scale)], u) + + usage = "Text {} Data {} BSS {}".format(unit(text), unit(data), unit(bss)) + avail = "ROM {} RAM {}".format(unit(rom_used), unit(ram_used)) + output = ["{0} {1:>{2}}".format(usage, avail, + abs(WIDTH-len(usage)-1) if device_name is not None else 0)] + + if device_name is not None: + for region, avail, uses in [ + ('ROM', rom_avail, [('|', text), ('|', data)]), + ('RAM', ram_avail, [('|', bss), ('|', data)])]: + barwidth = WIDTH-17 - len(region) + + used = sum(use for c, use in uses) + bars = [(c, (barwidth*use) // avail) for c, use in uses] + bars.append((' ', barwidth - sum(width for c, width in bars))) + bars = ''.join(c*width for c, width in bars) + + output.append("{0} [{2:<{1}}] {3:>13}".format( + region, barwidth, bars, + "{}/{}".format(unit(used), unit(avail)))) + + return '\n'.join(output) + toolchains = ["ARM", "ARM_STD", "ARM_MICRO", "GCC_ARM", "GCC_CR", "IAR"] def compute_report(self):