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..506a86a4443e3e571e2d22b78700466d349574f5
|
--- /dev/null
|
+++ b/tools/win/linker_verbose_tracking.py
|
@@ -0,0 +1,151 @@
|
+# 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)...
|
+ 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 tools\win\linker_verbose_tracking.py chrome_verbose_02.txt flac_crc
|
+Database loaded - 11277 xrefs found
|
+flac_crc.obj pulled in for symbol "_FLAC__crc8" by
|
+ stream_decoder.obj
|
+ bitwriter.obj
|
+
|
+stream_decoder.obj pulled in for symbol "_FLAC__stream_decoder_new" by
|
+ stream_encoder.obj
|
+bitwriter.obj pulled in for symbol "_FLAC__bitwriter_new" by
|
+ stream_encoder.obj
|
+
|
+stream_encoder.obj pulled in for symbol "_FLAC__stream_encoder_new" by
|
+ Command-line obj file: audio_encoder.obj
|
+"""
|
+
|
+import pdb
|
+import re
|
+import sys
|
+
|
+def ParseVerbose(input_file):
|
+ # This matches line like this:
|
+ # Referenced in skia.lib(SkError.obj)
|
+ # with the groups()[0] referring to the object file name without the file
|
+ # extension.
|
+ obj_match = re.compile('.*\((.*)\.obj\)')
|
+ # Prefix used for symbols that are referenced:
|
+ found_prefix = ' Found'
|
+
|
+ cross_refs = {}
|
+ cross_refed_symbols = {}
|
+
|
+ references = None
|
+ for line in open(input_file):
|
+ if line.startswith(found_prefix):
|
+ references = []
|
+ # Grab the symbol name
|
+ symbol = line[len(found_prefix):].strip()
|
+ if symbol[0] == '"':
|
+ # Strip off leading and trailing quotes if present.
|
+ symbol = symbol[1:-1]
|
+ 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 inside of a library. We discard the
|
+ # library name.
|
+ 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():
|
+ symbol = cross_refed_symbols[target]
|
+ printed = True
|
+ print '%s.obj pulled in for symbol "%s" by' % (target, symbol)
|
+ for ref in cross_refs[target]:
|
+ print '\t%s.obj' % ref
|
+ new_targets[ref] = True
|
+ if len(new_targets) == 0:
|
+ break
|
+ print
|
+ 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' % sys.argv[0]
|
+ 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())
|
|