-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathsymbol_server.py
134 lines (112 loc) · 4.7 KB
/
symbol_server.py
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
import sys, os, re, glob, subprocess, biplist
import tornado.ioloop, tornado.web, tornado.template
from tornado.web import asynchronous
serverroot = os.path.abspath(os.path.dirname(__file__))
symbolsdir = os.path.join(serverroot, "symbols")
templateLoader = tornado.template.Loader(serverroot)
serverhost = 'localhost'
serverport = '80'
# dSYM UDID => (dir, app bundle, dSYM)
symbolVersions = {}
def loadVersions():
for dir in os.walk(symbolsdir).next()[1]:
dir = os.path.join(symbolsdir, dir)
os.chdir(dir)
apps = glob.glob('*.app')
dsyms = glob.glob('*.dSYM')
if not apps or not dsyms:
print "Error: invalid version", dir
continue
dsym = os.path.join(dir, dsyms[0])
udid = getDsymUDID(dsym)
if not udid:
print "Error: invalid dSYM", dsym
continue
symbolVersions[udid] = (dir, apps[0], dsyms[0])
#print symbolVersions
def getDsymUDID(dsym):
# mdls output format:
# (
# "15BF90AE-951A-3DE7-B5E8-311E3D14C320"
# )
udid = subprocess.check_output("mdls -name com_apple_xcode_dsym_uuids -raw \"" + dsym + "\"", shell=True)
return re.findall(r'".+?"', udid)[0][1:-1]
def getAppExecutable(app):
plist = biplist.readPlist(os.path.join(app, "Info.plist"))
return plist["CFBundleExecutable"]
def getSymbol(dsymUDID, address, slide, baseAddr, arch):
if dsymUDID not in symbolVersions:
return None
dir, app, _ = symbolVersions[dsymUDID]
executable = getAppExecutable(app)
realAddr = hex(int(address, 16) + int(slide, 16) - int(baseAddr, 16))
os.chdir(dir)
cmd = "xcrun atos -arch %s -o '%s'/'%s' %s" % (arch, app.decode("utf-8"), executable, realAddr)
output = subprocess.check_output(cmd, shell=True)
output = re.sub(r"\(in .+?\)\s", "", output).strip()
return output
# crash log format:
# *** -[NSDictionary initWithDictionary:copyItems:]: dictionary argument is not an NSDictionary
# (null)
# (
# 0 CoreFoundation 0x328632bb + 186
# 1 libobjc.A.dylib 0x3a70e97f objc_exception_throw + 30
# 2 CoreFoundation 0x327efff5 + 212
# 3 CoreFoundation 0x327eff0b + 42
# 4 ???????????? 0x0009ff8f ???????????? + 233359
# 5 ???????????? 0x0009fefb ???????????? + 233211
# 29 libsystem_c.dylib 0x3ab651d8 thread_start + 8
# )
#
# dSYM UUID: 15BF90AE-951A-3DE7-B5E8-311E3D14C320
# CPU Type: armv7
# Slide Address: 0x00001000
# Binary Image: ????
# Base Address: 0x00067000
#
# - or like this -
#
# 5 AppBinaryImage 0x4d1841 _ZN6GBIter7AdvanceEjPb + 36
# 6 AppBinaryImage 0x2bb1d1 _ZNSt6vectorIcSaIcEE13_M_assign + 1648
def convertUmengCrashReport(report):
dsymUDID = re.search(r"dSYM UUID:\s*(.+)\s*", report).group(1)
arch = re.search(r"CPU Type:\s*(.+)\s*", report).group(1)
slide = re.search(r"Slide Address:\s*(.+)\s*", report).group(1)
baseAddr = re.search(r"Base Address:\s*(.+)\s*", report).group(1)
imageName = re.search(r"Binary Image:\s*(.+)\s*", report).group(1)
def replaceUnknownSymbol_Question(match):
addr = match.group(2)
return match.group(1) + getSymbol(dsymUDID, addr, slide, baseAddr, arch)
def replaceUnknownSymbol_Normal(match):
addr = match.group(2)
return match.group(1) + getSymbol(dsymUDID, addr, '0', '0', arch)
report = re.sub(r"((0x.+)\s+)(\?+|_mh_execute_header).*", replaceUnknownSymbol_Question, report)
report = re.sub(r"(" + imageName + r"\s+(0x.+?)\s+).*", replaceUnknownSymbol_Normal, report)
return report
class MainHandler(tornado.web.RequestHandler):
@asynchronous
def get(self):
self.finish(templateLoader.load("index.html").generate(host=serverhost, port=serverport))
class ConvertHandler(tornado.web.RequestHandler):
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*")
@asynchronous
def post(self):
report = self.get_argument("crashreport")
self.finish(convertUmengCrashReport(report))
application = tornado.web.Application([
(r"/", MainHandler),
(r"/convert", ConvertHandler),
], gzip=True)
if __name__ == "__main__":
if len(sys.argv) >= 2:
serverhost = sys.argv[1]
if len(sys.argv) >= 3:
serverport = sys.argv[2]
loadVersions()
if not symbolVersions:
print "No symbols found at", symbolsdir
exit(1)
application.listen(serverport)
print "Server started -", "http://%s:%s"%(serverhost, serverport)
tornado.ioloop.IOLoop.instance().start()