Commit 307e326f by Jim Stichnoth

Subzero: Generate a web page showing llvm2ice size breakdown.

"make -f Makefile.standalone bloat" generates a json file containing ./llvm2ice size data, and launches google-chrome on the output. bloat.py is taken verbatim from https://github.com/martine/bloat/blob/master/bloat.py . webtreemap.{js,css} are taken verbatim from https://github.com/martine/webtreemap . llvm2ice.bloat.html is adapted from https://github.com/martine/webtreemap/blob/gh-pages/demo/demo.html . BUG= none R=jfb@chromium.org Review URL: https://codereview.chromium.org/917203002
parent d442e7ee
...@@ -152,7 +152,7 @@ make_symlink: $(OBJDIR)/llvm2ice ...@@ -152,7 +152,7 @@ make_symlink: $(OBJDIR)/llvm2ice
@echo "Build Attributes:" @echo "Build Attributes:"
@$(OBJDIR)/llvm2ice --build-atts @$(OBJDIR)/llvm2ice --build-atts
.PHONY: all make_symlink .PHONY: all make_symlink bloat
# TODO(kschimpf): Fix python scripts to directly get build attributes # TODO(kschimpf): Fix python scripts to directly get build attributes
# rather than generating $(OBJDIR)/llvm2ice.build_atts. # rather than generating $(OBJDIR)/llvm2ice.build_atts.
...@@ -214,8 +214,13 @@ format-diff: ...@@ -214,8 +214,13 @@ format-diff:
$(LLVM_SRC_PATH)/../clang/tools/clang-format/clang-format-diff.py \ $(LLVM_SRC_PATH)/../clang/tools/clang-format/clang-format-diff.py \
-p1 -style=LLVM -i -p1 -style=LLVM -i
bloat: make_symlink
nm -C -S -l llvm2ice | \
bloat/bloat.py --nm-output=/dev/stdin syms > build/llvm2ice.bloat.json
@echo See Subzero size breakdown in bloat/llvm2ice.bloat.html
clean: clean:
rm -rf llvm2ice *.o $(OBJDIR) rm -rf llvm2ice *.o $(OBJDIR) build/llvm2ice.bloat.json
clean-all: clean clean-all: clean
rm -rf build/ rm -rf build/
Name: Bloat - Generate webtreemap-compatible JSON summaries of binary size
Short Name: bloat
URL: https://github.com/martine/bloat
Version: 0
Date: Apr 23, 2013
Revision: 2c22b831f081402a9160adb957b64b83316202e1
License: Apache 2.0
License File: NOT_SHIPPED
Security Critical: no
Description:
Generate webtreemap-compatible JSON summaries of binary size.
Local Modifications:
Only bloat.py is used (with no modifications).
========================================================================
Name: Webtreemap - Web-based treemap
Short Name: webtreemap
URL: https://github.com/martine/webtreemap
Version: 0
Date: Apr 23, 2013
Revision: a2fa353833eca1a4821ba4c93fb6a7d546aed02a
License: Apache 2.0
License File: NOT_SHIPPED
Security Critical: no
Description:
A simple treemap implementation using web technologies (DOM nodes, CSS
styling and transitions) rather than a big canvas/svg/plugin.
Local Modifications:
webtreemap.{js,css} are used with no modifications. demo/demo.html
was used as the basis for llvm2ice.bloat.html.
This diff is collapsed. Click to expand it.
<!DOCTYPE html>
<head>
<title>Subzero size breakdown</title>
<script src='../build/llvm2ice.bloat.json'></script>
<link rel='stylesheet' href='webtreemap.css'>
<style>
body {
font-family: sans-serif;
font-size: 0.8em;
margin: 2ex 4ex;
}
h1 {
font-weight: normal;
}
#map {
height: 70vh;
position: relative;
cursor: pointer;
-webkit-user-select: none;
}
#legend {
width: 200px;
}
</style>
</head>
<body>
<h1>Subzero size breakdown</h1>
<p>Click on a box to zoom in. Click on the outermost box to zoom out.</p>
<div id='map'></div>
<script src='webtreemap.js'></script>
<script>
var map = document.getElementById('map');
appendTreemap(map, kTree);
</script>
<div id='legend'>
<div class='webtreemap-symbol-bss'>bss</div>
<div class='webtreemap-symbol-bss webtreemap-aggregate'>bss aggregate</div>
<div class='webtreemap-symbol-data'>data</div>
<div class='webtreemap-symbol-data webtreemap-aggregate'>data aggregate</div>
<div class='webtreemap-symbol-read-only_data'>read-only data</div>
<div class='webtreemap-symbol-read-only_data webtreemap-aggregate'>read-only
data aggregate</div>
<div class='webtreemap-symbol-code'>code</div>
<div class='webtreemap-symbol-code webtreemap-aggregate'>code aggregate</div>
<div class='webtreemap-symbol-weak_symbol'>weak symbol</div>
<div class='webtreemap-symbol-weak_symbol webtreemap-aggregate'>weak symbol
aggregate</div>
</div>
</body>
.webtreemap-node {
/* Required attributes. */
position: absolute;
overflow: hidden; /* To hide overlong captions. */
background: white; /* Nodes must be opaque for zIndex layering. */
border: solid 1px black; /* Calculations assume 1px border. */
/* Optional: CSS animation. */
-webkit-transition: top 0.3s,
left 0.3s,
width 0.3s,
height 0.3s;
}
/* Optional: highlight nodes on mouseover. */
.webtreemap-node:hover {
background: #eee;
}
/* Optional: Different background colors depending on symbol. */
.webtreemap-symbol-bss {
background: #66C2A5;
}
.webtreemap-symbol-data {
background: #FC8D62;
}
.webtreemap-symbol-read-only_data {
background: #8DA0CB;
}
.webtreemap-symbol-code {
background: #E78AC3;
}
.webtreemap-symbol-weak_symbol {
background: #A6D854;
}
.webtreemap-symbol-bss.webtreemap-aggregate {
background: #B3E2CD;
}
.webtreemap-symbol-data.webtreemap-aggregate {
background: #FDCDAC;
}
.webtreemap-symbol-read-only_data.webtreemap-aggregate {
background: #CBD5E8;
}
.webtreemap-symbol-code.webtreemap-aggregate {
background: #F4CAE4;
}
.webtreemap-symbol-weak_symbol.webtreemap-aggregate {
background: #E6F5C9;
}
#legend > * {
border: solid 1px #444;
}
/* Optional: Different borders depending on level. */
.webtreemap-level0 {
border: solid 1px #444;
}
.webtreemap-level1 {
border: solid 1px #666;
}
.webtreemap-level2 {
border: solid 1px #888;
}
.webtreemap-level3 {
border: solid 1px #aaa;
}
.webtreemap-level4 {
border: solid 1px #ccc;
}
/* Optional: styling on node captions. */
.webtreemap-caption {
font-family: sans-serif;
font-size: 11px;
padding: 2px;
text-align: center;
}
/* Optional: styling on captions on mouse hover. */
/*.webtreemap-node:hover > .webtreemap-caption {
text-decoration: underline;
}*/
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Size of border around nodes.
// We could support arbitrary borders using getComputedStyle(), but I am
// skeptical the extra complexity (and performance hit) is worth it.
var kBorderWidth = 1;
// Padding around contents.
// TODO: do this with a nested div to allow it to be CSS-styleable.
var kPadding = 4;
var focused = null;
function focus(tree) {
focused = tree;
// Hide all visible siblings of all our ancestors by lowering them.
var level = 0;
var root = tree;
while (root.parent) {
root = root.parent;
level += 1;
for (var i = 0, sibling; sibling = root.children[i]; ++i) {
if (sibling.dom)
sibling.dom.style.zIndex = 0;
}
}
var width = root.dom.offsetWidth;
var height = root.dom.offsetHeight;
// Unhide (raise) and maximize us and our ancestors.
for (var t = tree; t.parent; t = t.parent) {
// Shift off by border so we don't get nested borders.
// TODO: actually make nested borders work (need to adjust width/height).
position(t.dom, -kBorderWidth, -kBorderWidth, width, height);
t.dom.style.zIndex = 1;
}
// And layout into the topmost box.
layout(tree, level, width, height);
}
function makeDom(tree, level) {
var dom = document.createElement('div');
dom.style.zIndex = 1;
dom.className = 'webtreemap-node webtreemap-level' + Math.min(level, 4);
if (tree.data['$symbol']) {
dom.className += (' webtreemap-symbol-' +
tree.data['$symbol'].replace(' ', '_'));
}
if (tree.data['$dominant_symbol']) {
dom.className += (' webtreemap-symbol-' +
tree.data['$dominant_symbol'].replace(' ', '_'));
dom.className += (' webtreemap-aggregate');
}
dom.onmousedown = function(e) {
if (e.button == 0) {
if (focused && tree == focused && focused.parent) {
focus(focused.parent);
} else {
focus(tree);
}
}
e.stopPropagation();
return true;
};
var caption = document.createElement('div');
caption.className = 'webtreemap-caption';
caption.innerHTML = tree.name;
dom.appendChild(caption);
tree.dom = dom;
return dom;
}
function position(dom, x, y, width, height) {
// CSS width/height does not include border.
width -= kBorderWidth*2;
height -= kBorderWidth*2;
dom.style.left = x + 'px';
dom.style.top = y + 'px';
dom.style.width = Math.max(width, 0) + 'px';
dom.style.height = Math.max(height, 0) + 'px';
}
// Given a list of rectangles |nodes|, the 1-d space available
// |space|, and a starting rectangle index |start|, compute an span of
// rectangles that optimizes a pleasant aspect ratio.
//
// Returns [end, sum], where end is one past the last rectangle and sum is the
// 2-d sum of the rectangles' areas.
function selectSpan(nodes, space, start) {
// Add rectangle one by one, stopping when aspect ratios begin to go
// bad. Result is [start,end) covering the best run for this span.
// http://scholar.google.com/scholar?cluster=5972512107845615474
var node = nodes[start];
var rmin = node.data['$area']; // Smallest seen child so far.
var rmax = rmin; // Largest child.
var rsum = 0; // Sum of children in this span.
var last_score = 0; // Best score yet found.
for (var end = start; node = nodes[end]; ++end) {
var size = node.data['$area'];
if (size < rmin)
rmin = size;
if (size > rmax)
rmax = size;
rsum += size;
// This formula is from the paper, but you can easily prove to
// yourself it's taking the larger of the x/y aspect ratio or the
// y/x aspect ratio. The additional magic fudge constant of 5
// makes us prefer wider rectangles to taller ones.
var score = Math.max(5*space*space*rmax / (rsum*rsum),
1*rsum*rsum / (space*space*rmin));
if (last_score && score > last_score) {
rsum -= size; // Undo size addition from just above.
break;
}
last_score = score;
}
return [end, rsum];
}
function layout(tree, level, width, height) {
if (!('children' in tree))
return;
var total = tree.data['$area'];
// XXX why do I need an extra -1/-2 here for width/height to look right?
var x1 = 0, y1 = 0, x2 = width - 1, y2 = height - 2;
x1 += kPadding; y1 += kPadding;
x2 -= kPadding; y2 -= kPadding;
y1 += 14; // XXX get first child height for caption spacing
var pixels_to_units = Math.sqrt(total / ((x2 - x1) * (y2 - y1)));
for (var start = 0, child; child = tree.children[start]; ++start) {
if (x2 - x1 < 60 || y2 - y1 < 40) {
if (child.dom) {
child.dom.style.zIndex = 0;
position(child.dom, -2, -2, 0, 0);
}
continue;
}
// In theory we can dynamically decide whether to split in x or y based
// on aspect ratio. In practice, changing split direction with this
// layout doesn't look very good.
// var ysplit = (y2 - y1) > (x2 - x1);
var ysplit = true;
var space; // Space available along layout axis.
if (ysplit)
space = (y2 - y1) * pixels_to_units;
else
space = (x2 - x1) * pixels_to_units;
var span = selectSpan(tree.children, space, start);
var end = span[0], rsum = span[1];
// Now that we've selected a span, lay out rectangles [start,end) in our
// available space.
var x = x1, y = y1;
for (var i = start; i < end; ++i) {
child = tree.children[i];
if (!child.dom) {
child.parent = tree;
child.dom = makeDom(child, level + 1);
tree.dom.appendChild(child.dom);
} else {
child.dom.style.zIndex = 1;
}
var size = child.data['$area'];
var frac = size / rsum;
if (ysplit) {
width = rsum / space;
height = size / width;
} else {
height = rsum / space;
width = size / height;
}
width /= pixels_to_units;
height /= pixels_to_units;
width = Math.round(width);
height = Math.round(height);
position(child.dom, x, y, width, height);
if ('children' in child) {
layout(child, level + 1, width, height);
}
if (ysplit)
y += height;
else
x += width;
}
// Shrink our available space based on the amount we used.
if (ysplit)
x1 += Math.round((rsum / space) / pixels_to_units);
else
y1 += Math.round((rsum / space) / pixels_to_units);
// end points one past where we ended, which is where we want to
// begin the next iteration, but subtract one to balance the ++ in
// the loop.
start = end - 1;
}
}
function appendTreemap(dom, data) {
var style = getComputedStyle(dom, null);
var width = parseInt(style.width);
var height = parseInt(style.height);
if (!data.dom)
makeDom(data, 0);
dom.appendChild(data.dom);
position(data.dom, 0, 0, width, height);
layout(data, 0, width, height);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment