OLD | NEW |
---|---|
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 464 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
475 sNmPattern = re.compile( | 475 sNmPattern = re.compile( |
476 r'([0-9a-f]{8,})[\s]+([0-9a-f]{8,})[\s]*(\S?)[\s*]([^\t]*)[\t]?(.*)') | 476 r'([0-9a-f]{8,})[\s]+([0-9a-f]{8,})[\s]*(\S?)[\s*]([^\t]*)[\t]?(.*)') |
477 | 477 |
478 class Progress(): | 478 class Progress(): |
479 def __init__(self): | 479 def __init__(self): |
480 self.count = 0 | 480 self.count = 0 |
481 self.skip_count = 0 | 481 self.skip_count = 0 |
482 self.collisions = 0 | 482 self.collisions = 0 |
483 self.time_last_output = time.time() | 483 self.time_last_output = time.time() |
484 self.count_last_output = 0 | 484 self.count_last_output = 0 |
485 self.disambiguations = 0 | |
Andrew Hayden (chromium.org)
2014/06/17 11:36:27
In the old code I also counted the number of disam
| |
485 | 486 |
486 | 487 |
487 def RunElfSymbolizer(outfile, library, addr2line_binary, nm_binary, jobs): | 488 def RunElfSymbolizer(outfile, library, addr2line_binary, nm_binary, jobs, |
489 disambiguate, src_path): | |
488 nm_output = RunNm(library, nm_binary) | 490 nm_output = RunNm(library, nm_binary) |
489 nm_output_lines = nm_output.splitlines() | 491 nm_output_lines = nm_output.splitlines() |
490 nm_output_lines_len = len(nm_output_lines) | 492 nm_output_lines_len = len(nm_output_lines) |
491 address_symbol = {} | 493 address_symbol = {} |
492 progress = Progress() | 494 progress = Progress() |
493 def map_address_symbol(symbol, addr): | 495 def map_address_symbol(symbol, addr): |
494 progress.count += 1 | 496 progress.count += 1 |
495 if addr in address_symbol: | 497 if addr in address_symbol: |
496 # 'Collision between %s and %s.' % (str(symbol.name), | 498 # 'Collision between %s and %s.' % (str(symbol.name), |
497 # str(address_symbol[addr].name)) | 499 # str(address_symbol[addr].name)) |
498 progress.collisions += 1 | 500 progress.collisions += 1 |
499 else: | 501 else: |
502 if symbol.disambiguated: | |
503 progress.disambiguations += 1 | |
500 address_symbol[addr] = symbol | 504 address_symbol[addr] = symbol |
501 | 505 |
502 progress_chunk = 100 | 506 progress_chunk = 100 |
503 if progress.count % progress_chunk == 0: | 507 if progress.count % progress_chunk == 0: |
504 time_now = time.time() | 508 time_now = time.time() |
505 time_spent = time_now - progress.time_last_output | 509 time_spent = time_now - progress.time_last_output |
506 if time_spent > 1.0: | 510 if time_spent > 1.0: |
507 # Only output at most once per second. | 511 # Only output at most once per second. |
508 progress.time_last_output = time_now | 512 progress.time_last_output = time_now |
509 chunk_size = progress.count - progress.count_last_output | 513 chunk_size = progress.count - progress.count_last_output |
510 progress.count_last_output = progress.count | 514 progress.count_last_output = progress.count |
511 if time_spent > 0: | 515 if time_spent > 0: |
512 speed = chunk_size / time_spent | 516 speed = chunk_size / time_spent |
513 else: | 517 else: |
514 speed = 0 | 518 speed = 0 |
515 progress_percent = (100.0 * (progress.count + progress.skip_count) / | 519 progress_percent = (100.0 * (progress.count + progress.skip_count) / |
516 nm_output_lines_len) | 520 nm_output_lines_len) |
517 print('%.1f%%: Looked up %d symbols (%d collisions) - %.1f lookups/s.' % | 521 print('%.1f%%: Looked up %d symbols (%d collisions, %d disambiguations)' |
518 (progress_percent, progress.count, progress.collisions, speed)) | 522 '- %.1f lookups/s.' % |
523 (progress_percent, progress.count, progress.collisions, | |
524 progress.disambiguations, speed)) | |
519 | 525 |
520 symbolizer = elf_symbolizer.ELFSymbolizer(library, addr2line_binary, | 526 symbolizer = elf_symbolizer.ELFSymbolizer(library, addr2line_binary, |
521 map_address_symbol, | 527 map_address_symbol, |
522 max_concurrent_jobs=jobs) | 528 max_concurrent_jobs=jobs, |
529 disambiguate=disambiguate, | |
530 disambiguation_source_path=src_path) | |
523 user_interrupted = False | 531 user_interrupted = False |
524 try: | 532 try: |
525 for line in nm_output_lines: | 533 for line in nm_output_lines: |
526 match = sNmPattern.match(line) | 534 match = sNmPattern.match(line) |
527 if match: | 535 if match: |
528 location = match.group(5) | 536 location = match.group(5) |
529 if not location: | 537 if not location: |
530 addr = int(match.group(1), 16) | 538 addr = int(match.group(1), 16) |
531 size = int(match.group(2), 16) | 539 size = int(match.group(2), 16) |
532 if addr in address_symbol: # Already looked up, shortcut | 540 if addr in address_symbol: # Already looked up, shortcut |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
592 if err_output: | 600 if err_output: |
593 raise Exception, err_output | 601 raise Exception, err_output |
594 else: | 602 else: |
595 raise Exception, process_output | 603 raise Exception, process_output |
596 | 604 |
597 print('Finished nm') | 605 print('Finished nm') |
598 return process_output | 606 return process_output |
599 | 607 |
600 | 608 |
601 def GetNmSymbols(nm_infile, outfile, library, jobs, verbose, | 609 def GetNmSymbols(nm_infile, outfile, library, jobs, verbose, |
602 addr2line_binary, nm_binary): | 610 addr2line_binary, nm_binary, disambiguate, src_path): |
603 if nm_infile is None: | 611 if nm_infile is None: |
604 if outfile is None: | 612 if outfile is None: |
605 outfile = tempfile.NamedTemporaryFile(delete=False).name | 613 outfile = tempfile.NamedTemporaryFile(delete=False).name |
606 | 614 |
607 if verbose: | 615 if verbose: |
608 print 'Running parallel addr2line, dumping symbols to ' + outfile | 616 print 'Running parallel addr2line, dumping symbols to ' + outfile |
609 RunElfSymbolizer(outfile, library, addr2line_binary, nm_binary, jobs) | 617 RunElfSymbolizer(outfile, library, addr2line_binary, nm_binary, jobs, |
618 disambiguate, src_path) | |
610 | 619 |
611 nm_infile = outfile | 620 nm_infile = outfile |
612 | 621 |
613 elif verbose: | 622 elif verbose: |
614 print 'Using nm input from ' + nm_infile | 623 print 'Using nm input from ' + nm_infile |
615 with file(nm_infile, 'r') as infile: | 624 with file(nm_infile, 'r') as infile: |
616 return list(binary_size_utils.ParseNm(infile)) | 625 return list(binary_size_utils.ParseNm(infile)) |
617 | 626 |
618 | 627 |
619 def _find_in_system_path(binary): | 628 def _find_in_system_path(binary): |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
709 help='be verbose, printing lots of status information.') | 718 help='be verbose, printing lots of status information.') |
710 parser.add_option('--nm-out', metavar='PATH', | 719 parser.add_option('--nm-out', metavar='PATH', |
711 help='keep the nm output file, and store it at the ' | 720 help='keep the nm output file, and store it at the ' |
712 'specified path. This is useful if you want to see the ' | 721 'specified path. This is useful if you want to see the ' |
713 'fully processed nm output after the symbols have been ' | 722 'fully processed nm output after the symbols have been ' |
714 'mapped to source locations. By default, a tempfile is ' | 723 'mapped to source locations. By default, a tempfile is ' |
715 'used and is deleted when the program terminates.' | 724 'used and is deleted when the program terminates.' |
716 'This argument is only valid when using --library.') | 725 'This argument is only valid when using --library.') |
717 parser.add_option('--legacy', action='store_true', | 726 parser.add_option('--legacy', action='store_true', |
718 help='emit legacy binary size report instead of modern') | 727 help='emit legacy binary size report instead of modern') |
728 parser.add_option('--disable-disambiguation', action='store_true', | |
729 help='disables the disambiguation process altogether,' | |
730 ' NOTE: this will produce output with some symbols at the' | |
731 ' top layer due to the fact that addr2line could not get' | |
732 ' the entire source path.') | |
733 parser.add_option('--source-path', default='./', | |
734 help='the path to the source code of the output binary, ' | |
735 'default set to current directory. Used in the' | |
736 ' disambiguation process.') | |
719 opts, _args = parser.parse_args() | 737 opts, _args = parser.parse_args() |
720 | 738 |
721 if ((not opts.library) and (not opts.nm_in)) or (opts.library and opts.nm_in): | 739 if ((not opts.library) and (not opts.nm_in)) or (opts.library and opts.nm_in): |
722 parser.error('exactly one of --library or --nm-in is required') | 740 parser.error('exactly one of --library or --nm-in is required') |
723 if (opts.nm_in): | 741 if (opts.nm_in): |
724 if opts.jobs: | 742 if opts.jobs: |
725 print >> sys.stderr, ('WARNING: --jobs has no effect ' | 743 print >> sys.stderr, ('WARNING: --jobs has no effect ' |
726 'when used with --nm-in') | 744 'when used with --nm-in') |
727 if not opts.destdir: | 745 if not opts.destdir: |
728 parser.error('--destdir is required argument') | 746 parser.error('--destdir is required argument') |
(...skipping 20 matching lines...) Expand all Loading... | |
749 assert nm_binary, 'Unable to find nm in the path. Use --nm-binary '\ | 767 assert nm_binary, 'Unable to find nm in the path. Use --nm-binary '\ |
750 'to specify location.' | 768 'to specify location.' |
751 | 769 |
752 print('addr2line: %s' % addr2line_binary) | 770 print('addr2line: %s' % addr2line_binary) |
753 print('nm: %s' % nm_binary) | 771 print('nm: %s' % nm_binary) |
754 | 772 |
755 CheckDebugFormatSupport(opts.library, addr2line_binary) | 773 CheckDebugFormatSupport(opts.library, addr2line_binary) |
756 | 774 |
757 symbols = GetNmSymbols(opts.nm_in, opts.nm_out, opts.library, | 775 symbols = GetNmSymbols(opts.nm_in, opts.nm_out, opts.library, |
758 opts.jobs, opts.verbose is True, | 776 opts.jobs, opts.verbose is True, |
759 addr2line_binary, nm_binary) | 777 addr2line_binary, nm_binary, |
778 not opts.disable_disambiguation, | |
779 opts.source_path) | |
760 if not os.path.exists(opts.destdir): | 780 if not os.path.exists(opts.destdir): |
761 os.makedirs(opts.destdir, 0755) | 781 os.makedirs(opts.destdir, 0755) |
762 | 782 |
763 | 783 |
764 if opts.legacy: # legacy report | 784 if opts.legacy: # legacy report |
765 DumpTreemap(symbols, os.path.join(opts.destdir, 'treemap-dump.js')) | 785 DumpTreemap(symbols, os.path.join(opts.destdir, 'treemap-dump.js')) |
766 DumpLargestSymbols(symbols, | 786 DumpLargestSymbols(symbols, |
767 os.path.join(opts.destdir, 'largest-symbols.js'), 100) | 787 os.path.join(opts.destdir, 'largest-symbols.js'), 100) |
768 DumpLargestSources(symbols, | 788 DumpLargestSources(symbols, |
769 os.path.join(opts.destdir, 'largest-sources.js'), 100) | 789 os.path.join(opts.destdir, 'largest-sources.js'), 100) |
(...skipping 22 matching lines...) Expand all Loading... | |
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()) |
OLD | NEW |