summaryrefslogtreecommitdiffstats
blob: dada92095c4909e1025fae92b09311ee2905bc7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python
# Note
# ----
# This script requires:
#   - cdb, the Windows command line debugger installed and available in PATH.
#   - the SWIFT_DIST environment variable set to a locatioon that contains msi and pdb.gz files.

import sys
from subprocess import call
from subprocess import Popen, PIPE
import ntpath
import shutil
import re
import urllib2
import os
import gzip
import time


swiftWindowBuildsPathPrefix = os.getenv("SWIFT_DIST")
if swiftWindowBuildsPathPrefix == None :
    print "Please set the SWIFT_DIST environment variable to a location containing msi and pdb.gz files."
    sys.exit(1)

if len(sys.argv) != 3:
    print "Usage: python WindowsMinidumpAnalyse.py VERSION MINIDUMP_FILE"
    sys.exit(1)

version = sys.argv[1]
minidump_file = sys.argv[2]
minidump_filename = ntpath.basename(minidump_file)
minidump_fullpath = os.path.abspath(minidump_file)
humantext_fullpath = os.path.splitext(minidump_fullpath)[0]+".txt"
symbol_cache_path = os.path.join(os.getenv("TEMP"), "\symbols")
working_folder = "tmp-crash-{0}".format(minidump_filename)
commit = ""

def downloadInstaller(version) :
    onlineFilename = "{0}.msi".format(version.capitalize())
    url = "{0}{1}".format(swiftWindowBuildsPathPrefix, onlineFilename)
    print("Download {0}.".format(url))
    file = urllib2.urlopen(url)
    with open(onlineFilename,'wb') as output:
        output.write(file.read())

def unpackInstaller(version) :
    msiFilename = "{0}.msi".format(version.capitalize())
    msiExtractDirectory = os.getcwd() + "\\msi"
    if not os.path.exists(msiExtractDirectory):
        os.makedirs(msiExtractDirectory)
    print("Unpack {0} to {1}.".format(msiFilename, os.getcwd()))
    call(["msiexec", "/a", msiFilename, "/qb", "TARGETDIR={0}".format(msiExtractDirectory)], shell=True)

def unpackDebugSymbols(version) :
    symbolsFilename = "{0}.pdb.gz".format(version.capitalize())
    print("Unpack {0}.".format(symbolsFilename))
    if not os.path.isdir(symbolsFilename):
        with gzip.open(symbolsFilename, 'rb') as in_file:
            s = in_file.read()

        path_to_store = symbolsFilename[:-3]

        with open("msi\PFiles\Swift\{0}".format("Swift.pdb"), 'wb') as f:
            f.write(s)

def downloadDebugSymbols(version) :
    onlineFilename = "{0}.pdb.gz".format(version.capitalize())
    url = "{0}{1}".format(swiftWindowBuildsPathPrefix, onlineFilename)
    print("Download {0}.".format(url))
    file = urllib2.urlopen(url)
    with open(onlineFilename,'wb') as output:
        output.write(file.read())

def copyMinidump(filename) :
    shutil.copyfile(filename, "msi\PFiles\Swift\{0}".format(minidump_filename))

def printHumanReadableReport():
    oldDir = os.getcwd()

    # change dir to Swift.exe dir
    os.chdir("msi\PFiles\Swift")

    # print all stacks and analyze crash for exceptions
    cdbCommand = ".symopt+0x40;.lines -e;.kframes 200;!analyze -v -p;!uniqstack -vp;.ecxr;k;q"

    symbolPath = "cache*{0};srv*https://msdl.microsoft.com/download/symbols;C:\\Qt\\Qt5.4.2\\5.4\\msvc2013_opengl\\bin;C:\\Qt\\Qt5.4.2\\5.4\\msvc2013_opengl\\lib;{1}".format(symbol_cache_path, os.getcwd())

    cdbFullCommand = ["cdb", "-i", os.getcwd(), "-y", symbolPath, "-z", minidump_filename, "-srcpath", oldDir, "-logo", humantext_fullpath, "-c", cdbCommand ]
    print("Run command: " + str(cdbFullCommand))
    call(cdbFullCommand)

# for testing, delete the old folder
try:
    shutil.rmtree(working_folder)
except:
    print ""

# clone local git repository into dedicated directory
call(["git", "clone",  ".", working_folder], shell=True)

# git version from swift version
match = re.match( r"(.*)-dev(\d+)", version)
if match:
    basetag = match.group(1)
    commits = int(match.group(2))
    process = Popen(["git", "-C", working_folder, "log", "--ancestry-path", "--format=%H", "{0}..HEAD".format(basetag)], stdout=PIPE)
    (output, err) = process.communicate()
    exit_code = process.wait()
    commit = output.splitlines()[-commits].strip()
else:
    basetag = version
    process = Popen(["git", "-C", working_folder, "log", "--format=%H", "-n", "1" "{0}".format(basetag)], stdout=PIPE)
    (output, err) = process.communicate()
    exit_code = process.wait()
    commit = output.strip()

assert(len(commit) > 0)

# Create symbol cache directory
if not os.path.exists(symbol_cache_path):
    os.makedirs(symbol_cache_path)

#print "Checking out commit {0}.".format(commit)
call(["git", "-C", working_folder, "checkout", commit])

os.chdir(working_folder)

downloadInstaller(version)
downloadDebugSymbols(version)
unpackInstaller(version)
unpackDebugSymbols(version)
copyMinidump(minidump_fullpath)
time.sleep(10)
printHumanReadableReport()