OLD | NEW |
| (Empty) |
1 # Copyright 2017 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Logic for clustering similar symbols.""" | |
6 | |
7 import collections | |
8 import logging | |
9 import re | |
10 | |
11 import function_signature | |
12 | |
13 | |
14 def _StripCloneSuffix(name): | |
15 # Multiple attributes could exist, so search from left-to-right. | |
16 idx = name.find(' [clone ') | |
17 if idx != -1: | |
18 return name[:idx] | |
19 return name | |
20 | |
21 | |
22 # Refer to models.SymbolGroup.Clustered() for pydoc | |
23 # TODO(agrieve): This logic should likely be combined with | |
24 # SymbolGroup.GroupedBy(), as it conceptually does the same thing. | |
25 # One could also thing of this as GroupedByFullName(). | |
26 def ClusterSymbols(symbols): | |
27 # http://unix.stackexchange.com/questions/223013/function-symbol-gets-part-suf
fix-after-compilation | |
28 # Example name suffixes: | |
29 # [clone .part.322] # GCC | |
30 # [clone .isra.322] # GCC | |
31 # [clone .constprop.1064] # GCC | |
32 # [clone .11064] # clang | |
33 | |
34 # Step 1: Create name map, find clones, collect star syms into replacements. | |
35 logging.debug('Creating name -> symbol map') | |
36 clone_indices = [] | |
37 indices_by_full_name = {} | |
38 # (section_name, full_name_no_attr) -> [(index, sym),...] | |
39 replacements_by_tup = collections.defaultdict(list) | |
40 for i, symbol in enumerate(symbols): | |
41 name = symbol.full_name | |
42 if not name: | |
43 continue | |
44 if name.startswith('*'): | |
45 # "symbol gap 3" -> "symbol gaps" | |
46 name = re.sub(r'\s+\d+( \(.*\))?$', 's', name) | |
47 replacements_by_tup[(symbol.section_name, name)].append((i, symbol)) | |
48 elif name.endswith(']') and ' [clone ' in name: | |
49 clone_indices.append(i) | |
50 else: | |
51 indices_by_full_name[name] = i | |
52 | |
53 # Step 2: Collect same-named clone symbols. | |
54 logging.debug('Grouping all clones') | |
55 group_names_by_index = {} | |
56 for i in clone_indices: | |
57 symbol = symbols[i] | |
58 stripped_full_name = _StripCloneSuffix(symbol.full_name) | |
59 name_tup = (symbol.section_name, stripped_full_name) | |
60 replacement_list = replacements_by_tup[name_tup] | |
61 | |
62 if not replacement_list: | |
63 # First occurance, check for non-clone symbol. | |
64 non_clone_idx = indices_by_full_name.get(stripped_full_name) | |
65 if non_clone_idx is not None: | |
66 non_clone_symbol = symbols[non_clone_idx] | |
67 replacement_list.append((non_clone_idx, non_clone_symbol)) | |
68 group_names_by_index[non_clone_idx] = stripped_full_name | |
69 | |
70 replacement_list.append((i, symbol)) | |
71 group_names_by_index[i] = stripped_full_name | |
72 | |
73 # Step 3: Undo clustering when length=1. | |
74 # Removing these groups means Diff() logic must know about [clone] suffix. | |
75 to_clear = [] | |
76 for name_tup, replacement_list in replacements_by_tup.iteritems(): | |
77 if len(replacement_list) == 1: | |
78 to_clear.append(name_tup) | |
79 for name_tup in to_clear: | |
80 del replacements_by_tup[name_tup] | |
81 | |
82 # Step 4: Replace first symbol from each cluster with a SymbolGroup. | |
83 before_symbol_count = sum(len(x) for x in replacements_by_tup.itervalues()) | |
84 logging.debug('Creating %d symbol groups from %d symbols. %d clones had only ' | |
85 'one symbol.', len(replacements_by_tup), before_symbol_count, | |
86 len(to_clear)) | |
87 | |
88 len_delta = len(replacements_by_tup) - before_symbol_count | |
89 grouped_symbols = [None] * (len(symbols) + len_delta) | |
90 dest_index = 0 | |
91 src_index = 0 | |
92 seen_tups = set() | |
93 index_and_name_tups = [] | |
94 for name_tup, replacement_list in replacements_by_tup.iteritems(): | |
95 for symbol_tup in replacement_list: | |
96 index_and_name_tups.append((symbol_tup[0], name_tup)) | |
97 | |
98 index_and_name_tups.sort(key=lambda tup: tup[0]) | |
99 for index, name_tup in index_and_name_tups: | |
100 count = index - src_index | |
101 grouped_symbols[dest_index:dest_index + count] = ( | |
102 symbols[src_index:src_index + count]) | |
103 src_index = index + 1 | |
104 dest_index += count | |
105 if name_tup not in seen_tups: | |
106 seen_tups.add(name_tup) | |
107 group_symbols = [tup[1] for tup in replacements_by_tup[name_tup]] | |
108 section_name, stripped_full_name = name_tup | |
109 if stripped_full_name.startswith('*'): | |
110 stripped_template_name = stripped_full_name | |
111 stripped_name = stripped_full_name | |
112 else: | |
113 stripped_template_name = _StripCloneSuffix( | |
114 group_symbols[0].template_name) | |
115 stripped_name = _StripCloneSuffix(group_symbols[0].name) | |
116 cluster = symbols._CreateTransformed( | |
117 group_symbols, full_name=stripped_full_name, | |
118 template_name=stripped_template_name, name=stripped_name, | |
119 section_name=section_name) | |
120 function_signature.InternSameNames(cluster) | |
121 grouped_symbols[dest_index] = cluster | |
122 dest_index += 1 | |
123 | |
124 assert len(grouped_symbols[dest_index:None]) == len(symbols[src_index:None]) | |
125 grouped_symbols[dest_index:None] = symbols[src_index:None] | |
126 logging.debug('Finished clustering symbols.') | |
127 return grouped_symbols | |
OLD | NEW |