| # 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 uses ShowGlobals.exe to compare two PDBs to see what interesting |
| globals are present in one but not the other. You can either pass in a .pdb file |
| or you can pass in a .txt file that contains the results of calling ShowGlobals. |
| This helps when investigating size regressions. Often code-size regressions are |
| associated with global variable changes, and those global variables can be |
| easier to track and investigate than the code. |
| |
| Typical output from ShowGlobals.exe is lines like these: |
| |
| #Dups DupSize Size Section Symbol name PDB name |
| |
| 0 0 122784 2 kBrotliDictionary chrome.dll.pdb |
| 1 1824 0 0 LcidToLocaleNameTable chrome.dll.pdb |
| """ |
| |
| import os |
| import subprocess |
| import sys |
| |
| |
| def LoadSymbols(pdb_name): |
| result = {} |
| extension = os.path.splitext(pdb_name)[1].lower() |
| if extension in ['.pdb']: |
| command = 'ShowGlobals.exe "%s"' % pdb_name |
| lines = subprocess.check_output(command).splitlines() |
| elif extension in ['.txt']: |
| lines = open(pdb_name).readlines() |
| else: |
| print 'Unrecognized extension in %s' % pdb_name |
| return result |
| for line in lines: |
| parts = line.split('\t') |
| if len(parts) >= 5 and not line.startswith('#'): |
| # Put the first four columns (the numerical data associated with a symbol) |
| # into a dictionary indexed by the fifth column, which is the symbol name. |
| symbol_name = parts[4] |
| result[symbol_name] = parts[:4] |
| return result |
| |
| |
| def ShowExtras(symbols_A, symbols_B, name_A, name_B): |
| print 'Symbols that are in %s but not in %s' % (name_A, name_B) |
| for key in symbols_A: |
| if not key in symbols_B: |
| # Print all the numerical data, followed by the symbol name, separated by |
| # tabs. |
| print '\t'.join(symbols_A[key] + [key]) |
| print |
| |
| |
| def ShowDifferences(symbols_A, symbols_B, name_A, name_B): |
| print 'Symbols that are changed from %s to %s' % (name_A, name_B) |
| for key in symbols_A: |
| if key in symbols_B: |
| value_a = symbols_A[key] |
| value_b = symbols_B[key] |
| if value_a != value_b: |
| # Print the symbol name and then the two versions of the numerical data, |
| # indented. |
| print '%s changed from/to:' % key |
| print '\t' + '\t'.join(value_a) |
| print '\t' + '\t'.join(value_b) |
| print |
| |
| |
| def main(): |
| symbols_1 = LoadSymbols(sys.argv[1]) |
| symbols_2 = LoadSymbols(sys.argv[2]) |
| |
| if len(symbols_1) == 0: |
| print 'No data found in %s - fastlink?' % sys.argv[1] |
| return |
| if len(symbols_2) == 0: |
| print 'No data found in %s - fastlink?' % sys.argv[2] |
| return |
| |
| print ('%d interesting globals in %s, %d interesting globals in %s' % |
| (len(symbols_1), sys.argv[1], len(symbols_2), sys.argv[2])) |
| |
| ShowExtras(symbols_1, symbols_2, sys.argv[1], sys.argv[2]) |
| ShowExtras(symbols_2, symbols_1, sys.argv[2], sys.argv[1]) |
| ShowDifferences(symbols_1, symbols_2, sys.argv[1], sys.argv[2]) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |