Chromium Code Reviews| 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. |
| 11 """ | 11 """ |
| 12 | 12 |
| 13 import collections | 13 import collections |
| 14 import json | 14 import json |
| 15 import logging | 15 import logging |
| 16 import multiprocessing | 16 import multiprocessing |
| 17 import optparse | 17 import optparse |
| 18 import os | 18 import os |
| 19 import re | 19 import re |
| 20 import shutil | 20 import shutil |
| 21 import struct | |
| 21 import subprocess | 22 import subprocess |
| 22 import sys | 23 import sys |
| 23 import tempfile | 24 import tempfile |
| 24 import time | 25 import time |
| 25 | 26 |
| 26 import binary_size_utils | 27 import binary_size_utils |
| 27 | 28 |
| 28 # This path changee is not beautiful. Temporary (I hope) measure until | 29 # This path changee is not beautiful. Temporary (I hope) measure until |
| 29 # the chromium project has figured out a proper way to organize the | 30 # the chromium project has figured out a proper way to organize the |
| 30 # library of python tools. http://crbug.com/375725 | 31 # library of python tools. http://crbug.com/375725 |
| (...skipping 578 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 609 RunElfSymbolizer(outfile, library, addr2line_binary, nm_binary, jobs) | 610 RunElfSymbolizer(outfile, library, addr2line_binary, nm_binary, jobs) |
| 610 | 611 |
| 611 nm_infile = outfile | 612 nm_infile = outfile |
| 612 | 613 |
| 613 elif verbose: | 614 elif verbose: |
| 614 print 'Using nm input from ' + nm_infile | 615 print 'Using nm input from ' + nm_infile |
| 615 with file(nm_infile, 'r') as infile: | 616 with file(nm_infile, 'r') as infile: |
| 616 return list(binary_size_utils.ParseNm(infile)) | 617 return list(binary_size_utils.ParseNm(infile)) |
| 617 | 618 |
| 618 | 619 |
| 620 PAK_RESOURCE_ID_TO_STRING = { "inited": False } | |
| 621 def GetReadablePakResourceName(pak_file, resource_id): | |
| 622 """Pak resources have a numeric identifier. It is not helpful when | |
| 623 trying to locate where footprint is generated. This does its best to | |
| 624 map the number to a usable string.""" | |
| 625 if not PAK_RESOURCE_ID_TO_STRING['inited']: | |
| 626 # Try to find resource header files generated by grit when | |
| 627 # building the pak file. We'll look for files named *resources.h" | |
| 628 # and lines of the type: | |
| 629 # #define MY_RESOURCE_JS 1234 | |
| 630 PAK_RESOURCE_ID_TO_STRING['inited'] = True | |
| 631 gen_dir = os.path.join(os.path.dirname(pak_file), 'gen') | |
| 632 if os.path.isdir(gen_dir): | |
| 633 for dirname, _dirs, files in os.walk(gen_dir): | |
| 634 for filename in files: | |
| 635 if filename.endswith('resources.h'): | |
| 636 with open(os.path.join(dirname, filename)) as resource_header: | |
| 637 for line in resource_header: | |
| 638 if line.startswith("#define "): | |
| 639 line_data = line.split() | |
| 640 if len(line_data) == 3: | |
| 641 try: | |
|
andrewhayden
2014/07/16 09:29:39
I'm a bit leery of any code that reaches the 10th
| |
| 642 resource_number = int(line_data[2]) | |
| 643 resource_name = line_data[1] | |
| 644 PAK_RESOURCE_ID_TO_STRING[resource_number] = resource_name | |
| 645 except ValueError: | |
| 646 pass | |
| 647 return PAK_RESOURCE_ID_TO_STRING.get(resource_id, | |
| 648 'Pak Resouce %d' % resource_id) | |
|
andrewhayden
2014/07/16 09:29:38
Typo: "Resouce"
| |
| 649 | |
| 650 def AddPakData(symbols, pak_file): | |
| 651 """Adds pseudo-symbols from a pak file.""" | |
| 652 pak_file = os.path.abspath(pak_file) | |
| 653 with open(pak_file, 'rb') as pak: | |
| 654 data = pak.read() | |
| 655 | |
| 656 PACK_FILE_VERSION = 4 | |
|
andrewhayden
2014/07/16 09:29:38
For consistency could we stick with PAK instead of
| |
| 657 HEADER_LENGTH = 2 * 4 + 1 # Two uint32s. (file version, number of entries) | |
| 658 # and one uint8 (encoding of text resources) | |
| 659 INDEX_ENTRY_SIZE = 2 + 4 # Each entry is a uint16 and a uint32. | |
| 660 version, num_entries, _encoding = struct.unpack('<IIB', data[:HEADER_LENGTH]) | |
| 661 assert version == PACK_FILE_VERSION, ('Unsupported pak file ' | |
| 662 'version (%d) in %s. Only ' | |
| 663 'support version %d' % | |
| 664 (version, pak_file, PACK_FILE_VERSION)) | |
| 665 if num_entries > 0: | |
| 666 # Read the index and data. | |
| 667 data = data[HEADER_LENGTH:] | |
| 668 for _ in range(num_entries): | |
| 669 resource_id, offset = struct.unpack('<HI', data[:INDEX_ENTRY_SIZE]) | |
| 670 data = data[INDEX_ENTRY_SIZE:] | |
| 671 _next_id, next_offset = struct.unpack('<HI', data[:INDEX_ENTRY_SIZE]) | |
| 672 resource_size = next_offset - offset | |
| 673 | |
| 674 symbol_name = GetReadablePakResourceName(pak_file, resource_id) | |
| 675 symbol_path = pak_file | |
| 676 symbol_type = 'd' # Data. Approximation. | |
|
andrewhayden
2014/07/16 09:29:39
Could you file a crbug after we finish this to add
| |
| 677 symbol_size = resource_size | |
| 678 symbols.append((symbol_name, symbol_type, symbol_size, symbol_path)) | |
| 679 | |
| 619 def _find_in_system_path(binary): | 680 def _find_in_system_path(binary): |
| 620 """Locate the full path to binary in the system path or return None | 681 """Locate the full path to binary in the system path or return None |
| 621 if not found.""" | 682 if not found.""" |
| 622 system_path = os.environ["PATH"].split(os.pathsep) | 683 system_path = os.environ["PATH"].split(os.pathsep) |
| 623 for path in system_path: | 684 for path in system_path: |
| 624 binary_path = os.path.join(path, binary) | 685 binary_path = os.path.join(path, binary) |
| 625 if os.path.isfile(binary_path): | 686 if os.path.isfile(binary_path): |
| 626 return binary_path | 687 return binary_path |
| 627 return None | 688 return None |
| 628 | 689 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 681 'present in the file; i.e., no addr2line symbol lookups ' | 742 'present in the file; i.e., no addr2line symbol lookups ' |
| 682 'will be performed when this option is specified. ' | 743 'will be performed when this option is specified. ' |
| 683 'Mutually exclusive with --library.') | 744 'Mutually exclusive with --library.') |
| 684 parser.add_option('--destdir', metavar='PATH', | 745 parser.add_option('--destdir', metavar='PATH', |
| 685 help='write output to the specified directory. An HTML ' | 746 help='write output to the specified directory. An HTML ' |
| 686 'report is generated here along with supporting files; ' | 747 'report is generated here along with supporting files; ' |
| 687 'any existing report will be overwritten.') | 748 'any existing report will be overwritten.') |
| 688 parser.add_option('--library', metavar='PATH', | 749 parser.add_option('--library', metavar='PATH', |
| 689 help='if specified, process symbols in the library at ' | 750 help='if specified, process symbols in the library at ' |
| 690 'the specified path. Mutually exclusive with --nm-in.') | 751 'the specified path. Mutually exclusive with --nm-in.') |
| 752 parser.add_option('--pak', metavar='PATH', | |
| 753 help='if specified, includes the contents of the ' | |
| 754 'specified *.pak file in the output.') | |
| 691 parser.add_option('--nm-binary', | 755 parser.add_option('--nm-binary', |
| 692 help='use the specified nm binary to analyze library. ' | 756 help='use the specified nm binary to analyze library. ' |
| 693 'This is to be used when the nm in the path is not for ' | 757 'This is to be used when the nm in the path is not for ' |
| 694 'the right architecture or of the right version.') | 758 'the right architecture or of the right version.') |
| 695 parser.add_option('--addr2line-binary', | 759 parser.add_option('--addr2line-binary', |
| 696 help='use the specified addr2line binary to analyze ' | 760 help='use the specified addr2line binary to analyze ' |
| 697 'library. This is to be used when the addr2line in ' | 761 'library. This is to be used when the addr2line in ' |
| 698 'the path is not for the right architecture or ' | 762 'the path is not for the right architecture or ' |
| 699 'of the right version.') | 763 'of the right version.') |
| 700 parser.add_option('--jobs', type='int', | 764 parser.add_option('--jobs', type='int', |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 742 'Use --addr2line-binary to specify location.' | 806 'Use --addr2line-binary to specify location.' |
| 743 | 807 |
| 744 if opts.nm_binary: | 808 if opts.nm_binary: |
| 745 assert os.path.isfile(opts.nm_binary) | 809 assert os.path.isfile(opts.nm_binary) |
| 746 nm_binary = opts.nm_binary | 810 nm_binary = opts.nm_binary |
| 747 else: | 811 else: |
| 748 nm_binary = _find_in_system_path('nm') | 812 nm_binary = _find_in_system_path('nm') |
| 749 assert nm_binary, 'Unable to find nm in the path. Use --nm-binary '\ | 813 assert nm_binary, 'Unable to find nm in the path. Use --nm-binary '\ |
| 750 'to specify location.' | 814 'to specify location.' |
| 751 | 815 |
| 816 if opts.pak: | |
| 817 assert os.path.isfile(opts.pak), 'Could not find ' % opts.pak | |
| 818 | |
| 752 print('addr2line: %s' % addr2line_binary) | 819 print('addr2line: %s' % addr2line_binary) |
| 753 print('nm: %s' % nm_binary) | 820 print('nm: %s' % nm_binary) |
| 754 | 821 |
| 755 CheckDebugFormatSupport(opts.library, addr2line_binary) | 822 CheckDebugFormatSupport(opts.library, addr2line_binary) |
| 756 | 823 |
| 757 symbols = GetNmSymbols(opts.nm_in, opts.nm_out, opts.library, | 824 symbols = GetNmSymbols(opts.nm_in, opts.nm_out, opts.library, |
| 758 opts.jobs, opts.verbose is True, | 825 opts.jobs, opts.verbose is True, |
| 759 addr2line_binary, nm_binary) | 826 addr2line_binary, nm_binary) |
| 827 | |
| 828 if opts.pak: | |
| 829 AddPakData(symbols, opts.pak) | |
| 830 | |
| 760 if not os.path.exists(opts.destdir): | 831 if not os.path.exists(opts.destdir): |
| 761 os.makedirs(opts.destdir, 0755) | 832 os.makedirs(opts.destdir, 0755) |
| 762 | 833 |
| 763 | 834 |
| 764 if opts.legacy: # legacy report | 835 if opts.legacy: # legacy report |
| 765 DumpTreemap(symbols, os.path.join(opts.destdir, 'treemap-dump.js')) | 836 DumpTreemap(symbols, os.path.join(opts.destdir, 'treemap-dump.js')) |
| 766 DumpLargestSymbols(symbols, | 837 DumpLargestSymbols(symbols, |
| 767 os.path.join(opts.destdir, 'largest-symbols.js'), 100) | 838 os.path.join(opts.destdir, 'largest-symbols.js'), 100) |
| 768 DumpLargestSources(symbols, | 839 DumpLargestSources(symbols, |
| 769 os.path.join(opts.destdir, 'largest-sources.js'), 100) | 840 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) | 863 shutil.copy(os.path.join(d3_src, 'LICENSE'), d3_out) |
| 793 shutil.copy(os.path.join(d3_src, 'd3.js'), d3_out) | 864 shutil.copy(os.path.join(d3_src, 'd3.js'), d3_out) |
| 794 shutil.copy(os.path.join(template_src, 'index.html'), opts.destdir) | 865 shutil.copy(os.path.join(template_src, 'index.html'), opts.destdir) |
| 795 shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), opts.destdir) | 866 shutil.copy(os.path.join(template_src, 'D3SymbolTreeMap.js'), opts.destdir) |
| 796 | 867 |
| 797 print 'Report saved to ' + opts.destdir + '/index.html' | 868 print 'Report saved to ' + opts.destdir + '/index.html' |
| 798 | 869 |
| 799 | 870 |
| 800 if __name__ == '__main__': | 871 if __name__ == '__main__': |
| 801 sys.exit(main()) | 872 sys.exit(main()) |
| OLD | NEW |