call_provers.ml 4.89 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
(**************************************************************************)
(*                                                                        *)
(*  Copyright (C) 2010-                                                   *)
(*    Francois Bobot                                                      *)
(*    Jean-Christophe Filliatre                                           *)
(*    Johannes Kanig                                                      *)
(*    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

let debug = Debug.register_flag "call_prover"

let call_prover command opt_cout buffer =
66
  let time = Unix.gettimeofday () in
67 68 69 70 71 72 73 74 75
  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
76 77
  let out = channel_contents cin in
  let ret = Unix.close_process p in
78
  let time = Unix.gettimeofday () -. time in
79
  Debug.dprintf debug "Call_provers: prover output:@\n%s@." out;
80 81
  ret, out, time

82 83 84 85
type post_prover_call = unit -> prover_result

type bare_prover_call = unit -> post_prover_call

86
let call_on_buffer ~command ?(timelimit=0) ?(memlimit=0)
87
                   ~regexps ~exitcodes ~filename buffer =
88
  let on_stdin = ref true in
89
  let on_timelimit = ref false in
90 91 92 93
  let cmd_regexp = Str.regexp "%\\(.\\)" in
  let replace file s = match Str.matched_group 1 s with
    | "%" -> "%"
    | "f" -> on_stdin := false; file
94
    | "t" -> on_timelimit := true; string_of_int timelimit
95 96 97 98
    | "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
99 100 101 102 103 104 105 106
  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
107
  in
108
  fun () ->
109
    let ret,out,time = call_prover cmd cout buffer in
110
    fun () ->
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
      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
      let ans = match ans with
        | HighFailure when !on_timelimit && timelimit > 0
          && time >= (0.9 *. float timelimit) -> Timeout
        | _ -> ans
      in
      { pr_answer = ans;
        pr_output = out;
        pr_time   = time }
131