Skip to content

Commit 542abe8

Browse files
add program generation
1 parent ef2dc75 commit 542abe8

File tree

6 files changed

+292
-3
lines changed

6 files changed

+292
-3
lines changed

src/trace_tracker.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,8 @@ let equal t1 t2 =
104104
(* any values under the same key are known to be equivalent, even if the exact sequence is not identical *))
105105
t1 t2
106106
== 0
107+
108+
109+
let subset t1 t2 =
110+
TraceMap.fold (fun key _ seen_all ->
111+
TraceMap.mem key t2 && seen_all) t1 true

src/trace_tracker.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ val print_traces : out_channel -> unit
77
val print : t -> out_channel -> unit
88
val equal : t -> t -> bool
99
val get_deps_str : t -> string
10+
val subset : t -> t -> bool

src/tracedAtomic.ml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,22 @@ let do_run init_func init_schedule =
310310
backtrack = IntSet.empty;
311311
}
312312

313+
let rec explore_random func state =
314+
let s = last_element state in
315+
let enabled = IntSet.to_seq s.enabled |> List.of_seq in
316+
let len = List.length enabled in
317+
if len == 0 then ()
318+
else
319+
let random_index = Random.int (List.length enabled) in
320+
let j = List.nth enabled random_index in
321+
let j_proc = List.nth s.procs j in
322+
let schedule =
323+
List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state
324+
@ [ (j, j_proc.op, j_proc.obj_ptr) ]
325+
in
326+
let statedash = state @ [ do_run func schedule ] in
327+
explore_random func statedash
328+
313329
let rec explore func state clock last_access =
314330
let s = last_element state in
315331
List.iter
@@ -371,18 +387,25 @@ let reset_state () =
371387

372388
let dscheck_trace_file_env = Sys.getenv_opt "dscheck_trace_file"
373389

390+
let random func iters =
391+
reset_state ();
392+
let empty_state = do_run func [ (0, Start, None) ] :: [] in
393+
for _ = 1 to iters do
394+
explore_random func empty_state
395+
done
396+
374397
let dpor func =
375398
reset_state ();
376399
let empty_state = do_run func [ (0, Start, None) ] :: [] in
377400
let empty_clock = IntMap.empty in
378401
let empty_last_access = IntMap.empty in
379402
explore func empty_state empty_clock empty_last_access
380403

381-
let trace ?interleavings ?(record_traces = false) func =
404+
let trace ?(impl = `Dpor) ?interleavings ?(record_traces = false) func =
382405
record_traces_flag := record_traces || Option.is_some dscheck_trace_file_env;
383406
interleavings_chan := interleavings;
384407

385-
dpor func;
408+
(match impl with `Dpor -> dpor func | `Random iters -> random func iters);
386409

387410
(* print reports *)
388411
(match !interleavings_chan with

src/tracedAtomic.mli

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ val decr : int t -> unit
4747
(** [decr r] atomically decrements the value of [r] by [1]. *)
4848

4949
val trace :
50-
?interleavings:out_channel -> ?record_traces:bool -> (unit -> unit) -> unit
50+
?impl:[ `Random of int | `Dpor ] ->
51+
?interleavings:out_channel ->
52+
?record_traces:bool ->
53+
(unit -> unit) ->
54+
unit
5155
(** start the simulation trace *)
5256

5357
val spawn : (unit -> unit) -> unit

tests/dune

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,8 @@
3737
(name test_conditional2)
3838
(libraries dscheck alcotest)
3939
(modules test_conditional2))
40+
41+
(executable
42+
(name gen_program)
43+
(libraries dscheck)
44+
(modules gen_program))

tests/gen_program.ml

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
module Atomic = Dscheck.TracedAtomic
2+
module IntSet = Set.Make (Int)
3+
4+
type config = {
5+
globals_count : int;
6+
value_limit : int;
7+
operations_count : int;
8+
thread_count : int;
9+
generate_conditionals : bool;
10+
print_tests : bool;
11+
}
12+
13+
let print_config t =
14+
Printf.printf "CONFIG\n";
15+
Printf.printf "globals_count: %d\n" t.globals_count;
16+
Printf.printf "value_limit: %d\n" t.value_limit;
17+
Printf.printf "operations_count: %d\n" t.operations_count;
18+
Printf.printf "thread_count: %d\n" t.thread_count;
19+
Printf.printf "generate_conditionals: %b\n%!" t.generate_conditionals
20+
21+
let var_name i = Char.chr (i + 97)
22+
23+
module Function = struct
24+
(* Arbitrary function from int to bool *)
25+
type t = IntSet.t
26+
27+
let gen value_limit =
28+
List.init value_limit (fun i -> if Random.bool () then Some i else None)
29+
|> List.filter_map Fun.id |> IntSet.of_list
30+
31+
let eval t input = IntSet.mem input t
32+
33+
let to_string t var_id =
34+
Printf.sprintf "IntSet.mem (Atomic.get %c) (IntSet.of_list [%s])"
35+
(var_name var_id)
36+
(IntSet.to_seq t |> List.of_seq |> List.map Int.to_string
37+
|> String.concat "; ")
38+
end
39+
40+
module Conditional = struct
41+
type t = { functions : Function.t list; operator : [ `And | `Or ] }
42+
43+
let gen function_count ~value_limit =
44+
let functions =
45+
List.init function_count (fun _ -> Function.gen value_limit)
46+
in
47+
let operator = if Random.bool () then `And else `Or in
48+
{ functions; operator }
49+
50+
let eval t variables =
51+
let functions = t.functions in
52+
let rec f operator vars funcs =
53+
match (vars, funcs) with
54+
| [], [] -> ( match operator with `And -> true | `Or -> false)
55+
| var :: vars_tl, func :: funcs_tl -> (
56+
let input = Atomic.get var in
57+
let output = Function.eval func input in
58+
match (operator, output) with
59+
| `Or, true -> true
60+
| `And, false -> false
61+
| `Or, false | `And, true -> f operator vars_tl funcs_tl)
62+
| _, [] | [], _ ->
63+
failwith
64+
"gen_program: lists of variables and functions have different \
65+
lengths"
66+
in
67+
f t.operator variables functions
68+
69+
let to_string t ~var_ids =
70+
let operator_str = match t.operator with `Or -> " || " | `And -> " && " in
71+
List.combine t.functions var_ids
72+
|> List.map (fun (func, var_id) -> Function.to_string func var_id)
73+
|> String.concat operator_str
74+
end
75+
76+
module Step = struct
77+
type t =
78+
| Write of { var_id : int; new_value : int; next : t }
79+
| Read of { var_id : int; next : t }
80+
| Conditional of {
81+
var_ids : int list;
82+
conditional : Conditional.t;
83+
on_true : t;
84+
next : t;
85+
}
86+
| Noop
87+
88+
let rec run ~globals = function
89+
| Write { var_id; new_value; next } ->
90+
Atomic.set (CCVector.get globals var_id) new_value;
91+
run ~globals next
92+
| Read { var_id; next } ->
93+
ignore (Atomic.get (CCVector.get globals var_id));
94+
run ~globals next
95+
| Conditional { var_ids; conditional; on_true; next } ->
96+
let variables =
97+
List.map (fun var_id -> CCVector.get globals var_id) var_ids
98+
in
99+
if Conditional.eval conditional variables then run ~globals on_true;
100+
run ~globals next
101+
| Noop -> ()
102+
103+
let rec print t ~depth =
104+
let indent = List.init depth (fun _ -> "\t") |> String.concat "" in
105+
match t with
106+
| Write { var_id; new_value; next } ->
107+
Printf.printf "%sAtomic.set %c %d;\n" indent (var_name var_id) new_value;
108+
print ~depth next
109+
| Read { var_id; next } ->
110+
Printf.printf "%sAtomic.get %c |> ignore;\n" indent (var_name var_id);
111+
print ~depth next
112+
| Conditional { var_ids; conditional; on_true; next } ->
113+
let s = Conditional.to_string conditional ~var_ids in
114+
Printf.printf "%sif (%s) then (\n" indent s;
115+
print ~depth:(depth + 1) on_true;
116+
Printf.printf "%s);\n" indent;
117+
print ~depth next
118+
| Noop -> ()
119+
120+
let rec gen ~config ~fuel () =
121+
let var_id = Random.int config.globals_count in
122+
let next fuel =
123+
if fuel > 1 then gen ~config ~fuel:(fuel - 1) () else Noop
124+
in
125+
let maybe_conditionals = if config.generate_conditionals then 1 else 0 in
126+
match Random.int (2 + maybe_conditionals) with
127+
| 0 ->
128+
let new_value = Random.int config.value_limit in
129+
Write { var_id; new_value; next = next fuel }
130+
| 1 -> Read { var_id; next = next fuel }
131+
| 2 ->
132+
let func_count =
133+
min (max 1 fuel) (min config.globals_count (1 + Random.int 2))
134+
in
135+
let var_ids =
136+
List.init func_count (fun _ -> Random.int config.globals_count)
137+
in
138+
let conditional =
139+
Conditional.gen func_count ~value_limit:config.value_limit
140+
in
141+
let fuel_a, fuel_b =
142+
let tmp = Random.int (max (fuel - func_count) 1) in
143+
(fuel - tmp, tmp)
144+
in
145+
146+
let on_true = gen ~config ~fuel:fuel_a () in
147+
Conditional { var_ids; conditional; on_true; next = next fuel_b }
148+
| _ -> failwith "drew number without corresponding instruction"
149+
end
150+
151+
module Program = struct
152+
type thread = Step.t
153+
type t = { globals : (int, CCVector.ro) CCVector.t; threads : thread list }
154+
155+
let run ~impl { globals; threads } =
156+
Atomic.trace ~impl ~record_traces:true (fun () ->
157+
let globals = CCVector.map Atomic.make globals |> CCVector.freeze in
158+
List.iter
159+
(fun thread -> Atomic.spawn (fun () -> Step.run ~globals thread))
160+
threads;
161+
());
162+
Dscheck.Trace_tracker.get_traces ()
163+
164+
let print { globals; threads } =
165+
CCVector.iteri
166+
(fun var_id value ->
167+
Printf.printf "let %c = Atomic.make %d in\n" (var_name var_id) value)
168+
globals;
169+
List.iter
170+
(fun thread ->
171+
Printf.printf "\nDomain.spawn (fun () -> \n";
172+
Step.print thread ~depth:1;
173+
Printf.printf ")\n%!")
174+
threads
175+
end
176+
177+
let run_random config () =
178+
Random.self_init ();
179+
let globals = CCVector.of_list (List.init config.globals_count Fun.id) in
180+
let thread_f = Step.gen ~config ~fuel:config.operations_count in
181+
let threads = List.init config.thread_count (fun _ -> thread_f ()) in
182+
let program = ({ globals; threads } : Program.t) in
183+
if config.print_tests then
184+
Program.print program;
185+
186+
let random = Program.run ~impl:(`Random 100) program in
187+
let dpor = Program.run ~impl:`Dpor program in
188+
if not (Dscheck.Trace_tracker.subset random dpor) then (
189+
Printf.printf "found mismatch\n\n%!";
190+
Program.print program;
191+
Dscheck.Trace_tracker.print dpor stdout;
192+
Dscheck.Trace_tracker.print random stdout;
193+
assert false)
194+
195+
let run config test_count =
196+
Printf.printf "\n\n";
197+
for i = 0 to test_count do
198+
Printf.printf "----run: %d/%d\r%!" i test_count;
199+
run_random config ()
200+
done;
201+
Printf.printf "\nall generated programs passed\n%!"
202+
203+
(* cmd *)
204+
let _ =
205+
let test_count = ref 100 in
206+
let globals_count = ref 6 in
207+
let value_limit = ref 5 in
208+
let operations_count = ref 3 in
209+
let thread_count = ref 3 in
210+
let generate_conditionals = ref true in
211+
let print_tests = ref false in
212+
let speclist =
213+
[
214+
( "-test-count",
215+
Arg.Set_int test_count,
216+
"number of programs to generate and test" );
217+
("-print-tests", Arg.Set print_tests, "print all tests");
218+
( "-global-vars-count",
219+
Arg.Set_int globals_count,
220+
"number of shared atomic variables (the more, the higher the reduction)"
221+
);
222+
( "-value-limit",
223+
Arg.Set_int value_limit,
224+
"range of values used by generated operations" );
225+
( "-operations-count",
226+
Arg.Set_int operations_count,
227+
"number of operations per thread" );
228+
("-thread-count", Arg.Set_int thread_count, "number of threads");
229+
( "-generate-conditionals",
230+
Arg.Set generate_conditionals,
231+
"enable/disable generation of conditional statements" );
232+
]
233+
in
234+
Arg.parse speclist
235+
(fun _ -> ())
236+
"gen_program.exe [-test-count INT] [-global-vars-count INT] [-value-limit \
237+
INT] [-operations-count INT] [-thread-count INT] [-generate-conditionals \
238+
BOOL]";
239+
let config =
240+
({
241+
globals_count = !globals_count;
242+
value_limit = !value_limit;
243+
operations_count = !operations_count;
244+
thread_count = !thread_count;
245+
generate_conditionals = !generate_conditionals;
246+
print_tests = !print_tests
247+
}
248+
: config)
249+
in
250+
print_config config;
251+
run config !test_count

0 commit comments

Comments
 (0)