forked from granular-storage/raft.tla
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtlc.py
More file actions
216 lines (195 loc) · 6.71 KB
/
tlc.py
File metadata and controls
216 lines (195 loc) · 6.71 KB
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the Apache 2.0 License.
#
# Python TLC wrapper script for the raft project
# Goals:
# - No dependencies, no venv, no pip install
# - Set sensible defaults with an eye on performance
# - Capture useful switches for CI, debugging
# - Expose specification configuration through CLI
# - Provide a useful --help, and basic sanity checks
import os
import sys
import shlex
import argparse
import pprint
import pathlib
DEFAULT_JVM_ARGS = [
"-XX:+UseParallelGC", # Use parallel garbage collection, for performance
"-Xmx8G", # Set maximum heap size to 8GB
"-Xms4G", # Set initial heap size to 4GB
"-XX:MaxDirectMemorySize=4G" # Set maximum direct memory size to 4GB
]
DEFAULT_CLASSPATH_ARGS = ["-cp", "tla2tools.jar:CommunityModules-deps.jar"]
USAGE = """
To forward arguments directly to TLC that the wrapper does not support,
run with the -n flag, and evaluate the output beforehand, e.g. `./tlc.py -n mc Spec.tla` -debugger
"""
def cli():
parser = argparse.ArgumentParser(
description="TLC model checker wrapper for the raft project", usage=USAGE
)
# Common options for all commands
parser.add_argument(
"-x",
action="store_true",
help="Print out command and environment before running",
)
parser.add_argument(
"-n",
action="store_true",
help="Print out command and environment, but do not run",
)
# Changes to TLC defaults
parser.add_argument(
"--disable-cdot", action="store_true", help="Disable \\cdot support"
)
parser.add_argument(
"--workers",
type=str,
default="auto",
help="Number of workers to use, default is 'auto'",
)
parser.add_argument(
"--checkpoint", type=int, default=0, help="Checkpoint interval, default is 0"
)
parser.add_argument(
"--lncheck",
type=str,
choices=["final", "default"],
default="final",
help="Liveness check, set to 'default' to run periodically or 'final' to run once at the end, default is final",
)
# Convenient shortcuts applicable to all commands
parser.add_argument(
"--jmx",
action="store_true",
help="Enable JMX to allow monitoring, use echo 'get -b tlc2.tool:type=ModelChecker CurrentState' | jmxterm -l localhost:55449 -i to query",
)
parser.add_argument(
"--dot", action="store_true", help="Generate a dot file for the state graph"
)
parser.add_argument(
"--coverage",
type=int,
default=None,
help="Enable coverage statistics collection with the specified level (typically 1)",
)
parser.add_argument(
"--trace-name",
type=str,
default=None,
help="Name to give to the trace files, defaults to the config name if provided, otherwise to the base name of the spec",
)
parser.add_argument(
"--config",
type=pathlib.Path,
default=None,
help="Path to the TLA+ configuration, defaults to spec name",
)
parser.add_argument(
"--difftrace",
action="store_true",
help="When printing a trace, show only the differences between states",
)
subparsers = parser.add_subparsers(dest="cmd")
# Model checking
mc = subparsers.add_parser("mc", help="Model checking")
# Control spec options of raft
mc.add_argument(
"--raft-configs",
type=str,
default="1C3N",
help="Raft configuration sequence, defaults to 1C3N",
)
# Simulation
sim = subparsers.add_parser("sim", help="Simulation")
sim.add_argument(
"--num",
type=int,
default=None,
help="Number of behaviours to simulate per worker thread",
)
sim.add_argument(
"--depth",
type=int,
default=500,
help="Set the depth of the simulation, defaults to 500",
)
sim.add_argument(
"--max-seconds",
type=int,
default=3600,
help="Set the timeout of the simulation, defaults to 3600 seconds",
)
parser.add_argument(
"spec", type=pathlib.Path, help="Path to the TLA+ specification"
)
return parser
CI = "CI" in os.environ
if __name__ == "__main__":
env = {}
args = cli().parse_args()
jvm_args = DEFAULT_JVM_ARGS
cp_args = DEFAULT_CLASSPATH_ARGS
tlc_args = [
"-workers",
args.workers,
"-checkpoint",
str(args.checkpoint),
"-lncheck",
args.lncheck,
]
trace_name = args.trace_name or os.path.basename(args.config or args.spec).replace(
".tla", ""
)
if CI:
# When run in CI, format output for GitHub, and participate in statistics collection
jvm_args.append("-Dtlc2.TLC.ide=Github")
jvm_args.append(
"-Dutil.ExecutionStatisticsCollector.id=be29f6283abeed2fb1fd0be898bc6601"
)
if args.trace_name or CI:
# When run in CI, or told explicitly, dump a trace
tlc_args.extend(["-dumpTrace", "json", f"{trace_name}.trace.json"])
if args.jmx:
jvm_args.append("-Dcom.sun.management.jmxremote")
jvm_args.append("-Dcom.sun.management.jmxremote.port=55449")
jvm_args.append("-Dcom.sun.management.jmxremote.ssl=false")
jvm_args.append("-Dcom.sun.management.jmxremote.authenticate=false")
if not args.disable_cdot:
jvm_args.append("-Dtlc2.tool.impl.Tool.cdot=true")
if args.config is not None:
tlc_args.extend(["-config", args.config])
if args.dot:
tlc_args.extend(
["-dump", "dot,constrained,colorize,actionlabels", f"{trace_name}.dot"]
)
if args.difftrace:
tlc_args.extend(["-difftrace"])
if args.coverage is not None:
tlc_args.extend(["-coverage", str(args.coverage)])
if args.cmd == "mc":
if args.raft_configs is not None:
env["RAFT_CONFIGS"] = args.raft_configs
elif args.cmd == "sim":
tlc_args.extend(["-simulate"])
if args.num is not None:
tlc_args.extend([f"num={args.num}"])
env["SIM_TIMEOUT"] = str(args.max_seconds)
if args.depth is not None:
tlc_args.extend(["-depth", str(args.depth)])
else:
raise ValueError(f"Unknown command: {args.cmd}")
cmd = ["java"] + jvm_args + cp_args + ["tlc2.TLC"] + tlc_args + [args.spec]
if args.x or args.n:
env_prefix = " ".join(
f"{key}={shlex.quote(value)}" for key, value in env.items()
)
print(f"env {env_prefix} {shlex.join(str(arg) for arg in cmd)}")
if args.n:
sys.exit(0)
merged_env = os.environ.copy()
merged_env.update(env)
os.execvpe("java", cmd, merged_env)