Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: tools/binary_size/run_binary_size_analysis.py

Issue 303453003: binary_size: Easier-to-read output (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased to newer master Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Generate a spatial analysis against an arbitrary library. 6 """Generate a spatial analysis against an arbitrary library.
7 7
8 To use, build the 'binary_size_tool' target. Then run this tool, passing 8 To use, build the 'binary_size_tool' target. Then run this tool, passing
9 in the location of the library to be analyzed along with any other options 9 in the location of the library to be analyzed along with any other options
10 you desire. 10 you desire.
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 logging.warning('A container node used as symbol for %s.' % symbol_name) 154 logging.warning('A container node used as symbol for %s.' % symbol_name)
155 # This is going to be used as a leaf so no use for child list. 155 # This is going to be used as a leaf so no use for child list.
156 del node[NODE_CHILDREN_KEY] 156 del node[NODE_CHILDREN_KEY]
157 node[NODE_SYMBOL_SIZE_KEY] = symbol_size 157 node[NODE_SYMBOL_SIZE_KEY] = symbol_size
158 node[NODE_SYMBOL_TYPE_KEY] = symbol_type 158 node[NODE_SYMBOL_TYPE_KEY] = symbol_type
159 node[NODE_TYPE_KEY] = 's' # s for symbol 159 node[NODE_TYPE_KEY] = 's' # s for symbol
160 160
161 return 2 # Depth of the added subtree. 161 return 2 # Depth of the added subtree.
162 162
163 163
164 def MakeCompactTree(symbols): 164 def MakeCompactTree(symbols, symbol_path_origin_dir):
165 result = {NODE_NAME_KEY: '/', 165 result = {NODE_NAME_KEY: '/',
166 NODE_CHILDREN_KEY: {}, 166 NODE_CHILDREN_KEY: {},
167 NODE_TYPE_KEY: 'p', 167 NODE_TYPE_KEY: 'p',
168 NODE_MAX_DEPTH_KEY: 0} 168 NODE_MAX_DEPTH_KEY: 0}
169 seen_symbol_with_path = False 169 seen_symbol_with_path = False
170 cwd = os.path.abspath(os.getcwd())
170 for symbol_name, symbol_type, symbol_size, file_path in symbols: 171 for symbol_name, symbol_type, symbol_size, file_path in symbols:
171 172
172 if 'vtable for ' in symbol_name: 173 if 'vtable for ' in symbol_name:
173 symbol_type = '@' # hack to categorize these separately 174 symbol_type = '@' # hack to categorize these separately
174 # Take path like '/foo/bar/baz', convert to ['foo', 'bar', 'baz'] 175 # Take path like '/foo/bar/baz', convert to ['foo', 'bar', 'baz']
175 if file_path: 176 if file_path and file_path != "??":
176 file_path = os.path.normpath(file_path) 177 file_path = os.path.abspath(os.path.join(symbol_path_origin_dir,
178 file_path))
179 # Let the output structure be relative to $CWD if inside $CWD,
180 # otherwise relative to the disk root. This is to avoid
181 # unnecessary click-through levels in the output.
182 if file_path.startswith(cwd + os.sep):
183 file_path = file_path[len(cwd):]
184 if file_path.startswith('/'):
185 file_path = file_path[1:]
177 seen_symbol_with_path = True 186 seen_symbol_with_path = True
178 else: 187 else:
179 file_path = NAME_NO_PATH_BUCKET 188 file_path = NAME_NO_PATH_BUCKET
180 189
181 if file_path.startswith('/'):
182 file_path = file_path[1:]
183 path_parts = file_path.split('/') 190 path_parts = file_path.split('/')
184 191
185 # Find pre-existing node in tree, or update if it already exists 192 # Find pre-existing node in tree, or update if it already exists
186 node = result 193 node = result
187 depth = 0 194 depth = 0
188 while len(path_parts) > 0: 195 while len(path_parts) > 0:
189 path_part = path_parts.pop(0) 196 path_part = path_parts.pop(0)
190 if len(path_part) == 0: 197 if len(path_part) == 0:
191 continue 198 continue
192 depth += 1 199 depth += 1
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 # Sort children by size, largest to smallest. 346 # Sort children by size, largest to smallest.
340 children.sort(key=lambda child: -child['data']['$area']) 347 children.sort(key=lambda child: -child['data']['$area'])
341 348
342 # For leaf nodes, the 'size' attribute is the size of the leaf; 349 # For leaf nodes, the 'size' attribute is the size of the leaf;
343 # Non-leaf nodes don't really have a size, but their 'size' attribute is 350 # Non-leaf nodes don't really have a size, but their 'size' attribute is
344 # the sum of the sizes of all their children. 351 # the sum of the sizes of all their children.
345 return {'name': name + ' (' + FormatBytes(tree['size']) + ')', 352 return {'name': name + ' (' + FormatBytes(tree['size']) + ')',
346 'data': { '$area': tree['size'] }, 353 'data': { '$area': tree['size'] },
347 'children': children } 354 'children': children }
348 355
349 def DumpCompactTree(symbols, outfile): 356 def DumpCompactTree(symbols, symbol_path_origin_dir, outfile):
350 tree_root = MakeCompactTree(symbols) 357 tree_root = MakeCompactTree(symbols, symbol_path_origin_dir)
351 with open(outfile, 'w') as out: 358 with open(outfile, 'w') as out:
352 out.write('var tree_data = ') 359 out.write('var tree_data=')
353 json.dump(tree_root, out) 360 # Use separators without whitespace to get a smaller file.
361 json.dump(tree_root, out, separators=(',', ':'))
354 print('Writing %d bytes json' % os.path.getsize(outfile)) 362 print('Writing %d bytes json' % os.path.getsize(outfile))
355 363
356 364
357 # TODO(andrewhayden): Only used for legacy reports. Delete. 365 # TODO(andrewhayden): Only used for legacy reports. Delete.
358 def DumpTreemap(symbols, outfile): 366 def DumpTreemap(symbols, outfile):
359 dirs = TreeifySymbols(symbols) 367 dirs = TreeifySymbols(symbols)
360 out = open(outfile, 'w') 368 out = open(outfile, 'w')
361 try: 369 try:
362 out.write('var kTree = ' + json.dumps(JsonifyTree(dirs, '/'))) 370 out.write('var kTree = ' + json.dumps(JsonifyTree(dirs, '/')))
363 finally: 371 finally:
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
492 progress = Progress() 500 progress = Progress()
493 def map_address_symbol(symbol, addr): 501 def map_address_symbol(symbol, addr):
494 progress.count += 1 502 progress.count += 1
495 if addr in address_symbol: 503 if addr in address_symbol:
496 # 'Collision between %s and %s.' % (str(symbol.name), 504 # 'Collision between %s and %s.' % (str(symbol.name),
497 # str(address_symbol[addr].name)) 505 # str(address_symbol[addr].name))
498 progress.collisions += 1 506 progress.collisions += 1
499 else: 507 else:
500 address_symbol[addr] = symbol 508 address_symbol[addr] = symbol
501 509
510 progress_output()
511
512 def progress_output():
502 progress_chunk = 100 513 progress_chunk = 100
503 if progress.count % progress_chunk == 0: 514 if progress.count % progress_chunk == 0:
504 time_now = time.time() 515 time_now = time.time()
505 time_spent = time_now - progress.time_last_output 516 time_spent = time_now - progress.time_last_output
506 if time_spent > 1.0: 517 if time_spent > 1.0:
507 # Only output at most once per second. 518 # Only output at most once per second.
508 progress.time_last_output = time_now 519 progress.time_last_output = time_now
509 chunk_size = progress.count - progress.count_last_output 520 chunk_size = progress.count - progress.count_last_output
510 progress.count_last_output = progress.count 521 progress.count_last_output = progress.count
511 if time_spent > 0: 522 if time_spent > 0:
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 symbolizer.Join() 560 symbolizer.Join()
550 except KeyboardInterrupt: 561 except KeyboardInterrupt:
551 # Don't want to abort here since we will be finished in a few seconds. 562 # Don't want to abort here since we will be finished in a few seconds.
552 user_interrupted = True 563 user_interrupted = True
553 print('Patience you must have my young padawan.') 564 print('Patience you must have my young padawan.')
554 565
555 if user_interrupted: 566 if user_interrupted:
556 print('Skipping the rest of the file mapping. ' 567 print('Skipping the rest of the file mapping. '
557 'Output will not be fully classified.') 568 'Output will not be fully classified.')
558 569
570 symbol_path_origin_dir = os.path.dirname(os.path.abspath(library))
571
559 with open(outfile, 'w') as out: 572 with open(outfile, 'w') as out:
560 for line in nm_output_lines: 573 for line in nm_output_lines:
561 match = sNmPattern.match(line) 574 match = sNmPattern.match(line)
562 if match: 575 if match:
563 location = match.group(5) 576 location = match.group(5)
564 if not location: 577 if not location:
565 addr = int(match.group(1), 16) 578 addr = int(match.group(1), 16)
566 symbol = address_symbol.get(addr) 579 symbol = address_symbol.get(addr)
567 if symbol is not None: 580 if symbol is not None:
568 path = '??' 581 path = '??'
569 if symbol.source_path is not None: 582 if symbol.source_path is not None:
570 path = symbol.source_path 583 path = os.path.abspath(os.path.join(symbol_path_origin_dir,
584 symbol.source_path))
571 line_number = 0 585 line_number = 0
572 if symbol.source_line is not None: 586 if symbol.source_line is not None:
573 line_number = symbol.source_line 587 line_number = symbol.source_line
574 out.write('%s\t%s:%d\n' % (line, path, line_number)) 588 out.write('%s\t%s:%d\n' % (line, path, line_number))
575 continue 589 continue
576 590
577 out.write('%s\n' % line) 591 out.write('%s\n' % line)
578 592
579 print('%d symbols in the results.' % len(address_symbol)) 593 print('%d symbols in the results.' % len(address_symbol))
580 594
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
772 treemap_out = os.path.join(opts.destdir, 'webtreemap') 786 treemap_out = os.path.join(opts.destdir, 'webtreemap')
773 if not os.path.exists(treemap_out): 787 if not os.path.exists(treemap_out):
774 os.makedirs(treemap_out, 0755) 788 os.makedirs(treemap_out, 0755)
775 treemap_src = os.path.join('third_party', 'webtreemap', 'src') 789 treemap_src = os.path.join('third_party', 'webtreemap', 'src')
776 shutil.copy(os.path.join(treemap_src, 'COPYING'), treemap_out) 790 shutil.copy(os.path.join(treemap_src, 'COPYING'), treemap_out)
777 shutil.copy(os.path.join(treemap_src, 'webtreemap.js'), treemap_out) 791 shutil.copy(os.path.join(treemap_src, 'webtreemap.js'), treemap_out)
778 shutil.copy(os.path.join(treemap_src, 'webtreemap.css'), treemap_out) 792 shutil.copy(os.path.join(treemap_src, 'webtreemap.css'), treemap_out)
779 shutil.copy(os.path.join('tools', 'binary_size', 'legacy_template', 793 shutil.copy(os.path.join('tools', 'binary_size', 'legacy_template',
780 'index.html'), opts.destdir) 794 'index.html'), opts.destdir)
781 else: # modern report 795 else: # modern report
782 DumpCompactTree(symbols, os.path.join(opts.destdir, 'data.js')) 796 if opts.library:
797 symbol_path_origin_dir = os.path.dirname(os.path.abspath(opts.library))
798 else:
799 # Just a guess. Hopefully all paths in the input file are absolute.
800 symbol_path_origin_dir = os.path.abspath(os.getcwd())
801 data_js_file_name = os.path.join(opts.destdir, 'data.js')
802 DumpCompactTree(symbols, symbol_path_origin_dir, data_js_file_name)
783 d3_out = os.path.join(opts.destdir, 'd3') 803 d3_out = os.path.join(opts.destdir, 'd3')
784 if not os.path.exists(d3_out): 804 if not os.path.exists(d3_out):
785 os.makedirs(d3_out, 0755) 805 os.makedirs(d3_out, 0755)
786 d3_src = os.path.join(os.path.dirname(__file__), 806 d3_src = os.path.join(os.path.dirname(__file__),
787 '..', 807 '..',
788 '..', 808 '..',
789 'third_party', 'd3', 'src') 809 'third_party', 'd3', 'src')
790 template_src = os.path.join(os.path.dirname(__file__), 810 template_src = os.path.join(os.path.dirname(__file__),
791 'template') 811 'template')
792 shutil.copy(os.path.join(d3_src, 'LICENSE'), d3_out) 812 shutil.copy(os.path.join(d3_src, 'LICENSE'), d3_out)
793 shutil.copy(os.path.join(d3_src, 'd3.js'), d3_out) 813 shutil.copy(os.path.join(d3_src, 'd3.js'), d3_out)
794 shutil.copy(os.path.join(template_src, 'index.html'), opts.destdir) 814 shutil.copy(os.path.join(template_src, 'index.html'), opts.destdir)
795 shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), opts.destdir) 815 shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), opts.destdir)
796 816
797 print 'Report saved to ' + opts.destdir + '/index.html' 817 print 'Report saved to ' + opts.destdir + '/index.html'
798 818
799 819
800 if __name__ == '__main__': 820 if __name__ == '__main__':
801 sys.exit(main()) 821 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698