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 |