#!/usr/bin/python import sys import os import os.path import subprocess import shlex import shutil import base64 from poster.encode import multipart_encode from poster.encode import MultipartParam from poster.streaminghttp import register_openers import urllib2 from optparse import OptionParser register_openers() breakpadSourceDir = os.environ['BREAKPAD_SOURCE_DIR'] breakpadUploadUrl = os.environ['BREAKPAD_UPLOAD_URL'] breakpadUserName = os.environ['BREAKPAD_USER_NAME'] breakpadUserPassword = os.environ['BREAKPAD_USER_PASSWORD'] if sys.platform == 'win32': nullfilename = 'nul:' else: nullfilename = '/dev/null' nullfile = open(nullfilename, 'r+b') def stdoutFromProcess(process): (stdout, stderr) = process.communicate() if stderr: print stderr raise SystemError() sys.exit(1) return stdout def toolPath(): if sys.platform == 'linux2': dumpsymsPath = os.path.join(breakpadSourceDir, 'src/tools/linux/dump_syms/dump_syms') elif sys.platform == 'darwin': dumpsymsPath = os.path.join(breakpadSourceDir, 'src/tools/mac/dump_syms/build/Release/dump_syms') elif sys.platform == 'win32': dumpsymsPath = os.path.join(breakpadSourceDir,'src\\tools\\windows\\binaries\\dump_syms.exe') else: sys.exit(1) return dumpsymsPath gitRootDirectoryCache = {} def gitRootDirectory(path): directory = os.path.dirname(path) if directory in gitRootDirectoryCache: return gitRootDirectoryCache[directory] directoryList = directory.split(os.sep) while len(directoryList) > 0: gitDirectory = os.sep.join(directoryList + ['.git']) if os.path.exists(gitDirectory): gitDirectory = os.sep.join(directoryList) gitRootDirectoryCache[directory] = gitDirectory return gitDirectory directoryList.pop() return None gitCommitShaCache = {} def gitCommitSha(gitRootPath): if gitRootPath in gitCommitShaCache: return gitCommitShaCache[gitRootPath] gitProcess = subprocess.Popen(shlex.split('git rev-parse HEAD'), stdout=subprocess.PIPE, stderr=nullfile, cwd=gitRootPath) commitSha = stdoutFromProcess(gitProcess).strip('\n') gitCommitShaCache[gitRootPath] = commitSha return commitSha gitRemoteRepositoryCache = {} def gitRemoteRepository(gitRootPath): if gitRootPath in gitRemoteRepositoryCache: return gitRemoteRepositoryCache[gitRootPath] gitProcess = \ subprocess.Popen(shlex.split('git config remote.origin.url'), stdout=subprocess.PIPE, stderr=nullfile, cwd=gitRootPath) repository = stdoutFromProcess(gitProcess).strip('\n') gitRemoteRepositoryCache[gitRootPath] = repository return repository gitFilePathCache = {} def populateFilePathCache(gitRootPath): if gitRootPath not in gitFilePathCache: gitProcess = \ subprocess.Popen(shlex.split('git ls-files --full-name'), stdout=subprocess.PIPE, stderr=nullfile, cwd=gitRootPath) fileNameList = stdoutFromProcess(gitProcess).split('\n') filePathCache = {} for fileName in fileNameList: baseFileName = os.path.basename(fileName) filePathCache[baseFileName] = fileName gitFilePathCache[gitRootPath] = filePathCache def gitFilePath(path, gitRootPath): if not gitRootPath: return path populateFilePathCache(gitRootPath) baseFileName = os.path.basename(path) filePathCache = gitFilePathCache[gitRootPath] if baseFileName in filePathCache: return filePathCache[baseFileName] else: return os.path.relpath(path, gitRootPath) def isInRepository(path): gitRootPath = gitRootDirectory(path) if not gitRootPath: return False populateFilePathCache(gitRootPath) baseFileName = os.path.basename(path) if baseFileName in gitFilePathCache[gitRootPath]: return True else: return False def sendSymbolsToServer( breakpadUploadUrl, symbolText, codeFile, debugFile, debugIdentifier, operatingSystem, cpu, ): (data, headers) = multipart_encode({ 'symbol_file': MultipartParam('symbol_file', value=symbolText, filename='symbol_file'), 'code_file': codeFile, 'debug_file': debugFile, 'debug_identifier': debugIdentifier, 'os': operatingSystem, 'cpu': cpu, }) request = urllib2.Request(breakpadUploadUrl, data, headers) auth = base64.encodestring('%s:%s' % (breakpadUserName, breakpadUserPassword))[:-1] # This is just standard un/pw encoding request.add_header('Authorization', 'Basic %s' % auth) # Add Auth header to request result = urllib2.urlopen(request).read() def generateSymbolFilesAndSend(binaryPath, projectPath): dumpsymsPath = toolPath() originalBinaryPath = binaryPath if sys.platform == 'darwin': dsymutilProcess = \ subprocess.Popen(shlex.split('/usr/bin/dsymutil "' + binaryPath + '"'), stdout=nullfile, stderr=nullfile) dsymutilProcess.wait() binaryPath += os.path.join('.dSYM/Contents/Resources/DWARF/', os.path.basename(binaryPath)) binaryPath = os.path.normpath(binaryPath) dumpsymsProcess = subprocess.Popen(shlex.split('"' + dumpsymsPath + '" "' + binaryPath + '"'), stdout=subprocess.PIPE, stderr=nullfile, cwd=projectPath) symbolList = stdoutFromProcess(dumpsymsProcess).split('\n') outputTextList = [] codeFile = os.path.basename(binaryPath) debugFile = '' debugIdentifier = '' operatingSystem = '' cpu = '' moduleNotParsed = True for line in symbolList: line = line.strip('\n').strip('\r') if line[:4] == 'FILE': (marker, idnumber, filepath) = line.split(' ', 2) filepath = os.path.normpath(os.path.join(projectPath, filepath)) if isInRepository(filepath): gitRootPath = gitRootDirectory(filepath) commitSha = gitCommitSha(gitRootPath) repository = \ gitRemoteRepository(gitRootPath).replace(':', '/') relativeFilePath = gitFilePath(filepath, gitRootPath).replace('\\', '/') outputTextList.append('FILE ' + idnumber + ' git:' + repository + ':' + relativeFilePath + ':' + commitSha) else: outputTextList.append(line) elif moduleNotParsed and line[:6] == 'MODULE': (operatingSystem, cpu, debugIdentifier, debugFile) = \ line[7:].split(' ', 3) moduleNotParsed = False elif line: outputTextList.append(line) if moduleNotParsed: print 'Module not parsed' sys.exit(1) sendSymbolsToServer( breakpadUploadUrl, '\n'.join(outputTextList), codeFile, debugFile, debugIdentifier, operatingSystem, cpu, ) if sys.platform == 'darwin': shutil.rmtree(originalBinaryPath + '.dSYM') def testForBreakpad(): try: dumpsymsPath = toolPath() if not dumpsymsPath: sys.exit(1) subprocess.Popen([dumpsymsPath], stdout=nullfile, stderr=nullfile) except (OSError, KeyError): print 'No dumpsyms can be executed. Maybe BREAKPAD_SOURCE_DIR is wrong.' sys.exit(1) sys.exit(0) def main(): usage = 'usage: %prog [options] binary projectpath' parser = OptionParser(usage=usage) parser.add_option('-v', '--verbose', action='store_true', dest='verbose') parser.add_option('-e', '--breakpad-exists', action='store_true', dest='testForBreakpad') (options, args) = parser.parse_args() if options.testForBreakpad == True: testForBreakpad() if len(args) > 1: generateSymbolFilesAndSend(args[0], args[1]) else: parser.print_help() if __name__ == '__main__': main()