Index: tools/binary_size/libsupersize/console.py |
diff --git a/tools/binary_size/libsupersize/console.py b/tools/binary_size/libsupersize/console.py |
index f019564557333c337a84594bc8f2436226ef2484..1e87f44416577c38177b03c8bea61c15832b71c8 100644 |
--- a/tools/binary_size/libsupersize/console.py |
+++ b/tools/binary_size/libsupersize/console.py |
@@ -66,7 +66,7 @@ def _WriteToStream(lines, use_pager=None, to_file=None): |
class _Session(object): |
_readline_initialized = False |
- def __init__(self, size_infos, lazy_paths): |
+ def __init__(self, size_infos, size_paths, lazy_paths): |
self._variables = { |
'Print': self._PrintFunc, |
'Diff': self._DiffFunc, |
@@ -77,6 +77,8 @@ class _Session(object): |
} |
self._lazy_paths = lazy_paths |
self._size_infos = size_infos |
+ self._size_paths = size_paths |
+ self._disassemble_prefix_len = None |
if len(size_infos) == 1: |
self._variables['size_info'] = size_infos[0] |
@@ -119,23 +121,71 @@ class _Session(object): |
lines = describe.GenerateLines(obj, verbose=verbose, recursive=recursive) |
_WriteToStream(lines, use_pager=use_pager, to_file=to_file) |
- def _ElfPathForSymbol(self, symbol): |
+ def _ElfPathAndToolPrefixForSymbol(self, symbol, elf_path, tool_prefix): |
size_info = None |
- for size_info in self._size_infos: |
+ size_path = None |
+ for size_info, size_path in zip(self._size_infos, self._size_paths): |
if symbol in size_info.symbols: |
break |
else: |
- assert False, 'Symbol does not belong to a size_info.' |
- |
- filename = size_info.metadata.get(models.METADATA_ELF_FILENAME) |
- output_dir = self._lazy_paths.output_directory or '' |
- path = os.path.normpath(os.path.join(output_dir, filename)) |
- |
- found_build_id = archive.BuildIdFromElf(path, self._lazy_paths.tool_prefix) |
+ # If symbols is from a diff(), use its address+name to find it. |
+ for size_info, size_path in zip(self._size_infos, self._size_paths): |
+ matched = size_info.symbols.WhereAddressInRange(symbol.address) |
+ # Use last matched symbol to skip over padding-only symbols. |
+ if len(matched) > 0 and matched[-1].full_name == symbol.full_name: |
+ symbol = matched[-1] |
+ break |
+ else: |
+ assert False, 'Symbol does not belong to a size_info.' |
+ |
+ orig_tool_prefix = size_info.metadata.get(models.METADATA_TOOL_PREFIX) |
+ if orig_tool_prefix: |
+ orig_tool_prefix = paths.FromSrcRootRelative(orig_tool_prefix) |
+ if os.path.exists(orig_tool_prefix + 'objdump'): |
+ tool_prefix = orig_tool_prefix |
+ |
+ # TODO(agrieve): Would be even better to use objdump --info to check that |
+ # the toolchain is for the correct architecture. |
+ assert tool_prefix is not None, ( |
+ 'Could not determine --tool-prefix. Possible fixes include setting ' |
+ '--tool-prefix, or setting --output-directory') |
+ |
+ if elf_path is None: |
+ filename = size_info.metadata.get(models.METADATA_ELF_FILENAME) |
+ output_dir = self._lazy_paths.output_directory |
+ size_path = self._size_paths[self._size_infos.index(size_info)] |
+ if output_dir: |
+ # Local build: File is located in output directory. |
+ path = os.path.normpath(os.path.join(output_dir, filename)) |
+ if not output_dir or not os.path.exists(path): |
+ # Downloaded build: File is located beside .size file. |
+ path = os.path.normpath(os.path.join( |
+ os.path.dirname(size_path), os.path.basename(filename))) |
+ |
+ assert os.path.exists(path), ( |
+ 'Could locate ELF file. If binary was built locally, ensure ' |
+ '--output-directory is set. If output directory is unavailable, ' |
+ 'ensure {} is located beside {}, or pass its path explicitly using ' |
+ 'elf_path=').format(os.path.basename(filename), size_path) |
+ |
+ found_build_id = archive.BuildIdFromElf(path, tool_prefix) |
expected_build_id = size_info.metadata.get(models.METADATA_ELF_BUILD_ID) |
assert found_build_id == expected_build_id, ( |
'Build ID does not match for %s' % path) |
- return path |
+ return path, tool_prefix |
+ |
+ def _DetectDisassemblePrefixLen(self, args): |
+ # Look for a line that looks like: |
+ # /usr/{snip}/src/out/Release/../../net/quic/core/quic_time.h:100 |
+ output = subprocess.check_output(args) |
+ for line in output.splitlines(): |
+ if line and line[0] == os.path.sep and line[-1].isdigit(): |
+ release_idx = line.find('Release') |
+ if release_idx == -1: |
+ break |
+ return line.count(os.path.sep, 0, release_idx) |
+ logging.warning('Found no source paths in objdump output.') |
+ return None |
def _DisassembleFunc(self, symbol, elf_path=None, use_pager=None, |
to_file=None): |
@@ -147,13 +197,34 @@ class _Session(object): |
when auto-detection fails. |
""" |
assert symbol.address and symbol.section_name == '.text' |
- if not elf_path: |
- elf_path = self._ElfPathForSymbol(symbol) |
+ |
tool_prefix = self._lazy_paths.tool_prefix |
+ if not elf_path: |
+ elf_path, tool_prefix = self._ElfPathAndToolPrefixForSymbol( |
+ symbol, elf_path, tool_prefix) |
+ |
args = [tool_prefix + 'objdump', '--disassemble', '--source', |
'--line-numbers', '--demangle', |
'--start-address=0x%x' % symbol.address, |
'--stop-address=0x%x' % symbol.end_address, elf_path] |
+ if self._disassemble_prefix_len is None: |
+ prefix_len = self._DetectDisassemblePrefixLen(args) |
+ if prefix_len is not None: |
+ self._disassemble_prefix_len = prefix_len |
+ |
+ if self._disassemble_prefix_len is not None: |
+ output_directory = self._lazy_paths.output_directory |
+ # Only matters for non-generated paths, so be lenient here. |
+ if output_directory is None: |
+ output_directory = os.path.join(paths.SRC_ROOT, 'out', 'Release') |
+ if not os.path.exists(output_directory): |
+ os.makedirs(output_directory) |
+ |
+ args += [ |
+ '--prefix-strip', str(self._disassemble_prefix_len), |
+ '--prefix', os.path.normpath(os.path.relpath(output_directory)) |
+ ] |
+ |
proc = subprocess.Popen(args, stdout=subprocess.PIPE) |
lines = itertools.chain(('Showing disassembly for %r' % symbol, |
'Command: %s' % ' '.join(args)), |
@@ -174,7 +245,7 @@ class _Session(object): |
'# Show two levels of .text, grouped by first two subdirectories', |
'text_syms = size_info.symbols.WhereInSection("t")', |
'by_path = text_syms.GroupedByPath(depth=2)', |
- 'Print(by_path.WhereBiggerThan(1024))', |
+ 'Print(by_path.WherePssBiggerThan(1024))', |
'', |
'# Show all non-vtable generated symbols', |
'generated_syms = size_info.symbols.WhereGeneratedByToolchain()', |
@@ -276,7 +347,7 @@ def Run(args, parser): |
lazy_paths = paths.LazyPaths(tool_prefix=args.tool_prefix, |
output_directory=args.output_directory, |
any_path_within_output_directory=args.inputs[0]) |
- session = _Session(size_infos, lazy_paths) |
+ session = _Session(size_infos, args.inputs, lazy_paths) |
if args.query: |
logging.info('Running query from command-line.') |