Chromium Code Reviews| Index: tools/win/linker_verbose_tracking.py |
| diff --git a/tools/win/linker_verbose_tracking.py b/tools/win/linker_verbose_tracking.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..585d7f0577db30ccf9e4246eae1bf9564ae4ffe2 |
| --- /dev/null |
| +++ b/tools/win/linker_verbose_tracking.py |
| @@ -0,0 +1,138 @@ |
| +# Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +""" |
| +This script parses the /verbose output from the VC++ linker and uses it to |
| +explain why a particular object file is being linked in. It parses records |
| +like these: |
| + |
| + Found "public: static void * __cdecl SkTLS::Get(void * (__cdecl*)(void),void (__cdecl*)(void *))" (?Get@SkTLS@@SAPAXP6APAXXZP6AXPAX@Z@Z) |
| + Referenced in chrome_crash_reporter_client_win.obj |
| + Referenced in skia.lib(SkError.obj) |
| + Loaded skia.lib(SkTLS.obj) |
| + |
| +and then uses the information to answer questions such as "why is SkTLS.obj |
| +being linked in. In this case it was requested by SkError.obj, and the process |
| +is then repeated for SkError.obj. It traces the dependency tree back to a file |
| +that was specified on the command line. Typically that file is part of a |
| +source_set, and if that source_set is causing unnecessary code and data to be |
| +pulled in then changing it to a static_library may reduce the binary size. See |
| +crrev.com/2556603002 for an example of a ~900 KB savings from such a change. |
| + |
| +In other cases the source_set to static_library fix does not work because some |
| +of the symbols are required, while others are pulling in unwanted object files. |
| +In these cases it can be necessary to see what symbol is causing one object file |
| +to reference another. Removing or moving the problematic symbol can fix the |
| +problem. See crrev.com/2559063002 for an example of such a change. |
| + |
| +One complication is that there are sometimes multiple source files with the |
| +same name, such as crc.c, which can make analysis more difficult or |
| +ambiguous. If this becomes a blocking issue they it may be necessary to |
| +temporarily rename the source file. |
| + |
| +Object file name matching is case sensitive. |
| + |
| +Typical output when run on chrome.dll verbose link output is: |
| + |
| +>python linker_verbose_tracking.py chrome_dll_verbose_01.txt chrome_browser_main |
| +Database loaded - 11286 xrefs found |
| +chrome_browser_main.obj referenced by |
| + chrome_browser_main_win.obj for symbol public: virtual __thiscall ChromeBrowserMainParts::~ChromeBrowserMainParts(void) |
| + |
| +chrome_browser_main_win.obj referenced by |
| + chrome_content_browser_client.obj for symbol public: __thiscall ChromeBrowserMainPartsWin::ChromeBrowserMainPartsWin(struct content::MainFunctionParams const &) |
| + |
| +chrome_content_browser_client.obj referenced by |
| + Command-line obj file: chrome_main_delegate.obj for symbol public: __thiscall ChromeContentBrowserClient::ChromeContentBrowserClient(void) |
| +""" |
| + |
| +import pdb |
| +import re |
| +import sys |
| + |
| +def ParseVerbose(input_file): |
| + 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.
|
| + |
| + cross_refs = {} |
| + cross_refed_symbols = {} |
| + |
| + references = None |
| + for line in open(input_file): |
| + if line.startswith(' Found'): |
| + references = [] |
| + # Grab the symbol name |
| + symbol = line[13:line.find('"', 13)] |
| + continue |
| + if type(references) == type([]): |
| + sub_line = line.strip() |
| + match = obj_match.match(sub_line) |
| + # See if the line is part of the list of places where this symbol was |
| + # referenced |
| + if sub_line.count('Referenced ') > 0: |
| + if match: |
| + # This indicates a match that is xxx.lib(yyy.obj), so a referencing |
| + # .obj file that was itself |
| + reference = match.groups()[0] |
| + else: |
| + # This indicates a match that is just a pure .obj file name |
| + # I think this means that the .obj file was specified on the linker |
| + # command line. |
| + reference = ('Command-line obj file: ' + |
| + sub_line[len('Referenced in '): -len('.obj')]) |
| + references.append(reference) |
| + elif sub_line.count('Loaded ') > 0: |
| + if match: |
| + loaded = match.groups()[0] |
| + cross_refs[loaded] = references |
| + cross_refed_symbols[loaded] = symbol |
| + references = None |
| + if line.startswith('Finished pass 1'): |
| + # Stop now because the remaining 90% of the verbose output is |
| + # not of interest. Could probably use /VERBOSE:REF to trim out |
| + # boring information. |
| + break |
| + return cross_refs, cross_refed_symbols |
| + |
| + |
| +def TrackObj(cross_refs, cross_refed_symbols, obj_name): |
| + if obj_name.lower().endswith('.obj'): |
| + obj_name = obj_name[:-len('.obj')] |
| + |
| + # Keep track of which references we've already followed. |
| + tracked = {} |
| + |
| + # Initial set of object files that we are tracking. |
| + targets = [obj_name] |
| + printed = False |
| + for i in range(100): |
| + new_targets = {} |
| + for target in targets: |
| + if not target in tracked: |
| + tracked[target] = True |
| + if target in cross_refs.keys(): |
| + printed = True |
| + print '%s.obj referenced by' % target |
| + for ref in cross_refs[target]: |
| + 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
|
| + print '\t%s.obj for symbol %s' % (ref, symbol) |
| + new_targets[ref] = True |
| + if len(new_targets) == 0: |
| + break |
| + targets = new_targets.keys() |
| + if not printed: |
| + print 'No references to %s.obj found.' % obj_name |
| + |
| + |
| +def main(): |
| + if len(sys.argv) < 3: |
| + print r'Usage: %s <verbose_output_file> <objfile>' % sys.argv[0] |
| + 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.
|
| + return 0 |
| + cross_refs, cross_refed_symbols = ParseVerbose(sys.argv[1]) |
| + print 'Database loaded - %d xrefs found' % len(cross_refs) |
| + TrackObj(cross_refs, cross_refed_symbols, sys.argv[2]) |
| + |
| +if __name__ == '__main__': |
| + sys.exit(main()) |