1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
|
(****************************************************************************)
(* the diy toolsuite *)
(* *)
(* Jade Alglave, University College London, UK. *)
(* Luc Maranget, INRIA Paris-Rocquencourt, France. *)
(* *)
(* Copyright 2010-present Institut National de Recherche en Informatique et *)
(* en Automatique and the authors. All rights reserved. *)
(* *)
(* This software is governed by the CeCILL-B license under French law and *)
(* abiding by the rules of distribution of free software. You can use, *)
(* modify and/ or redistribute the software under the terms of the CeCILL-B *)
(* license as circulated by CEA, CNRS and INRIA at the following URL *)
(* "http://www.cecill.info". We also give a copy in LICENSE.txt. *)
(****************************************************************************)
open Printf
open LogState
let verbose = ref 0
let logs = ref []
let select = ref []
let names = ref []
let rename = ref []
let excl = ref []
let npar = ref 1
let hexa = ref false
let int32 = ref true
let nargs = ref 64
let faulttype = ref true
let options =
let open CheckName in
[
("-q", Arg.Unit (fun _ -> verbose := -1),
"<non-default> be silent");
("-v", Arg.Unit (fun _ -> incr verbose),
"<non-default> show various diagnostics, repeat to increase verbosity");
("-j", Arg.Int (fun i -> npar := i),
(sprintf "<int> parallel sum using <n> processeses, default %i" !npar)) ;
("-width", Arg.Int (fun i -> nargs := i),
(sprintf "<int> merge width, when parallel sum enabled %i" !nargs)) ;
parse_hexa hexa; parse_int32 int32;
parse_rename rename;
parse_select select; parse_names names; parse_excl excl;
parse_faulttype faulttype;
]
let prog =
if Array.length Sys.argv > 0 then Sys.argv.(0)
else "sum"
let () =
Arg.parse options
(fun s -> logs := !logs @ [s])
(sprintf "Usage %s [options]* [log]*
[log]* are litmus log file names. If no command line argument is given,
log names are taken from standard input.
Options are:" prog)
let npar =
let nlogs = List.length !logs in
if 2* !npar > nlogs then max 1 (nlogs/2)
else max !npar 1
let nargs = !nargs
let select = !select
let rename = !rename
let names = !names
let excl = !excl
let verbose = !verbose
let hexa = !hexa
let int32 = !int32
let faulttype = !faulttype
module Verbose = struct let verbose = verbose end
(* Options for recursive calls *)
let expn_opt opt xs k =
List.fold_right
(fun x k -> opt::x::k)
xs k
let par_opts =
expn_opt "-select" select
(expn_opt "-rename" rename
(expn_opt "-names" names
(expn_opt "-excl" excl [])))
(* Now handle the same options, which are to be
honored only when there are no recursive calls *)
module Check =
CheckName.Make
(struct
let verbose = verbose
let rename = rename
let select = select
let names = names
let excl = excl
end)
let fnames = match !logs with
| [] ->
let rec read_rec k =
let o =
try Some (read_line ())
with End_of_file -> None in
match o with
| Some x -> read_rec (x::k)
| None -> k in
read_rec []
| xs -> xs
module LS = LogState.Make(Verbose)
module LL =
LexLog_tools.Make
(struct
let verbose = verbose
include Check
let hexa = hexa
let int32 = int32
let acceptBig = false
let faulttype = faulttype
end)
module D =
LogConstr.Dump
(struct
let hexa = hexa
let tr = Misc.identity
end)
let zyva fnames =
let tests = LL.read_names fnames in
(* Dumping of log files *)
let dump_hash chan = function
| None -> ()
| Some h -> fprintf chan "Hash=%s\n" h in
let dump_condition chan v c = match c,v with
| Some c,(Ok|No) ->
fprintf chan
"Condition %a is%s validated\n"
D.dump c
(if v = Ok then "" else " not")
| _,_ -> () in
let dump_prop chan c = match c with
| Some c ->
fprintf chan
"Condition (%a)\n"
D.dump_prop (ConstrGen.prop_of c)
| None -> () in
let dump_test chan t =
fprintf chan "Test %s%s\n" t.tname
(if is_reliable t.kind then " "^LS.pp_kind t.kind else "") ;
LS.dump_states chan t.states ;
if is_reliable t.kind then begin
fprintf chan "%s\n" (LS.pp_validation t.validation) ;
fprintf chan "Witnesses\n" ;
let p,n = t.witnesses in
fprintf chan "Positive: %s Negative: %s\n"
(Int64.to_string p) (Int64.to_string n) ;
dump_condition chan t.validation t.condition
end else begin
fprintf chan "??\n" ;
dump_prop chan t.condition
end ;
dump_hash chan t.hash ;
LS.dump_topologies chan t.topologies ;
begin match t.time with
| None -> ()
| Some time ->
fprintf chan "Time %s %0.2f\n" t.tname time
end ;
output_char chan '\n';
() in
let dump_log chan = Array.iter (dump_test chan) in
let total_outcomes tsts =
let k = ref Int64.zero in
Array.iter
(fun tst -> k := Int64.add !k (LS.get_nouts tst.states))
tsts ;
!k in
let sum = LS.union_logs tests in
dump_log stdout sum ;
flush stdout ;
if verbose >= 0 then
eprintf "total nouts: %.2fM\n" (LS.millions (total_outcomes sum)) ;
()
module type Opt = sig
val j : int (* Number of workers *)
val w : int (* Merge width *)
val verbose : int
val opts : string list
end
module Task(O:Opt) = struct
(* Work in temporary directory *)
let tmp = Filename.get_temp_dir_name ()
let dir =
let t = Filename.concat tmp (sprintf "sum.%i" (Unix.getpid ())) in
Unix.mkdir t 0o700 ;
t
let clean () =
let rec rm d =
let f =
try Some (Unix.readdir d)
with End_of_file -> None in
match f with
| None -> ()
| Some ("."|"..") -> rm d
| Some f ->
Unix.unlink (Filename.concat dir f) ;
rm d in
let d = Unix.opendir dir in
rm d ;
Unix.closedir d ;
Unix.rmdir dir
(* Protection *)
let () =
Sys.set_signal
Sys.sigint (Sys.Signal_handle (fun _ -> clean() ; exit 1))
(* Queue *)
type queue = string Queue.t
let queue_to_list q =
let rec do_rec k =
if Queue.is_empty q then k
else
let x = try Queue.take q with Queue.Empty -> assert false in
do_rec (x::k) in
do_rec []
let extract w q =
if Queue.length q < w then []
else
let rec do_rec k =
if k <= 0 then []
else
let x = try Queue.take q with Queue.Empty -> assert false in
x::do_rec (k-1) in
do_rec w
(* From file descriptor to task *)
type task = { chan:in_channel; outname:string; }
module FdMap =
Map.Make
(struct
type t = Unix.file_descr
let compare = compare
end)
let get_waiting m = FdMap.fold (fun fd _ k -> fd::k) m []
(* Spawn task *)
let opts = "-q" :: O.opts
let popen idx args =
let outname = Filename.concat dir
(sprintf "sum-%02i.txt" idx) in
let com =
let args = String.concat " " (opts@args) in
sprintf "%s %s >%s" prog args outname in
if O.verbose > 0 then eprintf "Starting '%s'\n%!" com ;
let chan = Unix.open_process_in com in
{ outname; chan; }
(* Current state *)
type st =
{ idx:int; (* gensym for file names *)
nfree:int; (* workers not working *)
m:task FdMap.t; (* From fd to running tasks *)
q:queue; (* Files to sum *) }
(* Spawn tasks, halt condition:
* + No free worker available.
* + Or, less then max (O.w,4) files to sum.
*)
let rec process_tasks w st =
if st.nfree <= 0 then st
else
let inputs = extract w st.q in
match inputs with
| [] ->
let ww = (w+1)/2 in
if ww > 4 then
process_tasks ww st
else st
| _::_ ->
let t = popen st.idx inputs in
let fd = Unix.descr_of_in_channel t.chan in
let st = { st with nfree=st.nfree-1; idx=st.idx+1; m=FdMap.add fd t st.m; } in
process_tasks w st
(* Polling loop *)
let rec loop st =
let fds = get_waiting st.m in
match fds with
| [] -> st
| _::_ ->
let ok,_,_ = Unix.select fds [] [] (-1.0) in
let st =
List.fold_left
(fun st fd ->
let t =
try FdMap.find fd st.m with Not_found -> assert false in
begin try while true do ignore (input_char t.chan) done
with End_of_file -> ignore (Unix.close_process_in t.chan) end ;
Queue.add t.outname st.q ;
{ st with nfree=st.nfree+1; m = FdMap.remove fd st.m; }) st ok in
let st = process_tasks O.w st in
loop st
(* Entry point, set O.j workers at work, then reallocate tasks untill
less then O.w files to sum. Then perform the last sum. *)
let run args =
let q = Queue.create () in
List.iter (fun s -> Queue.add s q) args ;
let st = { nfree=O.j; idx=0; q=q; m = FdMap.empty; } in
let st = process_tasks O.w st in
let st = loop st in
let args = queue_to_list st.q in
zyva args ;
clean ()
end
let () =
try
if npar > 1 then
let module M =
Task
(struct
let j = npar
let w = nargs
let verbose = verbose
let opts = par_opts
end) in
M.run fnames
else
zyva fnames
with Misc.Fatal msg|Misc.UserError msg ->
eprintf "Fatal error: %s\n%!" msg
|