Skip to content

Commit 360cfcd

Browse files
committed
Add CBMC-running GitHub Action;
This commit adds a GitHub Action that runs the CBMC proofs in this repository upon pushes and pull requests
1 parent e9f92ae commit 360cfcd

File tree

4 files changed

+124
-31
lines changed

4 files changed

+124
-31
lines changed

.github/workflows/ci.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,16 @@ jobs:
114114
with:
115115
config: .github/memory_statistics_config.json
116116
check_against: docs/doxygen/include/size_table.md
117+
proof_ci:
118+
runs-on: cbmc_ubuntu-latest_64-core
119+
steps:
120+
- name: Set up CBMC runner
121+
uses: FreeRTOS/CI-CD-Github-Actions/set_up_cbmc_runner@main
122+
with:
123+
cbmc_version: "5.73.0"
124+
cadical_tag: "latest"
125+
kissat_tag: "latest"
126+
- name: Run CBMC
127+
uses: FreeRTOS/CI-CD-Github-Actions/run_cbmc@main
128+
with:
129+
proofs_dir: test/cbmc/proofs
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4+
# SPDX-License-Identifier: MIT-0
5+
6+
7+
import logging
8+
import pathlib
9+
import shutil
10+
import subprocess
11+
12+
13+
_TOOLS = [
14+
"cadical",
15+
"cbmc",
16+
"cbmc-viewer",
17+
"cbmc-starter-kit-update",
18+
"kissat",
19+
"litani",
20+
]
21+
22+
23+
def _format_versions(table):
24+
lines = [
25+
"<table>",
26+
'<tr><td colspan="2" style="font-weight: bold">Tool Versions</td></tr>',
27+
]
28+
for tool, version in table.items():
29+
if version:
30+
v_str = f'<code><pre style="margin: 0">{version}</pre></code>'
31+
else:
32+
v_str = '<em>not found</em>'
33+
lines.append(
34+
f'<tr><td style="font-weight: bold; padding-right: 1em; '
35+
f'text-align: right;">{tool}:</td>'
36+
f'<td>{v_str}</td></tr>')
37+
lines.append("</table>")
38+
return "\n".join(lines)
39+
40+
41+
def _get_tool_versions():
42+
ret = {}
43+
for tool in _TOOLS:
44+
err = f"Could not determine version of {tool}: "
45+
ret[tool] = None
46+
if not shutil.which(tool):
47+
logging.error("%s'%s' not found on $PATH", err, tool)
48+
continue
49+
cmd = [tool, "--version"]
50+
proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE)
51+
try:
52+
out, _ = proc.communicate(timeout=10)
53+
except subprocess.TimeoutExpired:
54+
logging.error("%s'%s --version' timed out", err, tool)
55+
continue
56+
if proc.returncode:
57+
logging.error(
58+
"%s'%s --version' returned %s", err, tool, str(proc.returncode))
59+
continue
60+
ret[tool] = out.strip()
61+
return ret
62+
63+
64+
def main():
65+
exe_name = pathlib.Path(__file__).name
66+
logging.basicConfig(format=f"{exe_name}: %(message)s")
67+
68+
table = _get_tool_versions()
69+
out = _format_versions(table)
70+
print(out)
71+
72+
73+
if __name__ == "__main__":
74+
main()

test/cbmc/proofs/lib/summarize.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import argparse
55
import json
66
import logging
7+
import os
8+
import sys
79

810

911
DESCRIPTION = """Print 2 tables in GitHub-flavored Markdown that summarize
@@ -89,8 +91,9 @@ def _get_status_and_proof_summaries(run_dict):
8991
count_statuses[status_pretty_name] += 1
9092
except KeyError:
9193
count_statuses[status_pretty_name] = 1
92-
proof = proof_pipeline["name"]
93-
proofs.append([proof, status_pretty_name])
94+
if proof_pipeline["name"] == "print_tool_versions":
95+
continue
96+
proofs.append([proof_pipeline["name"], status_pretty_name])
9497
statuses = [["Status", "Count"]]
9598
for status, count in count_statuses.items():
9699
statuses.append([status, str(count)])
@@ -102,18 +105,39 @@ def print_proof_results(out_file):
102105
Print 2 strings that summarize the proof results.
103106
When printing, each string will render as a GitHub flavored Markdown table.
104107
"""
105-
print("## Summary of CBMC proof results")
106-
try:
107-
with open(out_file, encoding='utf-8') as run_json:
108-
run_dict = json.load(run_json)
109-
summaries = _get_status_and_proof_summaries(
110-
run_dict)
111-
for summary in summaries:
112-
print(_get_rendered_table(summary))
113-
except Exception as ex: # pylint: disable=broad-except
114-
logging.critical("Could not print results. Exception: %s", str(ex))
108+
output = "## Summary of CBMC proof results\n\n"
109+
with open(out_file, encoding='utf-8') as run_json:
110+
run_dict = json.load(run_json)
111+
status_table, proof_table = _get_status_and_proof_summaries(run_dict)
112+
for summary in (status_table, proof_table):
113+
output += _get_rendered_table(summary)
114+
115+
print(output)
116+
sys.stdout.flush()
117+
118+
github_summary_file = os.getenv("GITHUB_STEP_SUMMARY")
119+
if github_summary_file:
120+
with open(github_summary_file, "a") as handle:
121+
print(output, file=handle)
122+
handle.flush()
123+
else:
124+
logging.warning(
125+
"$GITHUB_STEP_SUMMARY not set, not writing summary file")
126+
127+
msg = (
128+
"Click the 'Summary' button to view a Markdown table "
129+
"summarizing all proof results")
130+
if run_dict["status"] != "success":
131+
logging.error("Not all proofs passed.")
132+
logging.error(msg)
133+
sys.exit(1)
134+
logging.info(msg)
115135

116136

117137
if __name__ == '__main__':
118138
args = get_args()
119-
print_proof_results(args.run_file)
139+
logging.basicConfig(format="%(levelname)s: %(message)s")
140+
try:
141+
print_proof_results(args.run_file)
142+
except Exception as ex: # pylint: disable=broad-except
143+
logging.critical("Could not print results. Exception: %s", str(ex))

test/cbmc/proofs/run-cbmc-proofs.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -334,22 +334,6 @@ async def configure_proof_dirs( # pylint: disable=too-many-arguments
334334
queue.task_done()
335335

336336

337-
def add_tool_version_job():
338-
cmd = [
339-
"litani", "add-job",
340-
"--command", "cbmc-starter-kit-print-tool-versions .",
341-
"--description", "printing out tool versions",
342-
"--phony-outputs", str(uuid.uuid4()),
343-
"--pipeline-name", "print_tool_versions",
344-
"--ci-stage", "report",
345-
"--tags", "front-page-text",
346-
]
347-
proc = subprocess.run(cmd)
348-
if proc.returncode:
349-
logging.critical("Could not add tool version printing job")
350-
sys.exit(1)
351-
352-
353337
async def main(): # pylint: disable=too-many-locals
354338
args = get_args()
355339
set_up_logging(args.verbose)
@@ -419,8 +403,6 @@ async def main(): # pylint: disable=too-many-locals
419403

420404
await proof_queue.join()
421405

422-
add_tool_version_job()
423-
424406
print_counter(counter)
425407
print("", file=sys.stderr)
426408

0 commit comments

Comments
 (0)