Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/Scripts/symbolicate.py
1 views
1
#!/usr/bin/env python3
2
3
import argparse
4
import json
5
import os
6
import subprocess
7
import sys
8
9
ENV_NAME = "SIGNAL_IOS_DSYMS"
10
11
12
def error_and_die(to_log):
13
print(file=sys.stderr)
14
print(to_log, file=sys.stderr)
15
print(file=sys.stderr)
16
sys.exit(1)
17
18
19
def run(args):
20
return subprocess.run(
21
args, capture_output=True, check=True, encoding="utf8"
22
).stdout.rstrip()
23
24
25
def parse_args():
26
parser = argparse.ArgumentParser(
27
description=(
28
"Symbolicates .ips files passed as arguments. "
29
"The symbolicated file is written to *.symbolicated.ips. "
30
f"The script assumes you’ve saved the dSYM files in ${ENV_NAME}."
31
)
32
)
33
parser.add_argument(
34
"--open",
35
"-o",
36
action="store_true",
37
help="Open files after they’re symbolicated.",
38
)
39
parser.add_argument(
40
"path",
41
nargs="+",
42
metavar="log.ips",
43
help="Paths to files that should be symbolicated.",
44
)
45
return parser.parse_args()
46
47
48
def parse_json_v2(path):
49
# The new format has a second JSON payload.
50
with open(path, "rb") as file:
51
next(file)
52
return json.load(file)
53
54
55
def get_version(path):
56
try:
57
parse_json_v2(path)
58
return 2
59
except json.decoder.JSONDecodeError:
60
return 1
61
62
63
SCRIPT_PATH_V1 = "Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash"
64
65
66
def symbolicate_v1(xcode_path, path, output_path):
67
script_path = os.path.join(xcode_path, SCRIPT_PATH_V1)
68
args = [script_path, path]
69
env = {**os.environ, "DEVELOPER_DIR": xcode_path}
70
with open(output_path, "wb") as file:
71
subprocess.run(args, check=True, stdout=file, env=env)
72
73
74
SCRIPT_PATH_V2 = "Contents/SharedFrameworks/CoreSymbolicationDT.framework/Resources/CrashSymbolicator.py"
75
76
77
def symbolicate_v2(xcode_path, path, output_path):
78
script_path = os.path.join(xcode_path, SCRIPT_PATH_V2)
79
# Don’t put this on the Desktop or in Documents -- the script can’t find it there.
80
# This directory is searched recursively for .dSYM files.
81
symbols_path = os.getenv(ENV_NAME)
82
if symbols_path is None or not os.path.exists(symbols_path):
83
error_and_die(
84
f"The {ENV_NAME} environment variable should be set to a directory containing .dSYM files."
85
)
86
args = [
87
"python3",
88
script_path,
89
"--dsym",
90
symbols_path,
91
"--output",
92
output_path,
93
"--pretty",
94
path,
95
]
96
subprocess.run(args, check=True)
97
98
99
def omit(d, key_to_remove):
100
return {key: value for key, value in d.items() if key != key_to_remove}
101
102
103
def ensure_symbolication_happened_v2(path, output_path):
104
def equal(a, b):
105
"""
106
Like `==` but ignores changes to `symbolLocation` because those can
107
change even if symbolication didn't happen.
108
"""
109
if not isinstance(a, type(b)):
110
return False
111
if isinstance(a, dict):
112
cleaned_a = omit(a, "symbolLocation")
113
cleaned_b = omit(b, "symbolLocation")
114
if len(cleaned_a) != len(cleaned_b):
115
return False
116
for key, a_value in cleaned_a.items():
117
if key not in cleaned_b:
118
return False
119
b_value = cleaned_b[key]
120
if not equal(a_value, b_value):
121
return False
122
return True
123
if isinstance(a, list):
124
if len(a) != len(b):
125
return False
126
for a_item, b_item in zip(a, b):
127
if not equal(a_item, b_item):
128
return False
129
return True
130
return a == b
131
132
original = parse_json_v2(path)
133
allegedly_symbolicated = parse_json_v2(output_path)
134
if equal(original, allegedly_symbolicated):
135
error_and_die(
136
"Nothing happened when you symbolicated. Do you have the version downloaded in the right place? Did you extract the relevant dSYMs.zip?"
137
)
138
139
140
def main():
141
ns = parse_args()
142
143
dev_path = run(["xcode-select", "-p"])
144
xcode_path = os.path.normpath(os.path.join(dev_path, *([os.pardir] * 2)))
145
146
for path in ns.path:
147
version = get_version(path)
148
base, ext = os.path.splitext(path)
149
output_path = base + ".symbolicated" + ext
150
if version == 2:
151
symbolicate_v2(xcode_path, path, output_path)
152
ensure_symbolication_happened_v2(path, output_path)
153
else:
154
symbolicate_v1(xcode_path, path, output_path)
155
if ns.open:
156
subprocess.run(["open", output_path], check=True)
157
158
159
if __name__ == "__main__":
160
main()
161
162