Commit d20a3819 authored by Samuel Huang's avatar Samuel Huang Committed by Commit Bot

Reland "[SuperSize] Add multiple Container support using .ssargs files."

This reverts commit fc8e613a.

Reason for revert: Fixed the issue.

Original change's description:
> Revert "[SuperSize] Add multiple Container support using .ssargs files."
> 
> This reverts commit 19d7a780.
> 
> Reason for revert: Breaking CQ (see bug)
> 
> Original change's description:
> > [SuperSize] Add multiple Container support using .ssargs files.
> > 
> > This CL adds SuperSize support for multiple containers. Details:
> > * Introduce .ssargs file, which is a text file to specify multiple
> >   containers for SuperSize-archive to process into a single .size file.
> >   * Container file types are auto-detected.
> >   * Container files are relative to .ssargs file location, unless
> >     absolute paths are given.
> > * Introduce Version 1.1 .size file format:
> >   * Header fields contain Container specs, now absorbing metadata and
> >     section_sizes.
> >   * Per-section data become per-container-per-section.
> > * For compatibility: Running SuperSize-archive on one container still
> >   generates Version 1.0 .size file format.
> > * Update SuperSize-console output (including diffs) to accommodate.
> > 
> > After this CL, basic Trichrome support is available. A recommended
> > .ssargs file (3 lines) for the official build is as follows:
> > 
> > TrichromeLibraryGoogle.apk --name Library
> > TrichromeChromeGoogle.minimal.apks --name Chrome --java-only
> > TrichromeWebViewGoogle.apk --name WebView --java-only
> > 
> > Bug: 1040645
> > Change-Id: I67df00731d07660163410b59c57854bf56ea651e
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2248860
> > Commit-Queue: Samuel Huang <huangs@chromium.org>
> > Reviewed-by: Andrew Grieve <agrieve@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#780594}
> 
> TBR=huangs@chromium.org,agrieve@chromium.org
> 
> Change-Id: Ic3175ea3e8ede41e3183c12b5a00a4a09be55a75
> No-Try: True
> Bug: 1040645, 1097572
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2256593
> Reviewed-by: Andrew Grieve <agrieve@chromium.org>
> Commit-Queue: Andrew Grieve <agrieve@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#780639}

TBR=huangs@chromium.org,agrieve@chromium.org

# Not skipping CQ checks because this is a reland.

