| OLD | NEW |
| 1 # Copyright (c) 2016 The Chromium Authors. All rights reserved. | 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 | 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 """ | 5 """ |
| 6 This script parses the /verbose output from the VC++ linker and uses it to | 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 | 7 explain why a particular object file is being linked in. It parses records |
| 8 like these: | 8 like these: |
| 9 | 9 |
| 10 Found "public: static void * __cdecl SkTLS::Get(void * (__cdecl*)(void)... | 10 Found "public: static void * __cdecl SkTLS::Get(void * (__cdecl*)(void)... |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 | 43 |
| 44 stream_decoder.obj pulled in for symbol "_FLAC__stream_decoder_new" by | 44 stream_decoder.obj pulled in for symbol "_FLAC__stream_decoder_new" by |
| 45 stream_encoder.obj | 45 stream_encoder.obj |
| 46 bitwriter.obj pulled in for symbol "_FLAC__bitwriter_new" by | 46 bitwriter.obj pulled in for symbol "_FLAC__bitwriter_new" by |
| 47 stream_encoder.obj | 47 stream_encoder.obj |
| 48 | 48 |
| 49 stream_encoder.obj pulled in for symbol "_FLAC__stream_encoder_new" by | 49 stream_encoder.obj pulled in for symbol "_FLAC__stream_encoder_new" by |
| 50 Command-line obj file: audio_encoder.obj | 50 Command-line obj file: audio_encoder.obj |
| 51 """ | 51 """ |
| 52 | 52 |
| 53 import io |
| 53 import pdb | 54 import pdb |
| 54 import re | 55 import re |
| 55 import sys | 56 import sys |
| 56 | 57 |
| 57 def ParseVerbose(input_file): | 58 def ParseVerbose(input_file): |
| 58 # This matches line like this: | 59 # This matches line like this: |
| 59 # Referenced in skia.lib(SkError.obj) | 60 # Referenced in skia.lib(SkError.obj) |
| 60 # with the groups()[0] referring to the object file name without the file | 61 # with the groups()[0] referring to the object file name without the file |
| 61 # extension. | 62 # extension. |
| 62 obj_match = re.compile('.*\((.*)\.obj\)') | 63 obj_match = re.compile('.*\((.*)\.obj\)') |
| 63 # Prefix used for symbols that are referenced: | 64 # Prefix used for symbols that are referenced: |
| 64 found_prefix = ' Found' | 65 found_prefix = ' Found' |
| 65 | 66 |
| 66 cross_refs = {} | 67 cross_refs = {} |
| 67 cross_refed_symbols = {} | 68 cross_refed_symbols = {} |
| 68 | 69 |
| 69 references = None | 70 references = None |
| 70 for line in open(input_file): | 71 # When you redirect the linker output to a file from a command prompt the |
| 71 if line.startswith(found_prefix): | 72 # result will be a utf-8 (or ASCII?) output file. However if you do the same |
| 72 references = [] | 73 # thing from PowerShell you get a utf-16 file. So, we need to handle both |
| 73 # Grab the symbol name | 74 # options. Only the first BOM option (\xff\xfe) has been tested, but it seems |
| 74 symbol = line[len(found_prefix):].strip() | 75 # appropriate to handle the other as well. |
| 75 if symbol[0] == '"': | 76 file_encoding = 'utf-8' |
| 76 # Strip off leading and trailing quotes if present. | 77 with open(input_file) as file_handle: |
| 77 symbol = symbol[1:-1] | 78 header = file_handle.read(2) |
| 78 continue | 79 if header == '\xff\xfe' or header == '\xfe\xff': |
| 79 if type(references) == type([]): | 80 file_encoding = 'utf-16' |
| 80 sub_line = line.strip() | 81 with io.open(input_file, encoding=file_encoding) as file_handle: |
| 81 match = obj_match.match(sub_line) | 82 for line in file_handle: |
| 82 # See if the line is part of the list of places where this symbol was | 83 if line.startswith(found_prefix): |
| 83 # referenced | 84 references = [] |
| 84 if sub_line.count('Referenced ') > 0: | 85 # Grab the symbol name |
| 85 if match: | 86 symbol = line[len(found_prefix):].strip() |
| 86 # This indicates a match that is xxx.lib(yyy.obj), so a referencing | 87 if symbol[0] == '"': |
| 87 # .obj file that was itself inside of a library. We discard the | 88 # Strip off leading and trailing quotes if present. |
| 88 # library name. | 89 symbol = symbol[1:-1] |
| 89 reference = match.groups()[0] | 90 continue |
| 90 else: | 91 if type(references) == type([]): |
| 91 # This indicates a match that is just a pure .obj file name | 92 sub_line = line.strip() |
| 92 # I think this means that the .obj file was specified on the linker | 93 match = obj_match.match(sub_line) |
| 93 # command line. | 94 # See if the line is part of the list of places where this symbol was |
| 94 reference = ('Command-line obj file: ' + | 95 # referenced |
| 95 sub_line[len('Referenced in '): -len('.obj')]) | 96 if sub_line.count('Referenced ') > 0: |
| 96 references.append(reference) | 97 if match: |
| 97 elif sub_line.count('Loaded ') > 0: | 98 # This indicates a match that is xxx.lib(yyy.obj), so a referencing |
| 98 if match: | 99 # .obj file that was itself inside of a library. We discard the |
| 99 loaded = match.groups()[0] | 100 # library name. |
| 100 cross_refs[loaded] = references | 101 reference = match.groups()[0] |
| 101 cross_refed_symbols[loaded] = symbol | 102 else: |
| 102 references = None | 103 # This indicates a match that is just a pure .obj file name |
| 103 if line.startswith('Finished pass 1'): | 104 # I think this means that the .obj file was specified on the linker |
| 104 # Stop now because the remaining 90% of the verbose output is | 105 # command line. |
| 105 # not of interest. Could probably use /VERBOSE:REF to trim out | 106 reference = ('Command-line obj file: ' + |
| 106 # boring information. | 107 sub_line[len('Referenced in '): -len('.obj')]) |
| 107 break | 108 references.append(reference) |
| 109 elif sub_line.count('Loaded ') > 0: |
| 110 if match: |
| 111 loaded = match.groups()[0] |
| 112 cross_refs[loaded] = references |
| 113 cross_refed_symbols[loaded] = symbol |
| 114 references = None |
| 115 if line.startswith('Finished pass 1'): |
| 116 # Stop now because the remaining 90% of the verbose output is |
| 117 # not of interest. Could probably use /VERBOSE:REF to trim out |
| 118 # boring information. |
| 119 break |
| 108 return cross_refs, cross_refed_symbols | 120 return cross_refs, cross_refed_symbols |
| 109 | 121 |
| 110 | 122 |
| 111 def TrackObj(cross_refs, cross_refed_symbols, obj_name): | 123 def TrackObj(cross_refs, cross_refed_symbols, obj_name): |
| 112 if obj_name.lower().endswith('.obj'): | 124 if obj_name.lower().endswith('.obj'): |
| 113 obj_name = obj_name[:-len('.obj')] | 125 obj_name = obj_name[:-len('.obj')] |
| 114 | 126 |
| 115 # Keep track of which references we've already followed. | 127 # Keep track of which references we've already followed. |
| 116 tracked = {} | 128 tracked = {} |
| 117 | 129 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 128 printed = True | 140 printed = True |
| 129 print '%s.obj pulled in for symbol "%s" by' % (target, symbol) | 141 print '%s.obj pulled in for symbol "%s" by' % (target, symbol) |
| 130 for ref in cross_refs[target]: | 142 for ref in cross_refs[target]: |
| 131 print '\t%s.obj' % ref | 143 print '\t%s.obj' % ref |
| 132 new_targets[ref] = True | 144 new_targets[ref] = True |
| 133 if len(new_targets) == 0: | 145 if len(new_targets) == 0: |
| 134 break | 146 break |
| 135 print | 147 print |
| 136 targets = new_targets.keys() | 148 targets = new_targets.keys() |
| 137 if not printed: | 149 if not printed: |
| 138 print 'No references to %s.obj found.' % obj_name | 150 print ('No references to %s.obj found. Directly specified in sources or a ' |
| 151 'source_set?' % obj_name) |
| 139 | 152 |
| 140 | 153 |
| 141 def main(): | 154 def main(): |
| 142 if len(sys.argv) < 3: | 155 if len(sys.argv) < 3: |
| 143 print r'Usage: %s <verbose_output_file> <objfile>' % sys.argv[0] | 156 print r'Usage: %s <verbose_output_file> <objfile>' % sys.argv[0] |
| 144 print r'Sample: %s chrome_dll_verbose.txt SkTLS' % sys.argv[0] | 157 print r'Sample: %s chrome_dll_verbose.txt SkTLS' % sys.argv[0] |
| 145 return 0 | 158 return 0 |
| 146 cross_refs, cross_refed_symbols = ParseVerbose(sys.argv[1]) | 159 cross_refs, cross_refed_symbols = ParseVerbose(sys.argv[1]) |
| 147 print 'Database loaded - %d xrefs found' % len(cross_refs) | 160 print 'Database loaded - %d xrefs found' % len(cross_refs) |
| 161 if not len(cross_refs): |
| 162 print 'No data found to analyze. Exiting' |
| 163 return 0 |
| 148 TrackObj(cross_refs, cross_refed_symbols, sys.argv[2]) | 164 TrackObj(cross_refs, cross_refed_symbols, sys.argv[2]) |
| 149 | 165 |
| 150 if __name__ == '__main__': | 166 if __name__ == '__main__': |
| 151 sys.exit(main()) | 167 sys.exit(main()) |
| OLD | NEW |