diff --git a/tools/build_api.py b/tools/build_api.py index b2247fe264..f7332fca28 100644 --- a/tools/build_api.py +++ b/tools/build_api.py @@ -613,6 +613,9 @@ def build_project(src_paths, build_path, target, toolchain_name, map_csv = join(build_path, name + "_map.csv") memap_instance.generate_output('csv-ci', stats_depth, map_csv) + map_html = join(build_path, name + "_map.html") + memap_instance.generate_output('html', stats_depth, map_html) + resources.detect_duplicates(toolchain) if report != None: diff --git a/tools/memap.py b/tools/memap.py index 83201745ba..7bc7fb190c 100644 --- a/tools/memap.py +++ b/tools/memap.py @@ -6,13 +6,16 @@ from __future__ import print_function, division, absolute_import from abc import abstractmethod, ABCMeta from sys import stdout, exit, argv from os import sep -from os.path import basename, dirname, join, relpath, commonprefix +from os.path import (basename, dirname, join, relpath, abspath, commonprefix, + splitext) import re import csv import json from argparse import ArgumentParser from copy import deepcopy from prettytable import PrettyTable +from jinja2 import FileSystemLoader, StrictUndefined +from jinja2.environment import Environment from .utils import (argparse_filestring_type, argparse_lowercase_hyphen_type, argparse_uppercase_type) @@ -477,6 +480,9 @@ class MemapParser(object): # Flash no associated with a module self.misc_flash_mem = 0 + # Name of the toolchain, for better headings + self.tc_name = None + def reduce_depth(self, depth): """ populates the short_modules attribute with a truncated module list @@ -505,7 +511,7 @@ class MemapParser(object): self.short_modules[new_name].setdefault(section_idx, 0) self.short_modules[new_name][section_idx] += self.modules[module_name][section_idx] - export_formats = ["json", "csv-ci", "table"] + export_formats = ["json", "csv-ci", "html", "table"] def generate_output(self, export_format, depth, file_output=None): """ Generates summary of memory map data @@ -531,6 +537,7 @@ class MemapParser(object): return False to_call = {'json': self.generate_json, + 'html': self.generate_html, 'csv-ci': self.generate_csv, 'table': self.generate_table}[export_format] output = to_call(file_desc) @@ -540,6 +547,80 @@ class MemapParser(object): return output + @staticmethod + def _move_up_tree(tree, next_module): + tree.setdefault("children", []) + for child in tree["children"]: + if child["name"] == next_module: + return child + else: + new_module = {"name": next_module, "value": 0} + tree["children"].append(new_module) + return new_module + + def generate_html(self, file_desc): + """Generate a json file from a memory map for D3 + + Positional arguments: + file_desc - the file to write out the final report to + """ + tree_text = {"name": ".text", "value": 0} + tree_bss = {"name": ".bss", "value": 0} + tree_data = {"name": ".data", "value": 0} + for name, dct in self.modules.items(): + cur_text = tree_text + cur_bss = tree_bss + cur_data = tree_data + modules = name.split(sep) + while True: + try: + cur_text["value"] += dct['.text'] + except KeyError: + pass + try: + cur_bss["value"] += dct['.bss'] + except KeyError: + pass + try: + cur_data["value"] += dct['.data'] + except KeyError: + pass + if not modules: + break + next_module = modules.pop(0) + cur_text = self._move_up_tree(cur_text, next_module) + cur_data = self._move_up_tree(cur_data, next_module) + cur_bss = self._move_up_tree(cur_bss, next_module) + + tree_rom = { + "name": "ROM", + "value": tree_text["value"] + tree_data["value"], + "children": [tree_text, tree_data] + } + tree_ram = { + "name": "RAM", + "value": tree_bss["value"] + tree_data["value"], + "children": [tree_bss, tree_data] + } + + jinja_loader = FileSystemLoader(dirname(abspath(__file__))) + jinja_environment = Environment(loader=jinja_loader, + undefined=StrictUndefined) + + template = jinja_environment.get_template("memap_flamegraph.html") + name, _ = splitext(basename(file_desc.name)) + if name.endswith("_map"): + name = name[:-4] + if self.tc_name: + name = "%s %s" % (name, self.tc_name) + data = { + "name": name, + "rom": json.dumps(tree_rom), + "ram": json.dumps(tree_ram), + } + file_desc.write(template.render(data)) + return None + def generate_json(self, file_desc): """Generate a json file from a memory map @@ -655,6 +736,7 @@ class MemapParser(object): mapfile - the file name of the memory map file toolchain - the toolchain used to create the file """ + self.tc_name = toolchain.title() if toolchain in ("ARM", "ARM_STD", "ARM_MICRO", "ARMC6"): parser = _ArmccParser() elif toolchain == "GCC_ARM" or toolchain == "GCC_CR": diff --git a/tools/memap_flamegraph.html b/tools/memap_flamegraph.html new file mode 100644 index 0000000000..20fc2e30d7 --- /dev/null +++ b/tools/memap_flamegraph.html @@ -0,0 +1,103 @@ + + +
+ + + + + + + + + +