Index: tools/binary_size/libsupersize/cluster_symbols.py |
diff --git a/tools/binary_size/libsupersize/cluster_symbols.py b/tools/binary_size/libsupersize/cluster_symbols.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ae85c1b844c85490de062e3294406126afea80a8 |
--- /dev/null |
+++ b/tools/binary_size/libsupersize/cluster_symbols.py |
@@ -0,0 +1,104 @@ |
+# 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. |
+ |
+"""Logic for clustering similar symbols.""" |
+ |
+import collections |
+import logging |
+import re |
+ |
+ |
+# Refer to models.SymbolGroup.Cluster() for pydoc |
+def ClusterSymbols(symbols): |
+ # http://unix.stackexchange.com/questions/223013/function-symbol-gets-part-suffix-after-compilation |
+ # Example name suffixes: |
+ # [clone .part.322] # GCC |
+ # [clone .isra.322] # GCC |
+ # [clone .constprop.1064] # GCC |
+ # [clone .11064] # clang |
+ |
+ # Step 1: Create name map, find clones, collect star syms into replacements. |
+ logging.debug('Creating name -> symbol map') |
+ clone_indices = [] |
+ indices_by_full_name = {} |
+ # (name, full_name) -> [(index, sym),...] |
+ replacements_by_name = collections.defaultdict(list) |
+ for i, symbol in enumerate(symbols): |
+ if symbol.name.startswith('**'): |
+ # "symbol gap 3" -> "symbol gaps" |
+ name = re.sub(r'\s+\d+$', 's', symbol.name) |
+ replacements_by_name[(name, None)].append((i, symbol)) |
+ elif symbol.full_name: |
+ if symbol.full_name.endswith(']') and ' [clone ' in symbol.full_name: |
+ clone_indices.append(i) |
+ else: |
+ indices_by_full_name[symbol.full_name] = i |
+ |
+ # Step 2: Collect same-named clone symbols. |
+ logging.debug('Grouping all clones') |
+ group_names_by_index = {} |
+ for i in clone_indices: |
+ symbol = symbols[i] |
+ # Multiple attributes could exist, so search from left-to-right. |
+ stripped_name = symbol.name[:symbol.name.index(' [clone ')] |
+ stripped_full_name = symbol.full_name[:symbol.full_name.index(' [clone ')] |
+ name_tup = (stripped_name, stripped_full_name) |
+ replacement_list = replacements_by_name[name_tup] |
+ |
+ if not replacement_list: |
+ # First occurance, check for non-clone symbol. |
+ non_clone_idx = indices_by_full_name.get(stripped_name) |
+ if non_clone_idx is not None: |
+ non_clone_symbol = symbols[non_clone_idx] |
+ replacement_list.append((non_clone_idx, non_clone_symbol)) |
+ group_names_by_index[non_clone_idx] = stripped_name |
+ |
+ replacement_list.append((i, symbol)) |
+ group_names_by_index[i] = stripped_name |
+ |
+ # Step 3: Undo clustering when length=1. |
+ # Removing these groups means Diff() logic must know about [clone] suffix. |
+ to_clear = [] |
+ for name_tup, replacement_list in replacements_by_name.iteritems(): |
+ if len(replacement_list) == 1: |
+ to_clear.append(name_tup) |
+ for name_tup in to_clear: |
+ del replacements_by_name[name_tup] |
+ |
+ # Step 4: Replace first symbol from each cluster with a SymbolGroup. |
+ before_symbol_count = sum(len(x) for x in replacements_by_name.itervalues()) |
+ logging.debug('Creating %d symbol groups from %d symbols. %d clones had only ' |
+ 'one symbol.', len(replacements_by_name), before_symbol_count, |
+ len(to_clear)) |
+ |
+ len_delta = len(replacements_by_name) - before_symbol_count |
+ grouped_symbols = [None] * (len(symbols) + len_delta) |
+ dest_index = 0 |
+ src_index = 0 |
+ seen_names = set() |
+ replacement_names_by_index = {} |
+ for name_tup, replacement_list in replacements_by_name.iteritems(): |
+ for tup in replacement_list: |
+ replacement_names_by_index[tup[0]] = name_tup |
+ |
+ sorted_items = replacement_names_by_index.items() |
+ sorted_items.sort(key=lambda tup: tup[0]) |
+ for index, name_tup in sorted_items: |
+ count = index - src_index |
+ grouped_symbols[dest_index:dest_index + count] = ( |
+ symbols[src_index:src_index + count]) |
+ src_index = index + 1 |
+ dest_index += count |
+ if name_tup not in seen_names: |
+ seen_names.add(name_tup) |
+ group_symbols = [tup[1] for tup in replacements_by_name[name_tup]] |
+ grouped_symbols[dest_index] = symbols._CreateTransformed( |
+ group_symbols, name=name_tup[0], full_name=name_tup[1], |
+ section_name=group_symbols[0].section_name) |
+ dest_index += 1 |
+ |
+ assert len(grouped_symbols[dest_index:None]) == len(symbols[src_index:None]) |
+ grouped_symbols[dest_index:None] = symbols[src_index:None] |
+ logging.debug('Finished clustering symbols.') |
+ return grouped_symbols |