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..2a75faf0ac585289abb15587a269a27fc8937835 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 |
@@ -93,6 +92,66 @@ def ParseNm(input): |
print >>sys.stderr, 'unparsed:', repr(line) |
+def _MkChild(node, name): |
+ child = None |
+ for test in node['children']: |
+ if test['n'] == name: |
+ child = test |
+ break |
+ if not child: |
+ child = {'n': name, 'children': []} |
+ node['children'].append(child) |
+ return child |
+ |
+ |
+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 = _MkChild(node, path_part); |
+ node['k'] = 'p' # p for path |
+ |
+ # 'node' is now the file node. Find the symbol-type bucket. |
+ node['lastPathElement'] = True |
+ node = _MkChild(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 = _MkChild(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 +247,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 +284,16 @@ 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 +304,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 +348,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 +371,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: |
@@ -325,6 +397,7 @@ def DumpLargestVTables(symbols, outfile, n): |
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 +415,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 +514,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 +539,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()) |