Skip to content

Commit 75f6130

Browse files
authored
Merge pull request #744 from goblint/yaml-witness
Add YAML witness output
2 parents 45b2269 + e9fc8bd commit 75f6130

File tree

16 files changed

+281
-99
lines changed

16 files changed

+281
-99
lines changed

dune-project

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
(sha (>= 1.12))
4646
cpu
4747
arg-complete
48+
yaml
49+
uuidm
4850
(conf-gmp (>= 3)) ; only needed transitively, but they don't have lower bound, which is needed on MacOS
4951
(conf-ruby :with-test)
5052
(benchmark :with-test) ; TODO: make this optional somehow, (optional) on bench executable doesn't work

goblint.opam

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ depends: [
4141
"sha" {>= "1.12"}
4242
"cpu"
4343
"arg-complete"
44+
"yaml"
45+
"uuidm"
4446
"conf-gmp" {>= "3"}
4547
"conf-ruby" {with-test}
4648
"benchmark" {with-test}

goblint.opam.locked

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ depends: [
3131
"bigarray-compat" {= "1.1.0"}
3232
"bigstringaf" {= "0.8.0"}
3333
"biniou" {= "1.2.1"}
34+
"bos" {= "0.2.1"}
3435
"camlidl" {= "1.09"}
3536
"cmdliner" {= "1.1.1" & with-doc}
3637
"conf-autoconf" {= "0.1"}
@@ -43,16 +44,20 @@ depends: [
4344
"cppo" {= "1.6.8"}
4445
"cpu" {= "2.0.0"}
4546
"csexp" {= "1.5.1"}
47+
"ctypes" {= "0.20.1"}
4648
"dune" {= "3.0.3"}
4749
"dune-private-libs" {= "3.0.3"}
4850
"dune-site" {= "3.0.3"}
4951
"dyn" {= "3.0.3"}
52+
"dune-configurator" {= "3.0.3"}
5053
"easy-format" {= "1.3.2"}
51-
"fmt" {= "0.9.0" & with-doc}
54+
"fmt" {= "0.9.0"}
5255
"fpath" {= "0.7.3"}
5356
"goblint-cil" {= "1.8.2"}
57+
"integers" {= "0.7.0"}
5458
"json-data-encoding" {= "0.11"}
5559
"jsonrpc" {= "1.10.3"}
60+
"logs" {= "0.7.0"}
5661
"mlgmpidl" {= "1.2.14"}
5762
"num" {= "1.4"}
5863
"ocaml" {= "4.14.0"}
@@ -80,6 +85,7 @@ depends: [
8085
"qcheck-ounit" {= "0.18.1" & with-test}
8186
"re" {= "1.10.3" & with-doc}
8287
"result" {= "1.5"}
88+
"rresult" {= "0.7.0"}
8389
"seq" {= "base" & with-test}
8490
"sexplib0" {= "v0.14.0"}
8591
"sha" {= "1.15.2"}
@@ -89,7 +95,9 @@ depends: [
8995
"topkg" {= "1.0.5"}
9096
"tyxml" {= "4.5.0" & with-doc}
9197
"uri" {= "4.2.0"}
98+
"uuidm" {= "0.9.8"}
9299
"uutf" {= "1.0.3" & with-doc}
100+
"yaml" {= "3.1.0"}
93101
"yojson" {= "1.7.0"}
94102
"zarith" {= "1.12"}
95103
]

src/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
(public_name goblint.lib)
99
(wrapped false)
1010
(modules :standard \ goblint mainarinc mainspec privPrecCompare apronPrecCompare messagesCompare)
11-
(libraries goblint.sites goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath
11+
(libraries goblint.sites goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm
1212
; Conditionally compile based on whether apron optional dependency is installed or not.
1313
; Alternative dependencies seem like the only way to optionally depend on optional dependencies.
1414
; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies.

src/framework/analyses.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ struct
207207
Messages.xml_file_name := fn;
208208
BatPrintf.printf "Writing xml to temp. file: %s\n%!" fn;
209209
BatPrintf.fprintf f "<run>";
210-
BatPrintf.fprintf f "<parameters>%a</parameters>" (BatArray.print ~first:"" ~last:"" ~sep:" " BatString.print) BatSys.argv;
210+
BatPrintf.fprintf f "<parameters>%s</parameters>" Goblintutil.command_line;
211211
BatPrintf.fprintf f "<statistics>";
212212
(* FIXME: This is a super ridiculous hack we needed because BatIO has no way to get the raw channel CIL expects here. *)
213213
let name, chn = Filename.open_temp_file "stat" "goblint" in
@@ -251,7 +251,7 @@ struct
251251
let p_file f x = fprintf f "{\n \"name\": \"%s\",\n \"path\": \"%s\",\n \"functions\": %a\n}" (Filename.basename x) x (p_list p_fun) (SH.find_all file2funs x) in
252252
let write_file f fn =
253253
printf "Writing json to temp. file: %s\n%!" fn;
254-
fprintf f "{\n \"parameters\": \"%a\",\n " (BatArray.print ~first:"" ~last:"" ~sep:" " BatString.print) BatSys.argv;
254+
fprintf f "{\n \"parameters\": \"%s\",\n " Goblintutil.command_line;
255255
fprintf f "\"files\": %a,\n " (p_enum p_file) (SH.keys file2funs);
256256
fprintf f "\"results\": [\n %a\n]\n" printJson (Lazy.force table);
257257
(*gtfxml f gtable;*)

src/framework/control.ml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ struct
495495
GobConfig.write_file config;
496496
let module Meta = struct
497497
type t = { command : string; version: string; timestamp : float; localtime : string } [@@deriving to_yojson]
498-
let json = to_yojson { command = GU.command; version = Version.goblint; timestamp = Unix.time (); localtime = localtime () }
498+
let json = to_yojson { command = GU.command_line; version = Version.goblint; timestamp = Unix.time (); localtime = localtime () }
499499
end
500500
in
501501
(* Yojson.Safe.to_file meta Meta.json; *)
@@ -678,6 +678,11 @@ struct
678678
if get_bool "ana.sv-comp.enabled" then
679679
WResult.write lh gh entrystates;
680680

681+
if get_bool "witness.yaml.enabled" then (
682+
let module YWitness = YamlWitness.Make (struct let file = file end) (Cfg) (Spec) (EQSys) (LHT) (GHT) in
683+
YWitness.write lh gh
684+
);
685+
681686
let marshal = Spec.finalize () in
682687
(* copied from solve_and_postprocess *)
683688
let gobview = get_bool "gobview" in

src/goblint.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let main () =
2121
handle_flags ();
2222
if get_bool "dbg.verbose" then (
2323
print_endline (localtime ());
24-
print_endline command;
24+
print_endline Goblintutil.command_line;
2525
);
2626
let file = Fun.protect ~finally:GoblintDir.finalize preprocess_and_merge in
2727
if get_bool "server.enabled" then Server.start file else (

src/transform/evalAssert.ml

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
open Prelude
22
open Cil
33
open Formatcil
4-
module ES = SetDomain.Make(Exp.Exp)
54

65
(** Instruments a program by inserting asserts either:
7-
- After an assignment to a variable (unless trans.assert.full is activated) and
6+
- After an assignment to a variable (unless witness.invariant.full is activated) and
87
- At join points about all local variables
98
109
OR
1110
12-
- Only after pthread_mutex_lock (trans.assert.only-at-locks), about all locals and globals
11+
- Only after pthread_mutex_lock (witness.invariant.after-lock), about all locals and globals
1312
14-
Limitations without trans.assert.only-at locks:
13+
Limitations without witness.invariant.after-lock:
1514
- Currently only works for top-level variables (not inside an array, a struct, ...)
1615
- Does not work for accesses through pointers
1716
- At join points asserts all locals, but ideally should only assert ones that are
@@ -23,9 +22,6 @@ module ES = SetDomain.Make(Exp.Exp)
2322
*)
2423

2524
module EvalAssert = struct
26-
(* should asserts of conjuncts be one-by-one instead of one big assert? *)
27-
let distinctAsserts = true
28-
2925
(* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *)
3026
let surroundByAtomic = true
3127

@@ -35,30 +31,12 @@ module EvalAssert = struct
3531
let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid [])
3632

3733

38-
(* Turns an expression into alist of conjuncts, pulling out common conjuncts from top-level disjunctions *)
39-
let rec pullOutCommonConjuncts e =
40-
let rec to_conjunct_set = function
41-
| BinOp(LAnd,e1,e2,_) -> ES.join (to_conjunct_set e1) (to_conjunct_set e2)
42-
| e -> ES.singleton e
43-
in
44-
let combine_conjuncts es = ES.fold (fun e acc -> match acc with | None -> Some e | Some acce -> Some (BinOp(LAnd,acce,e,Cil.intType))) es None in
45-
match e with
46-
| BinOp(LOr, e1, e2,t) ->
47-
let e1s = pullOutCommonConjuncts e1 in
48-
let e2s = pullOutCommonConjuncts e2 in
49-
let common = ES.inter e1s e2s in
50-
let e1s' = ES.diff e1s e2s in
51-
let e2s' = ES.diff e2s e1s in
52-
(match combine_conjuncts e1s', combine_conjuncts e2s' with
53-
| Some e1e, Some e2e -> ES.add (BinOp(LOr,e1e,e2e,Cil.intType)) common
54-
| _ -> common (* if one of the disjuncts is empty, it is equivalent to true here *)
55-
)
56-
| e -> to_conjunct_set e
57-
5834
class visitor (ask:Cil.location -> Queries.ask) = object(self)
5935
inherit nopCilVisitor
60-
val full = GobConfig.get_bool "trans.assert.full"
61-
val only_at_locks = GobConfig.get_bool "trans.assert.only-at-locks"
36+
val full = GobConfig.get_bool "witness.invariant.full"
37+
(* TODO: handle witness.invariant.loop-head *)
38+
val emit_after_lock = GobConfig.get_bool "witness.invariant.after-lock"
39+
val emit_other = GobConfig.get_bool "witness.invariant.other"
6240

6341
method! vstmt s =
6442
let is_lock exp args =
@@ -78,7 +56,7 @@ module EvalAssert = struct
7856
} in
7957
match (ask loc).f (Queries.Invariant context) with
8058
| `Lifted e ->
81-
let es = if distinctAsserts then ES.elements (pullOutCommonConjuncts e) else [e] in
59+
let es = WitnessUtil.InvariantExp.process_exp e in
8260
let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv ass); ("exp", Fe e)]) es in
8361
if surroundByAtomic then
8462
let abegin = (cInstr ("%v:__VERIFIER_atomic_begin();") loc [("__VERIFIER_atomic_begin", Fv atomicBegin)]) in
@@ -94,13 +72,13 @@ module EvalAssert = struct
9472
let unique_succ = s.succs <> [] && (List.hd s.succs).preds |> List.length < 2 in
9573
let instrument i loc =
9674
let instrument' lval =
97-
let lval_arg = if full || only_at_locks then None else lval in
75+
let lval_arg = if full then None else lval in
9876
make_assert loc lval_arg
9977
in
10078
match i with
101-
| Set (lval, _, _, _) when not only_at_locks -> instrument' (Some lval)
102-
| Call (lval, _, _, _, _) when not only_at_locks -> instrument' lval
103-
| Call (_, exp, args, _, _) when is_lock exp args -> instrument' None
79+
| Call (_, exp, args, _, _) when emit_after_lock && is_lock exp args -> instrument' None
80+
| Set (lval, _, _, _) when emit_other -> instrument' (Some lval)
81+
| Call (lval, _, _, _, _) when emit_other -> instrument' lval
10482
| _ -> []
10583
in
10684
let rec instrument_instructions = function
@@ -132,10 +110,10 @@ module EvalAssert = struct
132110

133111
let instrument_join s =
134112
match s.preds with
135-
| [p1; p2] when not only_at_locks ->
113+
| [p1; p2] when emit_other ->
136114
(* exactly two predecessors -> join point, assert locals if they changed *)
137115
let join_loc = get_stmtLoc s.skind in
138-
(* Possible enhancement: It would be nice to only assert locals here that were modified in either branch if trans.assert.full is false *)
116+
(* Possible enhancement: It would be nice to only assert locals here that were modified in either branch if witness.invariant.full is false *)
139117
let asserts = make_assert join_loc None in
140118
self#queueInstr asserts; ()
141119
| _ -> ()
@@ -161,7 +139,7 @@ module EvalAssert = struct
161139
else
162140
()
163141
in
164-
if not only_at_locks then (add_asserts b1; add_asserts b2);
142+
if emit_other then (add_asserts b1; add_asserts b2);
165143
s
166144
| _ -> s
167145
in

src/util/goblintutil.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ let arinc_period = if scrambled then "M165" else "PERIOD"
138138
let arinc_time_capacity = if scrambled then "M166" else "TIME_CAPACITY"
139139

140140
let exe_dir = Fpath.(parent (v Sys.executable_name))
141-
let command = String.concat " " (Array.to_list Sys.argv)
141+
let command_line = match Array.to_list Sys.argv with
142+
| command :: arguments -> Filename.quote_command command arguments
143+
| [] -> assert false
142144

143145
(* https://ocaml.org/api/Sys.html#2_SignalnumbersforthestandardPOSIXsignals *)
144146
(* https://ocaml.github.io/ocamlunix/signals.html *)

src/util/options.schema.json

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,27 +1059,6 @@
10591059
"description": "Output filename for transformations that output a transformed file.",
10601060
"type":"string",
10611061
"default": "transformed.c"
1062-
},
1063-
"assert": {
1064-
"title": "trans.assert",
1065-
"type": "object",
1066-
"properties": {
1067-
"full": {
1068-
"title": "trans.assert.full",
1069-
"description":
1070-
"Whether to dump assertions about all local variables or limitting it to modified ones where possible.",
1071-
"type": "boolean",
1072-
"default": true
1073-
},
1074-
"only-at-locks":{
1075-
"title": "trans.assert.only-at-locks",
1076-
"description":
1077-
"Only put locks after mutexes have been acquired.",
1078-
"type": "boolean",
1079-
"default": true
1080-
}
1081-
},
1082-
"additionalProperties": false
10831062
}
10841063
},
10851064
"additionalProperties": false
@@ -1286,6 +1265,7 @@
12861265
"title": "exp.architecture",
12871266
"description": "Architecture for analysis, currently for witness",
12881267
"type": "string",
1268+
"enum": ["64bit", "32bit"],
12891269
"default": "64bit"
12901270
},
12911271
"gcc_path": {
@@ -1753,13 +1733,39 @@
17531733
"title": "witness.invariant",
17541734
"type": "object",
17551735
"properties": {
1756-
"nodes": {
1757-
"title": "witness.invariant.nodes",
1736+
"loop-head": {
1737+
"title": "witness.invariant.loop-head",
17581738
"description":
1759-
"Which witness nodes to add invariants to? all/loop_heads/none",
1760-
"type": "string",
1761-
"enum": ["all", "loop_heads", "none"],
1762-
"default": "all"
1739+
"Emit invariants at loop heads",
1740+
"type": "boolean",
1741+
"default": true
1742+
},
1743+
"after-lock": {
1744+
"title": "witness.invariant.after-lock",
1745+
"description":
1746+
"Emit invariants after mutex locking",
1747+
"type": "boolean",
1748+
"default": true
1749+
},
1750+
"other": {
1751+
"title": "witness.invariant.other",
1752+
"description":
1753+
"Emit invariants at all other locations",
1754+
"type": "boolean",
1755+
"default": true
1756+
},
1757+
"split-conjunction": {
1758+
"title": "witness.invariant.split-conjunction",
1759+
"description": "Split conjunctive invariant to multiple invariants",
1760+
"type": "boolean",
1761+
"default": true
1762+
},
1763+
"full": {
1764+
"title": "witness.invariant.full",
1765+
"description":
1766+
"Whether to dump assertions about all local variables or limitting it to modified ones where possible.",
1767+
"type": "boolean",
1768+
"default": true
17631769
}
17641770
},
17651771
"additionalProperties": false
@@ -1788,6 +1794,25 @@
17881794
"description": "Output witness for unknown result",
17891795
"type": "boolean",
17901796
"default": true
1797+
},
1798+
"yaml": {
1799+
"title": "witness.yaml",
1800+
"type": "object",
1801+
"properties": {
1802+
"enabled": {
1803+
"title": "witness.yaml.enabled",
1804+
"description": "Output YAML witness",
1805+
"type": "boolean",
1806+
"default": false
1807+
},
1808+
"path": {
1809+
"title": "witness.yaml.path",
1810+
"description": "YAML witness output path",
1811+
"type": "string",
1812+
"default": "witness.yml"
1813+
}
1814+
},
1815+
"additionalProperties": false
17911816
}
17921817
},
17931818
"additionalProperties": false

0 commit comments

Comments
 (0)