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