Refactor Utils.SymOp into separate module

Summary:public
Refactor Utils.SymOp into a separate module, bringing the failure_kind
type and associated operations.

Reviewed By: cristianoc

Differential Revision: D3161640

fb-gh-sync-id: be3d7c9
fbshipit-source-id: be3d7c9
master
Josh Berdine 9 years ago committed by Facebook Github Bot 5
parent 607621920f
commit b881887bf2

@ -328,7 +328,7 @@ let store_to_file (filename : DB.filename) (call_graph : t) =
let pp_graph_dotty get_specs (g: t) fmt =
let nodes_with_calls = get_all_nodes g in
let num_specs n = try IList.length (get_specs n) with exn when exn_not_failure exn -> - 1 in
let num_specs n = try IList.length (get_specs n) with exn when SymOp.exn_not_failure exn -> - 1 in
let get_color (n, _) =
if num_specs n != 0 then "green" else "red" in
let get_shape (n, _) =

@ -0,0 +1,154 @@
(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** Symbolic Operations and Failures: the units in which analysis work is measured *)
open! Utils
module F = Format
type failure_kind =
| FKtimeout (* max time exceeded *)
| FKsymops_timeout of int (* max symop's exceeded *)
| FKrecursion_timeout of int (* max recursion level exceeded *)
| FKcrash of string (* uncaught exception or failed assertion *)
(** failure that prevented analysis from finishing *)
exception Analysis_failure_exe of failure_kind
let exn_not_failure = function
| Analysis_failure_exe _ -> false
| _ -> true
let pp_failure_kind fmt = function
| FKtimeout -> F.fprintf fmt "TIMEOUT"
| FKsymops_timeout symops -> F.fprintf fmt "SYMOPS TIMEOUT (%d)" symops
| FKrecursion_timeout level -> F.fprintf fmt "RECURSION TIMEOUT(%d)" level
| FKcrash msg -> F.fprintf fmt "CRASH (%s)" msg
(** Count the number of symbolic operations *)
(** Timeout in seconds for each function *)
let timeout_seconds = ref (!Config.seconds_per_iteration *. (float_of_int !Config.iterations))
(** Timeout in SymOps *)
let timeout_symops = ref (!Config.symops_per_iteration * !Config.iterations)
let get_timeout_seconds () = !timeout_seconds
(** Internal state of the module *)
type t =
{
(** Only throw timeout exception when alarm is active *)
mutable alarm_active : bool;
(** last wallclock set by an alarm, if any *)
mutable last_wallclock : float option;
(** Number of symop's *)
mutable symop_count : int;
(** Counter for the total number of symop's.
The new state created when save_state is called shares this counter
if keep_symop_total is true. Otherwise, a new counter is created. *)
symop_total : int ref;
}
let initial () : t =
{
alarm_active = false;
last_wallclock = None;
symop_count = 0;
symop_total = ref 0;
}
(** Global State *)
let gs : t ref = ref (initial ())
(** Restore the old state. *)
let restore_state state =
gs := state
(** Return the old state, and revert the current state to the initial one.
If keep_symop_total is true, share the total counter. *)
let save_state ~keep_symop_total =
let old_state = !gs in
let new_state =
let st = initial () in
if keep_symop_total
then
{ st with symop_total = old_state.symop_total }
else
st in
gs := new_state;
old_state
(** handler for the wallclock timeout *)
let wallclock_timeout_handler = ref None
(** set the handler for the wallclock timeout *)
let set_wallclock_timeout_handler handler =
wallclock_timeout_handler := Some handler
(** Set the wallclock alarm checked at every pay() *)
let set_wallclock_alarm nsecs =
!gs.last_wallclock <- Some (Unix.gettimeofday () +. nsecs)
(** Unset the wallclock alarm checked at every pay() *)
let unset_wallclock_alarm () =
!gs.last_wallclock <- None
(** if the wallclock alarm has expired, raise a timeout exception *)
let check_wallclock_alarm () =
match !gs.last_wallclock, !wallclock_timeout_handler with
| Some alarm_time, Some handler when Unix.gettimeofday () >= alarm_time ->
unset_wallclock_alarm ();
handler ()
| _ -> ()
(** Return the time remaining before the wallclock alarm expires *)
let get_remaining_wallclock_time () =
match !gs.last_wallclock with
| Some alarm_time ->
max 0.0 (alarm_time -. Unix.gettimeofday ())
| None ->
0.0
(** Return the total number of symop's since the beginning *)
let get_total () =
!(!gs.symop_total)
(** Reset the total number of symop's *)
let reset_total () =
!gs.symop_total := 0
(** Count one symop *)
let pay () =
!gs.symop_count <- !gs.symop_count + 1;
!gs.symop_total := !(!gs.symop_total) + 1;
if !gs.symop_count > !timeout_symops &&
!gs.alarm_active
then raise (Analysis_failure_exe (FKsymops_timeout !gs.symop_count));
check_wallclock_alarm ()
(** Reset the counter *)
let reset_count () =
!gs.symop_count <- 0
(** Reset the counter and activate the alarm *)
let set_alarm () =
reset_count ();
!gs.alarm_active <- true
(** De-activate the alarm *)
let unset_alarm () =
!gs.alarm_active <- false

@ -0,0 +1,71 @@
(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** Symbolic Operations and Failures: the units in which analysis work is measured *)
open! Utils
(** Internal state of the module *)
type t
(** if the wallclock alarm has expired, raise a timeout exception *)
val check_wallclock_alarm : unit -> unit
(** Return the time remaining before the wallclock alarm expires *)
val get_remaining_wallclock_time : unit -> float
(** Timeout in seconds for each function *)
val get_timeout_seconds : unit -> float
(** Return the total number of symop's since the beginning *)
val get_total : unit -> int
(** Count one symop *)
val pay : unit -> unit
(** Reset the total number of symop's *)
val reset_total : unit -> unit
(** Restore the old state. *)
val restore_state : t -> unit
(** Return the old state, and revert the current state to the initial one.
If keep_symop_total is true, share the total counter. *)
val save_state : keep_symop_total:bool -> t
(** Reset the counter and activate the alarm *)
val set_alarm : unit -> unit
(** Set the wallclock alarm checked at every pay() *)
val set_wallclock_alarm : float -> unit
(** set the handler for the wallclock timeout *)
val set_wallclock_timeout_handler : (unit -> unit) -> unit
(** De-activate the alarm *)
val unset_alarm : unit -> unit
(** Unset the wallclock alarm checked at every pay() *)
val unset_wallclock_alarm : unit -> unit
type failure_kind =
| FKtimeout (* max time exceeded *)
| FKsymops_timeout of int (* max symop's exceeded *)
| FKrecursion_timeout of int (* max recursion level exceeded *)
| FKcrash of string (* uncaught exception or failed assertion *)
(** Timeout exception *)
exception Analysis_failure_exe of failure_kind
(** check that the exception is not a timeout exception *)
val exn_not_failure : exn -> bool
val pp_failure_kind : Format.formatter -> failure_kind -> unit

@ -362,6 +362,29 @@ let default_failure_name = "ASSERTION_FAILURE"
let analyze_models = from_env_variable "INFER_ANALYZE_MODELS"
(** initial time of the analysis, i.e. when this module is loaded, gotten from Unix.time *)
let initial_analysis_time = Unix.time ()
let symops_timeout, seconds_timeout =
let default_symops_timeout = 333 in
let default_seconds_timeout = 10.0 in
let long_symops_timeout = 1000 in
let long_seconds_timeout = 30.0 in
if analyze_models then
(* use longer timeouts when analyzing models *)
long_symops_timeout, long_seconds_timeout
else
default_symops_timeout, default_seconds_timeout
(** number of symops to multiply by the number of iterations, after which there is a timeout *)
let symops_per_iteration = ref symops_timeout
(** number of seconds to multiply by the number of iterations, after which there is a timeout *)
let seconds_per_iteration = ref seconds_timeout
(** Set the timeout values in seconds and symops, computed as a multiple of the integer parameter *)
let iterations = ref 1
(** experimental: dynamic dispatch for interface calls only in Java. off by default because of the
cost *)
let sound_dynamic_dispatch = from_env_variable "INFER_SOUND_DYNAMIC_DISPATCH"

@ -513,7 +513,7 @@ let rec dotty_mk_set_links dotnodes sigma p f cycle =
let nodes_e = select_nodes_exp_lambda dotnodes e lambda in
let address_struct_id =
try get_coordinate_id (IList.hd (IList.filter (is_source_node_of_exp e) nodes_e))
with exn when exn_not_failure exn -> assert false in
with exn when SymOp.exn_not_failure exn -> assert false in
(* we need to exclude the address node from the sorce of fields. no fields should start from there*)
let nl'= IList.filter (fun id -> address_struct_id != id) nl in
let links_from_fields = IList.flatten (IList.map ff nl') in
@ -877,7 +877,7 @@ let pp_dotty_prop_list_in_path f plist prev_n curr_n =
pp_dotty f (Generic_proposition) po None) plist;
if prev_n <> - 1 then F.fprintf f "\n state%iN ->state%iN\n" prev_n curr_n;
F.fprintf f "\n } \n"
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
()
let pp_dotty_prop fmt (prop, cycle) =
@ -890,7 +890,7 @@ let pp_dotty_prop fmt (prop, cycle) =
let dotty_prop_to_str prop cycle =
try
Some (pp_to_string pp_dotty_prop (prop, cycle))
with exn when exn_not_failure exn -> None
with exn when SymOp.exn_not_failure exn -> None
(* create a dotty file with a single proposition *)
let dotty_prop_to_dotty_file fname prop cycle =
@ -899,7 +899,7 @@ let dotty_prop_to_dotty_file fname prop cycle =
let fmt_dot = Format.formatter_of_out_channel out_dot in
pp_dotty_prop fmt_dot (prop, cycle);
close_out out_dot
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
()
(* this is used only to print a list of prop parsed with the external parser. Basically deprecated.*)
@ -916,7 +916,7 @@ let pp_proplist_parsed2dotty_file filename plist =
let fmt = F.formatter_of_out_channel outc in
F.fprintf fmt "#### Dotty version: ####@.%a@.@." pp_list plist;
close_out outc
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
()
(********** START of Print interprocedural cfgs in dotty format *)
@ -1058,7 +1058,7 @@ let pp_speclist_to_file (filename : DB.filename) spec_list =
let pp_speclist_dotty_file (filename : DB.filename) spec_list =
try pp_speclist_to_file filename spec_list
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
()
(**********************************************************************)

@ -511,7 +511,7 @@ let explain_leak tenv hpred prop alloc_att_opt bucket =
(try
let t2 = Tenv.expand_type tenv _t2 in
Sil.typ_equal t1 t2
with exn when exn_not_failure exn -> false)
with exn when SymOp.exn_not_failure exn -> false)
| Some (Sil.Sizeof (Sil.Tint _, _)), Some (Sil.Sizeof (Sil.Tint _, _))
when is_file -> (* must be a file opened with "open" *)
true
@ -948,7 +948,7 @@ let explain_nth_function_parameter use_buckets deref_str prop n pvar_off =
Some (dexp_apply_pvar_off de pvar_off)
| None -> None in
create_dereference_desc ~use_buckets dexp_opt' deref_str prop loc
with exn when exn_not_failure exn -> Localise.no_desc)
with exn when SymOp.exn_not_failure exn -> Localise.no_desc)
| _ -> Localise.no_desc
(** Find a program variable whose value is [exp] or pointing to a struct containing [exp] *)

@ -252,7 +252,7 @@ let recognize_exception exn =
| Return_value_ignored (desc, ml_loc) ->
(Localise.return_value_ignored,
desc, Some ml_loc, Exn_user, Medium, None, Nocat)
| Analysis_failure_exe _ ->
| SymOp.Analysis_failure_exe _ ->
(Localise.from_string "Failure_exe",
Localise.no_desc, None, Exn_system, Low, None, Nocat)
| Skip_function desc ->

@ -60,7 +60,7 @@ let arg_desc =
"use file for the err channel"
;
"-iterations",
Arg.Int set_iterations,
Arg.Set_int Config.iterations,
Some "n",
"set the max number of operations for each function, \
expressed as a multiple of symbolic operations (default n=1)"
@ -194,7 +194,7 @@ let arg_desc =
"Add buckets to issue descriptions, useful when developing infer"
;
"-seconds_per_iteration",
Arg.Set_float seconds_per_iteration,
Arg.Set_float Config.seconds_per_iteration,
Some "n",
"set the number of seconds per iteration (default n=30)"
;
@ -204,10 +204,10 @@ let arg_desc =
"use the multirange subtyping domain"
;
"-symops_per_iteration",
Arg.Set_int symops_per_iteration,
Arg.Set_int Config.symops_per_iteration,
Some "n",
"set the number of symbolic operations per iteration (default n="
^ (string_of_int !symops_per_iteration) ^ ")"
^ (string_of_int !Config.symops_per_iteration) ^ ")"
;
"-tracing",
Arg.Unit (fun () -> Config.report_runtime_exceptions := true),

@ -388,7 +388,7 @@ let summary_values top_proc_set summary =
logscale (c1 * in_calls + c2 * out_calls) in
let pp_failure failure =
pp_to_string pp_failure_kind failure in
pp_to_string SymOp.pp_failure_kind failure in
{ vname = Procname.to_string proc_name;

@ -947,7 +947,7 @@ let perform_analysis_phase tenv (pname : Procname.t) (pdesc : Cfg.Procdesc.t)
if recursion_level > !Config.max_recursion then
begin
L.err "Reached maximum level of recursion, raising a Timeout@.";
raise (Analysis_failure_exe (FKrecursion_timeout recursion_level))
raise (SymOp.Analysis_failure_exe (FKrecursion_timeout recursion_level))
end in
let compute_footprint : (unit -> unit) * (unit -> Prop.normal Specs.spec list * Specs.phase) =
@ -1330,7 +1330,7 @@ let perform_transition exe_env tenv proc_name =
let start_node = Cfg.Procdesc.get_start_node pdesc in
f start_node
| None -> ()
with exn when exn_not_failure exn -> () in
with exn when SymOp.exn_not_failure exn -> () in
apply_start_node (do_before_node 0);
try
Config.allowleak := true;
@ -1338,7 +1338,7 @@ let perform_transition exe_env tenv proc_name =
Config.allowleak := allowleak;
apply_start_node do_after_node;
res
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
apply_start_node do_after_node;
Config.allowleak := allowleak;
L.err "Error in collect_preconditions for %a@." Procname.pp proc_name;

@ -150,7 +150,7 @@ struct
let modified_during_analysis path =
let fname = get_full_fname path in
if DB.file_exists fname then
DB.file_modified_time fname >= initial_analysis_time
DB.file_modified_time fname >= Config.initial_analysis_time
else false
(** Close an Html file *)

@ -210,12 +210,12 @@ let log_progress_timeout_event failure_kind =
if !Config.developer_mode then
begin
match failure_kind with
| FKtimeout ->
| SymOp.FKtimeout ->
log_progress_simple "T"
| FKsymops_timeout _ ->
| SymOp.FKsymops_timeout _ ->
log_progress_simple "S"
| FKrecursion_timeout _ ->
| SymOp.FKrecursion_timeout _ ->
log_progress_simple "R"
| FKcrash _ ->
| SymOp.FKcrash _ ->
log_progress_simple "C"
end

@ -151,4 +151,4 @@ val log_progress_file : unit -> unit
val log_progress_procedure : unit -> unit
(** Progress bar: log a timeout event if in developer mode. *)
val log_progress_timeout_event : failure_kind -> unit
val log_progress_timeout_event : SymOp.failure_kind -> unit

@ -191,7 +191,7 @@ let run_proc_analysis ~propagate_exceptions analyze_proc curr_pdesc callee_pdesc
raise exn
else
match exn with
| Analysis_failure_exe kind ->
| SymOp.Analysis_failure_exe kind ->
(* in production mode, log the timeout/crash and continue with the summary we had before
the failure occurred *)
log_error_and_continue exn kind

@ -318,7 +318,7 @@ end = struct
| Some node ->
pos_opt <> None && filter node
| None -> false
with exn when exn_not_failure exn -> false in
with exn when SymOp.exn_not_failure exn -> false in
let position_seen = ref false in
let inverse_sequence =
let log = ref [] in

@ -51,7 +51,7 @@ struct
let lines_arr = read_file (DB.source_file_to_string fname) in
Hashtbl.add hash fname lines_arr;
Some lines_arr
with exn when exn_not_failure exn -> None
with exn when SymOp.exn_not_failure exn -> None
let from_file_linenum_original hash fname linenum =
match file_data hash fname with

@ -17,7 +17,7 @@ module F = Format
let decrease_indent_when_exception thunk =
try (thunk ())
with exn when exn_not_failure exn -> (L.d_decrease_indent 1; raise exn)
with exn when SymOp.exn_not_failure exn -> (L.d_decrease_indent 1; raise exn)
let compute_max_from_nonempty_int_list l =
IList.hd (IList.rev (IList.sort Sil.Int.compare_value l))
@ -1853,7 +1853,7 @@ let rec hpred_imply tenv calc_index_frame calc_missing subs prop1 sigma2 hpred2
decrease_indent_when_exception
(fun () ->
try sigma_imply tenv calc_index_frame calc_missing subs prop1 hpred_list2
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
begin
(L.d_strln_color Red) "backtracking lseg: trying rhs of length exactly 1";
let (_, para_inst3) = Sil.hpara_instantiate para2 _e2 _f2 elist2 in

@ -292,7 +292,8 @@ type call_stats = CallStats.t
(** Execution statistics *)
type stats =
{ stats_time: float; (** Analysis time for the procedure *)
stats_failure: failure_kind option; (** what type of failure stopped the analysis (if any) *)
stats_failure:
SymOp.failure_kind option; (** what type of failure stopped the analysis (if any) *)
stats_calls: Cg.in_out_calls; (** num of procs calling, and called *)
symops: int; (** Number of SymOp's throughout the whole analysis of the function *)
mutable nodes_visited_fp : IntSet.t; (** Nodes visited during the footprint phase *)
@ -351,7 +352,7 @@ let pp_time whole_seconds fmt t =
else F.fprintf fmt "%f s" t
let pp_failure_kind_opt fmt failure_kind_opt = match failure_kind_opt with
| Some failure_kind -> pp_failure_kind fmt failure_kind
| Some failure_kind -> SymOp.pp_failure_kind fmt failure_kind
| None -> F.fprintf fmt "NONE"
let pp_stats err_log whole_seconds fmt stats =

@ -102,7 +102,8 @@ end
(** Execution statistics *)
type stats =
{ stats_time: float; (** Analysis time for the procedure *)
stats_failure: failure_kind option; (** what type of failure stopped the analysis (if any) *)
stats_failure:
SymOp.failure_kind option; (** what type of failure stopped the analysis (if any) *)
stats_calls: Cg.in_out_calls; (** num of procs calling, and called *)
symops: int; (** Number of SymOp's throughout the whole analysis of the function *)
mutable nodes_visited_fp : IntSet.t; (** Nodes visited during the footprint phase *)

@ -269,7 +269,7 @@ let extract_pre p tenv pdesc abstract_fun =
Sil.sub_of_list (IList.map (fun id -> incr count; (id, Sil.Var (Ident.create_normal Ident.name_spec !count))) idlist) in
let _, p' = Cfg.remove_locals_formals pdesc p in
let pre, _ = Prop.extract_spec p' in
let pre' = try abstract_fun tenv pre with exn when exn_not_failure exn -> pre in
let pre' = try abstract_fun tenv pre with exn when SymOp.exn_not_failure exn -> pre in
Prop.normalize (Prop.prop_sub sub pre')
(** return the normalized precondition extracted form the last prop seen, if any

@ -1212,7 +1212,7 @@ and instrs ?(mask_errors=false) tenv pdesc instrs ppl =
let exe_instr instr (p, path) =
L.d_str "Executing Generated Instruction "; Sil.d_instr instr; L.d_ln ();
try sym_exec tenv pdesc instr p path
with exn when exn_not_failure exn && mask_errors ->
with exn when SymOp.exn_not_failure exn && mask_errors ->
let err_name, _, ml_source, _ , _, _, _ = Exceptions.recognize_exception exn in
let loc = (match ml_source with
| Some ml_loc -> "at " ^ (L.ml_loc_to_string ml_loc)
@ -1409,7 +1409,7 @@ and check_variadic_sentinel
let letderef = Sil.Letderef (tmp_id_deref, lexp, typ, loc) in
try
instrs tenv pdesc [letderef] result
with e when exn_not_failure e ->
with e when SymOp.exn_not_failure e ->
if not fails_on_nil then
let deref_str = Localise.deref_str_nil_argument_in_variadic_method proc_name nargs i in
let err_desc =

@ -427,7 +427,7 @@ let rec fsel_star_fld fsel1 fsel2 = match fsel1, fsel2 with
and array_content_star se1 se2 =
try sexp_star_fld se1 se2 with
| exn when exn_not_failure exn -> se1 (* let postcondition override *)
| exn when SymOp.exn_not_failure exn -> se1 (* let postcondition override *)
and esel_star_fld esel1 esel2 = match esel1, esel2 with
| [], esel2 -> (* don't know whether element is read or written in fun call with array *)
@ -504,7 +504,7 @@ let sigma_star_fld (sigma1 : Sil.hpred list) (sigma2 : Sil.hpred list) : Sil.hpr
end
in
try star sigma1 sigma2
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
L.d_str "cannot star ";
Prop.d_sigma sigma1; L.d_str " and "; Prop.d_sigma sigma2;
L.d_ln ();
@ -537,7 +537,7 @@ let sigma_star_typ
| _ -> star sg1 typings2'
end in
try star sigma1 typings2
with exn when exn_not_failure exn ->
with exn when SymOp.exn_not_failure exn ->
L.d_str "cannot star ";
Prop.d_sigma sigma1; L.d_str " and "; Prover.d_typings typings2;
L.d_ln ();

@ -81,7 +81,7 @@ let set_status status =
let timeout_action _ =
unset_alarm ();
raise (Analysis_failure_exe (FKtimeout))
raise (SymOp.Analysis_failure_exe (FKtimeout))
let () = begin
match Config.os_type with
@ -111,7 +111,7 @@ let resume_previous_timeout () =
let exe_timeout f x =
let suspend_existing_timeout_and_start_new_one () =
suspend_existing_timeout ~keep_symop_total:true;
set_alarm (get_timeout_seconds ());
set_alarm (SymOp.get_timeout_seconds ());
SymOp.set_alarm () in
try
suspend_existing_timeout_and_start_new_one ();
@ -119,10 +119,10 @@ let exe_timeout f x =
resume_previous_timeout ();
None
with
| Analysis_failure_exe kind ->
| SymOp.Analysis_failure_exe kind ->
resume_previous_timeout ();
L.log_progress_timeout_event kind;
Errdesc.warning_err (State.get_loc ()) "TIMEOUT: %a@." pp_failure_kind kind;
Errdesc.warning_err (State.get_loc ()) "TIMEOUT: %a@." SymOp.pp_failure_kind kind;
Some kind
| exe ->
resume_previous_timeout ();

@ -12,7 +12,7 @@ open! Utils
(** Handle timeout events *)
(** Execute the function up to a given timeout. *)
val exe_timeout : ('a -> unit) -> 'a -> failure_kind option
val exe_timeout : ('a -> unit) -> 'a -> SymOp.failure_kind option
(** Resume a previously suspended timeout. *)
val resume_previous_timeout : unit -> unit

@ -16,12 +16,6 @@ module F = Format
functions and builtin equality. Use IList instead. *)
module List = struct end
(** initial time of the analysis, i.e. when this module is loaded, gotten from Unix.time *)
let initial_analysis_time = Unix.time ()
(** precise time of day at the start of the analysis *)
let initial_timeofday = Unix.gettimeofday ()
(** {2 Generic Utility Functions} *)
(** Compare police: generic compare disabled. *)
@ -232,175 +226,14 @@ let pp_current_time f () =
let tm = Unix.localtime (Unix.time ()) in
F.fprintf f "%02d/%02d/%4d %02d:%02d" tm.Unix.tm_mday tm.Unix.tm_mon (tm.Unix.tm_year + 1900) tm.Unix.tm_hour tm.Unix.tm_min
(** precise time of day at the start of the analysis *)
let initial_timeofday = Unix.gettimeofday ()
(** Print the time in seconds elapsed since the beginning of the execution of the current command. *)
let pp_elapsed_time fmt () =
let elapsed = Unix.gettimeofday () -. initial_timeofday in
Format.fprintf fmt "%f" elapsed
(** {2 SymOp and Failures: units of symbolic execution} *)
type failure_kind =
| FKtimeout (* max time exceeded *)
| FKsymops_timeout of int (* max symop's exceeded *)
| FKrecursion_timeout of int (* max recursion level exceeded *)
| FKcrash of string (* uncaught exception or failed assertion *)
(** failure that prevented analysis from finishing *)
exception Analysis_failure_exe of failure_kind
let exn_not_failure = function
| Analysis_failure_exe _ -> false
| _ -> true
let pp_failure_kind fmt = function
| FKtimeout -> F.fprintf fmt "TIMEOUT"
| FKsymops_timeout symops -> F.fprintf fmt "SYMOPS TIMEOUT (%d)" symops
| FKrecursion_timeout level -> F.fprintf fmt "RECURSION TIMEOUT(%d)" level
| FKcrash msg -> F.fprintf fmt "CRASH (%s)" msg
let symops_timeout, seconds_timeout =
(* default timeout and long timeout are the same for now, but this will change shortly *)
let default_symops_timeout = 333 in
let default_seconds_timeout = 10.0 in
let long_symops_timeout = 1000 in
let long_seconds_timeout = 30.0 in
if Config.analyze_models then
(* use longer timeouts when analyzing models *)
long_symops_timeout, long_seconds_timeout
else
default_symops_timeout, default_seconds_timeout
(** number of symops to multiply by the number of iterations, after which there is a timeout *)
let symops_per_iteration = ref symops_timeout
(** number of seconds to multiply by the number of iterations, after which there is a timeout *)
let seconds_per_iteration = ref seconds_timeout
(** Timeout in seconds for each function *)
let timeout_seconds = ref !seconds_per_iteration
(** Timeout in SymOps *)
let timeout_symops = ref !symops_per_iteration
(** Set the timeout values in seconds and symops, computed as a multiple of the integer parameter *)
let set_iterations n =
timeout_symops := !symops_per_iteration * n;
timeout_seconds := !seconds_per_iteration *. (float_of_int n)
let get_timeout_seconds () = !timeout_seconds
(** Count the number of symbolic operations *)
module SymOp = struct
(** Internal state of the module *)
type t =
{
(** Only throw timeout exception when alarm is active *)
mutable alarm_active : bool;
(** last wallclock set by an alarm, if any *)
mutable last_wallclock : float option;
(** Number of symop's *)
mutable symop_count : int;
(** Counter for the total number of symop's.
The new state created when save_state is called shares this counter
if keep_symop_total is true. Otherwise, a new counter is created. *)
symop_total : int ref;
}
let initial () : t =
{
alarm_active = false;
last_wallclock = None;
symop_count = 0;
symop_total = ref 0;
}
(** Global State *)
let gs : t ref = ref (initial ())
(** Restore the old state. *)
let restore_state state =
gs := state
(** Return the old state, and revert the current state to the initial one.
If keep_symop_total is true, share the total counter. *)
let save_state ~keep_symop_total =
let old_state = !gs in
let new_state =
let st = initial () in
if keep_symop_total
then
{ st with symop_total = old_state.symop_total }
else
st in
gs := new_state;
old_state
(** handler for the wallclock timeout *)
let wallclock_timeout_handler = ref None
(** set the handler for the wallclock timeout *)
let set_wallclock_timeout_handler handler =
wallclock_timeout_handler := Some handler
(** Set the wallclock alarm checked at every pay() *)
let set_wallclock_alarm nsecs =
!gs.last_wallclock <- Some (Unix.gettimeofday () +. nsecs)
(** Unset the wallclock alarm checked at every pay() *)
let unset_wallclock_alarm () =
!gs.last_wallclock <- None
(** if the wallclock alarm has expired, raise a timeout exception *)
let check_wallclock_alarm () =
match !gs.last_wallclock, !wallclock_timeout_handler with
| Some alarm_time, Some handler when Unix.gettimeofday () >= alarm_time ->
unset_wallclock_alarm ();
handler ()
| _ -> ()
(** Return the time remaining before the wallclock alarm expires *)
let get_remaining_wallclock_time () =
match !gs.last_wallclock with
| Some alarm_time ->
max 0.0 (alarm_time -. Unix.gettimeofday ())
| None ->
0.0
(** Return the total number of symop's since the beginning *)
let get_total () =
!(!gs.symop_total)
(** Reset the total number of symop's *)
let reset_total () =
!gs.symop_total := 0
(** Count one symop *)
let pay () =
!gs.symop_count <- !gs.symop_count + 1;
!gs.symop_total := !(!gs.symop_total) + 1;
if !gs.symop_count > !timeout_symops &&
!gs.alarm_active
then raise (Analysis_failure_exe (FKsymops_timeout !gs.symop_count));
check_wallclock_alarm ()
(** Reset the counter *)
let reset_count () =
!gs.symop_count <- 0
(** Reset the counter and activate the alarm *)
let set_alarm () =
reset_count ();
!gs.alarm_active <- true
(** De-activate the alarm *)
let unset_alarm () =
!gs.alarm_active <- false
end
(** Check if the lhs is a substring of the rhs. *)
let string_is_prefix s1 s2 =
String.length s1 <= String.length s2 &&

@ -142,80 +142,6 @@ val pp_current_time : Format.formatter -> unit -> unit
(** Print the time in seconds elapsed since the beginning of the execution of the current command. *)
val pp_elapsed_time : Format.formatter -> unit -> unit
(** {2 SymOp and Failures: units of symbolic execution} *)
(** initial time of the analysis, i.e. when this module is loaded, gotten from Unix.time *)
val initial_analysis_time : float
(** number of symops to multiply by the number of iterations, after which there is a timeout *)
val symops_per_iteration : int ref
(** number of seconds to multiply by the number of iterations, after which there is a timeout *)
val seconds_per_iteration : float ref
(** Timeout in seconds for each function *)
val get_timeout_seconds : unit -> float
(** Set the timeout values in seconds and symops, computed as a multiple of the integer parameter *)
val set_iterations : int -> unit
type failure_kind =
| FKtimeout (* max time exceeded *)
| FKsymops_timeout of int (* max symop's exceeded *)
| FKrecursion_timeout of int (* max recursion level exceeded *)
| FKcrash of string (* uncaught exception or failed assertion *)
(** Timeout exception *)
exception Analysis_failure_exe of failure_kind
(** check that the exception is not a timeout exception *)
val exn_not_failure : exn -> bool
val pp_failure_kind : Format.formatter -> failure_kind -> unit
(** Count the number of symbolic operations *)
module SymOp : sig
(** Internal state of the module *)
type t
(** if the wallclock alarm has expired, raise a timeout exception *)
val check_wallclock_alarm : unit -> unit
(** Return the time remaining before the wallclock alarm expires *)
val get_remaining_wallclock_time : unit -> float
(** Return the total number of symop's since the beginning *)
val get_total : unit -> int
(** Count one symop *)
val pay : unit -> unit
(** Reset the total number of symop's *)
val reset_total : unit -> unit
(** Restore the old state. *)
val restore_state : t -> unit
(** Return the old state, and revert the current state to the initial one.
If keep_symop_total is true, share the total counter. *)
val save_state : keep_symop_total:bool -> t
(** Reset the counter and activate the alarm *)
val set_alarm : unit -> unit
(** Set the wallclock alarm checked at every pay() *)
val set_wallclock_alarm : float -> unit
(** set the handler for the wallclock timeout *)
val set_wallclock_timeout_handler : (unit -> unit) -> unit
(** De-activate the alarm *)
val unset_alarm : unit -> unit
(** Unset the wallclock alarm checked at every pay() *)
val unset_wallclock_alarm : unit -> unit
end
module Arg : sig
include module type of Arg with type spec = Arg.spec

Loading…
Cancel
Save