| 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.')
|
|
|