|
| 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