Bug: 1040645, 1097572
Change-Id: I95a20b271d7522a1aa70cc3247ad9a1fab911dce
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2257279Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Commit-Queue: Samuel Huang <huangs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#780788}
parent 294050d8
This diff is collapsed.
This diff is collapsed.
...@@ -92,6 +92,8 @@ def _MatchSymbols(before, after, key_func, padding_by_section_name): ...@@ -92,6 +92,8 @@ def _MatchSymbols(before, after, key_func, padding_by_section_name):
def _DiffSymbolGroups(before, after): def _DiffSymbolGroups(before, after):
# For changed symbols, padding is zeroed out. In order to not lose the # For changed symbols, padding is zeroed out. In order to not lose the
# information entirely, store it in aggregate. # information entirely, store it in aggregate.
# Ignoring Containers, i.e., paddings from sections across Containers are
# combined.
padding_by_section_name = collections.defaultdict(int) padding_by_section_name = collections.defaultdict(int)
# Usually >90% of symbols are exact matches, so all of the time is spent in # Usually >90% of symbols are exact matches, so all of the time is spent in
...@@ -150,19 +152,25 @@ def _DiffObj(before_obj, after_obj): ...@@ -150,19 +152,25 @@ def _DiffObj(before_obj, after_obj):
def _DiffContainerLists(before_containers, after_containers): def _DiffContainerLists(before_containers, after_containers):
"""Computes diffs between two lists of Containers.""" """Computes diff of Containers lists, matching names."""
# TODO(huangs): Add support for multiple containers (needs name matching). # Find ordered unique names, preferring order of |container_after|.
assert len(before_containers) == 1 pairs = collections.OrderedDict()
assert len(after_containers) == 1 for c in after_containers:
pairs[c.name] = [models.Container.Empty(), c]
for c in before_containers:
if c.name in pairs:
pairs[c.name][0] = c
else:
pairs[c.name] = [c, models.Container.Empty()]
ret = [] ret = []
for (before_c, after_c) in zip(before_containers, after_containers): for name, [before_c, after_c] in pairs.items():
name = after_c.name
assert before_c.name == name
ret.append( ret.append(
models.Container(name=name, models.Container(name=name,
metadata=_DiffObj(before_c.metadata, after_c.metadata), metadata=_DiffObj(before_c.metadata, after_c.metadata),
section_sizes=_DiffObj(before_c.section_sizes, section_sizes=_DiffObj(before_c.section_sizes,
after_c.section_sizes))) after_c.section_sizes)))
# This update newly created diff Containers, not existing ones or EMPTY.
models.Container.AssignShortNames(ret)
return ret return ret
......
...@@ -37,6 +37,7 @@ def _CreateSizeInfo(aliases=None): ...@@ -37,6 +37,7 @@ def _CreateSizeInfo(aliases=None):
containers = [ containers = [
models.Container(name='', metadata=metadata, section_sizes=section_sizes) models.Container(name='', metadata=metadata, section_sizes=section_sizes)
] ]
models.Container.AssignShortNames(containers)
TEXT = models.SECTION_TEXT TEXT = models.SECTION_TEXT
symbols = [ symbols = [
_MakeSym(models.SECTION_DEX_METHOD, 10, 'a', 'com.Foo#bar()'), _MakeSym(models.SECTION_DEX_METHOD, 10, 'a', 'com.Foo#bar()'),
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
"""Deals with loading & saving .size and .sizediff files. """Deals with loading & saving .size and .sizediff files.
The .size file is written in the following format. There are no section The .size file is written in the following format. There are no section
delimiters, instead the end of a section is usually determined by a row count delimiters, instead the end of a section is usually determined by a row count on
on the first line of a section, followed by that number of rows. In other the first line of a section, followed by that number of rows. In other cases,
cases, the sections have a known size. the sections have a known size.
Header Header
------ ------
...@@ -116,7 +116,8 @@ import parallel ...@@ -116,7 +116,8 @@ import parallel
# File format version for .size files. # File format version for .size files.
_SERIALIZATION_VERSION = 'Size File Format v1' _SERIALIZATION_VERSION_SINGLE_CONTAINER = 'Size File Format v1'
_SERIALIZATION_VERSION_MULTI_CONTAINER = 'Size File Format v1.1'
# Header for .sizediff files # Header for .sizediff files
_SIZEDIFF_HEADER = '# Created by //tools/binary_size\nDIFF\n' _SIZEDIFF_HEADER = '# Created by //tools/binary_size\nDIFF\n'
...@@ -171,18 +172,18 @@ def CalculatePadding(raw_symbols): ...@@ -171,18 +172,18 @@ def CalculatePadding(raw_symbols):
# Padding not really required, but it is useful to check for large padding and # Padding not really required, but it is useful to check for large padding and
# log a warning. # log a warning.
# TODO(huangs): Add support for multiple containers: Need to group by seen_container_and_sections = set()
# container names and sections.
seen_sections = set()
for i, symbol in enumerate(raw_symbols[1:]): for i, symbol in enumerate(raw_symbols[1:]):
prev_symbol = raw_symbols[i] prev_symbol = raw_symbols[i]
if symbol.IsOverhead(): if symbol.IsOverhead():
# Overhead symbols are not actionable so should be padding-only. # Overhead symbols are not actionable so should be padding-only.
symbol.padding = symbol.size symbol.padding = symbol.size
if prev_symbol.section_name != symbol.section_name: if (prev_symbol.container.name != symbol.container.name
assert symbol.section_name not in seen_sections, ( or prev_symbol.section_name != symbol.section_name):
'Input symbols must be sorted by section, then address.') container_and_section = (symbol.container.name, symbol.section_name)
seen_sections.add(symbol.section_name) assert container_and_section not in seen_container_and_sections, (
'Input symbols must be sorted by container, section, then address.')
seen_container_and_sections.add(container_and_section)
continue continue
if (symbol.address <= 0 or prev_symbol.address <= 0 if (symbol.address <= 0 or prev_symbol.address <= 0
or not symbol.IsNative() or not prev_symbol.IsNative()): or not symbol.IsNative() or not prev_symbol.IsNative()):
...@@ -248,21 +249,34 @@ def _SaveSizeInfoToFile(size_info, ...@@ -248,21 +249,34 @@ def _SaveSizeInfoToFile(size_info,
else: else:
raw_symbols = size_info.raw_symbols raw_symbols = size_info.raw_symbols
num_containers = len(size_info.containers)
has_multi_containers = (num_containers > 1)
w = _Writer(file_obj) w = _Writer(file_obj)
# Created by supersize header # "Created by SuperSize" header
w.WriteLine('# Created by //tools/binary_size') w.WriteLine('# Created by //tools/binary_size')
w.WriteLine(_SERIALIZATION_VERSION) if has_multi_containers:
w.WriteLine(_SERIALIZATION_VERSION_MULTI_CONTAINER)
else:
w.WriteLine(_SERIALIZATION_VERSION_SINGLE_CONTAINER)
# JSON header fields # JSON header fields
fields = { fields = {
'has_components': True, 'has_components': True,
'has_padding': include_padding, 'has_padding': include_padding,
} }
num_containers = len(size_info.containers)
has_multi_containers = (num_containers > 1)
if has_multi_containers: if has_multi_containers:
raise ValueError('Multiple container not yet supported.') # Write using new format.
assert len(set(c.name for c in size_info.containers)) == num_containers, (
'Container names must be distinct.')
fields['build_config'] = size_info.build_config
fields['containers'] = [{
'name': c.name,
'metadata': c.metadata,
'section_sizes': c.section_sizes,
} for c in size_info.containers]
else: else:
# Write using old format. # Write using old format.
fields['metadata'] = size_info.metadata_legacy fields['metadata'] = size_info.metadata_legacy
...@@ -290,12 +304,18 @@ def _SaveSizeInfoToFile(size_info, ...@@ -290,12 +304,18 @@ def _SaveSizeInfoToFile(size_info,
w.WriteLine(comp) w.WriteLine(comp)
w.LogSize('components') w.LogSize('components')
# Symbol counts by section. # Symbol counts by container and section.
symbol_group_by_section = raw_symbols.GroupedBySectionName() symbol_group_by_section = raw_symbols.GroupedByContainerAndSectionName()
if has_multi_containers: if has_multi_containers:
raise ValueError('Multiple container not yet supported.') container_name_to_index = {
c.name: i
for i, c in enumerate(size_info.containers)
}
w.WriteLine('\t'.join('<%d>%s' %
(container_name_to_index[g.name[0]], g.name[1])
for g in symbol_group_by_section))
else: else:
w.WriteLine('\t'.join(g.name for g in symbol_group_by_section)) w.WriteLine('\t'.join(g.name[1] for g in symbol_group_by_section))
w.WriteLine('\t'.join(str(len(g)) for g in symbol_group_by_section)) w.WriteLine('\t'.join(str(len(g)) for g in symbol_group_by_section))
def gen_delta(gen, prev_value=0): def gen_delta(gen, prev_value=0):
...@@ -384,23 +404,35 @@ def _LoadSizeInfoFromFile(file_obj, size_path): ...@@ -384,23 +404,35 @@ def _LoadSizeInfoFromFile(file_obj, size_path):
""" """
# Split lines on '\n', since '\r' can appear in some lines! # Split lines on '\n', since '\r' can appear in some lines!
lines = io.TextIOWrapper(file_obj, newline='\n') lines = io.TextIOWrapper(file_obj, newline='\n')
_ReadLine(lines) # Line 0: Created by supersize header _ReadLine(lines) # Line 0: "Created by SuperSize" header
actual_version = _ReadLine(lines) actual_version = _ReadLine(lines)
assert actual_version == _SERIALIZATION_VERSION, ( if actual_version == _SERIALIZATION_VERSION_SINGLE_CONTAINER:
'Version mismatch. Need to write some upgrade code.') has_multi_containers = False
elif actual_version == _SERIALIZATION_VERSION_MULTI_CONTAINER:
has_multi_containers = True
else:
raise ValueError('Version mismatch. Need to write some upgrade code.')
# JSON header fields # JSON header fields
json_len = int(_ReadLine(lines)) json_len = int(_ReadLine(lines))
json_str = lines.read(json_len) json_str = lines.read(json_len)
fields = json.loads(json_str) fields = json.loads(json_str)
assert ('containers' in fields) == has_multi_containers
has_multi_containers = False assert ('build_config' in fields) == has_multi_containers
assert ('containers' in fields) == has_multi_containers
assert ('metadata' not in fields) == has_multi_containers
assert ('section_sizes' not in fields) == has_multi_containers
containers = [] containers = []
if has_multi_containers: # New format. if has_multi_containers: # New format.
raise ValueError('Multiple container not yet supported.') build_config = fields['build_config']
else: for cfield in fields['containers']:
# Parse old format, but separate data into build_config and metadata. c = models.Container(name=cfield['name'],
metadata=cfield['metadata'],
section_sizes=cfield['section_sizes'])
containers.append(c)
else: # Old format.
build_config = {} build_config = {}
metadata = fields.get('metadata') metadata = fields.get('metadata')
if metadata: if metadata:
...@@ -413,6 +445,7 @@ def _LoadSizeInfoFromFile(file_obj, size_path): ...@@ -413,6 +445,7 @@ def _LoadSizeInfoFromFile(file_obj, size_path):
models.Container(name='', models.Container(name='',
metadata=metadata, metadata=metadata,
section_sizes=section_sizes)) section_sizes=section_sizes))
models.Container.AssignShortNames(containers)
has_components = fields.get('has_components', False) has_components = fields.get('has_components', False)
has_padding = fields.get('has_padding', False) has_padding = fields.get('has_padding', False)
...@@ -433,7 +466,7 @@ def _LoadSizeInfoFromFile(file_obj, size_path): ...@@ -433,7 +466,7 @@ def _LoadSizeInfoFromFile(file_obj, size_path):
components = [_ReadLine(lines) for _ in range(num_components)] components = [_ReadLine(lines) for _ in range(num_components)]
# Symbol counts by section. # Symbol counts by section.
section_names = _ReadValuesFromLine(lines, split='\t') container_and_section_names = _ReadValuesFromLine(lines, split='\t')
symbol_counts = [int(c) for c in _ReadValuesFromLine(lines, split='\t')] symbol_counts = [int(c) for c in _ReadValuesFromLine(lines, split='\t')]
# Addresses, sizes, paddings, path indices, component indices # Addresses, sizes, paddings, path indices, component indices
...@@ -460,23 +493,28 @@ def _LoadSizeInfoFromFile(file_obj, size_path): ...@@ -460,23 +493,28 @@ def _LoadSizeInfoFromFile(file_obj, size_path):
if has_padding: if has_padding:
paddings = read_numeric(delta=False) paddings = read_numeric(delta=False)
else: else:
paddings = [None] * len(section_names) paddings = [None] * len(container_and_section_names)
path_indices = read_numeric(delta=True) path_indices = read_numeric(delta=True)
if has_components: if has_components:
component_indices = read_numeric(delta=True) component_indices = read_numeric(delta=True)
else: else:
component_indices = [None] * len(section_names) component_indices = [None] * len(container_and_section_names)
raw_symbols = [None] * sum(symbol_counts) raw_symbols = [None] * sum(symbol_counts)
symbol_idx = 0 symbol_idx = 0
for (cur_section_name, cur_symbol_count, cur_addresses, cur_sizes, for (cur_container_and_section_name, cur_symbol_count, cur_addresses,
cur_paddings, cur_path_indices, cur_sizes, cur_paddings, cur_path_indices,
cur_component_indices) in zip(section_names, symbol_counts, addresses, cur_component_indices) in zip(container_and_section_names, symbol_counts,
sizes, paddings, path_indices, addresses, sizes, paddings, path_indices,
component_indices): component_indices):
if has_multi_containers: if has_multi_containers:
raise ValueError('Multiple container not yet supported.') # Extract '<cur_container_idx_str>cur_section_name'.
assert cur_container_and_section_name.startswith('<')
cur_container_idx_str, cur_section_name = (
cur_container_and_section_name[1:].split('>', 1))
cur_container = containers[int(cur_container_idx_str)]
else: else:
cur_section_name = cur_container_and_section_name
cur_container = containers[0] cur_container = containers[0]
alias_counter = 0 alias_counter = 0
for i in range(cur_symbol_count): for i in range(cur_symbol_count):
......
...@@ -309,7 +309,10 @@ class IntegrationTest(unittest.TestCase): ...@@ -309,7 +309,10 @@ class IntegrationTest(unittest.TestCase):
merged_data_desc = describe.DescribeDict(size_info.metadata_legacy) merged_data_desc = describe.DescribeDict(size_info.metadata_legacy)
return itertools.chain(merged_data_desc, stats, sym_strs) return itertools.chain(merged_data_desc, stats, sym_strs)
else: else:
raise ValueError('Multiple container not yet supported.') build_config = describe.DescribeDict(size_info.build_config)
metadata = itertools.chain.from_iterable(
describe.DescribeDict(c.metadata) for c in size_info.containers)
return itertools.chain(build_config, metadata, stats, sym_strs)
@_CompareWithGolden() @_CompareWithGolden()
def test_Archive(self): def test_Archive(self):
......
...@@ -102,6 +102,8 @@ PAK_SECTIONS = ( ...@@ -102,6 +102,8 @@ PAK_SECTIONS = (
SECTION_PAK_TRANSLATIONS, SECTION_PAK_TRANSLATIONS,
) )
CONTAINER_MULTIPLE = '*'
SECTION_NAME_TO_SECTION = { SECTION_NAME_TO_SECTION = {
SECTION_BSS: 'b', SECTION_BSS: 'b',
SECTION_BSS_REL_RO: 'b', SECTION_BSS_REL_RO: 'b',
...@@ -201,16 +203,19 @@ def ClassifySections(section_names): ...@@ -201,16 +203,19 @@ def ClassifySections(section_names):
class Container(object): class Container(object):
"""Info for a a single input file for SuperSize, e.g., an APK file. """Info for a single SuperSize input file (e.g., APK file).
Fields: Fields:
name: Container name. Must be unique among containers, and can be ''. name: Container name. Must be unique among containers, and can be ''.
short_name: Short container name for compact display. This, also needs to be
unique among containers in the same SizeInfo, and can be ''.
metadata: A dict. metadata: A dict.
section_sizes: A dict of section_name -> size. section_sizes: A dict of section_name -> size.
classified_sections: Cache for ClassifySections(). classified_sections: Cache for ClassifySections().
""" """
__slots__ = ( __slots__ = (
'name', 'name',
'short_name',
'metadata', 'metadata',
'section_sizes', 'section_sizes',
'_classified_sections', '_classified_sections',
...@@ -220,15 +225,31 @@ class Container(object): ...@@ -220,15 +225,31 @@ class Container(object):
# name == '' hints that only one container exists, and there's no need to # name == '' hints that only one container exists, and there's no need to
# distinguish them. This can affect console output. # distinguish them. This can affect console output.
self.name = name self.name = name
self.short_name = None # Assigned by AssignShortNames().
self.metadata = metadata or {} self.metadata = metadata or {}
self.section_sizes = section_sizes # E.g. {SECTION_TEXT: 0} self.section_sizes = section_sizes # E.g. {SECTION_TEXT: 0}
self._classified_sections = None self._classified_sections = None
@staticmethod
def AssignShortNames(containers):
for i, c in enumerate(containers):
c.short_name = str(i) if c.name else ''
def ClassifySections(self): def ClassifySections(self):
if not self._classified_sections: if not self._classified_sections:
self._classified_sections = ClassifySections(self.section_sizes.keys()) self._classified_sections = ClassifySections(self.section_sizes.keys())
return self._classified_sections return self._classified_sections
@staticmethod
def Empty():
"""Returns a placeholder Container that should be read-only.
For simplicity, we're not enforcing read-only checks (frozenmap does not
exist, unfortunately). Creating a new instance instead of using a global
singleton for robustness.
"""
return Container(name='(empty)', metadata={}, section_sizes={})
class BaseSizeInfo(object): class BaseSizeInfo(object):
"""Base class for SizeInfo and DeltaSizeInfo. """Base class for SizeInfo and DeltaSizeInfo.
...@@ -261,6 +282,7 @@ class BaseSizeInfo(object): ...@@ -261,6 +282,7 @@ class BaseSizeInfo(object):
self._symbols = symbols self._symbols = symbols
self._native_symbols = None self._native_symbols = None
self._pak_symbols = None self._pak_symbols = None
Container.AssignShortNames(self.containers)
@property @property
def symbols(self): def symbols(self):
...@@ -296,6 +318,9 @@ class BaseSizeInfo(object): ...@@ -296,6 +318,9 @@ class BaseSizeInfo(object):
def metadata(self): def metadata(self):
return [c.metadata for c in self.containers] return [c.metadata for c in self.containers]
def ContainerForName(self, name, default=None):
return next((c for c in containers if c.name == name), default)
class SizeInfo(BaseSizeInfo): class SizeInfo(BaseSizeInfo):
"""Represents all size information for a single binary. """Represents all size information for a single binary.
...@@ -362,6 +387,10 @@ class BaseSymbol(object): ...@@ -362,6 +387,10 @@ class BaseSymbol(object):
def container_name(self): def container_name(self):
return self.container.name if self.container else '' return self.container.name if self.container else ''
@property
def container_short_name(self):
return self.container.short_name if self.container else ''
@property @property
def section(self): def section(self):
"""Returns the one-letter section.""" """Returns the one-letter section."""
...@@ -516,14 +545,17 @@ class Symbol(BaseSymbol): ...@@ -516,14 +545,17 @@ class Symbol(BaseSymbol):
self.component = '' self.component = ''
def __repr__(self): def __repr__(self):
# TODO(huangs): If container name is nonempty then display it. if self.container and self.container.name:
template = ('{}@{:x}(size_without_padding={},padding={},full_name={},' container_str = '<{}>'.format(self.container.name)
else:
container_str = ''
template = ('{}{}@{:x}(size_without_padding={},padding={},full_name={},'
'object_path={},source_path={},flags={},num_aliases={},' 'object_path={},source_path={},flags={},num_aliases={},'
'component={})') 'component={})')
return template.format( return template.format(container_str, self.section_name, self.address,
self.section_name, self.address, self.size_without_padding, self.size_without_padding, self.padding,
self.padding, self.full_name, self.object_path, self.source_path, self.full_name, self.object_path, self.source_path,
self.FlagsString(), self.num_aliases, self.component) self.FlagsString(), self.num_aliases, self.component)
def SetName(self, full_name, template_name=None, name=None): def SetName(self, full_name, template_name=None, name=None):
# Note that _NormalizeNames() will clobber these values. # Note that _NormalizeNames() will clobber these values.
...@@ -562,14 +594,24 @@ class DeltaSymbol(BaseSymbol): ...@@ -562,14 +594,24 @@ class DeltaSymbol(BaseSymbol):
self.after_symbol = after_symbol self.after_symbol = after_symbol
def __repr__(self): def __repr__(self):
# TODO(huangs): If container name is nonempty then display it. before_container_name = (self.before_symbol.container_name
template = ('{}{}@{:x}(size_without_padding={},padding={},full_name={},' if self.before_symbol else None)
after_container_name = (self.after_symbol.container_name
if self.after_symbol else None)
if after_container_name:
if before_container_name != after_container_name:
container_str = '<~{}>'.format(after_container_name)
else:
container_str = '<{}>'.format(after_container_name)
else: # None or ''.
container_str = ''
template = ('{}{}{}@{:x}(size_without_padding={},padding={},full_name={},'
'object_path={},source_path={},flags={})') 'object_path={},source_path={},flags={})')
return template.format( return template.format(DIFF_PREFIX_BY_STATUS[self.diff_status],
DIFF_PREFIX_BY_STATUS[self.diff_status], self.section_name, container_str, self.section_name, self.address,
self.address, self.size_without_padding, self.padding, self.size_without_padding, self.padding,
self.full_name, self.object_path, self.source_path, self.full_name, self.object_path, self.source_path,
self.FlagsString()) self.FlagsString())
def IsDelta(self): def IsDelta(self):
return True return True
...@@ -712,11 +754,11 @@ class SymbolGroup(BaseSymbol): ...@@ -712,11 +754,11 @@ class SymbolGroup(BaseSymbol):
'full_name', 'full_name',
'template_name', 'template_name',
'name', 'name',
'container_name',
'section_name', 'section_name',
'is_default_sorted', # True for groups created by Sorted() 'is_default_sorted', # True for groups created by Sorted()
) )
# template_name and full_name are useful when clustering symbol clones. # template_name and full_name are useful when clustering symbol clones.
def __init__(self, def __init__(self,
symbols, symbols,
...@@ -735,8 +777,6 @@ class SymbolGroup(BaseSymbol): ...@@ -735,8 +777,6 @@ class SymbolGroup(BaseSymbol):
self.full_name = full_name if full_name is not None else name self.full_name = full_name if full_name is not None else name
self.template_name = template_name if template_name is not None else name self.template_name = template_name if template_name is not None else name
self.name = name or '' self.name = name or ''
# TODO(huangs): Add support for multiple containers.
self.container_name = ''
self.section_name = section_name or SECTION_MULTIPLE self.section_name = section_name or SECTION_MULTIPLE
self.is_default_sorted = is_default_sorted self.is_default_sorted = is_default_sorted
...@@ -783,6 +823,20 @@ class SymbolGroup(BaseSymbol): ...@@ -783,6 +823,20 @@ class SymbolGroup(BaseSymbol):
def index(self, item): def index(self, item):
return self._symbols.index(item) return self._symbols.index(item)
@property
def container_name(self):
ret = set(s.container_name for s in self._symbols)
if ret:
return CONTAINER_MULTIPLE if len(ret) > 1 else (ret.pop() or '')
return ''
@property
def container_short_name(self):
ret = set(s.container_short_name for s in self._symbols)
if ret:
return CONTAINER_MULTIPLE if len(ret) > 1 else (ret.pop() or '')
return ''
@property @property
def address(self): def address(self):
first = self._symbols[0].address if self else 0 first = self._symbols[0].address if self else 0
...@@ -938,13 +992,23 @@ class SymbolGroup(BaseSymbol): ...@@ -938,13 +992,23 @@ class SymbolGroup(BaseSymbol):
def WherePssBiggerThan(self, min_pss): def WherePssBiggerThan(self, min_pss):
return self.Filter(lambda s: s.pss >= min_pss) return self.Filter(lambda s: s.pss >= min_pss)
def WhereInSection(self, section): def WhereInSection(self, section, container=None):
"""|section| can be section_name ('.bss'), or section chars ('bdr').""" """|section| can be section_name ('.bss'), or section chars ('bdr')."""
if section.startswith('.'): if section.startswith('.'):
ret = self.Filter(lambda s: s.section_name == section) if container:
short_name = container.short_name
ret = self.Filter(lambda s: (s.container.short_name == short_name and s.
section_name == section))
else:
ret = self.Filter(lambda s: s.section_name == section)
ret.section_name = section ret.section_name = section
else: else:
ret = self.Filter(lambda s: s.section in section) if container:
short_name = container.short_name
ret = self.Filter(lambda s: (s.container.short_name == short_name and s.
section in section))
else:
ret = self.Filter(lambda s: s.section in section)
if section in SECTION_TO_SECTION_NAME: if section in SECTION_TO_SECTION_NAME:
ret.section_name = SECTION_TO_SECTION_NAME[section] ret.section_name = SECTION_TO_SECTION_NAME[section]
return ret return ret
...@@ -1193,6 +1257,9 @@ class SymbolGroup(BaseSymbol): ...@@ -1193,6 +1257,9 @@ class SymbolGroup(BaseSymbol):
lambda s: (same_name_only and s.full_name, id(s.aliases or s)), lambda s: (same_name_only and s.full_name, id(s.aliases or s)),
min_count=min_count, group_factory=group_factory) min_count=min_count, group_factory=group_factory)
def GroupedByContainerAndSectionName(self):
return self.GroupedBy(lambda s: (s.container_name, s.section_name))
def GroupedBySectionName(self): def GroupedBySectionName(self):
return self.GroupedBy(lambda s: s.section_name) return self.GroupedBy(lambda s: s.section_name)
......
******************************************************************************** ********************************************************************************
Entering interactive Python shell. Quick reference: Entering interactive Python shell. Quick reference:
SizeInfo: all_section_sizes, build_config, containers, metadata, metadata_legacy, native_symbols, pak_symbols, raw_symbols, size_path, symbols SizeInfo: ContainerForName, all_section_sizes, build_config, containers, metadata, metadata_legacy, native_symbols, pak_symbols, raw_symbols, size_path, symbols
Symbol: FlagsString, IsBss, IsDelta, IsDex, IsGeneratedByToolchain, IsGroup, IsNameUnique, IsNative, IsOther, IsOverhead, IsPak, IsStringLiteral, IterLeafSymbols, SetName, address, aliases, component, container, container_name, end_address, flags, full_name, generated_source, is_anonymous, name, num_aliases, object_path, padding, padding_pss, pss, pss_without_padding, section, section_name, size, size_without_padding, source_path, template_name Symbol: FlagsString, IsBss, IsDelta, IsDex, IsGeneratedByToolchain, IsGroup, IsNameUnique, IsNative, IsOther, IsOverhead, IsPak, IsStringLiteral, IterLeafSymbols, SetName, address, aliases, component, container, container_name, container_short_name, end_address, flags, full_name, generated_source, is_anonymous, name, num_aliases, object_path, padding, padding_pss, pss, pss_without_padding, section, section_name, size, size_without_padding, source_path, template_name
SymbolGroup (extends Symbol): CountUniqueSymbols, Filter, GroupedBy, GroupedByAliases, GroupedByComponent, GroupedByFullName, GroupedByName, GroupedByPath, GroupedBySectionName, Inverted, IterUniqueSymbols, Sorted, SortedByAddress, SortedByCount, SortedByName, WhereAddressInRange, WhereComponentMatches, WhereFullNameMatches, WhereGeneratedByToolchain, WhereHasAnyAttribution, WhereHasComponent, WhereHasFlag, WhereHasPath, WhereInSection, WhereIsDex, WhereIsGroup, WhereIsNative, WhereIsPak, WhereIsTemplate, WhereMatches, WhereNameMatches, WhereObjectPathMatches, WherePathMatches, WherePssBiggerThan, WhereSizeBiggerThan, WhereSourceIsGenerated, WhereSourcePathMatches, WhereTemplateNameMatches, index, is_default_sorted SymbolGroup (extends Symbol): CountUniqueSymbols, Filter, GroupedBy, GroupedByAliases, GroupedByComponent, GroupedByContainerAndSectionName, GroupedByFullName, GroupedByName, GroupedByPath, GroupedBySectionName, Inverted, IterUniqueSymbols, Sorted, SortedByAddress, SortedByCount, SortedByName, WhereAddressInRange, WhereComponentMatches, WhereFullNameMatches, WhereGeneratedByToolchain, WhereHasAnyAttribution, WhereHasComponent, WhereHasFlag, WhereHasPath, WhereInSection, WhereIsDex, WhereIsGroup, WhereIsNative, WhereIsPak, WhereIsTemplate, WhereMatches, WhereNameMatches, WhereObjectPathMatches, WherePathMatches, WherePssBiggerThan, WhereSizeBiggerThan, WhereSourceIsGenerated, WhereSourcePathMatches, WhereTemplateNameMatches, index, is_default_sorted
DeltaSizeInfo: after, all_section_sizes, before, build_config, containers, metadata, native_symbols, pak_symbols, raw_symbols, symbols DeltaSizeInfo: ContainerForName, after, all_section_sizes, before, build_config, containers, metadata, native_symbols, pak_symbols, raw_symbols, symbols
DeltaSymbol (extends Symbol): after_symbol, before_symbol, diff_status DeltaSymbol (extends Symbol): after_symbol, before_symbol, diff_status
DeltaSymbolGroup (extends SymbolGroup): CountsByDiffStatus, WhereDiffStatusIs, diff_status DeltaSymbolGroup (extends SymbolGroup): CountsByDiffStatus, WhereDiffStatusIs, diff_status
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment