Skip to content

Commit 63a414d

Browse files
add program generation
1 parent 0a2a617 commit 63a414d

File tree

6 files changed

+295
-3
lines changed

6 files changed

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

0 commit comments

Comments
 (0)