Chromium Code Reviews| 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 |