fm-weck 1.4.4__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.
Files changed (123) hide show
  1. fm_weck/__init__.py +1 -1
  2. fm_weck/__main__.py +1 -0
  3. fm_weck/cache_mgr.py +47 -0
  4. fm_weck/cli.py +120 -11
  5. fm_weck/engine.py +86 -17
  6. fm_weck/exceptions.py +1 -0
  7. fm_weck/resources/{BenchExec-3.25-py3-none-any.whl → BenchExec-3.27-py3-none-any.whl} +0 -0
  8. fm_weck/resources/__init__.py +1 -1
  9. fm_weck/resources/fm_tools/2ls.yml +32 -7
  10. fm_weck/resources/fm_tools/aise.yml +6 -1
  11. fm_weck/resources/fm_tools/aprove.yml +73 -72
  12. fm_weck/resources/fm_tools/blast.yml +58 -0
  13. fm_weck/resources/fm_tools/brick.yml +4 -4
  14. fm_weck/resources/fm_tools/bubaak-split.yml +3 -3
  15. fm_weck/resources/fm_tools/bubaak.yml +4 -4
  16. fm_weck/resources/fm_tools/cadp.yml +95 -0
  17. fm_weck/resources/fm_tools/cbmc.yml +8 -4
  18. fm_weck/resources/fm_tools/cetfuzz.yml +4 -3
  19. fm_weck/resources/fm_tools/coastal.yml +13 -8
  20. fm_weck/resources/fm_tools/concurrentwitness2test.yml +4 -1
  21. fm_weck/resources/fm_tools/cooperace.yml +7 -6
  22. fm_weck/resources/fm_tools/coveriteam-verifier-algo-selection.yml +9 -20
  23. fm_weck/resources/fm_tools/coveriteam-verifier-parallel-portfolio.yml +9 -20
  24. fm_weck/resources/fm_tools/coveritest.yml +27 -6
  25. fm_weck/resources/fm_tools/cpa-bam-bnb.yml +13 -9
  26. fm_weck/resources/fm_tools/cpa-bam-smg.yml +12 -7
  27. fm_weck/resources/fm_tools/cpa-lockator.yml +13 -9
  28. fm_weck/resources/fm_tools/cpa-witness2test.yml +13 -3
  29. fm_weck/resources/fm_tools/cpachecker.yml +6 -3
  30. fm_weck/resources/fm_tools/cpv.yml +2 -0
  31. fm_weck/resources/fm_tools/crux.yml +20 -8
  32. fm_weck/resources/fm_tools/cseq.yml +9 -4
  33. fm_weck/resources/fm_tools/dartagnan.yml +12 -1
  34. fm_weck/resources/fm_tools/deagle.yml +5 -0
  35. fm_weck/resources/fm_tools/divine.yml +20 -11
  36. fm_weck/resources/fm_tools/ebf.yml +8 -5
  37. fm_weck/resources/fm_tools/emergentheta.yml +9 -2
  38. fm_weck/resources/fm_tools/esbmc-incr.yml +13 -10
  39. fm_weck/resources/fm_tools/esbmc-kind.yml +12 -11
  40. fm_weck/resources/fm_tools/fdse.yml +1 -1
  41. fm_weck/resources/fm_tools/fizzer.yml +16 -25
  42. fm_weck/resources/fm_tools/frama-c-sv.yml +3 -2
  43. fm_weck/resources/fm_tools/fshell-witness2test.yml +9 -6
  44. fm_weck/resources/fm_tools/fusebmc-ia.yml +6 -3
  45. fm_weck/resources/fm_tools/fusebmc.yml +26 -6
  46. fm_weck/resources/fm_tools/gazer-theta.yml +10 -7
  47. fm_weck/resources/fm_tools/gdart-llvm.yml +8 -4
  48. fm_weck/resources/fm_tools/gdart.yml +3 -0
  49. fm_weck/resources/fm_tools/goblint.yml +7 -2
  50. fm_weck/resources/fm_tools/graves-par.yml +9 -14
  51. fm_weck/resources/fm_tools/graves.yml +10 -4
  52. fm_weck/resources/fm_tools/gwit.yml +10 -4
  53. fm_weck/resources/fm_tools/hornix.yml +12 -7
  54. fm_weck/resources/fm_tools/hybridtiger.yml +12 -7
  55. fm_weck/resources/fm_tools/infer.yml +12 -7
  56. fm_weck/resources/fm_tools/java-ranger.yml +6 -3
  57. fm_weck/resources/fm_tools/jayhorn.yml +6 -4
  58. fm_weck/resources/fm_tools/jbmc.yml +11 -2
  59. fm_weck/resources/fm_tools/jcwit.yml +9 -5
  60. fm_weck/resources/fm_tools/jdart.yml +12 -7
  61. fm_weck/resources/fm_tools/klee.yml +13 -8
  62. fm_weck/resources/fm_tools/kleef.yml +3 -3
  63. fm_weck/resources/fm_tools/korn.yml +11 -1
  64. fm_weck/resources/fm_tools/lazycseq.yml +10 -7
  65. fm_weck/resources/fm_tools/legion-symcc.yml +6 -12
  66. fm_weck/resources/fm_tools/legion.yml +9 -14
  67. fm_weck/resources/fm_tools/lf-checker.yml +10 -6
  68. fm_weck/resources/fm_tools/liv.yml +19 -4
  69. fm_weck/resources/fm_tools/locksmith.yml +10 -6
  70. fm_weck/resources/fm_tools/metaval++.yml +19 -3
  71. fm_weck/resources/fm_tools/metaval.yml +21 -3
  72. fm_weck/resources/fm_tools/mlb.yml +6 -6
  73. fm_weck/resources/fm_tools/mopsa.yml +9 -7
  74. fm_weck/resources/fm_tools/nacpa.yml +7 -1
  75. fm_weck/resources/fm_tools/nitwit.yml +5 -3
  76. fm_weck/resources/fm_tools/owic.yml +4 -3
  77. fm_weck/resources/fm_tools/pesco.yml +10 -4
  78. fm_weck/resources/fm_tools/pichecker.yml +10 -6
  79. fm_weck/resources/fm_tools/pinaka.yml +13 -8
  80. fm_weck/resources/fm_tools/predatorhp.yml +6 -4
  81. fm_weck/resources/fm_tools/prism.yml +67 -0
  82. fm_weck/resources/fm_tools/proton.yml +6 -6
  83. fm_weck/resources/fm_tools/prtest.yml +22 -6
  84. fm_weck/resources/fm_tools/racerf.yml +14 -5
  85. fm_weck/resources/fm_tools/relay-sv.yml +8 -13
  86. fm_weck/resources/fm_tools/rizzer.yml +8 -12
  87. fm_weck/resources/fm_tools/schema.yml +60 -3
  88. fm_weck/resources/fm_tools/sikraken.yml +3 -3
  89. fm_weck/resources/fm_tools/spf.yml +13 -8
  90. fm_weck/resources/fm_tools/svf-svc.yml +15 -6
  91. fm_weck/resources/fm_tools/symbiotic-witch.yml +9 -5
  92. fm_weck/resources/fm_tools/symbiotic.yml +8 -4
  93. fm_weck/resources/fm_tools/testcov.yml +63 -5
  94. fm_weck/resources/fm_tools/theta.yml +8 -4
  95. fm_weck/resources/fm_tools/thorn.yml +3 -1
  96. fm_weck/resources/fm_tools/tracerx-wp.yml +11 -2
  97. fm_weck/resources/fm_tools/tracerx.yml +5 -1
  98. fm_weck/resources/fm_tools/uautomizer.yml +7 -12
  99. fm_weck/resources/fm_tools/ugemcutter.yml +8 -2
  100. fm_weck/resources/fm_tools/ukojak.yml +5 -2
  101. fm_weck/resources/fm_tools/ureferee.yml +3 -4
  102. fm_weck/resources/fm_tools/utaipan.yml +8 -2
  103. fm_weck/resources/fm_tools/utestgen.yml +11 -3
  104. fm_weck/resources/fm_tools/vercors.yml +94 -0
  105. fm_weck/resources/fm_tools/veriabs.yml +11 -9
  106. fm_weck/resources/fm_tools/veriabsl.yml +10 -8
  107. fm_weck/resources/fm_tools/verifuzz.yml +16 -266
  108. fm_weck/resources/fm_tools/verioover.yml +10 -6
  109. fm_weck/resources/fm_tools/wasp-c.yml +6 -4
  110. fm_weck/resources/fm_tools/wit4java.yml +20 -13
  111. fm_weck/resources/fm_tools/witch.yml +8 -4
  112. fm_weck/resources/fm_tools/witnesslint.yml +37 -9
  113. fm_weck/resources/fm_tools/witnessmap.yml +16 -2
  114. fm_weck/run_result.py +38 -0
  115. fm_weck/runexec_mode.py +7 -2
  116. fm_weck/serve.py +66 -12
  117. fm_weck/version_listing.py +25 -0
  118. {fm_weck-1.4.4.dist-info → fm_weck-1.4.6.dist-info}/METADATA +4 -2
  119. fm_weck-1.4.6.dist-info/RECORD +153 -0
  120. {fm_weck-1.4.4.dist-info → fm_weck-1.4.6.dist-info}/WHEEL +1 -1
  121. fm_weck-1.4.4.dist-info/RECORD +0 -146
  122. /fm_weck/resources/{BenchExec-3.25-py3-none-any.whl.license → BenchExec-3.27-py3-none-any.whl.license} +0 -0
  123. {fm_weck-1.4.4.dist-info → fm_weck-1.4.6.dist-info}/entry_points.txt +0 -0
