| OLD | NEW |
| 1 #!/usr/bin/env python | |
| 2 # Copyright 2017 The Chromium Authors. All rights reserved. | 1 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 5 | 4 |
| 6 """Tool for analyzing binary size of executables using nm or linker map files. | 5 """An interactive console for looking analyzing .size files.""" |
| 7 | |
| 8 Map files can be created by passing "-Map Foo.map" to the linker. If a map file | |
| 9 is unavailable, this tool can also be pointed at an unstripped executable, but | |
| 10 the information does not seem to be as accurate in this case. | |
| 11 | |
| 12 Inspired by SymbolSort for Windows: | |
| 13 https://github.com/adrianstone55/SymbolSort | |
| 14 """ | |
| 15 | 6 |
| 16 import argparse | 7 import argparse |
| 17 import atexit | 8 import atexit |
| 18 import code | 9 import code |
| 19 import contextlib | 10 import contextlib |
| 20 import itertools | 11 import itertools |
| 21 import logging | 12 import logging |
| 22 import os | 13 import os |
| 23 import readline | 14 import readline |
| 24 import subprocess | 15 import subprocess |
| 25 import sys | 16 import sys |
| 26 | 17 |
| 18 import archive |
| 27 import describe | 19 import describe |
| 28 import file_format | 20 import file_format |
| 29 import helpers | |
| 30 import map2size | |
| 31 import match_util | 21 import match_util |
| 32 import models | 22 import models |
| 33 import paths | 23 import paths |
| 34 | 24 |
| 35 | 25 |
| 36 # Number of lines before using less for Print(). | 26 # Number of lines before using less for Print(). |
| 37 _THRESHOLD_FOR_PAGER = 30 | 27 _THRESHOLD_FOR_PAGER = 30 |
| 38 | 28 |
| 39 | 29 |
| 40 @contextlib.contextmanager | 30 @contextlib.contextmanager |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 for size_info in self._size_infos: | 103 for size_info in self._size_infos: |
| 114 if symbol in size_info.symbols: | 104 if symbol in size_info.symbols: |
| 115 break | 105 break |
| 116 else: | 106 else: |
| 117 assert False, 'Symbol does not belong to a size_info.' | 107 assert False, 'Symbol does not belong to a size_info.' |
| 118 | 108 |
| 119 filename = size_info.metadata.get(models.METADATA_ELF_FILENAME) | 109 filename = size_info.metadata.get(models.METADATA_ELF_FILENAME) |
| 120 output_dir = self._lazy_paths.output_directory or '' | 110 output_dir = self._lazy_paths.output_directory or '' |
| 121 path = os.path.normpath(os.path.join(output_dir, filename)) | 111 path = os.path.normpath(os.path.join(output_dir, filename)) |
| 122 | 112 |
| 123 found_build_id = map2size.BuildIdFromElf( | 113 found_build_id = archive.BuildIdFromElf(path, self._lazy_paths.tool_prefix) |
| 124 path, self._lazy_paths.tool_prefix) | |
| 125 expected_build_id = size_info.metadata.get(models.METADATA_ELF_BUILD_ID) | 114 expected_build_id = size_info.metadata.get(models.METADATA_ELF_BUILD_ID) |
| 126 assert found_build_id == expected_build_id, ( | 115 assert found_build_id == expected_build_id, ( |
| 127 'Build ID does not match for %s' % path) | 116 'Build ID does not match for %s' % path) |
| 128 return path | 117 return path |
| 129 | 118 |
| 130 def _DisassembleFunc(self, symbol, elf_path=None, use_pager=None, | 119 def _DisassembleFunc(self, symbol, elf_path=None, use_pager=None, |
| 131 to_file=None): | 120 to_file=None): |
| 132 """Shows objdump disassembly for the given symbol. | 121 """Shows objdump disassembly for the given symbol. |
| 133 | 122 |
| 134 Args: | 123 Args: |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 def Eval(self, query): | 210 def Eval(self, query): |
| 222 eval_result = eval(query, self._variables) | 211 eval_result = eval(query, self._variables) |
| 223 if eval_result: | 212 if eval_result: |
| 224 self._PrintFunc(eval_result) | 213 self._PrintFunc(eval_result) |
| 225 | 214 |
| 226 def GoInteractive(self): | 215 def GoInteractive(self): |
| 227 _Session._InitReadline() | 216 _Session._InitReadline() |
| 228 code.InteractiveConsole(self._variables).interact(self._CreateBanner()) | 217 code.InteractiveConsole(self._variables).interact(self._CreateBanner()) |
| 229 | 218 |
| 230 | 219 |
| 231 def main(argv): | 220 def AddArguments(parser): |
| 232 parser = argparse.ArgumentParser() | 221 parser.add_argument( |
| 233 parser.add_argument('inputs', nargs='+', | 222 'inputs', nargs='+', |
| 234 help='Input .size files to load. For a single file, ' | 223 help='Input .size files to load. For a single file, it will be mapped to ' |
| 235 'it will be mapped to variables as: size_info & ' | 224 'the variable "size_info". For multiple inputs, the names will be ' |
| 236 'symbols (where symbols = size_info.symbols). For ' | 225 'size_info1, size_info2, etc.') |
| 237 'multiple inputs, the names will be size_info1, ' | 226 parser.add_argument( |
| 238 'symbols1, etc.') | 227 '--query', help='Print the result of the given snippet. Example: ' |
| 239 parser.add_argument('--query', | 228 'size_info.symbols.WhereInSection("d")' |
| 240 help='Print the result of the given snippet. Example: ' | 229 '.WhereBiggerThan(100)') |
| 241 'symbols.WhereInSection("d").' | 230 parser.add_argument('--tool-prefix', default='', |
| 242 'WhereBiggerThan(100)') | 231 help='Path prefix for objdump. Required only for ' |
| 243 paths.AddOptions(parser) | 232 'Disassemble().') |
| 244 args = helpers.AddCommonOptionsAndParseArgs(parser, argv) | 233 parser.add_argument('--output-directory', |
| 234 help='Path to the root build directory. Used only for ' |
| 235 'Disassemble().') |
| 245 | 236 |
| 237 |
| 238 def Run(args, parser): |
| 246 for path in args.inputs: | 239 for path in args.inputs: |
| 247 if not path.endswith('.size'): | 240 if not path.endswith('.size'): |
| 248 parser.error('All inputs must end with ".size"') | 241 parser.error('All inputs must end with ".size"') |
| 249 | 242 |
| 250 size_infos = [map2size.LoadAndPostProcessSizeInfo(p) for p in args.inputs] | 243 size_infos = [archive.LoadAndPostProcessSizeInfo(p) for p in args.inputs] |
| 251 lazy_paths = paths.LazyPaths(args=args, input_file=args.inputs[0]) | 244 lazy_paths = paths.LazyPaths(args=args, input_file=args.inputs[0]) |
| 252 session = _Session(size_infos, lazy_paths) | 245 session = _Session(size_infos, lazy_paths) |
| 253 | 246 |
| 254 if args.query: | 247 if args.query: |
| 255 logging.info('Running query from command-line.') | 248 logging.info('Running query from command-line.') |
| 256 session.Eval(args.query) | 249 session.Eval(args.query) |
| 257 else: | 250 else: |
| 258 logging.info('Entering interactive console.') | 251 logging.info('Entering interactive console.') |
| 259 session.GoInteractive() | 252 session.GoInteractive() |
| 260 | |
| 261 | |
| 262 if __name__ == '__main__': | |
| 263 sys.exit(main(sys.argv)) | |
| OLD | NEW |