call_provers.ml 5.31 KB
Newer Older
1 2 3
(**************************************************************************)
(*                                                                        *)
(*  Copyright (C) 2010-                                                   *)
MARCHE Claude's avatar
MARCHE Claude committed
4 5 6
(*    François Bobot                                                     *)
(*    Jean-Christophe Filliâtre                                          *)
(*    Claude Marché                                                      *)
7 8 9 10 11 12 13 14 15 16 17 18 19
(*    Andrei Paskevich                                                    *)
(*                                                                        *)
(*  This software is free software; you can redistribute it and/or        *)
(*  modify it under the terms of the GNU Library General Public           *)
(*  License version 2.1, with the special exception on linking            *)
(*  described in file LICENSE.                                            *)
(*                                                                        *)
(*  This software is distributed in the hope that it will be useful,      *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *)
(*                                                                        *)
(**************************************************************************)

20
open Format
21
open Sysutil
22

23
type prover_answer =
24 25 26
  | Valid
  | Invalid
  | Timeout
27 28
  | Unknown of string
  | Failure of string
29 30
  | HighFailure

31 32 33 34 35 36
type prover_result = {
  pr_answer : prover_answer;
  pr_output : string;
  pr_time   : float;
}

37 38 39
let print_prover_answer fmt = function
  | Valid -> fprintf fmt "Valid"
  | Invalid -> fprintf fmt "Invalid"
40
  | Timeout -> fprintf fmt "Timeout"
41 42
  | Unknown s -> fprintf fmt "Unknown: %s" s
  | Failure s -> fprintf fmt "Failure: %s" s
43 44
  | HighFailure -> fprintf fmt "HighFailure"

45 46 47
let print_prover_result fmt {pr_answer=ans; pr_output=out; pr_time=t} =
  fprintf fmt "%a (%.2fs)" print_prover_answer ans t;
  if ans == HighFailure then fprintf fmt "@\nProver output:@\n%s@." out
48

49
let rec grep out l = match l with
50
  | [] ->
51 52
      HighFailure
  | (re,pa) :: l ->
53 54 55
      begin try
        ignore (Str.search_forward re out 0);
        match pa with
56 57 58 59
        | Valid | Invalid | Timeout -> pa
        | Unknown s -> Unknown (Str.replace_matched s out)
        | Failure s -> Failure (Str.replace_matched s out)
        | HighFailure -> assert false
60
      with Not_found -> grep out l end
61

62 63 64 65 66 67 68 69 70 71
let rec grep_time out = function
  | [] -> None
  | (re,pa) :: l ->
    begin try
            ignore (Str.search_forward re out 0);
            let stime = (Str.replace_matched pa out) in
            Some (Scanf.sscanf stime "%f:%f:%f"
                    (fun h m s -> h *. 3600. +. m *. 60. +. s))
      with _ -> grep_time out l end

72 73 74 75

let debug = Debug.register_flag "call_prover"

let call_prover command opt_cout buffer =
76
  let time = Unix.gettimeofday () in
77 78 79 80 81 82 83 84 85
  let (cin,_) as p = match opt_cout with
    | Some cout ->
        Buffer.output_buffer cout buffer; close_out cout;
        Unix.open_process command
    | None ->
        let (_,cout) as p = Unix.open_process command in
        Buffer.output_buffer cout buffer; close_out cout;
        p
  in
86 87
  let out = channel_contents cin in
  let ret = Unix.close_process p in
88
  let time = Unix.gettimeofday () -. time in
89
  Debug.dprintf debug "Call_provers: prover output:@\n%s@." out;
90 91
  ret, out, time

92 93 94 95
type post_prover_call = unit -> prover_result

type bare_prover_call = unit -> post_prover_call

96
let call_on_buffer ~command ?(timelimit=0) ?(memlimit=0)
97
                   ~regexps ~regexpstime ~exitcodes ~filename buffer =
98
  let on_stdin = ref true in
99
  let on_timelimit = ref false in
100 101 102 103
  let cmd_regexp = Str.regexp "%\\(.\\)" in
  let replace file s = match Str.matched_group 1 s with
    | "%" -> "%"
    | "f" -> on_stdin := false; file
104
    | "t" -> on_timelimit := true; string_of_int timelimit
105 106 107 108
    | "m" -> string_of_int memlimit
    | _ -> failwith "unknown format specifier, use %%f, %%t or %%m"
  in
  let cmd = Str.global_substitute cmd_regexp (replace "") command in
109 110 111 112 113 114 115 116
  let on_stdin = !on_stdin in
  let cmd,fout,cout = if on_stdin then cmd, "", None else
    let fout,cout = Filename.open_temp_file "why_" ("_" ^ filename) in
    let cmd =
      try Str.global_substitute cmd_regexp (replace fout) command
      with e -> close_out cout; Sys.remove fout; raise e
    in
    cmd, fout, Some cout
117
  in
118
  fun () ->
119
    let ret,out,time = call_prover cmd cout buffer in
120
    fun () ->
121 122 123 124 125 126 127 128 129 130 131 132
      if not on_stdin && Debug.nottest_flag debug then Sys.remove fout;
      let ans = match ret with
        | Unix.WSTOPPED n ->
            Debug.dprintf debug "Call_provers: stopped by signal %d@." n;
            HighFailure
        | Unix.WSIGNALED n ->
            Debug.dprintf debug "Call_provers: killed by signal %d@." n;
            HighFailure
        | Unix.WEXITED n ->
            Debug.dprintf debug "Call_provers: exited with status %d@." n;
            (try List.assoc n exitcodes with Not_found -> grep out regexps)
      in
133
      let time = Util.default_option time (grep_time out regexpstime) in
134 135
      let ans = match ans with
        | HighFailure when !on_timelimit && timelimit > 0
136
          && time >= (float timelimit -. 0.1) -> Timeout
137 138 139 140 141
        | _ -> ans
      in
      { pr_answer = ans;
        pr_output = out;
        pr_time   = time }
142