fm_weck/__init__.py CHANGED
@@ -8,4 +8,4 @@
8
8
  from .config import Config # noqa: F401
9
9
  from .image_mgr import ImageMgr # noqa: F401
10
10
 
11
- __version__ = "1.4.4"
11
+ __version__ = "1.4.6"
fm_weck/__main__.py CHANGED
@@ -4,6 +4,7 @@
4
4
  # SPDX-FileCopyrightText: 2024 Dirk Beyer <https://www.sosy-lab.org>
5
5
  #
6
6
  # SPDX-License-Identifier: Apache-2.0
7
+ # PYTHON_ARGCOMPLETE_OK
7
8
 
8
9
  import sys
9
10
 
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
- "--skip-download",
123
+ "--offline",
78
124
  action="store_true",
79
- help="Do not download the fm-tool, even if it is not available in the cache.",
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." " To add them, separate them with '--' from the files.",
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
- return run_guided(
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
- skip_download=args.skip_download,
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
- return run_runexec(
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
- return run_manual(
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
- skip_download=args.skip_download,
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
- return engine.run(args.entry)
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
- shutil.copytree(file, self.output_dir, dirs_exist_ok=True)
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, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
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 _run_process(self, command: tuple[str, ...] | list[str]):
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
- process = subprocess.Popen(command)
372
+ run_and_poll(full_stdout.write)
315
373
  else:
316
- self.log_file.parent.mkdir(parents=True, exist_ok=True)
317
- with self.log_file.open("wb") as f:
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
- process.wait()
322
- return process.returncode
378
+ try:
379
+ process.wait(timeout=timeout_sec)
380
+ except subprocess.TimeoutExpired:
381
+ process.terminate()
382
+ process.wait()
323
383
 
324
- def run(self, *command: str) -> int:
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
- return_code = self._run_process(command)
404
+ result = self._run_process(command, timeout_sec=timeout_sec)
336
405
  self._move_output()
337
- return return_code
406
+ return result
338
407
 
339
408
 
340
409
  class Podman(Engine):
@@ -354,7 +423,7 @@ class Podman(Engine):
354
423
  return [
355
424
  # "--annotation",
356
425
  # "run.oci.keep_original_groups=1",
357
- "--cgroups=split",
426
+ # "--cgroups=split",
358
427
  "--security-opt",
359
428
  "unmask=/sys/fs/cgroup",
360
429
  "--security-opt",
fm_weck/exceptions.py CHANGED
@@ -5,5 +5,6 @@
5
5
  #
6
6
  # SPDX-License-Identifier: Apache-2.0
7
7
 
8
+
8
9
  class NoImageError(Exception):
9
10
  pass
@@ -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.25-py3-none-any.whl"
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-031-30820-8_31
82
- title: "2ls: Arrays and Loop Unwinding (Competition Contribution)"
83
- year: 2023
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: "2ls: Heap Analysis and Memory Safety (Competition Contribution)"
104
+ title: "2LS: Heap Analysis and Memory Safety (Competition Contribution)"
86
105
  year: 2020
87
- - doi: 10.1007/978-3-662-49674-9_56
88
- title: "2ls for Program Analysis (Competition Contribution)"
89
- year: 2016
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