fm-weck 1.4.5__py3-none-any.whl → 1.4.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fm_weck/__init__.py +1 -1
- fm_weck/__main__.py +1 -0
- fm_weck/cache_mgr.py +47 -0
- fm_weck/cli.py +120 -11
- fm_weck/engine.py +85 -16
- fm_weck/exceptions.py +1 -0
- fm_weck/resources/{BenchExec-3.25-py3-none-any.whl → BenchExec-3.27-py3-none-any.whl} +0 -0
- fm_weck/resources/__init__.py +1 -1
- fm_weck/resources/fm_tools/2ls.yml +32 -7
- fm_weck/resources/fm_tools/aise.yml +6 -1
- fm_weck/resources/fm_tools/aprove.yml +73 -72
- fm_weck/resources/fm_tools/blast.yml +58 -0
- fm_weck/resources/fm_tools/brick.yml +4 -4
- fm_weck/resources/fm_tools/bubaak-split.yml +3 -3
- fm_weck/resources/fm_tools/bubaak.yml +4 -4
- fm_weck/resources/fm_tools/cadp.yml +95 -0
- fm_weck/resources/fm_tools/cbmc.yml +8 -4
- fm_weck/resources/fm_tools/cetfuzz.yml +4 -3
- fm_weck/resources/fm_tools/coastal.yml +13 -8
- fm_weck/resources/fm_tools/concurrentwitness2test.yml +4 -1
- fm_weck/resources/fm_tools/cooperace.yml +7 -6
- fm_weck/resources/fm_tools/coveriteam-verifier-algo-selection.yml +9 -20
- fm_weck/resources/fm_tools/coveriteam-verifier-parallel-portfolio.yml +9 -20
- fm_weck/resources/fm_tools/coveritest.yml +27 -6
- fm_weck/resources/fm_tools/cpa-bam-bnb.yml +13 -9
- fm_weck/resources/fm_tools/cpa-bam-smg.yml +12 -7
- fm_weck/resources/fm_tools/cpa-lockator.yml +13 -9
- fm_weck/resources/fm_tools/cpa-witness2test.yml +13 -3
- fm_weck/resources/fm_tools/cpachecker.yml +3 -3
- fm_weck/resources/fm_tools/cpv.yml +2 -0
- fm_weck/resources/fm_tools/crux.yml +20 -8
- fm_weck/resources/fm_tools/cseq.yml +9 -4
- fm_weck/resources/fm_tools/dartagnan.yml +12 -1
- fm_weck/resources/fm_tools/deagle.yml +5 -0
- fm_weck/resources/fm_tools/divine.yml +20 -11
- fm_weck/resources/fm_tools/ebf.yml +8 -5
- fm_weck/resources/fm_tools/emergentheta.yml +9 -2
- fm_weck/resources/fm_tools/esbmc-incr.yml +13 -10
- fm_weck/resources/fm_tools/esbmc-kind.yml +12 -11
- fm_weck/resources/fm_tools/fdse.yml +1 -1
- fm_weck/resources/fm_tools/fizzer.yml +15 -24
- fm_weck/resources/fm_tools/frama-c-sv.yml +3 -2
- fm_weck/resources/fm_tools/fshell-witness2test.yml +9 -6
- fm_weck/resources/fm_tools/fusebmc-ia.yml +6 -3
- fm_weck/resources/fm_tools/fusebmc.yml +26 -6
- fm_weck/resources/fm_tools/gazer-theta.yml +10 -7
- fm_weck/resources/fm_tools/gdart-llvm.yml +8 -4
- fm_weck/resources/fm_tools/gdart.yml +3 -0
- fm_weck/resources/fm_tools/goblint.yml +7 -2
- fm_weck/resources/fm_tools/graves-par.yml +9 -14
- fm_weck/resources/fm_tools/graves.yml +10 -4
- fm_weck/resources/fm_tools/gwit.yml +10 -4
- fm_weck/resources/fm_tools/hornix.yml +12 -7
- fm_weck/resources/fm_tools/hybridtiger.yml +12 -7
- fm_weck/resources/fm_tools/infer.yml +12 -7
- fm_weck/resources/fm_tools/java-ranger.yml +6 -3
- fm_weck/resources/fm_tools/jayhorn.yml +6 -4
- fm_weck/resources/fm_tools/jbmc.yml +11 -2
- fm_weck/resources/fm_tools/jcwit.yml +9 -5
- fm_weck/resources/fm_tools/jdart.yml +12 -7
- fm_weck/resources/fm_tools/klee.yml +13 -8
- fm_weck/resources/fm_tools/kleef.yml +3 -3
- fm_weck/resources/fm_tools/korn.yml +11 -1
- fm_weck/resources/fm_tools/lazycseq.yml +10 -7
- fm_weck/resources/fm_tools/legion-symcc.yml +6 -12
- fm_weck/resources/fm_tools/legion.yml +9 -14
- fm_weck/resources/fm_tools/lf-checker.yml +10 -6
- fm_weck/resources/fm_tools/liv.yml +19 -4
- fm_weck/resources/fm_tools/locksmith.yml +10 -6
- fm_weck/resources/fm_tools/metaval++.yml +19 -3
- fm_weck/resources/fm_tools/metaval.yml +21 -3
- fm_weck/resources/fm_tools/mlb.yml +5 -5
- fm_weck/resources/fm_tools/mopsa.yml +9 -7
- fm_weck/resources/fm_tools/nacpa.yml +7 -1
- fm_weck/resources/fm_tools/nitwit.yml +5 -3
- fm_weck/resources/fm_tools/owic.yml +4 -3
- fm_weck/resources/fm_tools/pesco.yml +10 -4
- fm_weck/resources/fm_tools/pichecker.yml +10 -6
- fm_weck/resources/fm_tools/pinaka.yml +13 -8
- fm_weck/resources/fm_tools/predatorhp.yml +6 -4
- fm_weck/resources/fm_tools/prism.yml +67 -0
- fm_weck/resources/fm_tools/proton.yml +6 -6
- fm_weck/resources/fm_tools/prtest.yml +22 -6
- fm_weck/resources/fm_tools/racerf.yml +14 -5
- fm_weck/resources/fm_tools/relay-sv.yml +8 -13
- fm_weck/resources/fm_tools/rizzer.yml +8 -12
- fm_weck/resources/fm_tools/schema.yml +56 -3
- fm_weck/resources/fm_tools/sikraken.yml +3 -3
- fm_weck/resources/fm_tools/spf.yml +13 -8
- fm_weck/resources/fm_tools/svf-svc.yml +15 -6
- fm_weck/resources/fm_tools/symbiotic-witch.yml +9 -5
- fm_weck/resources/fm_tools/symbiotic.yml +6 -2
- fm_weck/resources/fm_tools/testcov.yml +8 -8
- fm_weck/resources/fm_tools/theta.yml +8 -4
- fm_weck/resources/fm_tools/thorn.yml +3 -1
- fm_weck/resources/fm_tools/tracerx-wp.yml +11 -2
- fm_weck/resources/fm_tools/tracerx.yml +5 -1
- fm_weck/resources/fm_tools/uautomizer.yml +7 -12
- fm_weck/resources/fm_tools/ugemcutter.yml +8 -2
- fm_weck/resources/fm_tools/ukojak.yml +5 -2
- fm_weck/resources/fm_tools/ureferee.yml +3 -4
- fm_weck/resources/fm_tools/utaipan.yml +8 -2
- fm_weck/resources/fm_tools/utestgen.yml +11 -3
- fm_weck/resources/fm_tools/vercors.yml +94 -0
- fm_weck/resources/fm_tools/veriabs.yml +11 -9
- fm_weck/resources/fm_tools/veriabsl.yml +10 -8
- fm_weck/resources/fm_tools/verifuzz.yml +16 -266
- fm_weck/resources/fm_tools/verioover.yml +10 -6
- fm_weck/resources/fm_tools/wasp-c.yml +6 -4
- fm_weck/resources/fm_tools/wit4java.yml +20 -13
- fm_weck/resources/fm_tools/witch.yml +8 -4
- fm_weck/resources/fm_tools/witnesslint.yml +37 -9
- fm_weck/resources/fm_tools/witnessmap.yml +16 -1
- fm_weck/run_result.py +38 -0
- fm_weck/runexec_mode.py +7 -2
- fm_weck/serve.py +66 -12
- fm_weck/version_listing.py +25 -0
- {fm_weck-1.4.5.dist-info → fm_weck-1.4.6.dist-info}/METADATA +4 -2
- fm_weck-1.4.6.dist-info/RECORD +153 -0
- {fm_weck-1.4.5.dist-info → fm_weck-1.4.6.dist-info}/WHEEL +1 -1
- fm_weck-1.4.5.dist-info/RECORD +0 -146
- /fm_weck/resources/{BenchExec-3.25-py3-none-any.whl.license → BenchExec-3.27-py3-none-any.whl.license} +0 -0
- {fm_weck-1.4.5.dist-info → fm_weck-1.4.6.dist-info}/entry_points.txt +0 -0
fm_weck/__init__.py
CHANGED
fm_weck/__main__.py
CHANGED
fm_weck/cache_mgr.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# This file is part of fm-weck: executing fm-tools in containerized environments.
|
|
2
|
+
# https://gitlab.com/sosy-lab/software/fm-weck
|
|
3
|
+
#
|
|
4
|
+
# SPDX-FileCopyrightText: 2024 Dirk Beyer <https://www.sosy-lab.org>
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import shutil
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def ask_and_clear(cache_location: str):
|
|
16
|
+
response = (
|
|
17
|
+
input(f"The following cache location will be deleted: {cache_location}\nDo you want to proceed? (Y/n): ")
|
|
18
|
+
.strip()
|
|
19
|
+
.lower()
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if response == "y":
|
|
23
|
+
clear_cache(cache_location)
|
|
24
|
+
elif response == "n":
|
|
25
|
+
return
|
|
26
|
+
else:
|
|
27
|
+
logger.error(f"Unknown command '{response}'\n")
|
|
28
|
+
ask_and_clear(cache_location)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clear_cache(cache_location: str):
|
|
32
|
+
if not cache_location:
|
|
33
|
+
logger.error("Cache location is unknown.")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
if os.path.isdir(cache_location):
|
|
37
|
+
for item in os.listdir(cache_location):
|
|
38
|
+
item_path = os.path.join(cache_location, item)
|
|
39
|
+
try:
|
|
40
|
+
if os.path.isdir(item_path):
|
|
41
|
+
shutil.rmtree(item_path)
|
|
42
|
+
else:
|
|
43
|
+
os.remove(item_path)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
logger.error(f"Error removing {item_path}: {e}")
|
|
46
|
+
else:
|
|
47
|
+
logger.error(f"The path {cache_location} is not a valid directory.")
|
fm_weck/cli.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#
|
|
6
6
|
# SPDX-License-Identifier: Apache-2.0
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
import argparse
|
|
9
10
|
import logging
|
|
10
11
|
import os
|
|
@@ -31,7 +32,10 @@ except ImportError:
|
|
|
31
32
|
return self.value
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
import contextlib
|
|
36
|
+
|
|
34
37
|
from fm_weck import Config
|
|
38
|
+
from fm_weck.cache_mgr import ask_and_clear, clear_cache
|
|
35
39
|
from fm_weck.config import _SEARCH_ORDER
|
|
36
40
|
from fm_weck.resources import iter_fm_data, iter_properties
|
|
37
41
|
|
|
@@ -61,6 +65,48 @@ class ToolQualifier:
|
|
|
61
65
|
return
|
|
62
66
|
|
|
63
67
|
|
|
68
|
+
class ShellCompletion:
|
|
69
|
+
@staticmethod
|
|
70
|
+
def properties_completer(prefix, parsed_args, **kwargs):
|
|
71
|
+
return list_known_properties()
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def versions_completer(prefix, parsed_args, **kwargs):
|
|
75
|
+
return list_known_tools()
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def tool_completer(prefix, parsed_args, **kwargs):
|
|
79
|
+
try:
|
|
80
|
+
import yaml
|
|
81
|
+
except ImportError:
|
|
82
|
+
logger.error("PyYAML is not installed. Cannot complete tool names.")
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
tools_and_versions: dict[str, list] = {}
|
|
86
|
+
|
|
87
|
+
for tool_path in iter_fm_data():
|
|
88
|
+
with open(tool_path) as stream:
|
|
89
|
+
tool_data = yaml.safe_load(stream)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
versions = [version_data["version"] for version_data in tool_data["versions"]]
|
|
93
|
+
except KeyError:
|
|
94
|
+
continue
|
|
95
|
+
tools_and_versions[tool_data["name"]] = versions
|
|
96
|
+
|
|
97
|
+
tools_and_versions_list = [
|
|
98
|
+
f"{tool.lower()}:{version}" for tool, versions in tools_and_versions.items() for version in versions
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
selected_tools = set([tool.split(":")[0] for tool in tools_and_versions_list if tool.startswith(prefix)])
|
|
102
|
+
if len(selected_tools) == 1:
|
|
103
|
+
# If exactly one tool has been selected, suggest versions
|
|
104
|
+
return [tool for tool in tools_and_versions_list if tool.startswith(prefix)]
|
|
105
|
+
|
|
106
|
+
# If no tool has been selected, suggest tool names
|
|
107
|
+
return selected_tools
|
|
108
|
+
|
|
109
|
+
|
|
64
110
|
def add_tool_arg(parser, nargs="?"):
|
|
65
111
|
parser.add_argument(
|
|
66
112
|
"TOOL",
|
|
@@ -69,14 +115,15 @@ def add_tool_arg(parser, nargs="?"):
|
|
|
69
115
|
"the path to a fm-tools yaml file.",
|
|
70
116
|
type=ToolQualifier,
|
|
71
117
|
nargs=nargs,
|
|
72
|
-
)
|
|
118
|
+
).completer = ShellCompletion.tool_completer
|
|
73
119
|
|
|
74
120
|
|
|
75
121
|
def add_shared_args_for_run_modes(parser):
|
|
76
122
|
parser.add_argument(
|
|
77
|
-
"--
|
|
123
|
+
"--offline",
|
|
78
124
|
action="store_true",
|
|
79
|
-
help="
|
|
125
|
+
help="Run the tools offline. The offline mode assumes that both the tool and its info-module are located "
|
|
126
|
+
"at the location specified by the config's 'cache_location' field.",
|
|
80
127
|
)
|
|
81
128
|
|
|
82
129
|
add_tool_arg(parser, nargs=None)
|
|
@@ -149,7 +196,7 @@ def parse(raw_args: list[str]) -> Tuple[Callable[[], None], Namespace]:
|
|
|
149
196
|
),
|
|
150
197
|
required=False,
|
|
151
198
|
default=None,
|
|
152
|
-
)
|
|
199
|
+
).completer = ShellCompletion.properties_completer
|
|
153
200
|
|
|
154
201
|
run.add_argument(
|
|
155
202
|
"-d",
|
|
@@ -178,7 +225,7 @@ def parse(raw_args: list[str]) -> Tuple[Callable[[], None], Namespace]:
|
|
|
178
225
|
"argument_list",
|
|
179
226
|
metavar="args",
|
|
180
227
|
nargs="*",
|
|
181
|
-
help="Additional arguments for the fm-tool.
|
|
228
|
+
help="Additional arguments for the fm-tool. To add them, separate them with '--' from the files.",
|
|
182
229
|
)
|
|
183
230
|
run.set_defaults(main=main_run)
|
|
184
231
|
|
|
@@ -248,6 +295,32 @@ def parse(raw_args: list[str]) -> Tuple[Callable[[], None], Namespace]:
|
|
|
248
295
|
runexec.add_argument("argument_list", metavar="args", nargs="*", help="Arguments for runexec.")
|
|
249
296
|
runexec.set_defaults(main=main_runexec)
|
|
250
297
|
|
|
298
|
+
clear_cache = subparsers.add_parser("clear-cache", help="Clear the cache directory.")
|
|
299
|
+
clear_cache.add_argument(
|
|
300
|
+
"--yes",
|
|
301
|
+
"-y",
|
|
302
|
+
"-Y",
|
|
303
|
+
action="store_true",
|
|
304
|
+
help="Add automatic approval for clearing the cache.",
|
|
305
|
+
required=False,
|
|
306
|
+
default=False,
|
|
307
|
+
)
|
|
308
|
+
clear_cache.set_defaults(main=main_clear_cache)
|
|
309
|
+
|
|
310
|
+
versions = subparsers.add_parser("versions", help="Show the versions of the chosen tool(s).")
|
|
311
|
+
versions.add_argument(
|
|
312
|
+
"TOOL",
|
|
313
|
+
help="The tool(s) for which to print the versions.",
|
|
314
|
+
type=ToolQualifier,
|
|
315
|
+
nargs="+",
|
|
316
|
+
).completer = ShellCompletion.versions_completer
|
|
317
|
+
versions.set_defaults(main=main_versions)
|
|
318
|
+
|
|
319
|
+
with contextlib.suppress(ImportError):
|
|
320
|
+
import argcomplete
|
|
321
|
+
|
|
322
|
+
argcomplete.autocomplete(parser)
|
|
323
|
+
|
|
251
324
|
def help_callback():
|
|
252
325
|
parser.print_help()
|
|
253
326
|
|
|
@@ -333,22 +406,25 @@ def main_run(args: argparse.Namespace):
|
|
|
333
406
|
logger.error("Unknown property %s", args.property)
|
|
334
407
|
return 1
|
|
335
408
|
|
|
336
|
-
|
|
409
|
+
result = run_guided(
|
|
337
410
|
fm_tool=fm_data,
|
|
338
411
|
version=args.TOOL.version,
|
|
339
412
|
configuration=Config(),
|
|
340
413
|
prop=property_path,
|
|
414
|
+
witness=Path(args.witness) if args.witness else None,
|
|
341
415
|
program_files=args.files,
|
|
342
416
|
additional_args=args.argument_list,
|
|
343
417
|
data_model=args.data_model,
|
|
344
|
-
|
|
418
|
+
offline_mode=args.offline,
|
|
345
419
|
)
|
|
346
420
|
|
|
421
|
+
return result.exit_code
|
|
422
|
+
|
|
347
423
|
|
|
348
424
|
def main_runexec(args: argparse.Namespace):
|
|
349
425
|
from .runexec_mode import run_runexec
|
|
350
426
|
|
|
351
|
-
|
|
427
|
+
result = run_runexec(
|
|
352
428
|
benchexec_package=args.benchexec_package,
|
|
353
429
|
use_image=args.use_image,
|
|
354
430
|
configuration=Config(),
|
|
@@ -356,6 +432,8 @@ def main_runexec(args: argparse.Namespace):
|
|
|
356
432
|
command=args.argument_list,
|
|
357
433
|
)
|
|
358
434
|
|
|
435
|
+
return result.exit_code
|
|
436
|
+
|
|
359
437
|
|
|
360
438
|
def main_manual(args: argparse.Namespace):
|
|
361
439
|
from .serve import run_manual
|
|
@@ -369,14 +447,16 @@ def main_manual(args: argparse.Namespace):
|
|
|
369
447
|
logger.error("Unknown tool %s", args.TOOL)
|
|
370
448
|
return 1
|
|
371
449
|
|
|
372
|
-
|
|
450
|
+
result = run_manual(
|
|
373
451
|
fm_tool=fm_data,
|
|
374
452
|
version=args.TOOL.version,
|
|
375
453
|
configuration=Config(),
|
|
376
454
|
command=args.argument_list,
|
|
377
|
-
|
|
455
|
+
offline_mode=args.offline,
|
|
378
456
|
)
|
|
379
457
|
|
|
458
|
+
return result.exit_code
|
|
459
|
+
|
|
380
460
|
|
|
381
461
|
def main_install(args: argparse.Namespace):
|
|
382
462
|
from .serve import setup_fm_tool
|
|
@@ -413,7 +493,35 @@ def main_shell(args: argparse.Namespace):
|
|
|
413
493
|
return 1
|
|
414
494
|
engine = Engine.from_config(fm_data, args.TOOL.version, Config())
|
|
415
495
|
engine.interactive = True
|
|
416
|
-
|
|
496
|
+
result = engine.run(args.entry)
|
|
497
|
+
return result.exit_code
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def main_clear_cache(args: argparse.Namespace):
|
|
501
|
+
if args.yes:
|
|
502
|
+
clear_cache(Config().get("defaults").get("cache_location"))
|
|
503
|
+
else:
|
|
504
|
+
ask_and_clear(Config().get("defaults").get("cache_location"))
|
|
505
|
+
return
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def main_versions(args: argparse.Namespace):
|
|
509
|
+
from .version_listing import VersionListing
|
|
510
|
+
|
|
511
|
+
tools = args.TOOL
|
|
512
|
+
tool_paths = []
|
|
513
|
+
if not args.TOOL:
|
|
514
|
+
logger.error("No fm-tool given. Aborting...")
|
|
515
|
+
return 1
|
|
516
|
+
|
|
517
|
+
for tool in tools:
|
|
518
|
+
try:
|
|
519
|
+
tool_paths.append(resolve_tool(tool))
|
|
520
|
+
except KeyError:
|
|
521
|
+
logger.error("Unknown tool %s", tool)
|
|
522
|
+
return 1
|
|
523
|
+
|
|
524
|
+
VersionListing(tool_paths).print_versions()
|
|
417
525
|
|
|
418
526
|
|
|
419
527
|
def log_no_image_error(tool, config):
|
|
@@ -462,6 +570,7 @@ to your .fm-weck file.
|
|
|
462
570
|
def cli(raw_args: list[str]):
|
|
463
571
|
help_callback, args = parse(raw_args)
|
|
464
572
|
configuration = Config().load(args.config)
|
|
573
|
+
|
|
465
574
|
set_log_options(args.loglevel, args.logfile, configuration)
|
|
466
575
|
if args.dry_run:
|
|
467
576
|
Config().set_dry_run(True)
|
fm_weck/engine.py
CHANGED
|
@@ -5,15 +5,19 @@
|
|
|
5
5
|
#
|
|
6
6
|
# SPDX-License-Identifier: Apache-2.0
|
|
7
7
|
|
|
8
|
+
import io
|
|
8
9
|
import logging
|
|
9
10
|
import shutil
|
|
10
11
|
import signal
|
|
11
12
|
import subprocess
|
|
13
|
+
import sys
|
|
12
14
|
from abc import ABC, abstractmethod
|
|
13
15
|
from functools import cached_property, singledispatchmethod
|
|
14
16
|
from pathlib import Path
|
|
15
17
|
from tempfile import mkdtemp
|
|
16
|
-
from typing import List, Optional, Union
|
|
18
|
+
from typing import Callable, List, Optional, Union
|
|
19
|
+
|
|
20
|
+
from fm_weck.run_result import RunResult
|
|
17
21
|
|
|
18
22
|
try:
|
|
19
23
|
from fm_tools.fmdata import FmData, FmImageConfig
|
|
@@ -33,7 +37,6 @@ from fm_weck.image_mgr import ImageMgr
|
|
|
33
37
|
|
|
34
38
|
logger = logging.getLogger(__name__)
|
|
35
39
|
|
|
36
|
-
|
|
37
40
|
CWD_MOUNT_LOCATION = "/home/cwd"
|
|
38
41
|
CACHE_MOUNT_LOCATION = "/home/fm-weck_cache"
|
|
39
42
|
OUTPUT_MOUNT_LOCATION = "/home/output"
|
|
@@ -43,6 +46,8 @@ RESERVED_LOCATIONS = frozenset([CACHE_MOUNT_LOCATION, CWD_MOUNT_LOCATION, OUTPUT
|
|
|
43
46
|
|
|
44
47
|
class Engine(ABC):
|
|
45
48
|
interactive: bool = False
|
|
49
|
+
handle_io: bool = True
|
|
50
|
+
print_output_to_stdout: bool = True
|
|
46
51
|
add_benchexec_capabilities: bool = False
|
|
47
52
|
add_mounting_capabilities: bool = True
|
|
48
53
|
image: Optional[str] = None
|
|
@@ -111,6 +116,8 @@ class Engine(ABC):
|
|
|
111
116
|
|
|
112
117
|
def setup_command(self):
|
|
113
118
|
return [
|
|
119
|
+
"--security-opt",
|
|
120
|
+
"label=disable",
|
|
114
121
|
"--entrypoint",
|
|
115
122
|
'[""]',
|
|
116
123
|
"-v",
|
|
@@ -137,7 +144,18 @@ class Engine(ABC):
|
|
|
137
144
|
if file.is_file():
|
|
138
145
|
shutil.copy(file, self.output_dir / file.name)
|
|
139
146
|
elif file.is_dir():
|
|
140
|
-
|
|
147
|
+
try:
|
|
148
|
+
# This may fail if there are some permission errors,
|
|
149
|
+
# but we don't want to stop the whole process in case this happens.
|
|
150
|
+
# One such example is UAutomizer's output which contains
|
|
151
|
+
# some config files produced by Java.
|
|
152
|
+
shutil.copytree(
|
|
153
|
+
file,
|
|
154
|
+
self.output_dir,
|
|
155
|
+
dirs_exist_ok=True,
|
|
156
|
+
)
|
|
157
|
+
except shutil.Error as e:
|
|
158
|
+
logger.warning(f"Error while copying the output {file} directory: {e}")
|
|
141
159
|
|
|
142
160
|
def assemble_command(self, command: tuple[str, ...]) -> list[str]:
|
|
143
161
|
base = self.base_command()
|
|
@@ -258,7 +276,8 @@ class Engine(ABC):
|
|
|
258
276
|
return engine
|
|
259
277
|
|
|
260
278
|
@abstractmethod
|
|
261
|
-
def image_from(self, containerfile: Path) -> "BuildCommand":
|
|
279
|
+
def image_from(self, containerfile: Path) -> "BuildCommand":
|
|
280
|
+
pass
|
|
262
281
|
|
|
263
282
|
class BuildCommand(ABC):
|
|
264
283
|
build_args: List[str] = []
|
|
@@ -289,15 +308,43 @@ class Engine(ABC):
|
|
|
289
308
|
|
|
290
309
|
logging.debug("Running command: %s", cmd)
|
|
291
310
|
|
|
292
|
-
ret = subprocess.run(cmd,
|
|
311
|
+
ret = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
293
312
|
|
|
294
313
|
tag = ret.stdout.decode().splitlines()[-1].strip()
|
|
295
314
|
logger.info("Built image %s", tag)
|
|
296
315
|
logger.debug("Output of build image was:\n%s", ret.stdout.decode())
|
|
316
|
+
logger.debug("Error of build image was:\n%s", ret.stderr.decode())
|
|
317
|
+
if ret.returncode != 0:
|
|
318
|
+
raise subprocess.CalledProcessError(
|
|
319
|
+
ret.returncode, cmd, output=ret.stdout.decode(), stderr=ret.stderr.decode()
|
|
320
|
+
)
|
|
297
321
|
|
|
298
322
|
return tag
|
|
299
323
|
|
|
300
|
-
def
|
|
324
|
+
def _run_process_without_attaching_io(
|
|
325
|
+
self, command: tuple[str, ...] | list[str], timeout_sec: Optional[float] = None
|
|
326
|
+
) -> RunResult:
|
|
327
|
+
def terminate_process_group(signal_received, frame):
|
|
328
|
+
if process:
|
|
329
|
+
logging.info("Received signal %s. Terminating container process.", signal_received)
|
|
330
|
+
process.send_signal(signal.SIGTERM)
|
|
331
|
+
|
|
332
|
+
# Register signal handler
|
|
333
|
+
signal.signal(signal.SIGINT, terminate_process_group)
|
|
334
|
+
signal.signal(signal.SIGTERM, terminate_process_group)
|
|
335
|
+
|
|
336
|
+
logger.debug("\n\nRunning command:\n%s\n\n", " ".join(map(str, command)))
|
|
337
|
+
process = subprocess.Popen(command)
|
|
338
|
+
|
|
339
|
+
try:
|
|
340
|
+
process.wait(timeout=timeout_sec)
|
|
341
|
+
except subprocess.TimeoutExpired:
|
|
342
|
+
process.terminate()
|
|
343
|
+
process.wait()
|
|
344
|
+
|
|
345
|
+
return RunResult(command, process.returncode, io.StringIO())
|
|
346
|
+
|
|
347
|
+
def _run_process(self, command: tuple[str, ...] | list[str], timeout_sec: Optional[float] = None) -> RunResult:
|
|
301
348
|
process = None # To make sure process is defined if a signal is caught early
|
|
302
349
|
|
|
303
350
|
def terminate_process_group(signal_received, frame):
|
|
@@ -310,18 +357,37 @@ class Engine(ABC):
|
|
|
310
357
|
signal.signal(signal.SIGTERM, terminate_process_group)
|
|
311
358
|
|
|
312
359
|
logger.debug("\n\nRunning command:\n%s\n\n", " ".join(map(str, command)))
|
|
360
|
+
|
|
361
|
+
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
362
|
+
full_stdout = io.StringIO()
|
|
363
|
+
|
|
364
|
+
def run_and_poll(writer: Callable[[str], None]):
|
|
365
|
+
while process.poll() is None:
|
|
366
|
+
line = process.stdout.readline().decode("utf-8")
|
|
367
|
+
writer(line)
|
|
368
|
+
if self.print_output_to_stdout:
|
|
369
|
+
sys.stdout.write(line)
|
|
370
|
+
|
|
313
371
|
if self.log_file is None:
|
|
314
|
-
|
|
372
|
+
run_and_poll(full_stdout.write)
|
|
315
373
|
else:
|
|
316
|
-
self.log_file.
|
|
317
|
-
|
|
318
|
-
process = subprocess.Popen(command, stdout=f, stderr=f)
|
|
374
|
+
with self.log_file.open("w") as log:
|
|
375
|
+
run_and_poll(log.write)
|
|
319
376
|
|
|
320
377
|
assert process is not None, "Process should be defined at this point."
|
|
321
|
-
|
|
322
|
-
|
|
378
|
+
try:
|
|
379
|
+
process.wait(timeout=timeout_sec)
|
|
380
|
+
except subprocess.TimeoutExpired:
|
|
381
|
+
process.terminate()
|
|
382
|
+
process.wait()
|
|
323
383
|
|
|
324
|
-
|
|
384
|
+
if self.log_file is not None:
|
|
385
|
+
with self.log_file.open("r") as log:
|
|
386
|
+
return RunResult(command, process.returncode, log.read())
|
|
387
|
+
|
|
388
|
+
return RunResult(command, process.returncode, full_stdout.read())
|
|
389
|
+
|
|
390
|
+
def run(self, *command: str, timeout_sec: Optional[float] = None) -> RunResult:
|
|
325
391
|
if self.image is None:
|
|
326
392
|
raise NoImageError("No image set for engine.")
|
|
327
393
|
|
|
@@ -330,11 +396,14 @@ class Engine(ABC):
|
|
|
330
396
|
if self.dry_run:
|
|
331
397
|
print("Command to be executed:")
|
|
332
398
|
print(" ".join(map(str, command)))
|
|
333
|
-
return 0
|
|
399
|
+
return 0, ""
|
|
400
|
+
|
|
401
|
+
if self.interactive or not self.handle_io:
|
|
402
|
+
return self._run_process_without_attaching_io(command, timeout_sec=timeout_sec)
|
|
334
403
|
|
|
335
|
-
|
|
404
|
+
result = self._run_process(command, timeout_sec=timeout_sec)
|
|
336
405
|
self._move_output()
|
|
337
|
-
return
|
|
406
|
+
return result
|
|
338
407
|
|
|
339
408
|
|
|
340
409
|
class Podman(Engine):
|
fm_weck/exceptions.py
CHANGED
|
Binary file
|
fm_weck/resources/__init__.py
CHANGED
|
@@ -16,7 +16,7 @@ CONTAINERFILE = resource_dir / "Containerfile"
|
|
|
16
16
|
FM_DATA_LOCATION = resource_dir / "fm_tools"
|
|
17
17
|
PROPERTY_LOCATION = resource_dir / "properties"
|
|
18
18
|
RUN_WITH_OVERLAY = "run_with_overlay.sh"
|
|
19
|
-
BENCHEXEC_WHL = resource_dir / "BenchExec-3.
|
|
19
|
+
BENCHEXEC_WHL = resource_dir / "BenchExec-3.27-py3-none-any.whl"
|
|
20
20
|
RUNEXEC_SCRIPT = "runexec"
|
|
21
21
|
|
|
22
22
|
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
id: 2ls
|
|
2
2
|
name: 2LS
|
|
3
|
+
description: |
|
|
4
|
+
2LS ("tools") is a verification tool for C programs, built upon the CPROVER framework.
|
|
5
|
+
It allows one to verify user-specified assertions, undefined behaviour, memory leaks,
|
|
6
|
+
and termination properties. The analysis is performed by translating the verification
|
|
7
|
+
task into a second-order logic formula over bitvector, array, and floating-point
|
|
8
|
+
arithmetic theories. The formula is solved by a modular combination of algorithms
|
|
9
|
+
involving unfolding and template-based invariant synthesis with the help of
|
|
10
|
+
incremental SAT solving.
|
|
3
11
|
input_languages:
|
|
4
12
|
- C
|
|
5
13
|
project_url: https://github.com/diffblue/2ls
|
|
@@ -69,21 +77,38 @@ techniques:
|
|
|
69
77
|
- Shape Analysis
|
|
70
78
|
- Bit-Precise Analysis
|
|
71
79
|
- Ranking Functions
|
|
80
|
+
- Template-Based Predicate Inference
|
|
72
81
|
|
|
73
82
|
frameworks_solvers:
|
|
74
83
|
- CProver
|
|
75
84
|
- MiniSAT
|
|
85
|
+
- Glucose
|
|
76
86
|
|
|
77
87
|
literature:
|
|
78
88
|
- doi: 10.1007/978-3-662-48288-9_9
|
|
79
89
|
title: "Safety Verification and Refutation by k-Invariants and k-Induction"
|
|
80
90
|
year: 2015
|
|
81
|
-
- doi: 10.1007/978-3-
|
|
82
|
-
title: "
|
|
83
|
-
year:
|
|
91
|
+
- doi: 10.1007/978-3-662-49674-9_56
|
|
92
|
+
title: "2LS for Program Analysis (Competition Contribution)"
|
|
93
|
+
year: 2016
|
|
94
|
+
- doi: 10.1145/3121136
|
|
95
|
+
title: Bit-Precise Procedure-Modular Termination Analysis
|
|
96
|
+
year: 2018
|
|
97
|
+
- doi: 10.1007/978-3-319-89963-3_24
|
|
98
|
+
title: "2LS: Memory Safety and Non-termination - (Competition Contribution)"
|
|
99
|
+
year: 2018
|
|
100
|
+
- doi: 10.23919/FMCAD.2018.8603009
|
|
101
|
+
title: Template-Based Verification of Heap-Manipulating Programs
|
|
102
|
+
year: 2019
|
|
84
103
|
- doi: 10.1007/978-3-030-45237-7_22
|
|
85
|
-
title: "
|
|
104
|
+
title: "2LS: Heap Analysis and Memory Safety (Competition Contribution)"
|
|
86
105
|
year: 2020
|
|
87
|
-
- doi: 10.1007/978-3-
|
|
88
|
-
title: "
|
|
89
|
-
year:
|
|
106
|
+
- doi: 10.1007/978-3-031-30820-8_31
|
|
107
|
+
title: "2LS: Arrays and Loop Unwinding (Competition Contribution)"
|
|
108
|
+
year: 2023
|
|
109
|
+
- doi: 10.48550/arXiv.2302.02380
|
|
110
|
+
title: "2LS for Program Analysis"
|
|
111
|
+
year: 2023
|
|
112
|
+
- doi: 10.1007/978-3-031-56222-8_12
|
|
113
|
+
title: Template-Based Verification of Array-Manipulating Programs
|
|
114
|
+
year: 2024
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
id: aise
|
|
2
2
|
name: aise
|
|
3
|
+
description: |
|
|
4
|
+
AISE is a software verifer that verifes C programs with respect to reachability properties.
|
|
5
|
+
The key idea of AISE is to synergize symbolic execution (SE) and abstract interpretation (AI).
|
|
3
6
|
input_languages:
|
|
4
7
|
- C
|
|
5
8
|
project_url: https://github.com/zbchen/aise-verifier
|
|
@@ -50,7 +53,9 @@ competition_participations:
|
|
|
50
53
|
techniques:
|
|
51
54
|
- Symbolic Execution
|
|
52
55
|
|
|
53
|
-
frameworks_solvers:
|
|
56
|
+
frameworks_solvers:
|
|
57
|
+
- CVC
|
|
58
|
+
- Z3
|
|
54
59
|
|
|
55
60
|
literature:
|
|
56
61
|
- doi: 10.1007/978-3-031-57256-2_19
|