call_provers.ml 4.74 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
let call_on_buffer ~command ?(timelimit=0) ?(memlimit=0)
                   ~regexps ~exitcodes ~filename buffer () =
84
  let on_stdin = ref true in
85
  let on_timelimit = ref false in
86 87 88 89
  let cmd_regexp = Str.regexp "%\\(.\\)" in
  let replace file s = match Str.matched_group 1 s with
    | "%" -> "%"
    | "f" -> on_stdin := false; file
90
    | "t" -> on_timelimit := true; string_of_int timelimit
91 92 93 94 95
    | "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
  let ret, out, time =
96
    if !on_stdin then call_prover cmd None buffer else begin
97 98 99
      let fout,cout = Filename.open_temp_file "why_" ("_" ^ filename) in
      try
        let cmd = Str.global_substitute cmd_regexp (replace fout) command in
100 101
        let res = call_prover cmd (Some cout) buffer in
        if Debug.nottest_flag debug then Sys.remove fout;
102 103 104
        res
      with e ->
        close_out cout;
105
        if Debug.nottest_flag debug then Sys.remove fout;
106 107 108
        raise e
    end
  in
109 110
  let ans = match ret with
    | Unix.WSTOPPED n ->
111
        Debug.dprintf debug "Call_provers: stopped by signal %d@." n;
112 113
        HighFailure
    | Unix.WSIGNALED n ->
114
        Debug.dprintf debug "Call_provers: killed by signal %d@." n;
115 116
        HighFailure
    | Unix.WEXITED n ->
117
        Debug.dprintf debug "Call_provers: exited with status %d@." n;
118 119 120
        (try List.assoc n exitcodes with Not_found -> grep out regexps)
  in
  let ans = match ans with
Andrei Paskevich's avatar
minor  
Andrei Paskevich committed
121
    | HighFailure when !on_timelimit && timelimit > 0
122
      && time >= (0.9 *. float timelimit) -> Timeout
123
    | _ -> ans
124 125 126
  in
  { pr_answer = ans;
    pr_output = out;
127
    pr_time   = time }
128