| Index: tools/symsrc/source_index.py
|
| diff --git a/tools/symsrc/source_index.py b/tools/symsrc/source_index.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..152fe220a0eb0f8ddfa1b4ed6093a302ec9f4599
|
| --- /dev/null
|
| +++ b/tools/symsrc/source_index.py
|
| @@ -0,0 +1,193 @@
|
| +#!/usr/bin/env python
|
| +
|
| +# Copyright (c) 2008 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.
|
| +
|
| +"""Usage: <win-path-to-pdb.pdb>
|
| +This tool will take a PDB on the command line, extract the source files that
|
| +were used in building the PDB, query SVN for which repository and revision
|
| +these files are at, and then finally write this information back into the PDB
|
| +in a format that the debugging tools understand. This allows for automatic
|
| +source debugging, as all of the information is contained in the PDB, and the
|
| +debugger can go out and fetch the source files via SVN.
|
| +
|
| +You most likely want to run these immediately after a build, since the source
|
| +input files need to match the generated PDB, and we want the correct SVN
|
| +revision information for the exact files that were used for the build.
|
| +
|
| +The following files from a windbg + source server installation are expected
|
| +to reside in the same directory as this python script:
|
| + dbghelp.dll
|
| + pdbstr.exe
|
| + srctool.exe
|
| +
|
| +NOTE: Expected to run under a native win32 python, NOT cygwin. All paths are
|
| +dealt with as win32 paths, since we have to interact with the Microsoft tools.
|
| +"""
|
| +
|
| +import sys
|
| +import os
|
| +import time
|
| +import subprocess
|
| +import tempfile
|
| +
|
| +# This serves two purposes. First, it acts as a whitelist, and only files
|
| +# from repositories listed here will be source indexed. Second, it allows us
|
| +# to map from one SVN URL to another, so we can map to external SVN servers.
|
| +REPO_MAP = {
|
| + "svn://chrome-svn/chrome": "http://src.chromium.org/svn",
|
| + "svn://chrome-svn.corp.google.com/chrome": "http://src.chromium.org/svn",
|
| + "http://v8.googlecode.com/svn": None,
|
| + "http://google-breakpad.googlecode.com/svn": None,
|
| + "http://googletest.googlecode.com/svn": None,
|
| + "http://open-vcdiff.googlecode.com/svn": None,
|
| + "http://google-url.googlecode.com/svn": None,
|
| +}
|
| +
|
| +def FindFile(filename):
|
| + """Return the full windows path to a file in the same dir as this code."""
|
| + thisdir = os.path.dirname(os.path.join(os.path.curdir, __file__))
|
| + return os.path.abspath(os.path.join(thisdir, filename))
|
| +
|
| +
|
| +def ExtractSourceFiles(pdb_filename):
|
| + """Extract a list of local paths of the source files from a PDB."""
|
| + srctool = subprocess.Popen([FindFile('srctool.exe'), '-r', pdb_filename],
|
| + stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| + filelist = srctool.stdout.read()
|
| + res = srctool.wait()
|
| + if res != 0 or filelist.startswith("srctool: "):
|
| + raise "srctool failed: " + filelist
|
| + return [x for x in filelist.split('\r\n') if len(x) != 0]
|
| +
|
| +def ReadSourceStream(pdb_filename):
|
| + """Read the contents of the source information stream from a PDB."""
|
| + srctool = subprocess.Popen([FindFile('pdbstr.exe'),
|
| + '-r', '-s:srcsrv',
|
| + '-p:%s' % pdb_filename],
|
| + stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| + data = srctool.stdout.read()
|
| + res = srctool.wait()
|
| +
|
| + if (res != 0 and res != -1) or data.startswith("pdbstr: "):
|
| + raise "pdbstr failed: " + data
|
| + return data
|
| +
|
| +def WriteSourceStream(pdb_filename, data):
|
| + """Write the contents of the source information stream to a PDB."""
|
| + # Write out the data to a temporary filename that we can pass to pdbstr.
|
| + (f, fname) = tempfile.mkstemp()
|
| + f = os.fdopen(f, "wb")
|
| + f.write(data)
|
| + f.close()
|
| +
|
| + srctool = subprocess.Popen([FindFile('pdbstr.exe'),
|
| + '-w', '-s:srcsrv',
|
| + '-i:%s' % fname,
|
| + '-p:%s' % pdb_filename],
|
| + stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| + data = srctool.stdout.read()
|
| + res = srctool.wait()
|
| +
|
| + if (res != 0 and res != -1) or data.startswith("pdbstr: "):
|
| + raise "pdbstr failed: " + data
|
| +
|
| + os.unlink(fname)
|
| +
|
| +# TODO for performance, we should probably work in directories instead of
|
| +# files. I'm scared of DEPS and generated files, so for now we query each
|
| +# individual file, and don't make assumptions that all files in the same
|
| +# directory are part of the same repository or at the same revision number.
|
| +def ExtractSvnInfo(local_filename):
|
| + """Calls svn info to extract the repository, path, and revision."""
|
| + # We call svn.bat to make sure and get the depot tools SVN and not cygwin.
|
| + srctool = subprocess.Popen(['svn.bat', 'info', local_filename],
|
| + stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| + info = srctool.stdout.read()
|
| + res = srctool.wait()
|
| + if res != 0:
|
| + return None
|
| + # Hack up into a dictionary of the fields printed by svn info.
|
| + vals = dict((y.split(': ', 2) for y in info.split('\r\n') if y))
|
| +
|
| + root = vals['Repository Root']
|
| + if not vals['URL'].startswith(root):
|
| + raise "URL is not inside of the repository root?!?"
|
| + path = vals['URL'][len(root):]
|
| + rev = int(vals['Revision'])
|
| +
|
| + return [root, path, rev]
|
| +
|
| +def UpdatePDB(pdb_filename):
|
| + """Update a pdb file with source information."""
|
| + dir_blacklist = { }
|
| + # TODO(deanm) look into "compressing" our output, by making use of vars
|
| + # and other things, so we don't need to duplicate the repo path and revs.
|
| + lines = [
|
| + 'SRCSRV: ini ------------------------------------------------',
|
| + 'VERSION=1',
|
| + 'INDEXVERSION=2',
|
| + 'VERCTRL=Subversion',
|
| + 'DATETIME=%s' % time.asctime(),
|
| + 'SRCSRV: variables ------------------------------------------',
|
| + 'SVN_EXTRACT_TARGET=%targ%\%fnbksl%(%var3%)\%var4%\%fnfile%(%var1%)',
|
| + 'SVN_EXTRACT_CMD=cmd /c svn cat "%var2%%var3%@%var4%" --non-interactive > "%svn_extract_target%"',
|
| + 'SRCSRVTRG=%SVN_extract_target%',
|
| + 'SRCSRVCMD=%SVN_extract_cmd%',
|
| + 'SRCSRV: source files ---------------------------------------',
|
| + ]
|
| +
|
| + if ReadSourceStream(pdb_filename):
|
| + raise "PDB already has source indexing information!"
|
| +
|
| + filelist = ExtractSourceFiles(pdb_filename)
|
| + for filename in filelist:
|
| + filedir = os.path.dirname(filename)
|
| +
|
| + print "Processing: %s" % filename
|
| + # This directory is blacklisted, either because it's not part of the SVN
|
| + # repository, or from one we're not interested in indexing.
|
| + if dir_blacklist.get(filedir, False):
|
| + print " skipping, directory is blacklisted."
|
| + continue
|
| +
|
| + info = ExtractSvnInfo(filename)
|
| +
|
| + # Skip the file if it's not under an svn repository. To avoid constantly
|
| + # querying SVN for files outside of SVN control (for example, the CRT
|
| + # sources), check if the directory is outside of SVN and blacklist it.
|
| + if not info:
|
| + if not ExtractSvnInfo(filedir):
|
| + dir_blacklist[filedir] = True
|
| + print " skipping, file is not in an SVN repository"
|
| + continue
|
| +
|
| + root = info[0]
|
| + path = info[1]
|
| + rev = info[2]
|
| +
|
| + # Check if file was from a svn repository we don't know about, or don't
|
| + # want to index. Blacklist the entire directory.
|
| + if not REPO_MAP.has_key(info[0]):
|
| + print " skipping, file is from an unknown SVN repository %s" % root
|
| + dir_blacklist[filedir] = True
|
| + continue
|
| +
|
| + # We might want to map an internal repository URL to an external repository.
|
| + if REPO_MAP[root]:
|
| + root = REPO_MAP[root]
|
| +
|
| + lines.append('%s*%s*%s*%s' % (filename, root, path, rev))
|
| + print " indexed file."
|
| +
|
| + lines.append('SRCSRV: end ------------------------------------------------')
|
| +
|
| + WriteSourceStream(pdb_filename, '\r\n'.join(lines))
|
| +
|
| +if __name__ == '__main__':
|
| + if len(sys.argv) != 2:
|
| + print "usage: file.pdb"
|
| + sys.exit(1)
|
| +
|
| + UpdatePDB(sys.argv[1])
|
|
|