Commit a444ac46 authored by Andrew Grieve's avatar Andrew Grieve Committed by Commit Bot

SuperSize: Add --method-count to html_report

This generates an html report that shows java method counts rather than
binary size.

Change-Id: I6d6ba23969fa495e714ea3d1264cd1dbf61bd4bc
Reviewed-on: https://chromium-review.googlesource.com/1055855Reviewed-by: default avatarPeter Wen <wnwen@chromium.org>
Commit-Queue: agrieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558303}
parent 454c4015
...@@ -239,6 +239,9 @@ Example Usage: ...@@ -239,6 +239,9 @@ Example Usage:
``` bash ``` bash
tools/binary_size/supersize html_report chrome.size --report-dir size-report -v tools/binary_size/supersize html_report chrome.size --report-dir size-report -v
xdg-open size-report/index.html xdg-open size-report/index.html
# Report showing Dex method counts rather than binary size:
tools/binary_size/supersize html_report chrome.size --report-dir size-report -v --method-count
``` ```
### Usage: diff ### Usage: diff
......
...@@ -135,6 +135,7 @@ def ParseJava(name): ...@@ -135,6 +135,7 @@ def ParseJava(name):
if '(' in name: if '(' in name:
package, method_sig = name.split(' ', 1) package, method_sig = name.split(' ', 1)
class_name = package.split('.')[-1] class_name = package.split('.')[-1]
# E.g. <init>() <-- no return type
if method_sig.startswith('<'): if method_sig.startswith('<'):
method_name = method_sig method_name = method_sig
else: else:
......
...@@ -18,6 +18,7 @@ import path_util ...@@ -18,6 +18,7 @@ import path_util
# Node dictionary keys. These are output in json read by the webapp so # Node dictionary keys. These are output in json read by the webapp so
# keep them short to save file size. # keep them short to save file size.
# Note: If these change, the webapp must also change. # Note: If these change, the webapp must also change.
_METHOD_COUNT_MODE_KEY = 'methodCountMode'
_NODE_TYPE_KEY = 'k' _NODE_TYPE_KEY = 'k'
_NODE_TYPE_BUCKET = 'b' _NODE_TYPE_BUCKET = 'b'
_NODE_TYPE_PATH = 'p' _NODE_TYPE_PATH = 'p'
...@@ -91,6 +92,21 @@ def _MakeChildrenDictsIntoLists(node): ...@@ -91,6 +92,21 @@ def _MakeChildrenDictsIntoLists(node):
len(children)) len(children))
def _CombineSingleChildNodes(node):
"""Collapse "java"->"com"->"google" into ."java/com/google"."""
children = node.get(_NODE_CHILDREN_KEY)
if children:
child = children[0]
if len(children) == 1 and node[_NODE_TYPE_KEY] == child[_NODE_TYPE_KEY]:
node[_NODE_NAME_KEY] = '{}/{}'.format(
node[_NODE_NAME_KEY], child[_NODE_NAME_KEY])
node[_NODE_CHILDREN_KEY] = child[_NODE_CHILDREN_KEY]
_CombineSingleChildNodes(node)
else:
for child in children:
_CombineSingleChildNodes(child)
def _AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size, def _AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size,
min_symbol_size): min_symbol_size):
"""Puts symbol into the file path node |node|.""" """Puts symbol into the file path node |node|."""
...@@ -112,12 +128,16 @@ def _AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size, ...@@ -112,12 +128,16 @@ def _AddSymbolIntoFileNode(node, symbol_type, symbol_name, symbol_size,
node[_NODE_SYMBOL_TYPE_KEY] = symbol_type node[_NODE_SYMBOL_TYPE_KEY] = symbol_type
def _MakeCompactTree(symbols, min_symbol_size): def _MakeCompactTree(symbols, min_symbol_size, method_count_mode):
if method_count_mode:
# Include all symbols and avoid bucket nodes.
min_symbol_size = -1
result = { result = {
_NODE_NAME_KEY: '/', _NODE_NAME_KEY: '/',
_NODE_CHILDREN_KEY: {}, _NODE_CHILDREN_KEY: {},
_NODE_TYPE_KEY: 'p', _NODE_TYPE_KEY: 'p',
_NODE_MAX_DEPTH_KEY: 0, _NODE_MAX_DEPTH_KEY: 0,
_METHOD_COUNT_MODE_KEY: bool(method_count_mode),
} }
for symbol in symbols: for symbol in symbols:
file_path = symbol.source_path or symbol.object_path or _NAME_NO_PATH_BUCKET file_path = symbol.source_path or symbol.object_path or _NAME_NO_PATH_BUCKET
...@@ -134,7 +154,8 @@ def _MakeCompactTree(symbols, min_symbol_size): ...@@ -134,7 +154,8 @@ def _MakeCompactTree(symbols, min_symbol_size):
symbol_type = _NODE_SYMBOL_TYPE_VTABLE symbol_type = _NODE_SYMBOL_TYPE_VTABLE
elif symbol.name.endswith(']'): elif symbol.name.endswith(']'):
symbol_type = _NODE_SYMBOL_TYPE_GENERATED symbol_type = _NODE_SYMBOL_TYPE_GENERATED
_AddSymbolIntoFileNode(node, symbol_type, symbol.template_name, symbol.pss, symbol_size = 1 if method_count_mode else symbol.pss
_AddSymbolIntoFileNode(node, symbol_type, symbol.template_name, symbol_size,
min_symbol_size) min_symbol_size)
depth += 2 depth += 2
result[_NODE_MAX_DEPTH_KEY] = max(result[_NODE_MAX_DEPTH_KEY], depth) result[_NODE_MAX_DEPTH_KEY] = max(result[_NODE_MAX_DEPTH_KEY], depth)
...@@ -146,6 +167,7 @@ def _MakeCompactTree(symbols, min_symbol_size): ...@@ -146,6 +167,7 @@ def _MakeCompactTree(symbols, min_symbol_size):
_SplitLargeBucket(no_path_bucket) _SplitLargeBucket(no_path_bucket)
_MakeChildrenDictsIntoLists(result) _MakeChildrenDictsIntoLists(result)
_CombineSingleChildNodes(result)
return result return result
...@@ -174,6 +196,8 @@ def AddArguments(parser): ...@@ -174,6 +196,8 @@ def AddArguments(parser):
parser.add_argument('--min-symbol-size', type=float, default=1024, parser.add_argument('--min-symbol-size', type=float, default=1024,
help='Minimum size (PSS) for a symbol to be included as ' help='Minimum size (PSS) for a symbol to be included as '
'an independent node.') 'an independent node.')
parser.add_argument('--method-count', action='store_true',
help='Show dex method count rather than size')
def Run(args, parser): def Run(args, parser):
...@@ -183,9 +207,10 @@ def Run(args, parser): ...@@ -183,9 +207,10 @@ def Run(args, parser):
logging.info('Reading .size file') logging.info('Reading .size file')
size_info = archive.LoadAndPostProcessSizeInfo(args.input_file) size_info = archive.LoadAndPostProcessSizeInfo(args.input_file)
symbols = size_info.raw_symbols symbols = size_info.raw_symbols
if not args.include_bss: if args.method_count:
symbols = symbols.WhereInSection('m')
elif not args.include_bss:
symbols = symbols.WhereInSection('b').Inverted() symbols = symbols.WhereInSection('b').Inverted()
symbols = symbols.WherePssBiggerThan(0)
# Copy report boilerplate into output directory. This also proves that the # Copy report boilerplate into output directory. This also proves that the
# output directory is safe for writing, so there should be no problems writing # output directory is safe for writing, so there should be no problems writing
...@@ -193,7 +218,7 @@ def Run(args, parser): ...@@ -193,7 +218,7 @@ def Run(args, parser):
_CopyTemplateFiles(args.report_dir) _CopyTemplateFiles(args.report_dir)
logging.info('Creating JSON objects') logging.info('Creating JSON objects')
tree_root = _MakeCompactTree(symbols, args.min_symbol_size) tree_root = _MakeCompactTree(symbols, args.min_symbol_size, args.method_count)
logging.info('Serializing JSON') logging.info('Serializing JSON')
with open(os.path.join(args.report_dir, 'data.js'), 'w') as out_file: with open(os.path.join(args.report_dir, 'data.js'), 'w') as out_file:
......
...@@ -12,6 +12,7 @@ function D3SymbolTreeMap(mapWidth, mapHeight, levelsToShow) { ...@@ -12,6 +12,7 @@ function D3SymbolTreeMap(mapWidth, mapHeight, levelsToShow) {
this._mapHeight = mapHeight; this._mapHeight = mapHeight;
this.boxPadding = {'l': 5, 'r': 5, 't': 20, 'b': 5}; this.boxPadding = {'l': 5, 'r': 5, 't': 20, 'b': 5};
this.infobox = undefined; this.infobox = undefined;
this.methodCountMode = false;
this._maskContainer = undefined; this._maskContainer = undefined;
this._highlightContainer = undefined; this._highlightContainer = undefined;
// Transition in this order: // Transition in this order:
...@@ -53,7 +54,10 @@ D3SymbolTreeMap._pretty = function(num) { ...@@ -53,7 +54,10 @@ D3SymbolTreeMap._pretty = function(num) {
* Express a number in terms of KiB, MiB, GiB, etc. * Express a number in terms of KiB, MiB, GiB, etc.
* Note that these are powers of 2, not of 10. * Note that these are powers of 2, not of 10.
*/ */
D3SymbolTreeMap._byteify = function(num) { D3SymbolTreeMap.prototype._byteify = function(num) {
if (this.methodCountMode) {
return num + ' methods';
}
var suffix; var suffix;
if (num >= 1024) { if (num >= 1024) {
if (num >= 1024 * 1024 * 1024) { if (num >= 1024 * 1024 * 1024) {
...@@ -145,6 +149,7 @@ D3SymbolTreeMap.prototype.init = function() { ...@@ -145,6 +149,7 @@ D3SymbolTreeMap.prototype.init = function() {
.style('margin', 0) .style('margin', 0)
.style('box-shadow', '5px 5px 5px #888'); .style('box-shadow', '5px 5px 5px #888');
this._layout = this._createTreeMapLayout(); this._layout = this._createTreeMapLayout();
this.methodCountMode = tree_data['methodCountMode'];
this._setData(tree_data); // TODO: Don't use global 'tree_data' this._setData(tree_data); // TODO: Don't use global 'tree_data'
} }
...@@ -446,7 +451,7 @@ D3SymbolTreeMap.prototype._handleInodes = function() { ...@@ -446,7 +451,7 @@ D3SymbolTreeMap.prototype._handleInodes = function() {
return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible'; return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
}) })
.text(function(datum) { .text(function(datum) {
var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']' var sizeish = ' [' + thisTreeMap._byteify(datum.value) + ']'
var text; var text;
if (datum.k === 'b') { // bucket if (datum.k === 'b') { // bucket
if (datum === thisTreeMap._currentRoot) { if (datum === thisTreeMap._currentRoot) {
...@@ -515,7 +520,7 @@ D3SymbolTreeMap.prototype._handleInodes = function() { ...@@ -515,7 +520,7 @@ D3SymbolTreeMap.prototype._handleInodes = function() {
.style('width', function(datum) { return datum.dx; }) .style('width', function(datum) { return datum.dx; })
.style('height', function(datum) { return thisTreeMap.boxPadding.t; }) .style('height', function(datum) { return thisTreeMap.boxPadding.t; })
.text(function(datum) { .text(function(datum) {
var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']' var sizeish = ' [' + thisTreeMap._byteify(datum.value) + ']'
var text; var text;
if (datum.k === 'b') { if (datum.k === 'b') {
if (datum === thisTreeMap._currentRoot) { if (datum === thisTreeMap._currentRoot) {
...@@ -758,8 +763,10 @@ D3SymbolTreeMap.prototype._createInfoBox = function() { ...@@ -758,8 +763,10 @@ D3SymbolTreeMap.prototype._createInfoBox = function() {
D3SymbolTreeMap.prototype._showInfoBox = function(datum) { D3SymbolTreeMap.prototype._showInfoBox = function(datum) {
this.infobox.text(''); this.infobox.text('');
var numSymbols = 0; var numSymbols = 0;
var sizeish = D3SymbolTreeMap._pretty(datum.value) + ' bytes (' + var sizeish = this._byteify(datum.value);
D3SymbolTreeMap._byteify(datum.value) + ')'; if (!this.methodCountMode) {
sizeish = D3SymbolTreeMap._pretty(datum.value) + ' bytes (' + sizeish + ')'
}
if (datum.k === 'p' || datum.k === 'b') { // path or bucket if (datum.k === 'p' || datum.k === 'b') { // path or bucket
if (datum.symbol_stats) { // can be empty if filters are applied if (datum.symbol_stats) { // can be empty if filters are applied
for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) { for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
...@@ -793,10 +800,11 @@ D3SymbolTreeMap.prototype._showInfoBox = function(datum) { ...@@ -793,10 +800,11 @@ D3SymbolTreeMap.prototype._showInfoBox = function(datum) {
this.infobox.append('div').text('Location: ' + this.pathFor(datum)) this.infobox.append('div').text('Location: ' + this.pathFor(datum))
} }
} }
if (datum.k === 'p') { if (datum.k === 'p' && !this.methodCountMode) {
this.infobox.append('div') this.infobox.append('div')
.text('Number of symbols: ' + D3SymbolTreeMap._pretty(numSymbols)); .text('Number of symbols: ' + D3SymbolTreeMap._pretty(numSymbols));
if (datum.symbol_stats) { // can be empty if filters are applied // can be empty if filters are applied
if (datum.symbol_stats) {
var table = this.infobox.append('table') var table = this.infobox.append('table')
.attr('border', 1).append('tbody'); .attr('border', 1).append('tbody');
var header = table.append('tr'); var header = table.append('tr');
......
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