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)... |
| 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 tools\win\linker_verbose_tracking.py chrome_verbose_02.txt flac_crc |
| 39 Database loaded - 11277 xrefs found |
| 40 flac_crc.obj pulled in for symbol "_FLAC__crc8" by |
| 41 stream_decoder.obj |
| 42 bitwriter.obj |
| 43 |
| 44 stream_decoder.obj pulled in for symbol "_FLAC__stream_decoder_new" by |
| 45 stream_encoder.obj |
| 46 bitwriter.obj pulled in for symbol "_FLAC__bitwriter_new" by |
| 47 stream_encoder.obj |
| 48 |
| 49 stream_encoder.obj pulled in for symbol "_FLAC__stream_encoder_new" by |
| 50 Command-line obj file: audio_encoder.obj |
| 51 """ |
| 52 |
| 53 import pdb |
| 54 import re |
| 55 import sys |
| 56 |
| 57 def ParseVerbose(input_file): |
| 58 # This matches line like this: |
| 59 # Referenced in skia.lib(SkError.obj) |
| 60 # with the groups()[0] referring to the object file name without the file |
| 61 # extension. |
| 62 obj_match = re.compile('.*\((.*)\.obj\)') |
| 63 # Prefix used for symbols that are referenced: |
| 64 found_prefix = ' Found' |
| 65 |
| 66 cross_refs = {} |
| 67 cross_refed_symbols = {} |
| 68 |
| 69 references = None |
| 70 for line in open(input_file): |
| 71 if line.startswith(found_prefix): |
| 72 references = [] |
| 73 # Grab the symbol name |
| 74 symbol = line[len(found_prefix):].strip() |
| 75 if symbol[0] == '"': |
| 76 # Strip off leading and trailing quotes if present. |
| 77 symbol = symbol[1:-1] |
| 78 continue |
| 79 if type(references) == type([]): |
| 80 sub_line = line.strip() |
| 81 match = obj_match.match(sub_line) |
| 82 # See if the line is part of the list of places where this symbol was |
| 83 # referenced |
| 84 if sub_line.count('Referenced ') > 0: |
| 85 if match: |
| 86 # This indicates a match that is xxx.lib(yyy.obj), so a referencing |
| 87 # .obj file that was itself inside of a library. We discard the |
| 88 # library name. |
| 89 reference = match.groups()[0] |
| 90 else: |
| 91 # This indicates a match that is just a pure .obj file name |
| 92 # I think this means that the .obj file was specified on the linker |
| 93 # command line. |
| 94 reference = ('Command-line obj file: ' + |
| 95 sub_line[len('Referenced in '): -len('.obj')]) |
| 96 references.append(reference) |
| 97 elif sub_line.count('Loaded ') > 0: |
| 98 if match: |
| 99 loaded = match.groups()[0] |
| 100 cross_refs[loaded] = references |
| 101 cross_refed_symbols[loaded] = symbol |
| 102 references = None |
| 103 if line.startswith('Finished pass 1'): |
| 104 # Stop now because the remaining 90% of the verbose output is |
| 105 # not of interest. Could probably use /VERBOSE:REF to trim out |
| 106 # boring information. |
| 107 break |
| 108 return cross_refs, cross_refed_symbols |
| 109 |
| 110 |
| 111 def TrackObj(cross_refs, cross_refed_symbols, obj_name): |
| 112 if obj_name.lower().endswith('.obj'): |
| 113 obj_name = obj_name[:-len('.obj')] |
| 114 |
| 115 # Keep track of which references we've already followed. |
| 116 tracked = {} |
| 117 |
| 118 # Initial set of object files that we are tracking. |
| 119 targets = [obj_name] |
| 120 printed = False |
| 121 for i in range(100): |
| 122 new_targets = {} |
| 123 for target in targets: |
| 124 if not target in tracked: |
| 125 tracked[target] = True |
| 126 if target in cross_refs.keys(): |
| 127 symbol = cross_refed_symbols[target] |
| 128 printed = True |
| 129 print '%s.obj pulled in for symbol "%s" by' % (target, symbol) |
| 130 for ref in cross_refs[target]: |
| 131 print '\t%s.obj' % ref |
| 132 new_targets[ref] = True |
| 133 if len(new_targets) == 0: |
| 134 break |
| 135 print |
| 136 targets = new_targets.keys() |
| 137 if not printed: |
| 138 print 'No references to %s.obj found.' % obj_name |
| 139 |
| 140 |
| 141 def main(): |
| 142 if len(sys.argv) < 3: |
| 143 print r'Usage: %s <verbose_output_file> <objfile>' % sys.argv[0] |
| 144 print r'Sample: %s chrome_dll_verbose.txt SkTLS' % sys.argv[0] |
| 145 return 0 |
| 146 cross_refs, cross_refed_symbols = ParseVerbose(sys.argv[1]) |
| 147 print 'Database loaded - %d xrefs found' % len(cross_refs) |
| 148 TrackObj(cross_refs, cross_refed_symbols, sys.argv[2]) |
| 149 |
| 150 if __name__ == '__main__': |
| 151 sys.exit(main()) |
OLD | NEW |