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 |