Chromium Code Reviews| Index: tools/binary_size/run_binary_size_analysis.py |
| diff --git a/tools/binary_size/run_binary_size_analysis.py b/tools/binary_size/run_binary_size_analysis.py |
| index 37c8254fb4d916d4ab661758f69703d355987772..ab371cc1919b6cf7075fb0c2174162b2ee00f303 100755 |
| --- a/tools/binary_size/run_binary_size_analysis.py |
| +++ b/tools/binary_size/run_binary_size_analysis.py |
| @@ -23,6 +23,7 @@ import sys |
| import tempfile |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def FormatBytes(bytes): |
| """Pretty-print a number of bytes.""" |
| if bytes > 1e6: |
| @@ -34,6 +35,7 @@ def FormatBytes(bytes): |
| return str(bytes) |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def SymbolTypeToHuman(type): |
| """Convert a symbol type as printed by nm into a human-readable name.""" |
| return {'b': 'bss', |
| @@ -70,10 +72,7 @@ def ParseNm(input): |
| if match: |
| size, type, sym = match.groups()[0:3] |
| size = int(size, 16) |
| - type = type.lower() |
| - if type == 'v': |
| - type = 'w' # just call them all weak |
| - if type == 'b': |
| + if type.lower() == 'b': |
| continue # skip all BSS for now |
| path = match.group(4) |
| yield sym, type, size, path |
| @@ -92,7 +91,65 @@ def ParseNm(input): |
| print >>sys.stderr, 'unparsed:', repr(line) |
|
bulach
2014/04/16 16:32:16
nit: another \n
also, _MkChuld
Andrew Hayden (chromium.org)
2014/04/16 17:53:21
Done.
|
| - |
| +def _mk_child(node, name): |
| + child = None |
| + for test in node['children']: |
| + if test['n'] == name: |
| + child = test |
| + break |
| + if child == None: |
|
bulach
2014/04/16 16:32:16
nit:
if not child:
Andrew Hayden (chromium.org)
2014/04/16 17:53:21
Done.
|
| + child = {'n': name, 'children': []} |
| + node['children'].append(child) |
| + return child |
| + |
|
bulach
2014/04/16 16:32:16
nit: another \n
Andrew Hayden (chromium.org)
2014/04/16 17:53:21
Done throughout
|
| +def MakeCompactTree(symbols): |
| + result = {'n': '/', 'children': [], 'k': 'p', 'maxDepth': 0} |
| + for symbol_name, symbol_type, symbol_size, file_path in symbols: |
| + |
| + if 'vtable for ' in symbol_name: |
| + symbol_type = '@' # hack to categorize these separately |
| + # Take path like '/foo/bar/baz', convert to ['foo', 'bar', 'baz'] |
| + if file_path: |
| + file_path = os.path.normpath(file_path) |
| + else: |
| + file_path = '(No Path)' |
| + |
| + if file_path.startswith('/'): |
| + file_path = file_path[1:] |
| + path_parts = file_path.split('/') |
| + |
| + # Find pre-existing node in tree, or update if it already exists |
| + node = result |
| + depth = 0 |
| + while len(path_parts) > 0: |
| + path_part = path_parts.pop(0) |
| + if len(path_part) == 0: |
| + continue |
| + depth += 1 |
| + node = _mk_child(node, path_part); |
| + node['k'] = 'p' # p for path |
| + |
| + # 'node' is now the file node. Find the symbol-type bucket. |
| + node['lastPathElement'] = True |
| + node = _mk_child(node, symbol_type) |
| + node['t'] = symbol_type |
| + node['k'] = 'b' # b for bucket |
| + depth += 1 |
| + |
| + # 'node' is now the symbol-type bucket. Make the child entry. |
| + node = _mk_child(node, symbol_name) |
| + if 'children' in node: # Only possible if we're adding duplicate entries!!! |
| + del node['children'] |
| + node['value'] = symbol_size |
| + node['t'] = symbol_type |
| + node['k'] = 's' # s for symbol |
| + depth += 1 |
| + result['maxDepth'] = max(result['maxDepth'], depth); |
| + |
| + return result |
| + |
| + |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def TreeifySymbols(symbols): |
| """Convert symbols into a path-based tree, calculating size information |
| along the way. |
| @@ -188,6 +245,7 @@ def TreeifySymbols(symbols): |
| return dirs |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def JsonifyTree(tree, name): |
| """Convert TreeifySymbols output to a JSON treemap. |
| @@ -224,7 +282,15 @@ def JsonifyTree(tree, name): |
| 'data': { '$area': tree['size'] }, |
| 'children': children } |
| +def DumpCompactTree(symbols, outfile): |
| + out = open(outfile, 'w') |
| + try: |
| + out.write('var tree_data = ' + json.dumps(MakeCompactTree(symbols))) |
| + finally: |
| + out.flush() |
| + out.close() |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def DumpTreemap(symbols, outfile): |
| dirs = TreeifySymbols(symbols) |
| out = open(outfile, 'w') |
| @@ -235,6 +301,7 @@ def DumpTreemap(symbols, outfile): |
| out.close() |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def DumpLargestSymbols(symbols, outfile, n): |
| # a list of (sym, type, size, path); sort by size. |
| symbols = sorted(symbols, key=lambda x: -x[2]) |
| @@ -278,6 +345,7 @@ def MakeSourceMap(symbols): |
| return sources |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def DumpLargestSources(symbols, outfile, n): |
| map = MakeSourceMap(symbols) |
| sources = sorted(map.values(), key=lambda x: -x['size']) |
| @@ -300,6 +368,7 @@ def DumpLargestSources(symbols, outfile, n): |
| out.close() |
| +# TODO(andrewhayden): Only used for legacy reports. Delete. |
| def DumpLargestVTables(symbols, outfile, n): |
| vtables = [] |
| for symbol, type, size, path in symbols: |
| @@ -324,7 +393,7 @@ def DumpLargestVTables(symbols, outfile, n): |
| out.flush() |
| out.close() |
| - |
| +# TODO(andrewhayden): Switch to Primiano's python-based version. |
| def RunParallelAddress2Line(outfile, library, arch, jobs, verbose): |
| """Run a parallel addr2line processing engine to dump and resolve symbols.""" |
| out_dir = os.getenv('CHROMIUM_OUT_DIR', 'out') |
| @@ -342,15 +411,15 @@ def RunParallelAddress2Line(outfile, library, arch, jobs, verbose): |
| cmd.append('--verbose') |
| prefix = os.path.join('third_party', 'android_tools', 'ndk', 'toolchains') |
| if arch == 'android-arm': |
| - prefix = os.path.join(prefix, 'arm-linux-androideabi-4.7', 'prebuilt', |
| + prefix = os.path.join(prefix, 'arm-linux-androideabi-4.8', 'prebuilt', |
| 'linux-x86_64', 'bin', 'arm-linux-androideabi-') |
| cmd.extend(['--nm', prefix + 'nm', '--addr2line', prefix + 'addr2line']) |
| elif arch == 'android-mips': |
| - prefix = os.path.join(prefix, 'mipsel-linux-android-4.7', 'prebuilt', |
| + prefix = os.path.join(prefix, 'mipsel-linux-android-4.8', 'prebuilt', |
| 'linux-x86_64', 'bin', 'mipsel-linux-android-') |
| cmd.extend(['--nm', prefix + 'nm', '--addr2line', prefix + 'addr2line']) |
| elif arch == 'android-x86': |
| - prefix = os.path.join(prefix, 'x86-4.7', 'prebuilt', |
| + prefix = os.path.join(prefix, 'x86-4.8', 'prebuilt', |
| 'linux-x86_64', 'bin', 'i686-linux-android-') |
| cmd.extend(['--nm', prefix + 'nm', '--addr2line', prefix + 'addr2line']) |
| # else, use whatever is in PATH (don't pass --nm or --addr2line) |
| @@ -441,6 +510,8 @@ def main(): |
| 'mapped to source locations. By default, a tempfile is ' |
| 'used and is deleted when the program terminates.' |
| 'This argument is only valid when using --library.') |
| + parser.add_option('--legacy', action='store_true', |
| + help='emit legacy binary size report instead of modern') |
| opts, args = parser.parse_args() |
| if ((not opts.library) and (not opts.nm_in)) or (opts.library and opts.nm_in): |
| @@ -464,27 +535,40 @@ def main(): |
| if not os.path.exists(opts.destdir): |
| os.makedirs(opts.destdir, 0755) |
| - DumpTreemap(symbols, os.path.join(opts.destdir, 'treemap-dump.js')) |
| - DumpLargestSymbols(symbols, |
| - os.path.join(opts.destdir, 'largest-symbols.js'), 100) |
| - DumpLargestSources(symbols, |
| - os.path.join(opts.destdir, 'largest-sources.js'), 100) |
| - DumpLargestVTables(symbols, |
| - os.path.join(opts.destdir, 'largest-vtables.js'), 100) |
| - |
| - # TODO(andrewhayden): Switch to D3 for greater flexibility |
| - treemap_out = os.path.join(opts.destdir, 'webtreemap') |
| - if not os.path.exists(treemap_out): |
| - os.makedirs(treemap_out, 0755) |
| - treemap_src = os.path.join('third_party', 'webtreemap', 'src') |
| - shutil.copy(os.path.join(treemap_src, 'COPYING'), treemap_out) |
| - shutil.copy(os.path.join(treemap_src, 'webtreemap.js'), treemap_out) |
| - shutil.copy(os.path.join(treemap_src, 'webtreemap.css'), treemap_out) |
| - shutil.copy(os.path.join('tools', 'binary_size', 'template', 'index.html'), |
| - opts.destdir) |
| + |
| + if opts.legacy: # legacy report |
| + DumpTreemap(symbols, os.path.join(opts.destdir, 'treemap-dump.js')) |
| + DumpLargestSymbols(symbols, |
| + os.path.join(opts.destdir, 'largest-symbols.js'), 100) |
| + DumpLargestSources(symbols, |
| + os.path.join(opts.destdir, 'largest-sources.js'), 100) |
| + DumpLargestVTables(symbols, |
| + os.path.join(opts.destdir, 'largest-vtables.js'), 100) |
| + treemap_out = os.path.join(opts.destdir, 'webtreemap') |
| + if not os.path.exists(treemap_out): |
| + os.makedirs(treemap_out, 0755) |
| + treemap_src = os.path.join('third_party', 'webtreemap', 'src') |
| + shutil.copy(os.path.join(treemap_src, 'COPYING'), treemap_out) |
| + shutil.copy(os.path.join(treemap_src, 'webtreemap.js'), treemap_out) |
| + shutil.copy(os.path.join(treemap_src, 'webtreemap.css'), treemap_out) |
| + shutil.copy(os.path.join('tools', 'binary_size', 'legacy_template', |
| + 'index.html'), opts.destdir) |
| + else: # modern report |
| + DumpCompactTree(symbols, os.path.join(opts.destdir, 'data.js')) |
| + d3_out = os.path.join(opts.destdir, 'd3') |
| + if not os.path.exists(d3_out): |
| + os.makedirs(d3_out, 0755) |
| + d3_src = os.path.join('third_party', 'd3', 'src') |
| + template_src = os.path.join('tools', 'binary_size', |
| + 'template') |
| + shutil.copy(os.path.join(d3_src, 'LICENSE'), d3_out) |
| + shutil.copy(os.path.join(d3_src, 'd3.js'), d3_out) |
| + shutil.copy(os.path.join(template_src, 'index.html'), opts.destdir) |
| + shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), opts.destdir) |
| + |
| if opts.verbose: |
| print 'Report saved to ' + opts.destdir + '/index.html' |
| if __name__ == '__main__': |
| - sys.exit(main()) |
| + sys.exit(main()) |