mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #6582 from theotherjimmy/memap-flamegraph
Implement zoomable html-flamegraph memap outputpull/6969/merge
commit
78d9c4f330
|
@ -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:
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||
integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.css"
|
||||
integrity="sha256-w762vSe6WGrkVZ7gEOpnn2Y+FSmAGlX77jYj7nhuCyY="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<style>
|
||||
/* Space out content a bit */
|
||||
body {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
/* Custom page header */
|
||||
.header {
|
||||
padding-bottom: 20px;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
/* Make the masthead heading the same height as the navigation */
|
||||
.header h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
line-height: 40px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<title>{{name}} Memory Details</title>
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js" integrity="sha256-4OrICDjBYfKefEbVT7wETRLNFkuq4TJV5WLGvjqpGAk=" crossorigin="anonymous"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js" integrity="sha256-g6iAfvZp+nDQ2TdTR/VVKJf3bGro4ub5fvWSWVRi2NE=" crossorigin="anonymous"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header clearfix">
|
||||
<h3 class="text-muted">{{name}} Memory Details</h3>
|
||||
</div>
|
||||
<div id="chart-rom">
|
||||
</div>
|
||||
<hr/>
|
||||
<div id="chart-ram">
|
||||
</div>
|
||||
<hr/>
|
||||
<div id="details"></div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript"
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"
|
||||
integrity="sha256-r7j1FXNTvPzHR41+V71Jvej6fIq4v4Kzu5ee7J/RitM="
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
<script type="text/javascript"
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"
|
||||
integrity="sha256-z0A2CQF8xxCKuOJsn4sJ5HBjxiHHRAfTX8hDF4RSN5s="
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
<script type="text/javascript"
|
||||
src="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.js"
|
||||
integrity="sha256-I1CkrWbmjv+GWjgbulJ4i0vbzdrDGfxqdye2qNlhG3Q="
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var tip = d3.tip()
|
||||
.direction("s")
|
||||
.offset([8, 0])
|
||||
.attr('class', 'd3-flame-graph-tip')
|
||||
.html(function(d) { return "module: " + d.data.name + ", bytes: " + d.data.value; });
|
||||
var flameGraph_rom = d3.flameGraph()
|
||||
.transitionDuration(250)
|
||||
.transitionEase(d3.easeCubic)
|
||||
.sort(true)
|
||||
.tooltip(tip);
|
||||
var flameGraph_ram = d3.flameGraph()
|
||||
.transitionDuration(250)
|
||||
.transitionEase(d3.easeCubic)
|
||||
.sort(true)
|
||||
.tooltip(tip);
|
||||
var rom_elem = d3.select("#chart-rom");
|
||||
flameGraph_rom.width(rom_elem.node().getBoundingClientRect().width);
|
||||
rom_elem.datum({{rom}}).call(flameGraph_rom);
|
||||
var ram_elem = d3.select("#chart-ram");
|
||||
flameGraph_ram.width(ram_elem.node().getBoundingClientRect().width);
|
||||
ram_elem.datum({{ram}}).call(flameGraph_ram);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue