Chromium Code Reviews| Index: tools/binary_size/query.py |
| diff --git a/tools/binary_size/query.py b/tools/binary_size/query.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..259953cc3f82c305ef9ca3125d7e0e9bd5f30a27 |
| --- /dev/null |
| +++ b/tools/binary_size/query.py |
| @@ -0,0 +1,136 @@ |
| +#!/usr/bin/env python |
| +# Copyright 2017 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Tool for analyzing binary size of executables using nm or linker map files. |
| + |
| +Map files can be created by passing "-Map Foo.map" to the linker. If a map file |
| +is unavailable, this tool can also be pointed at an unstripped executable, but |
| +the information does not seem to be as accurate in this case. |
| + |
| +Inspired by SymbolSort for Windows: |
| + https://github.com/adrianstone55/SymbolSort |
| +""" |
| + |
| +import argparse |
| +import code |
| +import contextlib |
| +import logging |
| +import readline |
| +import subprocess |
| +import sys |
| + |
| +import analyze |
| +import helpers |
| +import symbols |
| + |
| + |
| +# Number of lines before using less for Print(). |
| +_THRESHOLD_FOR_PAGER = 30 |
| + |
| + |
| +@contextlib.contextmanager |
| +def _LessPipe(): |
| + """Output to `less`. Yields the write function.""" |
| + try: |
| + proc = subprocess.Popen(['less'], stdin=subprocess.PIPE, stdout=sys.stdout) |
| + yield proc.stdin.write |
| + proc.stdin.close() |
| + |
| + proc.wait() |
| + except IOError: |
| + pass # Happens when less is quit before all data is written. |
| + except KeyboardInterrupt: |
| + pass # Assume used to break out of less. |
| + |
| + |
| +def _PrintSymbolGroup(group, show_elided=True, use_pager=None): |
| + """Prints out the given list of symbols. |
| + |
| + Args: |
| + show_elided: Whether to print out group.filtered_symbols. |
| + """ |
| + by_size = group.Sorted() |
| + # TODO(agrieve): Taking line-wrapping into account for groups vs. symbols |
| + # would make sense here. |
| + if use_pager is None: |
| + count = sum(1 if s.IsGroup() else 2 for s in group) |
| + if show_elided and group.filtered_symbols: |
| + count += 1 |
| + use_pager = count > _THRESHOLD_FOR_PAGER |
| + |
| + def write_to_func(write): |
| + write('Showing {:,} results with total size: {:,} bytes\n'.format( |
| + len(group), group.size)) |
| + for s in by_size: |
| + if s.IsGroup(): |
| + write('{} {:<7,} {} ({})\n'.format(s.section, s.size, s.name, len(s))) |
| + else: |
| + template = '{}@0x{:<8x} {:<7} {}\n{:22}{}\n' |
|
estevenson
2017/03/20 14:13:03
This might look a little nicer if the two lines we
agrieve
2017/03/20 19:58:09
Leaving as-is for now, because I'm sure we'll fidd
|
| + write(template.format(s.section, s.address, s.size, |
| + s.path or '<no path>', '', s.name or '<no name>')) |
| + if show_elided and group.filtered_symbols: |
| + elided = group.Inverted() |
| + write('* Filtered out {:,} symbols comprising {:<7,} bytes.\n'.format( |
| + len(elided), elided.size)) |
| + |
| + if use_pager: |
| + with _LessPipe() as write: |
| + write_to_func(write) |
| + else: |
| + write_to_func(sys.stdout.write) |
| + |
| + |
| +def main(): |
| + parser = argparse.ArgumentParser() |
| + parser.add_argument('--query', |
| + help='Print the result of the given snippet. Example: ' |
| + 'all_syms.WhereInSection("d").WhereBiggerThan(100)') |
| + analyze.AddOptions(parser) |
| + helpers.AddCommonOptions(parser) |
| + args = parser.parse_args() |
| + helpers.HandleCommonOptions(args) |
| + |
| + result = analyze.AnalyzeWithArgs(args) |
| + |
| + variables = { |
| + 'Print': _PrintSymbolGroup, |
| + 'all_syms': result.symbol_group, |
| + } |
| + |
| + if args.query: |
| + logging.info('Running query from command-line.') |
| + eval_result = eval(args.query, locals=variables) |
| + if isinstance(eval_result, symbols.SymbolGroup): |
| + _PrintSymbolGroup(eval_result, show_elided=False, use_pager=False) |
| + return |
| + |
| + logging.info('Entering interactive console.') |
| + |
| + print '*' * 80 |
| + print 'Entering interactive Python shell. Here is some inspiration:' |
| + print '# Show two levels of .text, grouped by first two subdirectories' |
| + print 'text_syms = all_syms.WhereInSection("t")' |
| + print 'by_path = text_syms.GroupByPath(depth=2)' |
| + print 'Print(by_path.WhereBiggerThan(1024, include_filtered=True))' |
| + print '# Show all non-vtable generated symbols' |
| + print 'Print(all_syms.WhereNameMatches(r"(?<!vtable)(?<!\[)\]$"))' |
| + print '*' * 80 |
| + print 'locals:', variables.keys() |
| + print 'method quick reference:', ( |
| + [m for m in dir(symbols.SymbolGroup) if m[0].isupper()]) |
| + print '*' * 80 |
| + |
| + # Without initializing readline, arrow keys don't even work! |
| + readline.parse_and_bind('tab: complete') |
| + code.InteractiveConsole(locals=variables).interact() |
| + |
| + |
| +if __name__ == '__main__': |
| + main() |