OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (c) 2016 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 """ | |
6 This script parses the /verbose output from the VC++ linker and uses it to | |
7 explain why a particular object file is being linked in. It parses records | |
8 like these: | |
9 | |
10 Found "public: static void * __cdecl SkTLS::Get(void * (__cdecl*)(void),vo id (__cdecl*)(void *))" (?Get@SkTLS@@SAPAXP6APAXXZP6AXPAX@Z@Z) | |
11 Referenced in chrome_crash_reporter_client_win.obj | |
12 Referenced in skia.lib(SkError.obj) | |
13 Loaded skia.lib(SkTLS.obj) | |
14 | |
15 and then uses the information to answer questions such as "why is SkTLS.obj | |
16 being linked in. In this case it was requested by SkError.obj, and the process | |
17 is then repeated for SkError.obj. It traces the dependency tree back to a file | |
18 that was specified on the command line. Typically that file is part of a | |
19 source_set, and if that source_set is causing unnecessary code and data to be | |
20 pulled in then changing it to a static_library may reduce the binary size. See | |
21 crrev.com/2556603002 for an example of a ~900 KB savings from such a change. | |
22 | |
23 In other cases the source_set to static_library fix does not work because some | |
24 of the symbols are required, while others are pulling in unwanted object files. | |
25 In these cases it can be necessary to see what symbol is causing one object file | |
26 to reference another. Removing or moving the problematic symbol can fix the | |
27 problem. See crrev.com/2559063002 for an example of such a change. | |
28 | |
29 One complication is that there are sometimes multiple source files with the | |
30 same name, such as crc.c, which can make analysis more difficult or | |
31 ambiguous. If this becomes a blocking issue they it may be necessary to | |
32 temporarily rename the source file. | |
33 | |
34 Object file name matching is case sensitive. | |
35 | |
36 Typical output when run on chrome.dll verbose link output is: | |
37 | |
38 >python linker_verbose_tracking.py chrome_dll_verbose_01.txt chrome_browser_main | |
39 Database loaded - 11286 xrefs found | |
40 chrome_browser_main.obj referenced by | |
41 chrome_browser_main_win.obj for symbol public: virtual __thiscall Chrome BrowserMainParts::~ChromeBrowserMainParts(void) | |
42 | |
43 chrome_browser_main_win.obj referenced by | |
44 chrome_content_browser_client.obj for symbol public: __thiscall ChromeBr owserMainPartsWin::ChromeBrowserMainPartsWin(struct content::MainFunctionParams const &) | |
45 | |
46 chrome_content_browser_client.obj referenced by | |
47 Command-line obj file: chrome_main_delegate.obj for symbol public: __thi scall ChromeContentBrowserClient::ChromeContentBrowserClient(void) | |
48 """ | |
49 | |
50 import pdb | |
51 import re | |
52 import sys | |
53 | |
54 def ParseVerbose(input_file): | |
55 obj_match = re.compile('.*\((.*)\.obj\)') | |
stanisc
2016/12/10 00:13:11
It would help to see a comment with an example of
brucedawson
2016/12/10 00:59:57
Done.
| |
56 | |
57 cross_refs = {} | |
58 cross_refed_symbols = {} | |
59 | |
60 references = None | |
61 for line in open(input_file): | |
62 if line.startswith(' Found'): | |
63 references = [] | |
64 # Grab the symbol name | |
65 symbol = line[13:line.find('"', 13)] | |
66 continue | |
67 if type(references) == type([]): | |
68 sub_line = line.strip() | |
69 match = obj_match.match(sub_line) | |
70 # See if the line is part of the list of places where this symbol was | |
71 # referenced | |
72 if sub_line.count('Referenced ') > 0: | |
73 if match: | |
74 # This indicates a match that is xxx.lib(yyy.obj), so a referencing | |
75 # .obj file that was itself | |
76 reference = match.groups()[0] | |
77 else: | |
78 # This indicates a match that is just a pure .obj file name | |
79 # I think this means that the .obj file was specified on the linker | |
80 # command line. | |
81 reference = ('Command-line obj file: ' + | |
82 sub_line[len('Referenced in '): -len('.obj')]) | |
83 references.append(reference) | |
84 elif sub_line.count('Loaded ') > 0: | |
85 if match: | |
86 loaded = match.groups()[0] | |
87 cross_refs[loaded] = references | |
88 cross_refed_symbols[loaded] = symbol | |
89 references = None | |
90 if line.startswith('Finished pass 1'): | |
91 # Stop now because the remaining 90% of the verbose output is | |
92 # not of interest. Could probably use /VERBOSE:REF to trim out | |
93 # boring information. | |
94 break | |
95 return cross_refs, cross_refed_symbols | |
96 | |
97 | |
98 def TrackObj(cross_refs, cross_refed_symbols, obj_name): | |
99 if obj_name.lower().endswith('.obj'): | |
100 obj_name = obj_name[:-len('.obj')] | |
101 | |
102 # Keep track of which references we've already followed. | |
103 tracked = {} | |
104 | |
105 # Initial set of object files that we are tracking. | |
106 targets = [obj_name] | |
107 printed = False | |
108 for i in range(100): | |
109 new_targets = {} | |
110 for target in targets: | |
111 if not target in tracked: | |
112 tracked[target] = True | |
113 if target in cross_refs.keys(): | |
114 printed = True | |
115 print '%s.obj referenced by' % target | |
116 for ref in cross_refs[target]: | |
117 symbol = cross_refed_symbols[target] | |
stanisc
2016/12/10 00:13:11
Does this need to be inside the inner for loop?
brucedawson
2016/12/10 00:59:57
Good point. And since it is invariant for the targ
| |
118 print '\t%s.obj for symbol %s' % (ref, symbol) | |
119 new_targets[ref] = True | |
120 if len(new_targets) == 0: | |
121 break | |
122 print | |
123 targets = new_targets.keys() | |
124 if not printed: | |
125 print 'No references to %s.obj found.' % obj_name | |
126 | |
127 | |
128 def main(): | |
129 if len(sys.argv) < 3: | |
130 print r'Usage: %s <verbose_output_file> <objfile>' % sys.argv[0] | |
131 print r'Sample: %s chrome_dll_verbose.txt SkTLS' | |
stanisc
2016/12/10 00:13:11
It looks like this also needs % sys.argv[0] argume
brucedawson
2016/12/10 00:59:57
Good catch. Done.
| |
132 return 0 | |
133 cross_refs, cross_refed_symbols = ParseVerbose(sys.argv[1]) | |
134 print 'Database loaded - %d xrefs found' % len(cross_refs) | |
135 TrackObj(cross_refs, cross_refed_symbols, sys.argv[2]) | |
136 | |
137 if __name__ == '__main__': | |
138 sys.exit(main()) | |
OLD | NEW |