Skip to content

Commit 0492acf

Browse files
committed
JULIET: add more commands
1 parent b99744b commit 0492acf

File tree

6 files changed

+975
-230
lines changed

6 files changed

+975
-230
lines changed

chc/cmdline/chkc

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,84 @@ The chkc juliet set of commands is used to analyze the juliet test suite cases
200200
provided in
201201
202202
https://github.com/static-analysis-engineering/CodeHawk-C-Targets-Juliet
203+
204+
All commands in the juliet collection of commands assume that a pointer to
205+
the location of these (pre-parsed) tests is configured in util/Config.py
206+
or in util/ConfigLocal.py. To check wheter this is the case, run
207+
208+
> chkc juliet check_config
209+
210+
It should show the analyzer configuration and the location of the juliet test
211+
case index file.
212+
213+
214+
The command
215+
216+
> chkc juliet list
217+
218+
lists the individual test cases provided with the date/time the analyzer was
219+
last run on this test, and the date/time when the results were compared with
220+
the accompanying scoring key.
221+
222+
223+
The command
224+
225+
> chkc juliet analyze <cwe> <test> (e.g., CWE121 char_type_overrun_memcpy)
226+
227+
runs the analysis on a juliet test. An individual juliet test typically
228+
consists of 18-60 files. Analysis of all of these files can be run in parallel
229+
with the optional commandline option
230+
231+
--maxprocesses <n>
232+
233+
where <n> is the number of processors to be applied.
234+
235+
A summary of the results is saved in the file summaryresults.json for later
236+
reference. Detailed results are saved in the .cch/a directory, as usual.
237+
238+
239+
The command
240+
241+
> chkc juliet analyze-sets
242+
243+
can be used to automatically run all (or a subset) of the juliet tests
244+
provided. In this case parallelism is applied to the sets, rather than to
245+
the individual files within a set.
246+
247+
The commands below can be run only on tests that have already been analyzed.
248+
249+
The command
250+
251+
> chkc juliet report <cwe> <test>
252+
253+
outputs a summary of the proof obligation analysis statistics for the given
254+
test.
255+
256+
257+
The command
258+
259+
> chkc juliet report-file <cwe> <test> <filename> (e.g., x01.c)
260+
261+
outputs the original source code annotated with the proof obligations and
262+
their analysis results for each line of code for the given source file, as well
263+
as the proof obligation analysis statistics for that source file.
264+
265+
266+
The command
267+
268+
> chkc juliet score <cwe> <test>
269+
270+
compares the analysis results the given test with the expected analysis,
271+
as provided by the scorekey.json file that comes with each test.
272+
273+
274+
The command
275+
276+
> chkc juliet investigate <cwe> <test>
277+
278+
outputs the list of open/violated/delegated proof obligations, together with
279+
their dependencies and diagnostics for the given test.
280+
203281
"""
204282
print(descr)
205283
exit(0)
@@ -384,6 +462,20 @@ def parse() -> argparse.Namespace:
384462

385463
julietanalyze.set_defaults(func=J.juliet_analyze)
386464

465+
# --- analyze-sets
466+
julietanalyzesets = julietparsers.add_parser("analyze-sets")
467+
julietanalyzesets.add_argument(
468+
"--maxprocesses",
469+
help="maximum number of processors to use",
470+
type=int,
471+
default=1)
472+
julietanalyzesets.add_argument(
473+
"--cwes",
474+
nargs="*",
475+
help="restrict analysis to these cwe's (default is all)",
476+
default=[])
477+
julietanalyzesets.set_defaults(func=J.juliet_analyze_sets)
478+
387479
# --- report
388480
julietreport = julietparsers.add_parser("report")
389481
julietreport.add_argument("cwe", help="name of cwe, e.g., CWE121")
@@ -396,7 +488,20 @@ def parse() -> argparse.Namespace:
396488
julietreportfile.add_argument("cwe", help="name of cwe, e.g., CWE121")
397489
julietreportfile.add_argument(
398490
"test", help="name of test case, e.g., CWE129_large")
399-
julietreportfile.add_argument("filename", help="name of c file, e.g., x01.c")
491+
julietreportfile.add_argument(
492+
"filename", help="name of c file, e.g., x01.c")
493+
julietreportfile.add_argument(
494+
"--showcode",
495+
action="store_true",
496+
help="show the proof obligations on the code")
497+
julietreportfile.add_argument(
498+
"--open",
499+
action="store_true",
500+
help="show only open proof obligations on the code")
501+
julietreportfile.add_argument(
502+
"--showinvariants",
503+
action="store_true",
504+
help="show invariants for open proof obligations")
400505
julietreportfile.set_defaults(func=J.juliet_report_file)
401506

402507
# --- score
@@ -420,6 +525,47 @@ def parse() -> argparse.Namespace:
420525

421526
julietscore.set_defaults(func=J.juliet_score)
422527

528+
# --- investigate
529+
julietinvestigate = julietparsers.add_parser("investigate")
530+
julietinvestigate.add_argument("cwe", help="name of cwe, e.g., CWE121")
531+
julietinvestigate.add_argument(
532+
"test", help="name of test case, e.g., CWE129_large")
533+
julietinvestigate.add_argument(
534+
"--xdelegated",
535+
help="exclude delegated proof obligations",
536+
action="store_true")
537+
julietinvestigate.add_argument(
538+
"--xviolated",
539+
help="exclude violated proof obligations",
540+
action="store_true")
541+
julietinvestigate.add_argument(
542+
"--predicates",
543+
nargs="*",
544+
help="predicates of interest (default: all)",
545+
default=[])
546+
julietinvestigate.set_defaults(func=J.juliet_investigate)
547+
548+
# --- report-requests
549+
julietreprequests = julietparsers.add_parser("report-requests")
550+
julietreprequests.add_argument("cwe", help="name of cwe, e.g., CWE121")
551+
julietreprequests.add_argument(
552+
"test", help="name of test case, e.g., CWE129_large")
553+
julietreprequests.set_defaults(func=J.juliet_report_requests)
554+
555+
# --- dashboard
556+
julietdashboard = julietparsers.add_parser("dashboard")
557+
julietdashboard.add_argument(
558+
"--cwe", help="only report results on the given cwe")
559+
julietdashboard.add_argument(
560+
"--variant", help="only report results on the given variant")
561+
julietdashboard.set_defaults(func=J.juliet_dashboard)
562+
563+
# --- project-dashboard
564+
julietpdashboard = julietparsers.add_parser("project-dashboard")
565+
julietpdashboard.add_argument(
566+
"--cwe", help="only report results on the given cwe")
567+
julietpdashboard.set_defaults(func=J.juliet_project_dashboard)
568+
423569
# --------------------------------------------------------------- c-file ---
424570
cfilecmd = subparsers.add_parser("c-file")
425571
cfilecmd.set_defaults(func=cfilecommand)

chc/cmdline/juliet/JulietTestFileRef.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
# ------------------------------------------------------------------------------
2929
"""Juliet scoring reference for a juliet test set file."""
3030

31-
from typing import Any, Dict, List, Optional, TYPE_CHECKING
31+
from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING
32+
3233

3334
if TYPE_CHECKING:
3435
from chc.cmdline.juliet.JulietTestRef import JulietTestRef
@@ -41,9 +42,9 @@ def __init__(
4142
self,
4243
testref: "JulietTestRef",
4344
test: str,
44-
d: Dict[str, Any]) -> None:
45+
d: Dict[str, Dict[str, List[str]]]) -> None:
4546
self._testref = testref
46-
self._test: str
47+
self._test = test
4748
self._d = d
4849
self._violations: Optional[Dict[int, List[JulietViolation]]] = None
4950
self._safecontrols: Optional[Dict[int, List[JulietSafeControl]]] = None
@@ -57,7 +58,7 @@ def test(self) -> str:
5758
return self._test
5859

5960
@property
60-
def violations(self) -> Dict[int, List[JulietViolation]]:
61+
def violations(self) -> Dict[int, List["JulietViolation"]]:
6162
if self._violations is None:
6263
self._violations = {}
6364
for line in self._d["violations"]:
@@ -70,7 +71,7 @@ def violations(self) -> Dict[int, List[JulietViolation]]:
7071
return self._violations
7172

7273
@property
73-
def safe_controls(self) -> Dict[int, List[JulietSafeControl]]:
74+
def safe_controls(self) -> Dict[int, List["JulietSafeControl"]]:
7475
if self._safecontrols is None:
7576
self._safecontrols = {}
7677
for line in self._d["safe-controls"]:
@@ -85,18 +86,34 @@ def safe_controls(self) -> Dict[int, List[JulietSafeControl]]:
8586
def expand(self, m: str) -> List[Dict[str, Any]]:
8687
return self.testref.expand_macro(m)
8788

88-
def get_violations(self) -> List[JulietViolation]:
89+
def get_violations(self) -> List["JulietViolation"]:
8990
result: List[JulietViolation] = []
9091
for (line, v) in sorted(self.violations.items()):
9192
result.extend(v)
9293
return result
9394

94-
def get_safe_controls(self) -> List[JulietSafeControl]:
95+
def iter_violations(
96+
self, f: Callable[[int, "JulietPpo"], None]) -> None:
97+
for (line, ppos) in self.violations.items():
98+
for ppo in ppos:
99+
f(line, ppo)
100+
101+
def get_safe_controls(self) -> List["JulietSafeControl"]:
95102
result: List[JulietSafeControl] = []
96103
for (line, s) in sorted(self.safe_controls.items()):
97104
result.extend(s)
98105
return result
99106

107+
def iter_safe_controls(
108+
self, f: Callable[[int, "JulietPpo"], None]) -> None:
109+
for (line, ppos) in self.safe_controls.items():
110+
for ppo in ppos:
111+
f(line, ppo)
112+
113+
def iter(self, f: Callable[[int, "JulietPpo"], None]) -> None:
114+
self.iter_violations(f)
115+
self.iter_safe_controls(f)
116+
100117
def __str__(self) -> str:
101118
lines: List[str] = []
102119
lines.append(" violations:")
@@ -139,6 +156,14 @@ def test(self) -> str:
139156
def line(self) -> int:
140157
return self._line
141158

159+
@property
160+
def is_violation(self) -> bool:
161+
return False
162+
163+
@property
164+
def is_safe_control(self) -> bool:
165+
return False
166+
142167
@property
143168
def predicate(self) -> str:
144169
return self._d["P"]
@@ -248,6 +273,7 @@ def __init__(
248273
d: Dict[str, Any]) -> None:
249274
JulietPpo.__init__(self, testfileref, test, line, d)
250275

276+
@property
251277
def is_violation(self) -> bool:
252278
return True
253279

@@ -262,5 +288,6 @@ def __init__(
262288
d: Dict[str, Any]) -> None:
263289
JulietPpo.__init__(self, testfileref, test, line, d)
264290

291+
@property
265292
def is_violation(self) -> bool:
266293
return False

chc/cmdline/juliet/JulietTestRef.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
# SOFTWARE.
2828
# ------------------------------------------------------------------------------
2929

30-
from typing import Any, Dict, List, Optional, TYPE_CHECKING
30+
from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING
3131

3232
from chc.cmdline.juliet.JulietTestFileRef import JulietSafeControl
3333
from chc.cmdline.juliet.JulietTestFileRef import JulietTestFileRef
@@ -66,6 +66,10 @@ def cfiles(self) -> Dict[str, JulietTestFileRef]:
6666
self, self.test, self._d["cfiles"][f])
6767
return self._cfiles
6868

69+
def iter(self, f: Callable[[str, JulietTestFileRef], None]) -> None:
70+
for (filename, fileref) in self.cfiles.items():
71+
f(filename, fileref)
72+
6973
def expand_macro(self, m: str) -> List[Dict[str, Any]]:
7074
return self.testsetref.expand_macro(m)
7175

0 commit comments

Comments
 (0)