[specs] move biabduction-specific stuff to BiabductionSummary.ml

Summary:
Move the biabduction-specific payloads (the "`'a spec`" stuff) from specs.ml
into a new `BiabductinoSummary` module, similar to other checkers.

Reviewed By: ngorogiannis

Differential Revision: D7935815

fbshipit-source-id: bdff3b9
master
Jules Villard 7 years ago committed by Facebook Github Bot
parent 766a16cd90
commit d207f29287

@ -101,13 +101,15 @@ let summary_values summary =
let err_log = Specs.get_err_log summary in let err_log = Specs.get_err_log summary in
let proc_name = Specs.get_proc_name summary in let proc_name = Specs.get_proc_name summary in
let vsignature = Specs.get_signature summary in let vsignature = Specs.get_signature summary in
let specs = Specs.get_specs_from_payload summary in let specs = Tabulation.get_specs_from_payload summary in
let lines_visited = let lines_visited =
let visited = ref Specs.Visitedset.empty in let visited = ref BiabductionSummary.Visitedset.empty in
let do_spec spec = visited := Specs.Visitedset.union spec.Specs.visited !visited in let do_spec spec =
visited := BiabductionSummary.Visitedset.union spec.BiabductionSummary.visited !visited
in
List.iter ~f:do_spec specs ; List.iter ~f:do_spec specs ;
let visited_lines = ref Int.Set.empty in let visited_lines = ref Int.Set.empty in
Specs.Visitedset.iter BiabductionSummary.Visitedset.iter
(fun (_, ls) -> List.iter ~f:(fun l -> visited_lines := Int.Set.add !visited_lines l) ls) (fun (_, ls) -> List.iter ~f:(fun l -> visited_lines := Int.Set.add !visited_lines l) ls)
!visited ; !visited ;
Int.Set.elements !visited_lines Int.Set.elements !visited_lines
@ -490,7 +492,7 @@ module Stats = struct
let process_summary error_filter summary linereader stats = let process_summary error_filter summary linereader stats =
let specs = Specs.get_specs_from_payload summary in let specs = Tabulation.get_specs_from_payload summary in
let found_errors = process_err_log error_filter linereader (Specs.get_err_log summary) stats in let found_errors = process_err_log error_filter linereader (Specs.get_err_log summary) stats in
let is_defective = found_errors in let is_defective = found_errors in
let is_verified = specs <> [] && not is_defective in let is_verified = specs <> [] && not is_defective in
@ -528,7 +530,7 @@ end
module StatsLogs = struct module StatsLogs = struct
let process _ (summary: Specs.summary) _ _ = let process _ (summary: Specs.summary) _ _ =
let num_preposts = let num_preposts =
match summary.payload.preposts with Some preposts -> List.length preposts | None -> 0 match summary.payload.biabduction with Some {preposts} -> List.length preposts | None -> 0
in in
let clang_method_kind = let clang_method_kind =
ProcAttributes.string_of_clang_method_kind (Specs.get_attributes summary).clang_method_kind ProcAttributes.string_of_clang_method_kind (Specs.get_attributes summary).clang_method_kind
@ -571,8 +573,10 @@ module PreconditionStats = struct
let nr_dataconstraints = ref 0 let nr_dataconstraints = ref 0
let do_summary proc_name summary = let do_summary proc_name summary =
let specs = Specs.get_specs_from_payload summary in let specs = Tabulation.get_specs_from_payload summary in
let preconditions = List.map ~f:(fun spec -> Specs.Jprop.to_prop spec.Specs.pre) specs in let preconditions =
List.map ~f:(fun spec -> BiabductionSummary.Jprop.to_prop spec.BiabductionSummary.pre) specs
in
match Prop.CategorizePreconditions.categorize preconditions with match Prop.CategorizePreconditions.categorize preconditions with
| Prop.CategorizePreconditions.Empty -> | Prop.CategorizePreconditions.Empty ->
incr nr_empty ; incr nr_empty ;

@ -1209,7 +1209,7 @@ let print_icfg_dotty source cfg =
(********** END of Printing dotty files ***********) (********** END of Printing dotty files ***********)
(** Dotty printing for specs *) (** Dotty printing for specs *)
let pp_speclist_dotty f (splist: Prop.normal Specs.spec list) = let pp_speclist_dotty f (splist: Prop.normal BiabductionSummary.spec list) =
let pp_simple_saved = !Config.pp_simple in let pp_simple_saved = !Config.pp_simple in
Config.pp_simple := true ; Config.pp_simple := true ;
reset_proposition_counter () ; reset_proposition_counter () ;
@ -1218,7 +1218,10 @@ let pp_speclist_dotty f (splist: Prop.normal Specs.spec list) =
F.fprintf f "@\n compound = true; @\n" ; F.fprintf f "@\n compound = true; @\n" ;
(* F.fprintf f "@\n size=\"12,7\"; ratio=fill; @\n"; *) (* F.fprintf f "@\n size=\"12,7\"; ratio=fill; @\n"; *)
List.iter List.iter
~f:(fun s -> pp_dotty_one_spec f (Specs.Jprop.to_prop s.Specs.pre) s.Specs.posts) ~f:(fun s ->
pp_dotty_one_spec f
(BiabductionSummary.Jprop.to_prop s.BiabductionSummary.pre)
s.BiabductionSummary.posts )
splist ; splist ;
F.fprintf f "@\n}" ; F.fprintf f "@\n}" ;
Config.pp_simple := pp_simple_saved Config.pp_simple := pp_simple_saved

@ -19,5 +19,5 @@ val print_icfg_dotty : SourceFile.t -> Cfg.t -> unit
(** {2 Specs} *) (** {2 Specs} *)
val pp_speclist_dotty_file : DB.filename -> Prop.normal Specs.spec list -> unit val pp_speclist_dotty_file : DB.filename -> Prop.normal BiabductionSummary.spec list -> unit
(** Dotty printing for specs *) (** Dotty printing for specs *)

@ -136,7 +136,12 @@ let run_proc_analysis analyze_proc ~caller_pdesc callee_pdesc =
let log_error_and_continue exn summary kind = let log_error_and_continue exn summary kind =
Reporting.log_error summary exn ; Reporting.log_error summary exn ;
let stats = {summary.Specs.stats with Specs.stats_failure= Some kind} in let stats = {summary.Specs.stats with Specs.stats_failure= Some kind} in
let payload = {summary.Specs.payload with Specs.preposts= Some []} in let payload =
let biabduction =
Some BiabductionSummary.{preposts= []; phase= Tabulation.get_phase summary}
in
{summary.Specs.payload with Specs.biabduction}
in
let new_summary = {summary with Specs.stats; payload} in let new_summary = {summary with Specs.stats; payload} in
Specs.store_summary new_summary ; Specs.store_summary new_summary ;
remove_active callee_pname ; remove_active callee_pname ;

@ -99,7 +99,7 @@ let pp_node_link path_to_root ?proof_cover ~description fmt node =
let isproof = let isproof =
match proof_cover with match proof_cover with
| Some proof_cover -> | Some proof_cover ->
Specs.Visitedset.mem (Procdesc.Node.get_id node, []) proof_cover BiabductionSummary.Visitedset.mem (Procdesc.Node.get_id node, []) proof_cover
| None -> | None ->
false false
in in
@ -208,11 +208,13 @@ let force_delayed_print fmt =
il Io_infer.Html.pp_end_color () il Io_infer.Html.pp_end_color ()
else Sil.pp_instr_list Pp.text fmt il else Sil.pp_instr_list Pp.text fmt il
| L.PTjprop_list, shallow_jpl -> | L.PTjprop_list, shallow_jpl ->
let (shallow: bool), (jpl: Prop.normal Specs.Jprop.t list) = Obj.obj shallow_jpl in let (shallow: bool), (jpl: Prop.normal BiabductionSummary.Jprop.t list) =
Specs.Jprop.pp_list pe_default shallow fmt jpl Obj.obj shallow_jpl
in
BiabductionSummary.Jprop.pp_list pe_default shallow fmt jpl
| L.PTjprop_short, jp -> | L.PTjprop_short, jp ->
let jp : Prop.normal Specs.Jprop.t = Obj.obj jp in let jp : Prop.normal BiabductionSummary.Jprop.t = Obj.obj jp in
Specs.Jprop.pp_short pe_default fmt jp BiabductionSummary.Jprop.pp_short pe_default fmt jp
| L.PTloc, loc -> | L.PTloc, loc ->
let loc : Location.t = Obj.obj loc in let loc : Location.t = Obj.obj loc in
Location.pp fmt loc Location.pp fmt loc
@ -263,8 +265,10 @@ let force_delayed_print fmt =
let sigma : Sil.hpred list = Obj.obj sigma in let sigma : Sil.hpred list = Obj.obj sigma in
Prop.pp_sigma pe_default fmt sigma Prop.pp_sigma pe_default fmt sigma
| L.PTspec, spec -> | L.PTspec, spec ->
let spec : Prop.normal Specs.spec = Obj.obj spec in let spec : Prop.normal BiabductionSummary.spec = Obj.obj spec in
Specs.pp_spec (if Config.write_html then Pp.html Blue else Pp.text) None fmt spec BiabductionSummary.pp_spec
(if Config.write_html then Pp.html Blue else Pp.text)
None fmt spec
| L.PTstr, s -> | L.PTstr, s ->
let s : string = Obj.obj s in let s : string = Obj.obj s in
F.pp_print_string fmt s F.pp_print_string fmt s
@ -436,8 +440,10 @@ let write_html_proc source proof_cover table_nodes_at_linenum global_err_log pro
() ()
| Some summary -> | Some summary ->
List.iter List.iter
~f:(fun sp -> proof_cover := Specs.Visitedset.union sp.Specs.visited !proof_cover) ~f:(fun sp ->
(Specs.get_specs_from_payload summary) ; proof_cover :=
BiabductionSummary.Visitedset.union sp.BiabductionSummary.visited !proof_cover )
(Tabulation.get_specs_from_payload summary) ;
Errlog.update global_err_log (Specs.get_err_log summary) ) Errlog.update global_err_log (Specs.get_err_log summary) )
@ -482,7 +488,7 @@ let write_html_file linereader filename procs =
| None -> | None ->
0 0
| Some summary -> | Some summary ->
List.length (Specs.get_specs_from_payload summary) List.length (Tabulation.get_specs_from_payload summary)
in in
let label = let label =
F.sprintf "%s: %d specs" F.sprintf "%s: %d specs"
@ -500,7 +506,7 @@ let write_html_file linereader filename procs =
pp_prelude () ; pp_prelude () ;
let global_err_log = Errlog.empty () in let global_err_log = Errlog.empty () in
let table_nodes_at_linenum = Hashtbl.create 11 in let table_nodes_at_linenum = Hashtbl.create 11 in
let proof_cover = ref Specs.Visitedset.empty in let proof_cover = ref BiabductionSummary.Visitedset.empty in
List.iter ~f:(write_html_proc filename proof_cover table_nodes_at_linenum global_err_log) procs ; List.iter ~f:(write_html_proc filename proof_cover table_nodes_at_linenum global_err_log) procs ;
let table_err_per_line = create_table_err_per_line global_err_log in let table_err_per_line = create_table_err_per_line global_err_log in
let linenum = ref 0 in let linenum = ref 0 in

@ -9,239 +9,8 @@
*) *)
open! IStd open! IStd
open PolyVariantEqual
(** Specifications and spec table *)
module L = Logging
module F = Format module F = Format
open PolyVariantEqual
(* =============== START of support for spec tables =============== *)
(** Module for joined props *)
module Jprop = struct
(** type aliases for component of t values that compare should ignore *)
type id_ = int
let compare_id_ _ _ = 0
(** Remember when a prop is obtained as the join of two other props; the first parameter is an id *)
type 'a t = Prop of id_ * 'a Prop.t | Joined of id_ * 'a Prop.t * 'a t * 'a t
[@@deriving compare]
(** Comparison for joined_prop *)
let compare jp1 jp2 = compare (fun _ _ -> 0) jp1 jp2
(** Return true if the two join_prop's are equal *)
let equal jp1 jp2 = Int.equal (compare jp1 jp2) 0
let to_prop = function Prop (_, p) -> p | Joined (_, p, _, _) -> p
let rec sorted_gen_free_vars tenv =
let open Sequence.Generator in
function
| Prop (_, p) ->
Prop.dfs_sort tenv p |> Prop.sorted_gen_free_vars
| Joined (_, p, jp1, jp2) ->
Prop.dfs_sort tenv p |> Prop.sorted_gen_free_vars
>>= fun () -> sorted_gen_free_vars tenv jp1 >>= fun () -> sorted_gen_free_vars tenv jp2
let rec normalize tenv = function
| Prop (n, p) ->
Prop (n, Prop.normalize tenv p)
| Joined (n, p, jp1, jp2) ->
Joined (n, Prop.normalize tenv p, normalize tenv jp1, normalize tenv jp2)
(** Return a compact representation of the jprop *)
let rec compact sh = function
| Prop (n, p) ->
Prop (n, Prop.prop_compact sh p)
| Joined (n, p, jp1, jp2) ->
Joined (n, Prop.prop_compact sh p, compact sh jp1, compact sh jp2)
(** Print the toplevel prop *)
let pp_short pe f jp = Prop.pp_prop pe f (to_prop jp)
(** Dump the toplevel prop *)
let d_shallow (jp: Prop.normal t) = L.add_print_action (L.PTjprop_short, Obj.repr jp)
(** Get identifies of the jprop *)
let get_id = function Prop (n, _) -> n | Joined (n, _, _, _) -> n
(** Print a list of joined props, the boolean indicates whether to print subcomponents of joined props *)
let pp_list pe shallow f jplist =
let rec pp_seq_newline f = function
| [] ->
()
| [Prop (n, p)] ->
F.fprintf f "PROP %d:@\n%a" n (Prop.pp_prop pe) p
| [Joined (n, p, p1, p2)] ->
if not shallow then F.fprintf f "%a@\n" pp_seq_newline [p1] ;
if not shallow then F.fprintf f "%a@\n" pp_seq_newline [p2] ;
F.fprintf f "PROP %d (join of %d,%d):@\n%a" n (get_id p1) (get_id p2) (Prop.pp_prop pe) p
| jp :: l ->
F.fprintf f "%a@\n" pp_seq_newline [jp] ;
pp_seq_newline f l
in
pp_seq_newline f jplist
(** dump a joined prop list, the boolean indicates whether to print toplevel props only *)
let d_list (shallow: bool) (jplist: Prop.normal t list) =
L.add_print_action (L.PTjprop_list, Obj.repr (shallow, jplist))
let rec gen_free_vars =
let open Sequence.Generator in
function
| Prop (_, p) ->
Prop.gen_free_vars p
| Joined (_, p, jp1, jp2) ->
Prop.gen_free_vars p >>= fun () -> gen_free_vars jp1 >>= fun () -> gen_free_vars jp2
let free_vars jp = Sequence.Generator.run (gen_free_vars jp)
let rec jprop_sub sub = function
| Prop (n, p) ->
Prop (n, Prop.prop_sub sub p)
| Joined (n, p, jp1, jp2) ->
let p' = Prop.prop_sub sub p in
let jp1' = jprop_sub sub jp1 in
let jp2' = jprop_sub sub jp2 in
Joined (n, p', jp1', jp2')
let filter (f: 'a t -> 'b option) jpl =
let rec do_filter acc = function
| [] ->
acc
| (Prop _ as jp) :: jpl -> (
match f jp with Some x -> do_filter (x :: acc) jpl | None -> do_filter acc jpl )
| (Joined (_, _, jp1, jp2) as jp) :: jpl ->
match f jp with
| Some x ->
do_filter (x :: acc) jpl
| None ->
do_filter acc (jpl @ [jp1; jp2])
in
do_filter [] jpl
let rec map (f: 'a Prop.t -> 'b Prop.t) = function
| Prop (n, p) ->
Prop (n, f p)
| Joined (n, p, jp1, jp2) ->
Joined (n, f p, map f jp1, map f jp2)
(*
let rec jprop_sub sub = function
| Prop (n, p) -> Prop (n, Prop.prop_sub sub p)
| Joined (n, p, jp1, jp2) ->
Joined (n, Prop.prop_sub sub p, jprop_sub sub jp1, jprop_sub sub jp2)
*)
end
(***** End of module Jprop *****)
module Visitedset = Caml.Set.Make (struct
type t = Procdesc.Node.id * int list
let compare (node_id1, _) (node_id2, _) = Procdesc.Node.compare_id node_id1 node_id2
end)
let visited_str vis =
let s = ref "" in
let lines = ref Int.Set.empty in
let do_one (_, ns) =
(* if List.length ns > 1 then
begin
let ss = ref "" in
List.iter ~f:(fun n -> ss := !ss ^ " " ^ string_of_int n) ns;
L.out "Node %d has lines %s@." node !ss
end; *)
List.iter ~f:(fun n -> lines := Int.Set.add !lines n) ns
in
Visitedset.iter do_one vis ;
Int.Set.iter ~f:(fun n -> s := !s ^ " " ^ string_of_int n) !lines ;
!s
(** A spec consists of:
pre: a joined prop
post: a list of props with path
visited: a list of pairs (node_id, line) for the visited nodes *)
type 'a spec = {pre: 'a Jprop.t; posts: ('a Prop.t * Paths.Path.t) list; visited: Visitedset.t}
(** encapsulate type for normalized specs *)
module NormSpec : sig
type t
val normalize : Tenv.t -> Prop.normal spec -> t
val tospecs : t list -> Prop.normal spec list
val compact : Sil.sharing_env -> t -> t
(** Return a compact representation of the spec *)
val erase_join_info_pre : Tenv.t -> t -> t
(** Erase join info from pre of spec *)
end = struct
type t = Prop.normal spec
let tospecs specs = specs
let gen_free_vars tenv (spec: Prop.normal spec) =
let open Sequence.Generator in
Jprop.sorted_gen_free_vars tenv spec.pre
>>= fun () ->
ISequence.gen_sequence_list spec.posts ~f:(fun (p, _) ->
Prop.dfs_sort tenv p |> Prop.sorted_gen_free_vars )
let free_vars tenv spec = Sequence.Generator.run (gen_free_vars tenv spec)
let spec_sub tenv sub spec =
{ pre= Jprop.normalize tenv (Jprop.jprop_sub sub spec.pre)
; posts=
List.map ~f:(fun (p, path) -> (Prop.normalize tenv (Prop.prop_sub sub p), path)) spec.posts
; visited= spec.visited }
(** Convert spec into normal form w.r.t. variable renaming *)
let normalize tenv (spec: Prop.normal spec) : Prop.normal spec =
let idlist = free_vars tenv spec |> Ident.hashqueue_of_sequence |> Ident.HashQueue.keys in
let count = ref 0 in
let sub =
Sil.subst_of_list
(List.map
~f:(fun id -> incr count ; (id, Exp.Var (Ident.create_normal Ident.name_spec !count)))
idlist)
in
spec_sub tenv sub spec
(** Return a compact representation of the spec *)
let compact sh spec =
let pre = Jprop.compact sh spec.pre in
let posts = List.map ~f:(fun (p, path) -> (Prop.prop_compact sh p, path)) spec.posts in
{pre; posts; visited= spec.visited}
(** Erase join info from pre of spec *)
let erase_join_info_pre tenv spec =
let spec' = {spec with pre= Jprop.Prop (1, Jprop.to_prop spec.pre)} in
normalize tenv spec'
end
(** Convert spec into normal form w.r.t. variable renaming *)
let spec_normalize = NormSpec.normalize
(** Cast a list of normalized specs to a list of specs *)
let normalized_specs_to_specs = NormSpec.tospecs
(** Execution statistics *) (** Execution statistics *)
type stats = type stats =
@ -259,17 +28,13 @@ let pp_status fmt status = F.pp_print_string fmt (string_of_status status)
let equal_status = [%compare.equal : status] let equal_status = [%compare.equal : status]
type phase = FOOTPRINT | RE_EXECUTION [@@deriving compare]
let equal_phase = [%compare.equal : phase]
(** Payload: results of some analysis *) (** Payload: results of some analysis *)
type payload = type payload =
{ annot_map: AnnotReachabilityDomain.astate option { annot_map: AnnotReachabilityDomain.astate option
; biabduction: BiabductionSummary.t option
; buffer_overrun: BufferOverrunDomain.Summary.t option ; buffer_overrun: BufferOverrunDomain.Summary.t option
; crashcontext_frame: Stacktree_t.stacktree option ; crashcontext_frame: Stacktree_t.stacktree option
; litho: LithoDomain.astate option ; litho: LithoDomain.astate option
; preposts: NormSpec.t list option
; quandary: QuandarySummary.t option ; quandary: QuandarySummary.t option
; racerd: RacerDDomain.summary option ; racerd: RacerDDomain.summary option
; resources: ResourceLeakDomain.summary option ; resources: ResourceLeakDomain.summary option
@ -280,8 +45,7 @@ type payload =
; starvation: StarvationDomain.summary option } ; starvation: StarvationDomain.summary option }
type summary = type summary =
{ phase: phase (** in FOOTPRINT phase or in RE_EXECUTION PHASE *) { payload: payload (** payload containing the result of some analysis *)
; payload: payload (** payload containing the result of some analysis *)
; sessions: int ref (** Session number: how many nodes went trough symbolic execution *) ; sessions: int ref (** Session number: how many nodes went trough symbolic execution *)
; stats: stats (** statistics: execution time and list of errors *) ; stats: stats (** statistics: execution time and list of errors *)
; status: status (** Analysis status of the procedure *) ; status: status (** Analysis status of the procedure *)
@ -303,9 +67,6 @@ let get_err_log summary = (get_attributes summary).ProcAttributes.err_log
let get_loc summary = (get_attributes summary).ProcAttributes.loc let get_loc summary = (get_attributes summary).ProcAttributes.loc
(** Return the current phase for the proc *)
let get_phase summary = summary.phase
type spec_tbl = summary Typ.Procname.Hash.t type spec_tbl = summary Typ.Procname.Hash.t
let spec_tbl : spec_tbl = Typ.Procname.Hash.create 128 let spec_tbl : spec_tbl = Typ.Procname.Hash.create 128
@ -329,58 +90,6 @@ let pp_stats fmt stats =
F.fprintf fmt "FAILURE:%a SYMOPS:%d@\n" pp_failure_kind_opt stats.stats_failure stats.symops F.fprintf fmt "FAILURE:%a SYMOPS:%d@\n" pp_failure_kind_opt stats.stats_failure stats.symops
(** Print the spec *)
let pp_spec pe num_opt fmt spec =
let num_str =
match num_opt with
| None ->
"----------"
| Some (n, tot) ->
Format.sprintf "%d of %d [nvisited:%s]" n tot (visited_str spec.visited)
in
let pre = Jprop.to_prop spec.pre in
let pe_post = Prop.prop_update_obj_sub pe pre in
let post_list = List.map ~f:fst spec.posts in
match pe.Pp.kind with
| TEXT ->
F.fprintf fmt "--------------------------- %s ---------------------------@\n" num_str ;
F.fprintf fmt "PRE:@\n%a@\n" (Prop.pp_prop Pp.text) pre ;
F.fprintf fmt "%a@\n" (Propgraph.pp_proplist pe_post "POST" (pre, true)) post_list ;
F.pp_print_string fmt "----------------------------------------------------------------"
| HTML ->
F.fprintf fmt "--------------------------- %s ---------------------------@\n" num_str ;
F.fprintf fmt "PRE:@\n%a%a%a@\n" Io_infer.Html.pp_start_color Pp.Blue
(Prop.pp_prop (Pp.html Blue))
pre Io_infer.Html.pp_end_color () ;
(Propgraph.pp_proplist pe_post "POST" (pre, true)) fmt post_list ;
F.pp_print_string fmt "----------------------------------------------------------------"
(** Dump a spec *)
let d_spec (spec: 'a spec) = L.add_print_action (L.PTspec, Obj.repr spec)
let pp_specs pe fmt specs =
let total = List.length specs in
let cnt = ref 0 in
match pe.Pp.kind with
| TEXT ->
List.iter
~f:(fun spec ->
incr cnt ;
(pp_spec pe (Some (!cnt, total))) fmt spec )
specs
| HTML ->
List.iter
~f:(fun spec ->
incr cnt ;
F.fprintf fmt "%a<br>@\n" (pp_spec pe (Some (!cnt, total))) spec )
specs
let describe_phase summary =
("Phase", if equal_phase summary.phase FOOTPRINT then "FOOTPRINT" else "RE_EXECUTION")
(** Return the signature of a procedure declaration as a string *) (** Return the signature of a procedure declaration as a string *)
let get_signature summary = let get_signature summary =
let s = ref "" in let s = ref "" in
@ -394,19 +103,13 @@ let get_signature summary =
(get_proc_name summary) !s (get_proc_name summary) !s
let get_specs_from_preposts preposts = Option.value_map ~f:NormSpec.tospecs ~default:[] preposts
let get_specs_from_payload summary = get_specs_from_preposts summary.payload.preposts
let pp_summary_no_stats_specs fmt summary = let pp_summary_no_stats_specs fmt summary =
let pp_pair fmt (x, y) = F.fprintf fmt "%s: %s" x y in
F.fprintf fmt "%s@\n" (get_signature summary) ; F.fprintf fmt "%s@\n" (get_signature summary) ;
F.fprintf fmt "%a@\n" pp_status summary.status ; F.fprintf fmt "%a@\n" pp_status summary.status
F.fprintf fmt "%a@\n" pp_pair (describe_phase summary)
let pp_payload pe fmt let pp_payload pe fmt
{ preposts { biabduction
; typestate ; typestate
; crashcontext_frame ; crashcontext_frame
; quandary ; quandary
@ -425,8 +128,8 @@ let pp_payload pe fmt
() ()
in in
F.fprintf fmt "%a%a%a%a%a%a%a%a%a%a%a%a@\n" F.fprintf fmt "%a%a%a%a%a%a%a%a%a%a%a%a@\n"
(pp_opt "PrePosts" (pp_specs pe)) (pp_opt "Biabduction" (BiabductionSummary.pp pe))
(Option.map ~f:NormSpec.tospecs preposts) biabduction
(pp_opt "TypeState" (TypeState.pp TypeState.unit_ext)) (pp_opt "TypeState" (TypeState.pp TypeState.unit_ext))
typestate typestate
(pp_opt "CrashContext" Crashcontext.pp_stacktree) (pp_opt "CrashContext" Crashcontext.pp_stacktree)
@ -471,17 +174,6 @@ let empty_stats =
{stats_failure= None; symops= 0; nodes_visited_fp= IntSet.empty; nodes_visited_re= IntSet.empty} {stats_failure= None; symops= 0; nodes_visited_fp= IntSet.empty; nodes_visited_re= IntSet.empty}
let payload_compact sh payload =
match payload.preposts with
| Some specs ->
{payload with preposts= Some (List.map ~f:(NormSpec.compact sh) specs)}
| None ->
payload
(** Return a compact representation of the summary *)
let summary_compact sh summary = {summary with payload= payload_compact sh summary.payload}
(** Add the summary to the table for the given function *) (** Add the summary to the table for the given function *)
let add_summary (proc_name: Typ.Procname.t) (summary: summary) : unit = let add_summary (proc_name: Typ.Procname.t) (summary: summary) : unit =
Typ.Procname.Hash.replace spec_tbl proc_name summary Typ.Procname.Hash.replace spec_tbl proc_name summary
@ -552,14 +244,7 @@ let get_summary proc_name =
load_summary_to_spec_table proc_name load_summary_to_spec_table proc_name
let get_summary_unsafe s proc_name = let get_summary_unsafe proc_name = Option.value_exn (get_summary proc_name)
match get_summary proc_name with
| None ->
L.(die InternalError)
"[%s] Specs.get_summary_unsafe: %a Not found" s Typ.Procname.pp proc_name
| Some summary ->
summary
(** Check if the procedure is from a library: (** Check if the procedure is from a library:
It's not defined, and there is no spec file for it. *) It's not defined, and there is no spec file for it. *)
@ -598,12 +283,8 @@ let pdesc_resolve_attributes proc_desc =
(** Save summary for the procedure into the spec database *) (** Save summary for the procedure into the spec database *)
let store_summary (summ1: summary) = let store_summary (summ: summary) =
let summ2 = let final_summary = {summ with status= Analyzed} in
if Config.save_compact_summaries then summary_compact (Sil.create_sharing_env ()) summ1
else summ1
in
let final_summary = {summ2 with status= Analyzed} in
let proc_name = get_proc_name final_summary in let proc_name = get_proc_name final_summary in
(* Make sure the summary in memory is identical to the saved one *) (* Make sure the summary in memory is identical to the saved one *)
add_summary proc_name final_summary ; add_summary proc_name final_summary ;
@ -613,7 +294,7 @@ let store_summary (summ1: summary) =
let empty_payload = let empty_payload =
{ preposts= None { biabduction= None
; typestate= None ; typestate= None
; annot_map= None ; annot_map= None
; crashcontext_frame= None ; crashcontext_frame= None
@ -633,12 +314,7 @@ let empty_payload =
initializes the summary for [proc_name] given dependent procs in list [depend_list]. *) initializes the summary for [proc_name] given dependent procs in list [depend_list]. *)
let init_summary proc_desc = let init_summary proc_desc =
let summary = let summary =
{ phase= FOOTPRINT {sessions= ref 0; payload= empty_payload; stats= empty_stats; status= Pending; proc_desc}
; sessions= ref 0
; payload= empty_payload
; stats= empty_stats
; status= Pending
; proc_desc }
in in
Typ.Procname.Hash.replace spec_tbl (Procdesc.get_proc_name proc_desc) summary ; Typ.Procname.Hash.replace spec_tbl (Procdesc.get_proc_name proc_desc) summary ;
summary summary

@ -14,63 +14,6 @@ open! IStd
(** {2 Spec Tables} *) (** {2 Spec Tables} *)
(** Module for joined props: the result of joining together propositions repeatedly *)
module Jprop : sig
(** Remember when a prop is obtained as the join of two other props; the first parameter is an id *)
type 'a t = Prop of int * 'a Prop.t | Joined of int * 'a Prop.t * 'a t * 'a t
val compare : 'a t -> 'a t -> int
(** Comparison for joined_prop *)
val equal : 'a t -> 'a t -> bool
(** Return true if the two join_prop's are equal *)
val d_shallow : Prop.normal t -> unit
(** Dump the toplevel prop *)
val d_list : bool -> Prop.normal t list -> unit
(** dump a joined prop list, the boolean indicates whether to print toplevel props only *)
val free_vars : Prop.normal t -> Ident.t Sequence.t
val filter : ('a t -> 'b option) -> 'a t list -> 'b list
(** [jprop_filter filter joinedprops] applies [filter] to the elements
of [joindeprops] and applies it to the subparts if the result is
[None]. Returns the most absract results which pass [filter]. *)
val jprop_sub : Sil.subst -> Prop.normal t -> Prop.exposed t
(** apply a substitution to a jprop *)
val map : ('a Prop.t -> 'b Prop.t) -> 'a t -> 'b t
(** map the function to each prop in the jprop, pointwise *)
val pp_list : Pp.env -> bool -> Format.formatter -> Prop.normal t list -> unit
(** Print a list of joined props, the boolean indicates whether to print subcomponents of joined props *)
val pp_short : Pp.env -> Format.formatter -> Prop.normal t -> unit
(** Print the toplevel prop *)
val to_prop : 'a t -> 'a Prop.t
(** Extract the toplevel jprop of a prop *)
end
(** set of visited nodes: node id and list of lines of all the instructions *)
module Visitedset : Caml.Set.S with type elt = Procdesc.Node.id * int list
(** A spec consists of:
pre: a joined prop
posts: a list of props with path
visited: a list of pairs (node_id, line) for the visited nodes *)
type 'a spec = {pre: 'a Jprop.t; posts: ('a Prop.t * Paths.Path.t) list; visited: Visitedset.t}
(** encapsulate type for normalized specs *)
module NormSpec : sig
type t
val erase_join_info_pre : Tenv.t -> t -> t
(** Erase join info from pre of spec *)
end
(** Execution statistics *) (** Execution statistics *)
type stats = type stats =
{ stats_failure: SymOp.failure_kind option { stats_failure: SymOp.failure_kind option
@ -88,17 +31,13 @@ val equal_status : status -> status -> bool
val string_of_status : status -> string val string_of_status : status -> string
type phase = FOOTPRINT | RE_EXECUTION
val equal_phase : phase -> phase -> bool
(** Payload: results of some analysis *) (** Payload: results of some analysis *)
type payload = type payload =
{ annot_map: AnnotReachabilityDomain.astate option { annot_map: AnnotReachabilityDomain.astate option
; biabduction: BiabductionSummary.t option
; buffer_overrun: BufferOverrunDomain.Summary.t option ; buffer_overrun: BufferOverrunDomain.Summary.t option
; crashcontext_frame: Stacktree_t.stacktree option ; crashcontext_frame: Stacktree_t.stacktree option
; litho: LithoDomain.astate option ; litho: LithoDomain.astate option
; preposts: NormSpec.t list option
; quandary: QuandarySummary.t option ; quandary: QuandarySummary.t option
; racerd: RacerDDomain.summary option ; racerd: RacerDDomain.summary option
; resources: ResourceLeakDomain.summary option ; resources: ResourceLeakDomain.summary option
@ -110,8 +49,7 @@ type payload =
(** Procedure summary *) (** Procedure summary *)
type summary = type summary =
{ phase: phase (** in FOOTPRINT phase or in RE_EXECUTION PHASE *) { payload: payload (** payload containing the result of some analysis *)
; payload: payload (** payload containing the result of some analysis *)
; sessions: int ref (** Session number: how many nodes went trough symbolic execution *) ; sessions: int ref (** Session number: how many nodes went trough symbolic execution *)
; stats: stats (** statistics: execution time and list of errors *) ; stats: stats (** statistics: execution time and list of errors *)
; status: status (** Analysis status of the procedure *) ; status: status (** Analysis status of the procedure *)
@ -129,9 +67,6 @@ val summary_exists_in_models : Typ.Procname.t -> bool
val clear_spec_tbl : unit -> unit val clear_spec_tbl : unit -> unit
(** remove all the elements from the spec table *) (** remove all the elements from the spec table *)
val d_spec : 'a spec -> unit
(** Dump a spec *)
val get_summary : Typ.Procname.t -> summary option val get_summary : Typ.Procname.t -> summary option
(** Return the summary option for the procedure name *) (** Return the summary option for the procedure name *)
@ -146,9 +81,6 @@ val get_attributes : summary -> ProcAttributes.t
val get_formals : summary -> (Mangled.t * Typ.t) list val get_formals : summary -> (Mangled.t * Typ.t) list
(** Get the formal parameters of the procedure *) (** Get the formal parameters of the procedure *)
val get_phase : summary -> phase
(** Return the current phase for the proc *)
val get_err_log : summary -> Errlog.t val get_err_log : summary -> Errlog.t
val get_loc : summary -> Location.t val get_loc : summary -> Location.t
@ -156,10 +88,7 @@ val get_loc : summary -> Location.t
val get_signature : summary -> string val get_signature : summary -> string
(** Return the signature of a procedure declaration as a string *) (** Return the signature of a procedure declaration as a string *)
val get_specs_from_payload : summary -> Prop.normal spec list val get_summary_unsafe : Typ.Procname.t -> summary
(** Get the specs from the payload of the summary. *)
val get_summary_unsafe : string -> Typ.Procname.t -> summary
(** @deprecated Return the summary for the procedure name. Raises an exception when not found. *) (** @deprecated Return the summary for the procedure name. Raises an exception when not found. *)
val get_status : summary -> status val get_status : summary -> status
@ -171,12 +100,6 @@ val reset_summary : Procdesc.t -> summary
val load_summary : DB.filename -> summary option val load_summary : DB.filename -> summary option
(** Load procedure summary from the given file *) (** Load procedure summary from the given file *)
val normalized_specs_to_specs : NormSpec.t list -> Prop.normal spec list
(** Cast a list of normalized specs to a list of specs *)
val pp_spec : Pp.env -> (int * int) option -> Format.formatter -> Prop.normal spec -> unit
(** Print the spec *)
val pp_summary_html : SourceFile.t -> Pp.color -> Format.formatter -> summary -> unit val pp_summary_html : SourceFile.t -> Pp.color -> Format.formatter -> summary -> unit
(** Print the summary in html format *) (** Print the summary in html format *)
@ -197,8 +120,5 @@ val proc_is_library : ProcAttributes.t -> bool
(** Check if the procedure is from a library: (** Check if the procedure is from a library:
It's not defined, and there is no spec file for it. *) It's not defined, and there is no spec file for it. *)
val spec_normalize : Tenv.t -> Prop.normal spec -> NormSpec.t
(** Convert spec into normal form w.r.t. variable renaming *)
val store_summary : summary -> unit val store_summary : summary -> unit
(** Save summary for the procedure into the spec database *) (** Save summary for the procedure into the spec database *)

@ -0,0 +1,301 @@
(*
* Copyright (c) 2018 - 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.
*)
open! IStd
(** Specifications and spec table *)
module L = Logging
module F = Format
(** Module for joined props *)
module Jprop = struct
(** type aliases for component of t values that compare should ignore *)
type id_ = int
let compare_id_ _ _ = 0
(** Remember when a prop is obtained as the join of two other props; the first parameter is an id *)
type 'a t = Prop of id_ * 'a Prop.t | Joined of id_ * 'a Prop.t * 'a t * 'a t
[@@deriving compare]
(** Comparison for joined_prop *)
let compare jp1 jp2 = compare (fun _ _ -> 0) jp1 jp2
(** Return true if the two join_prop's are equal *)
let equal jp1 jp2 = Int.equal (compare jp1 jp2) 0
let to_prop = function Prop (_, p) -> p | Joined (_, p, _, _) -> p
let rec sorted_gen_free_vars tenv =
let open Sequence.Generator in
function
| Prop (_, p) ->
Prop.dfs_sort tenv p |> Prop.sorted_gen_free_vars
| Joined (_, p, jp1, jp2) ->
Prop.dfs_sort tenv p |> Prop.sorted_gen_free_vars
>>= fun () -> sorted_gen_free_vars tenv jp1 >>= fun () -> sorted_gen_free_vars tenv jp2
let rec normalize tenv = function
| Prop (n, p) ->
Prop (n, Prop.normalize tenv p)
| Joined (n, p, jp1, jp2) ->
Joined (n, Prop.normalize tenv p, normalize tenv jp1, normalize tenv jp2)
(** Return a compact representation of the jprop *)
let rec compact sh = function
| Prop (n, p) ->
Prop (n, Prop.prop_compact sh p)
| Joined (n, p, jp1, jp2) ->
Joined (n, Prop.prop_compact sh p, compact sh jp1, compact sh jp2)
(** Print the toplevel prop *)
let pp_short pe f jp = Prop.pp_prop pe f (to_prop jp)
(** Dump the toplevel prop *)
let d_shallow (jp: Prop.normal t) = L.add_print_action (L.PTjprop_short, Obj.repr jp)
(** Get identifies of the jprop *)
let get_id = function Prop (n, _) -> n | Joined (n, _, _, _) -> n
(** Print a list of joined props, the boolean indicates whether to print subcomponents of joined props *)
let pp_list pe shallow f jplist =
let rec pp_seq_newline f = function
| [] ->
()
| [Prop (n, p)] ->
F.fprintf f "PROP %d:@\n%a" n (Prop.pp_prop pe) p
| [Joined (n, p, p1, p2)] ->
if not shallow then F.fprintf f "%a@\n" pp_seq_newline [p1] ;
if not shallow then F.fprintf f "%a@\n" pp_seq_newline [p2] ;
F.fprintf f "PROP %d (join of %d,%d):@\n%a" n (get_id p1) (get_id p2) (Prop.pp_prop pe) p
| jp :: l ->
F.fprintf f "%a@\n" pp_seq_newline [jp] ;
pp_seq_newline f l
in
pp_seq_newline f jplist
(** dump a joined prop list, the boolean indicates whether to print toplevel props only *)
let d_list (shallow: bool) (jplist: Prop.normal t list) =
L.add_print_action (L.PTjprop_list, Obj.repr (shallow, jplist))
let rec gen_free_vars =
let open Sequence.Generator in
function
| Prop (_, p) ->
Prop.gen_free_vars p
| Joined (_, p, jp1, jp2) ->
Prop.gen_free_vars p >>= fun () -> gen_free_vars jp1 >>= fun () -> gen_free_vars jp2
let free_vars jp = Sequence.Generator.run (gen_free_vars jp)
let rec jprop_sub sub = function
| Prop (n, p) ->
Prop (n, Prop.prop_sub sub p)
| Joined (n, p, jp1, jp2) ->
let p' = Prop.prop_sub sub p in
let jp1' = jprop_sub sub jp1 in
let jp2' = jprop_sub sub jp2 in
Joined (n, p', jp1', jp2')
let filter (f: 'a t -> 'b option) jpl =
let rec do_filter acc = function
| [] ->
acc
| (Prop _ as jp) :: jpl -> (
match f jp with Some x -> do_filter (x :: acc) jpl | None -> do_filter acc jpl )
| (Joined (_, _, jp1, jp2) as jp) :: jpl ->
match f jp with
| Some x ->
do_filter (x :: acc) jpl
| None ->
do_filter acc (jpl @ [jp1; jp2])
in
do_filter [] jpl
let rec map (f: 'a Prop.t -> 'b Prop.t) = function
| Prop (n, p) ->
Prop (n, f p)
| Joined (n, p, jp1, jp2) ->
Joined (n, f p, map f jp1, map f jp2)
(*
let rec jprop_sub sub = function
| Prop (n, p) -> Prop (n, Prop.prop_sub sub p)
| Joined (n, p, jp1, jp2) ->
Joined (n, Prop.prop_sub sub p, jprop_sub sub jp1, jprop_sub sub jp2)
*)
end
(***** End of module Jprop *****)
module Visitedset = Caml.Set.Make (struct
type t = Procdesc.Node.id * int list
let compare (node_id1, _) (node_id2, _) = Procdesc.Node.compare_id node_id1 node_id2
end)
let visited_str vis =
let s = ref "" in
let lines = ref Int.Set.empty in
let do_one (_, ns) =
(* if List.length ns > 1 then
begin
let ss = ref "" in
List.iter ~f:(fun n -> ss := !ss ^ " " ^ string_of_int n) ns;
L.out "Node %d has lines %s@." node !ss
end; *)
List.iter ~f:(fun n -> lines := Int.Set.add !lines n) ns
in
Visitedset.iter do_one vis ;
Int.Set.iter ~f:(fun n -> s := !s ^ " " ^ string_of_int n) !lines ;
!s
(** A spec consists of:
pre: a joined prop
post: a list of props with path
visited: a list of pairs (node_id, line) for the visited nodes *)
type 'a spec = {pre: 'a Jprop.t; posts: ('a Prop.t * Paths.Path.t) list; visited: Visitedset.t}
(** encapsulate type for normalized specs *)
module NormSpec : sig
type t
val normalize : Tenv.t -> Prop.normal spec -> t
val tospecs : t list -> Prop.normal spec list
val compact : Sil.sharing_env -> t -> t
(** Return a compact representation of the spec *)
val erase_join_info_pre : Tenv.t -> t -> t
(** Erase join info from pre of spec *)
end = struct
type t = Prop.normal spec
let tospecs specs = specs
let gen_free_vars tenv (spec: Prop.normal spec) =
let open Sequence.Generator in
Jprop.sorted_gen_free_vars tenv spec.pre
>>= fun () ->
ISequence.gen_sequence_list spec.posts ~f:(fun (p, _) ->
Prop.dfs_sort tenv p |> Prop.sorted_gen_free_vars )
let free_vars tenv spec = Sequence.Generator.run (gen_free_vars tenv spec)
let spec_sub tenv sub spec =
{ pre= Jprop.normalize tenv (Jprop.jprop_sub sub spec.pre)
; posts=
List.map ~f:(fun (p, path) -> (Prop.normalize tenv (Prop.prop_sub sub p), path)) spec.posts
; visited= spec.visited }
(** Convert spec into normal form w.r.t. variable renaming *)
let normalize tenv (spec: Prop.normal spec) : Prop.normal spec =
let idlist = free_vars tenv spec |> Ident.hashqueue_of_sequence |> Ident.HashQueue.keys in
let count = ref 0 in
let sub =
Sil.subst_of_list
(List.map
~f:(fun id -> incr count ; (id, Exp.Var (Ident.create_normal Ident.name_spec !count)))
idlist)
in
spec_sub tenv sub spec
(** Return a compact representation of the spec *)
let compact sh spec =
let pre = Jprop.compact sh spec.pre in
let posts = List.map ~f:(fun (p, path) -> (Prop.prop_compact sh p, path)) spec.posts in
{pre; posts; visited= spec.visited}
(** Erase join info from pre of spec *)
let erase_join_info_pre tenv spec =
let spec' = {spec with pre= Jprop.Prop (1, Jprop.to_prop spec.pre)} in
normalize tenv spec'
end
(** Convert spec into normal form w.r.t. variable renaming *)
let spec_normalize = NormSpec.normalize
(** Cast a list of normalized specs to a list of specs *)
let normalized_specs_to_specs = NormSpec.tospecs
type phase = FOOTPRINT | RE_EXECUTION [@@deriving compare]
let equal_phase = [%compare.equal : phase]
(** Print the spec *)
let pp_spec pe num_opt fmt spec =
let num_str =
match num_opt with
| None ->
"----------"
| Some (n, tot) ->
Format.sprintf "%d of %d [nvisited:%s]" n tot (visited_str spec.visited)
in
let pre = Jprop.to_prop spec.pre in
let pe_post = Prop.prop_update_obj_sub pe pre in
let post_list = List.map ~f:fst spec.posts in
match pe.Pp.kind with
| TEXT ->
F.fprintf fmt "--------------------------- %s ---------------------------@\n" num_str ;
F.fprintf fmt "PRE:@\n%a@\n" (Prop.pp_prop Pp.text) pre ;
F.fprintf fmt "%a@\n" (Propgraph.pp_proplist pe_post "POST" (pre, true)) post_list ;
F.pp_print_string fmt "----------------------------------------------------------------"
| HTML ->
F.fprintf fmt "--------------------------- %s ---------------------------@\n" num_str ;
F.fprintf fmt "PRE:@\n%a%a%a@\n" Io_infer.Html.pp_start_color Pp.Blue
(Prop.pp_prop (Pp.html Blue))
pre Io_infer.Html.pp_end_color () ;
(Propgraph.pp_proplist pe_post "POST" (pre, true)) fmt post_list ;
F.pp_print_string fmt "----------------------------------------------------------------"
(** Dump a spec *)
let d_spec (spec: 'a spec) = L.add_print_action (L.PTspec, Obj.repr spec)
let pp_specs pe fmt specs =
let total = List.length specs in
let cnt = ref 0 in
match pe.Pp.kind with
| TEXT ->
List.iter
~f:(fun spec ->
incr cnt ;
(pp_spec pe (Some (!cnt, total))) fmt spec )
specs
| HTML ->
List.iter
~f:(fun spec ->
incr cnt ;
F.fprintf fmt "%a<br>@\n" (pp_spec pe (Some (!cnt, total))) spec )
specs
let string_of_phase = function FOOTPRINT -> "FOOTPRINT" | RE_EXECUTION -> "RE_EXECUTION"
let get_specs_from_preposts preposts = Option.value_map ~f:NormSpec.tospecs ~default:[] preposts
type t = {preposts: NormSpec.t list; phase: phase}
let pp pe fmt {preposts; phase} =
F.fprintf fmt "phase= %s@\n%a" (string_of_phase phase) (pp_specs pe) (NormSpec.tospecs preposts)

@ -0,0 +1,92 @@
(*
* Copyright (c) 2018 - 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.
*)
open! IStd
(** Module for joined props: the result of joining together propositions repeatedly *)
module Jprop : sig
(** Remember when a prop is obtained as the join of two other props; the first parameter is an id *)
type 'a t = Prop of int * 'a Prop.t | Joined of int * 'a Prop.t * 'a t * 'a t
val compare : 'a t -> 'a t -> int
(** Comparison for joined_prop *)
val equal : 'a t -> 'a t -> bool
(** Return true if the two join_prop's are equal *)
val d_shallow : Prop.normal t -> unit
(** Dump the toplevel prop *)
val d_list : bool -> Prop.normal t list -> unit
(** dump a joined prop list, the boolean indicates whether to print toplevel props only *)
val free_vars : Prop.normal t -> Ident.t Sequence.t
val filter : ('a t -> 'b option) -> 'a t list -> 'b list
(** [jprop_filter filter joinedprops] applies [filter] to the elements
of [joindeprops] and applies it to the subparts if the result is
[None]. Returns the most absract results which pass [filter]. *)
val jprop_sub : Sil.subst -> Prop.normal t -> Prop.exposed t
(** apply a substitution to a jprop *)
val map : ('a Prop.t -> 'b Prop.t) -> 'a t -> 'b t
(** map the function to each prop in the jprop, pointwise *)
val pp_list : Pp.env -> bool -> Format.formatter -> Prop.normal t list -> unit
(** Print a list of joined props, the boolean indicates whether to print subcomponents of joined props *)
val pp_short : Pp.env -> Format.formatter -> Prop.normal t -> unit
(** Print the toplevel prop *)
val to_prop : 'a t -> 'a Prop.t
(** Extract the toplevel jprop of a prop *)
end
(** set of visited nodes: node id and list of lines of all the instructions *)
module Visitedset : Caml.Set.S with type elt = Procdesc.Node.id * int list
(** A spec consists of:
pre: a joined prop
posts: a list of props with path
visited: a list of pairs (node_id, line) for the visited nodes *)
type 'a spec = {pre: 'a Jprop.t; posts: ('a Prop.t * Paths.Path.t) list; visited: Visitedset.t}
(** encapsulate type for normalized specs *)
module NormSpec : sig
type t
val compact : Sil.sharing_env -> t -> t
(** Return a compact representation of the spec *)
val erase_join_info_pre : Tenv.t -> t -> t
(** Erase join info from pre of spec *)
end
val normalized_specs_to_specs : NormSpec.t list -> Prop.normal spec list
(** Cast a list of normalized specs to a list of specs *)
val d_spec : 'a spec -> unit
(** Dump a spec *)
val spec_normalize : Tenv.t -> Prop.normal spec -> NormSpec.t
(** Convert spec into normal form w.r.t. variable renaming *)
val pp_spec : Pp.env -> (int * int) option -> Format.formatter -> Prop.normal spec -> unit
(** Print the spec *)
type phase = FOOTPRINT | RE_EXECUTION
val equal_phase : phase -> phase -> bool
val get_specs_from_preposts : NormSpec.t list option -> Prop.normal spec list
type t = {preposts: NormSpec.t list; phase: phase}
val pp : Pp.env -> Format.formatter -> t -> unit

@ -2023,35 +2023,38 @@ let pathset_collapse_impl pname tenv pset =
let jprop_partial_join tenv mode jp1 jp2 = let jprop_partial_join tenv mode jp1 jp2 =
let p1, p2 = (Prop.expose (Specs.Jprop.to_prop jp1), Prop.expose (Specs.Jprop.to_prop jp2)) in let p1, p2 =
( Prop.expose (BiabductionSummary.Jprop.to_prop jp1)
, Prop.expose (BiabductionSummary.Jprop.to_prop jp2) )
in
try try
let p = eprop_partial_join tenv mode p1 p2 in let p = eprop_partial_join tenv mode p1 p2 in
let p_renamed = Prop.prop_rename_primed_footprint_vars tenv p in let p_renamed = Prop.prop_rename_primed_footprint_vars tenv p in
Some (Specs.Jprop.Joined (0, p_renamed, jp1, jp2)) Some (BiabductionSummary.Jprop.Joined (0, p_renamed, jp1, jp2))
with Sil.JoinFail -> None with Sil.JoinFail -> None
let jplist_collapse tenv mode jplist = let jplist_collapse tenv mode jplist =
let f = jprop_partial_join tenv mode in let f = jprop_partial_join tenv mode in
list_reduce "JOIN" Specs.Jprop.d_shallow f jplist list_reduce "JOIN" BiabductionSummary.Jprop.d_shallow f jplist
(** Add identifiers to a list of jprops *) (** Add identifiers to a list of jprops *)
let jprop_list_add_ids jplist = let jprop_list_add_ids jplist =
let seq_number = ref 0 in let seq_number = ref 0 in
let rec do_jprop = function let rec do_jprop = function
| Specs.Jprop.Prop (_, p) -> | BiabductionSummary.Jprop.Prop (_, p) ->
incr seq_number ; Specs.Jprop.Prop (!seq_number, p) incr seq_number ; BiabductionSummary.Jprop.Prop (!seq_number, p)
| Specs.Jprop.Joined (_, p, jp1, jp2) -> | BiabductionSummary.Jprop.Joined (_, p, jp1, jp2) ->
let jp1' = do_jprop jp1 in let jp1' = do_jprop jp1 in
let jp2' = do_jprop jp2 in let jp2' = do_jprop jp2 in
incr seq_number ; Specs.Jprop.Joined (!seq_number, p, jp1', jp2') incr seq_number ; BiabductionSummary.Jprop.Joined (!seq_number, p, jp1', jp2')
in in
List.map ~f:(fun (p, path) -> (do_jprop p, path)) jplist List.map ~f:(fun (p, path) -> (do_jprop p, path)) jplist
let proplist_collapse tenv mode plist = let proplist_collapse tenv mode plist =
let jplist = List.map ~f:(fun (p, path) -> (Specs.Jprop.Prop (0, p), path)) plist in let jplist = List.map ~f:(fun (p, path) -> (BiabductionSummary.Jprop.Prop (0, p), path)) plist in
let jplist_joined = jplist_collapse tenv mode (jplist_collapse tenv mode jplist) in let jplist_joined = jplist_collapse tenv mode (jplist_collapse tenv mode jplist) in
jprop_list_add_ids jplist_joined jprop_list_add_ids jplist_joined
@ -2065,7 +2068,7 @@ let pathset_collapse tenv pset =
let plist = Paths.PathSet.elements pset in let plist = Paths.PathSet.elements pset in
let plist' = proplist_collapse tenv JoinState.Post plist in let plist' = proplist_collapse tenv JoinState.Post plist in
Paths.PathSet.from_renamed_list Paths.PathSet.from_renamed_list
(List.map ~f:(fun (p, path) -> (Specs.Jprop.to_prop p, path)) plist') (List.map ~f:(fun (p, path) -> (BiabductionSummary.Jprop.to_prop p, path)) plist')
let pathset_join pname tenv (pset1: Paths.PathSet.t) (pset2: Paths.PathSet.t) let pathset_join pname tenv (pset1: Paths.PathSet.t) (pset2: Paths.PathSet.t)

@ -19,7 +19,8 @@ val pathset_join :
-> Paths.PathSet.t * Paths.PathSet.t -> Paths.PathSet.t * Paths.PathSet.t
(** Join two pathsets *) (** Join two pathsets *)
val proplist_collapse_pre : Tenv.t -> Prop.normal Prop.t list -> Prop.normal Specs.Jprop.t list val proplist_collapse_pre :
Tenv.t -> Prop.normal Prop.t list -> Prop.normal BiabductionSummary.Jprop.t list
val pathset_collapse : Tenv.t -> Paths.PathSet.t -> Paths.PathSet.t val pathset_collapse : Tenv.t -> Paths.PathSet.t -> Paths.PathSet.t

@ -373,7 +373,7 @@ let reason_to_skip ~callee_desc : string option =
| `Summary callee_summary -> | `Summary callee_summary ->
let attr_reason = Specs.get_attributes callee_summary |> reason_from_attributes in let attr_reason = Specs.get_attributes callee_summary |> reason_from_attributes in
if Option.is_some attr_reason then attr_reason if Option.is_some attr_reason then attr_reason
else if List.is_empty (Specs.get_specs_from_payload callee_summary) then else if List.is_empty (Tabulation.get_specs_from_payload callee_summary) then
Some "empty list of specs" Some "empty list of specs"
else (* we are not skipping *) None else (* we are not skipping *) None
| `ProcDesc procdesc -> | `ProcDesc procdesc ->

@ -98,6 +98,17 @@ let log_call_trace caller_name callee_name ?reason loc res =
(***************) (***************)
let get_specs_from_payload summary =
Option.map summary.Specs.payload.biabduction ~f:(fun BiabductionSummary.({preposts}) -> preposts)
|> BiabductionSummary.get_specs_from_preposts
(** Return the current phase for the proc *)
let get_phase summary =
Option.value_map summary.Specs.payload.biabduction ~default:BiabductionSummary.FOOTPRINT ~f:
(fun BiabductionSummary.({phase}) -> phase )
(** Rename the variables in the spec. *) (** Rename the variables in the spec. *)
let spec_rename_vars pname spec = let spec_rename_vars pname spec =
let prop_add_callee_suffix p = let prop_add_callee_suffix p =
@ -105,34 +116,39 @@ let spec_rename_vars pname spec =
Prop.prop_expmap f p Prop.prop_expmap f p
in in
let jprop_add_callee_suffix = function let jprop_add_callee_suffix = function
| Specs.Jprop.Prop (n, p) -> | BiabductionSummary.Jprop.Prop (n, p) ->
Specs.Jprop.Prop (n, prop_add_callee_suffix p) BiabductionSummary.Jprop.Prop (n, prop_add_callee_suffix p)
| Specs.Jprop.Joined (n, p, jp1, jp2) -> | BiabductionSummary.Jprop.Joined (n, p, jp1, jp2) ->
Specs.Jprop.Joined (n, prop_add_callee_suffix p, jp1, jp2) BiabductionSummary.Jprop.Joined (n, prop_add_callee_suffix p, jp1, jp2)
in in
let fav = let fav =
let fav = Specs.Jprop.free_vars spec.Specs.pre |> Ident.hashqueue_of_sequence in let fav =
List.fold_left spec.Specs.posts ~init:fav ~f:(fun fav (p, _) -> BiabductionSummary.Jprop.free_vars spec.BiabductionSummary.pre |> Ident.hashqueue_of_sequence
in
List.fold_left spec.BiabductionSummary.posts ~init:fav ~f:(fun fav (p, _) ->
Prop.free_vars p |> Ident.hashqueue_of_sequence ~init:fav ) Prop.free_vars p |> Ident.hashqueue_of_sequence ~init:fav )
in in
let ids = Ident.HashQueue.keys fav in let ids = Ident.HashQueue.keys fav in
let ids' = List.map ~f:(fun i -> (i, Ident.create_fresh Ident.kprimed)) ids in let ids' = List.map ~f:(fun i -> (i, Ident.create_fresh Ident.kprimed)) ids in
let ren_sub = Sil.subst_of_list (List.map ~f:(fun (i, i') -> (i, Exp.Var i')) ids') in let ren_sub = Sil.subst_of_list (List.map ~f:(fun (i, i') -> (i, Exp.Var i')) ids') in
let pre' = Specs.Jprop.jprop_sub ren_sub spec.Specs.pre in let pre' = BiabductionSummary.Jprop.jprop_sub ren_sub spec.BiabductionSummary.pre in
let posts' = List.map ~f:(fun (p, path) -> (Prop.prop_sub ren_sub p, path)) spec.Specs.posts in let posts' =
List.map ~f:(fun (p, path) -> (Prop.prop_sub ren_sub p, path)) spec.BiabductionSummary.posts
in
let pre'' = jprop_add_callee_suffix pre' in let pre'' = jprop_add_callee_suffix pre' in
let posts'' = List.map ~f:(fun (p, path) -> (prop_add_callee_suffix p, path)) posts' in let posts'' = List.map ~f:(fun (p, path) -> (prop_add_callee_suffix p, path)) posts' in
{Specs.pre= pre''; Specs.posts= posts''; Specs.visited= spec.Specs.visited} BiabductionSummary.{pre= pre''; posts= posts''; visited= spec.BiabductionSummary.visited}
(** Find and number the specs for [proc_name], (** Find and number the specs for [proc_name],
after renaming their vars, and also return the parameters *) after renaming their vars, and also return the parameters *)
let spec_find_rename trace_call summary : (int * Prop.exposed Specs.spec) list * Pvar.t list = let spec_find_rename trace_call summary
: (int * Prop.exposed BiabductionSummary.spec) list * Pvar.t list =
let proc_name = Specs.get_proc_name summary in let proc_name = Specs.get_proc_name summary in
try try
let count = ref 0 in let count = ref 0 in
let f spec = incr count ; (!count, spec_rename_vars proc_name spec) in let f spec = incr count ; (!count, spec_rename_vars proc_name spec) in
let specs = Specs.get_specs_from_payload summary in let specs = get_specs_from_payload summary in
let formals = Specs.get_formals summary in let formals = Specs.get_formals summary in
if List.is_empty specs then ( if List.is_empty specs then (
trace_call CR_not_found ; trace_call CR_not_found ;
@ -1093,11 +1109,11 @@ let add_missing_field_to_tenv ~missing_sigma exe_env caller_tenv callee_pname hp
(** Perform symbolic execution for a single spec *) (** Perform symbolic execution for a single spec *)
let exe_spec exe_env tenv ret_id (n, nspecs) caller_pdesc callee_pname loc prop path_pre let exe_spec exe_env tenv ret_id (n, nspecs) caller_pdesc callee_pname loc prop path_pre
(spec: Prop.exposed Specs.spec) actual_params formal_params : abduction_res = (spec: Prop.exposed BiabductionSummary.spec) actual_params formal_params : abduction_res =
let caller_pname = Procdesc.get_proc_name caller_pdesc in let caller_pname = Procdesc.get_proc_name caller_pdesc in
let posts = mk_posts tenv prop callee_pname spec.Specs.posts in let posts = mk_posts tenv prop callee_pname spec.BiabductionSummary.posts in
let actual_pre = mk_actual_precondition tenv prop actual_params formal_params in let actual_pre = mk_actual_precondition tenv prop actual_params formal_params in
let spec_pre = Specs.Jprop.to_prop spec.Specs.pre in let spec_pre = BiabductionSummary.Jprop.to_prop spec.BiabductionSummary.pre in
L.d_strln ("EXECUTING SPEC " ^ string_of_int n ^ "/" ^ string_of_int nspecs) ; L.d_strln ("EXECUTING SPEC " ^ string_of_int n ^ "/" ^ string_of_int nspecs) ;
L.d_strln "ACTUAL PRECONDITION =" ; L.d_strln "ACTUAL PRECONDITION =" ;
L.d_increase_indent 1 ; L.d_increase_indent 1 ;
@ -1106,7 +1122,7 @@ let exe_spec exe_env tenv ret_id (n, nspecs) caller_pdesc callee_pname loc prop
L.d_ln () ; L.d_ln () ;
L.d_strln "SPEC =" ; L.d_strln "SPEC =" ;
L.d_increase_indent 1 ; L.d_increase_indent 1 ;
Specs.d_spec spec ; BiabductionSummary.d_spec spec ;
L.d_decrease_indent 1 ; L.d_decrease_indent 1 ;
L.d_ln () ; L.d_ln () ;
SymOp.pay () ; SymOp.pay () ;

@ -51,3 +51,9 @@ val exe_function_call :
-> (Exp.t * Typ.t) list -> Prop.normal Prop.t -> Paths.Path.t -> (Exp.t * Typ.t) list -> Prop.normal Prop.t -> Paths.Path.t
-> (Prop.normal Prop.t * Paths.Path.t) list -> (Prop.normal Prop.t * Paths.Path.t) list
(** Execute the function call and return the list of results with return value *) (** Execute the function call and return the list of results with return value *)
val get_phase : Specs.summary -> BiabductionSummary.phase
(** Return the current phase for the proc *)
val get_specs_from_payload : Specs.summary -> Prop.normal BiabductionSummary.spec list
(** Get the specs from the payload of the summary. *)

@ -198,7 +198,7 @@ let do_meet_pre tenv pset =
(** Find the preconditions in the current spec table, (** Find the preconditions in the current spec table,
apply meet then join, and return the joined preconditions *) apply meet then join, and return the joined preconditions *)
let collect_preconditions tenv summary : Prop.normal Specs.Jprop.t list = let collect_preconditions tenv summary : Prop.normal BiabductionSummary.Jprop.t list =
let proc_name = Specs.get_proc_name summary in let proc_name = Specs.get_proc_name summary in
let collect_do_abstract_one tenv prop = let collect_do_abstract_one tenv prop =
if !Config.footprint then Config.run_in_re_execution_mode (Abs.abstract_no_symop tenv) prop if !Config.footprint then Config.run_in_re_execution_mode (Abs.abstract_no_symop tenv) prop
@ -206,8 +206,8 @@ let collect_preconditions tenv summary : Prop.normal Specs.Jprop.t list =
in in
let pres = let pres =
List.map List.map
~f:(fun spec -> Specs.Jprop.to_prop spec.Specs.pre) ~f:(fun spec -> BiabductionSummary.Jprop.to_prop spec.BiabductionSummary.pre)
(Specs.get_specs_from_payload summary) (Tabulation.get_specs_from_payload summary)
in in
let pset = Propset.from_proplist tenv pres in let pset = Propset.from_proplist tenv pres in
let pset' = let pset' =
@ -235,26 +235,26 @@ let collect_preconditions tenv summary : Prop.normal Specs.Jprop.t list =
L.d_ln () ; L.d_ln () ;
L.d_strln ("#### Footprint of " ^ Typ.Procname.to_string proc_name ^ " after Join ####") ; L.d_strln ("#### Footprint of " ^ Typ.Procname.to_string proc_name ^ " after Join ####") ;
L.d_increase_indent 1 ; L.d_increase_indent 1 ;
Specs.Jprop.d_list false jplist ; BiabductionSummary.Jprop.d_list false jplist ;
L.d_decrease_indent 1 ; L.d_decrease_indent 1 ;
L.d_ln () ; L.d_ln () ;
let jplist' = let jplist' =
List.map ~f:(Specs.Jprop.map (Prop.prop_rename_primed_footprint_vars tenv)) jplist List.map ~f:(BiabductionSummary.Jprop.map (Prop.prop_rename_primed_footprint_vars tenv)) jplist
in in
L.d_strln ("#### Renamed footprint of " ^ Typ.Procname.to_string proc_name ^ ": ####") ; L.d_strln ("#### Renamed footprint of " ^ Typ.Procname.to_string proc_name ^ ": ####") ;
L.d_increase_indent 1 ; L.d_increase_indent 1 ;
Specs.Jprop.d_list false jplist' ; BiabductionSummary.Jprop.d_list false jplist' ;
L.d_decrease_indent 1 ; L.d_decrease_indent 1 ;
L.d_ln () ; L.d_ln () ;
let jplist'' = let jplist'' =
let f p = let f p =
Prop.prop_primed_vars_to_normal_vars tenv (collect_do_abstract_one proc_name tenv p) Prop.prop_primed_vars_to_normal_vars tenv (collect_do_abstract_one proc_name tenv p)
in in
List.map ~f:(Specs.Jprop.map f) jplist' List.map ~f:(BiabductionSummary.Jprop.map f) jplist'
in in
L.d_strln ("#### Abstracted footprint of " ^ Typ.Procname.to_string proc_name ^ ": ####") ; L.d_strln ("#### Abstracted footprint of " ^ Typ.Procname.to_string proc_name ^ ": ####") ;
L.d_increase_indent 1 ; L.d_increase_indent 1 ;
Specs.Jprop.d_list false jplist'' ; BiabductionSummary.Jprop.d_list false jplist'' ;
L.d_decrease_indent 1 ; L.d_decrease_indent 1 ;
L.d_ln () ; L.d_ln () ;
jplist'' jplist''
@ -434,9 +434,13 @@ let forward_tabulate exe_env tenv proc_cfg wl =
in in
let print_node_preamble curr_node session pathset_todo = let print_node_preamble curr_node session pathset_todo =
let log_string proc_name = let log_string proc_name =
let summary = Specs.get_summary_unsafe "forward_tabulate" proc_name in let summary = Specs.get_summary_unsafe proc_name in
let phase_string = let phase_string =
if Specs.equal_phase (Specs.get_phase summary) Specs.FOOTPRINT then "FP" else "RE" if
BiabductionSummary.equal_phase (Tabulation.get_phase summary)
BiabductionSummary.FOOTPRINT
then "FP"
else "RE"
in in
let status = Specs.get_status summary in let status = Specs.get_status summary in
F.sprintf "[%s:%s] %s" phase_string (Specs.string_of_status status) F.sprintf "[%s:%s] %s" phase_string (Specs.string_of_status status)
@ -505,7 +509,7 @@ let forward_tabulate exe_env tenv proc_cfg wl =
in in
while not (Worklist.is_empty wl) do while not (Worklist.is_empty wl) do
let curr_node = Worklist.remove wl in let curr_node = Worklist.remove wl in
let summary = Specs.get_summary_unsafe "forward_tabulate" pname in let summary = Specs.get_summary_unsafe pname in
mark_visited summary curr_node ; mark_visited summary curr_node ;
(* mark nodes visited in fp and re phases *) (* mark nodes visited in fp and re phases *)
let session = incr summary.Specs.sessions ; !(summary.Specs.sessions) in let session = incr summary.Specs.sessions ; !(summary.Specs.sessions) in
@ -555,7 +559,7 @@ let vset_ref_add_pathset vset_ref pathset =
let compute_visited vset = let compute_visited vset =
let res = ref Specs.Visitedset.empty in let res = ref BiabductionSummary.Visitedset.empty in
let node_get_all_lines n = let node_get_all_lines n =
let node_loc = Procdesc.Node.get_loc n in let node_loc = Procdesc.Node.get_loc n in
let instrs_loc = List.map ~f:Sil.instr_get_loc (ProcCfg.Exceptional.instrs n) in let instrs_loc = List.map ~f:Sil.instr_get_loc (ProcCfg.Exceptional.instrs n) in
@ -563,14 +567,14 @@ let compute_visited vset =
List.remove_consecutive_duplicates ~equal:Int.equal (List.sort ~compare:Int.compare lines) List.remove_consecutive_duplicates ~equal:Int.equal (List.sort ~compare:Int.compare lines)
in in
let do_node n = let do_node n =
res := Specs.Visitedset.add (Procdesc.Node.get_id n, node_get_all_lines n) !res res := BiabductionSummary.Visitedset.add (Procdesc.Node.get_id n, node_get_all_lines n) !res
in in
Procdesc.NodeSet.iter do_node vset ; Procdesc.NodeSet.iter do_node vset ;
!res !res
(** Extract specs from a pathset *) (** Extract specs from a pathset *)
let extract_specs tenv pdesc pathset : Prop.normal Specs.spec list = let extract_specs tenv pdesc pathset : Prop.normal BiabductionSummary.spec list =
let pname = Procdesc.get_proc_name pdesc in let pname = Procdesc.get_proc_name pdesc in
let sub = let sub =
let fav = let fav =
@ -605,7 +609,8 @@ let extract_specs tenv pdesc pathset : Prop.normal Specs.spec list =
let pre_post_map = let pre_post_map =
let add map (pre, post, visited) = let add map (pre, post, visited) =
let current_posts, current_visited = let current_posts, current_visited =
try Pmap.find pre map with Caml.Not_found -> (Paths.PathSet.empty, Specs.Visitedset.empty) try Pmap.find pre map with Caml.Not_found ->
(Paths.PathSet.empty, BiabductionSummary.Visitedset.empty)
in in
let new_posts = let new_posts =
match post with match post with
@ -614,7 +619,7 @@ let extract_specs tenv pdesc pathset : Prop.normal Specs.spec list =
| Some (post, path) -> | Some (post, path) ->
Paths.PathSet.add_renamed_prop post path current_posts Paths.PathSet.add_renamed_prop post path current_posts
in in
let new_visited = Specs.Visitedset.union visited current_visited in let new_visited = BiabductionSummary.Visitedset.union visited current_visited in
Pmap.add pre (new_posts, new_visited) map Pmap.add pre (new_posts, new_visited) map
in in
List.fold ~f:add ~init:Pmap.empty pre_post_visited_list List.fold ~f:add ~init:Pmap.empty pre_post_visited_list
@ -626,13 +631,13 @@ let extract_specs tenv pdesc pathset : Prop.normal Specs.spec list =
~f:(fun (p, path) -> (PropUtil.remove_seed_vars tenv p, path)) ~f:(fun (p, path) -> (PropUtil.remove_seed_vars tenv p, path))
(Paths.PathSet.elements (do_join_post pname tenv posts)) (Paths.PathSet.elements (do_join_post pname tenv posts))
in in
let spec = {Specs.pre= Specs.Jprop.Prop (1, pre); Specs.posts= posts'; Specs.visited} in let spec = BiabductionSummary.{pre= Jprop.Prop (1, pre); posts= posts'; visited} in
specs := spec :: !specs specs := spec :: !specs
in in
Pmap.iter add_spec pre_post_map ; !specs Pmap.iter add_spec pre_post_map ; !specs
let collect_postconditions wl tenv proc_cfg : Paths.PathSet.t * Specs.Visitedset.t = let collect_postconditions wl tenv proc_cfg : Paths.PathSet.t * BiabductionSummary.Visitedset.t =
let pname = Procdesc.get_proc_name (ProcCfg.Exceptional.proc_desc proc_cfg) in let pname = Procdesc.get_proc_name (ProcCfg.Exceptional.proc_desc proc_cfg) in
let pathset = collect_analysis_result tenv wl proc_cfg in let pathset = collect_analysis_result tenv wl proc_cfg in
(* Assuming C++ developers use RAII, remove resources from the constructor posts *) (* Assuming C++ developers use RAII, remove resources from the constructor posts *)
@ -751,17 +756,20 @@ let initial_prop_from_pre tenv curr_f pre =
(** Re-execute one precondition and return some spec if there was no re-execution error. *) (** Re-execute one precondition and return some spec if there was no re-execution error. *)
let execute_filter_prop exe_env wl tenv proc_cfg init_node let execute_filter_prop exe_env wl tenv proc_cfg init_node
(precondition: Prop.normal Specs.Jprop.t) : Prop.normal Specs.spec option = (precondition: Prop.normal BiabductionSummary.Jprop.t)
: Prop.normal BiabductionSummary.spec option =
let pdesc = ProcCfg.Exceptional.proc_desc proc_cfg in let pdesc = ProcCfg.Exceptional.proc_desc proc_cfg in
let pname = Procdesc.get_proc_name pdesc in let pname = Procdesc.get_proc_name pdesc in
do_before_node 0 init_node ; do_before_node 0 init_node ;
L.d_strln ("#### Start: RE-execution for " ^ Typ.Procname.to_string pname ^ " ####") ; L.d_strln ("#### Start: RE-execution for " ^ Typ.Procname.to_string pname ^ " ####") ;
L.d_indent 1 ; L.d_indent 1 ;
L.d_strln "Precond:" ; L.d_strln "Precond:" ;
Specs.Jprop.d_shallow precondition ; BiabductionSummary.Jprop.d_shallow precondition ;
L.d_ln () ; L.d_ln () ;
L.d_ln () ; L.d_ln () ;
let init_prop = initial_prop_from_pre tenv pdesc (Specs.Jprop.to_prop precondition) in let init_prop =
initial_prop_from_pre tenv pdesc (BiabductionSummary.Jprop.to_prop precondition)
in
let init_edgeset = let init_edgeset =
Paths.PathSet.add_renamed_prop init_prop (Paths.Path.start init_node) Paths.PathSet.empty Paths.PathSet.add_renamed_prop init_prop (Paths.Path.start init_node) Paths.PathSet.empty
in in
@ -775,7 +783,7 @@ let execute_filter_prop exe_env wl tenv proc_cfg init_node
("#### Finished: RE-execution for " ^ Typ.Procname.to_string pname ^ " ####") ; ("#### Finished: RE-execution for " ^ Typ.Procname.to_string pname ^ " ####") ;
L.d_increase_indent 1 ; L.d_increase_indent 1 ;
L.d_strln "Precond:" ; L.d_strln "Precond:" ;
Prop.d_prop (Specs.Jprop.to_prop precondition) ; Prop.d_prop (BiabductionSummary.Jprop.to_prop precondition) ;
L.d_ln () ; L.d_ln () ;
let posts, visited = let posts, visited =
let pset, visited = collect_postconditions wl tenv proc_cfg in let pset, visited = collect_postconditions wl tenv proc_cfg in
@ -787,14 +795,16 @@ let execute_filter_prop exe_env wl tenv proc_cfg init_node
(plist, visited) (plist, visited)
in in
let pre = let pre =
let p = PropUtil.remove_locals_ret tenv pdesc (Specs.Jprop.to_prop precondition) in let p =
PropUtil.remove_locals_ret tenv pdesc (BiabductionSummary.Jprop.to_prop precondition)
in
match precondition with match precondition with
| Specs.Jprop.Prop (n, _) -> | BiabductionSummary.Jprop.Prop (n, _) ->
Specs.Jprop.Prop (n, p) BiabductionSummary.Jprop.Prop (n, p)
| Specs.Jprop.Joined (n, _, jp1, jp2) -> | BiabductionSummary.Jprop.Joined (n, _, jp1, jp2) ->
Specs.Jprop.Joined (n, p, jp1, jp2) BiabductionSummary.Jprop.Joined (n, p, jp1, jp2)
in in
let spec = {Specs.pre; Specs.posts; Specs.visited} in let spec = BiabductionSummary.{pre; posts; visited} in
L.d_decrease_indent 1 ; do_after_node init_node ; Some spec L.d_decrease_indent 1 ; do_after_node init_node ; Some spec
with RE_EXE_ERROR -> with RE_EXE_ERROR ->
do_before_node 0 init_node ; do_before_node 0 init_node ;
@ -802,14 +812,15 @@ let execute_filter_prop exe_env wl tenv proc_cfg init_node
L.d_strln_color Red ("#### [FUNCTION " ^ Typ.Procname.to_string pname ^ "] ...ERROR") ; L.d_strln_color Red ("#### [FUNCTION " ^ Typ.Procname.to_string pname ^ "] ...ERROR") ;
L.d_increase_indent 1 ; L.d_increase_indent 1 ;
L.d_strln "when starting from pre:" ; L.d_strln "when starting from pre:" ;
Prop.d_prop (Specs.Jprop.to_prop precondition) ; Prop.d_prop (BiabductionSummary.Jprop.to_prop precondition) ;
L.d_strln "This precondition is filtered out." ; L.d_strln "This precondition is filtered out." ;
L.d_decrease_indent 1 ; L.d_decrease_indent 1 ;
do_after_node init_node ; do_after_node init_node ;
None None
type exe_phase = (unit -> unit) * (unit -> Prop.normal Specs.spec list * Specs.phase) type exe_phase =
(unit -> unit) * (unit -> Prop.normal BiabductionSummary.spec list * BiabductionSummary.phase)
(** Return functions to perform one phase of the analysis for a procedure. (** Return functions to perform one phase of the analysis for a procedure.
Given [proc_name], return [do, get_results] where [go ()] performs the analysis phase Given [proc_name], return [do, get_results] where [go ()] performs the analysis phase
@ -826,12 +837,12 @@ let perform_analysis_phase exe_env tenv (summary: Specs.summary) (proc_cfg: Proc
let init_prop = initial_prop_from_emp tenv pdesc in let init_prop = initial_prop_from_emp tenv pdesc in
(* use existing pre's (in recursion some might exist) as starting points *) (* use existing pre's (in recursion some might exist) as starting points *)
let init_props_from_pres = let init_props_from_pres =
let specs = Specs.get_specs_from_payload summary in let specs = Tabulation.get_specs_from_payload summary in
(* rename spec vars to footprint vars, and copy current to footprint *) (* rename spec vars to footprint vars, and copy current to footprint *)
let mk_init precondition = let mk_init precondition =
initial_prop_from_pre tenv pdesc (Specs.Jprop.to_prop precondition) initial_prop_from_pre tenv pdesc (BiabductionSummary.Jprop.to_prop precondition)
in in
List.map ~f:(fun spec -> mk_init spec.Specs.pre) specs List.map ~f:(fun spec -> mk_init spec.BiabductionSummary.pre) specs
in in
let init_props = Propset.from_proplist tenv (init_prop :: init_props_from_pres) in let init_props = Propset.from_proplist tenv (init_prop :: init_props_from_pres) in
let init_edgeset = let init_edgeset =
@ -863,14 +874,16 @@ let perform_analysis_phase exe_env tenv (summary: Specs.summary) (proc_cfg: Proc
Reporting.log_error_deprecated pname exn ; Reporting.log_error_deprecated pname exn ;
(* retuning no specs *) [] (* retuning no specs *) []
in in
(specs, Specs.FOOTPRINT) (specs, BiabductionSummary.FOOTPRINT)
in in
let wl = path_set_create_worklist proc_cfg in let wl = path_set_create_worklist proc_cfg in
(go wl, get_results wl) (go wl, get_results wl)
in in
let re_execution () : exe_phase = let re_execution () : exe_phase =
let candidate_preconditions = let candidate_preconditions =
List.map ~f:(fun spec -> spec.Specs.pre) (Specs.get_specs_from_payload summary) List.map
~f:(fun spec -> spec.BiabductionSummary.pre)
(Tabulation.get_specs_from_payload summary)
in in
let valid_specs = ref [] in let valid_specs = ref [] in
let go () = let go () =
@ -880,7 +893,8 @@ let perform_analysis_phase exe_env tenv (summary: Specs.summary) (proc_cfg: Proc
(match speco with None -> () | Some spec -> valid_specs := !valid_specs @ [spec]) ; (match speco with None -> () | Some spec -> valid_specs := !valid_specs @ [spec]) ;
speco speco
in in
if Config.undo_join then ignore (Specs.Jprop.filter filter candidate_preconditions) if Config.undo_join then
ignore (BiabductionSummary.Jprop.filter filter candidate_preconditions)
else ignore (List.map ~f:filter candidate_preconditions) else ignore (List.map ~f:filter candidate_preconditions)
in in
let get_results () = let get_results () =
@ -891,14 +905,14 @@ let perform_analysis_phase exe_env tenv (summary: Specs.summary) (proc_cfg: Proc
[Typ.Procname.to_filename pname] [Typ.Procname.to_filename pname]
in in
if Config.write_dotty then Dotty.pp_speclist_dotty_file filename specs ; if Config.write_dotty then Dotty.pp_speclist_dotty_file filename specs ;
(specs, Specs.RE_EXECUTION) (specs, BiabductionSummary.RE_EXECUTION)
in in
(go, get_results) (go, get_results)
in in
match Specs.get_phase summary with match Tabulation.get_phase summary with
| Specs.FOOTPRINT -> | FOOTPRINT ->
compute_footprint () compute_footprint ()
| Specs.RE_EXECUTION -> | RE_EXECUTION ->
re_execution () re_execution ()
@ -927,9 +941,11 @@ let exception_preconditions tenv pname summary =
(exns, false) (exns, false)
in in
let collect_spec errors spec = let collect_spec errors spec =
List.fold ~f:(collect_exceptions spec.Specs.pre) ~init:errors spec.Specs.posts List.fold
~f:(collect_exceptions spec.BiabductionSummary.pre)
~init:errors spec.BiabductionSummary.posts
in in
List.fold ~f:collect_spec ~init:([], true) (Specs.get_specs_from_payload summary) List.fold ~f:collect_spec ~init:([], true) (Tabulation.get_specs_from_payload summary)
(* Collect all pairs of the kind (precondition, custom error) from a summary *) (* Collect all pairs of the kind (precondition, custom error) from a summary *)
@ -942,9 +958,11 @@ let custom_error_preconditions summary =
((pre, e) :: errors, all_post_error) ((pre, e) :: errors, all_post_error)
in in
let collect_spec errors spec = let collect_spec errors spec =
List.fold ~f:(collect_errors spec.Specs.pre) ~init:errors spec.Specs.posts List.fold
~f:(collect_errors spec.BiabductionSummary.pre)
~init:errors spec.BiabductionSummary.posts
in in
List.fold ~f:collect_spec ~init:([], true) (Specs.get_specs_from_payload summary) List.fold ~f:collect_spec ~init:([], true) (Tabulation.get_specs_from_payload summary)
(* Remove the constrain of the form this != null which is true for all Java virtual calls *) (* Remove the constrain of the form this != null which is true for all Java virtual calls *)
@ -975,7 +993,7 @@ let remove_this_not_null tenv prop =
This means that the post-conditions associated with this precondition cannot be prevented This means that the post-conditions associated with this precondition cannot be prevented
by the calling context. *) by the calling context. *)
let is_unavoidable tenv pre = let is_unavoidable tenv pre =
let prop = remove_this_not_null tenv (Specs.Jprop.to_prop pre) in let prop = remove_this_not_null tenv (BiabductionSummary.Jprop.to_prop pre) in
match Prop.CategorizePreconditions.categorize [prop] with match Prop.CategorizePreconditions.categorize [prop] with
| Prop.CategorizePreconditions.NoPres | Prop.CategorizePreconditions.Empty -> | Prop.CategorizePreconditions.NoPres | Prop.CategorizePreconditions.Empty ->
true true
@ -1008,7 +1026,9 @@ let report_runtime_exceptions tenv pdesc summary =
in in
let report (pre, runtime_exception) = let report (pre, runtime_exception) =
if should_report pre then if should_report pre then
let pre_str = F.asprintf "%a" (Prop.pp_prop Pp.text) (Specs.Jprop.to_prop pre) in let pre_str =
F.asprintf "%a" (Prop.pp_prop Pp.text) (BiabductionSummary.Jprop.to_prop pre)
in
let exn_desc = Localise.java_unchecked_exn_desc pname runtime_exception pre_str in let exn_desc = Localise.java_unchecked_exn_desc pname runtime_exception pre_str in
let exn = Exceptions.Java_runtime_exception (runtime_exception, pre_str, exn_desc) in let exn = Exceptions.Java_runtime_exception (runtime_exception, pre_str, exn_desc) in
Reporting.log_error_deprecated pname exn Reporting.log_error_deprecated pname exn
@ -1030,62 +1050,67 @@ let report_custom_errors tenv summary =
module SpecMap = Caml.Map.Make (struct module SpecMap = Caml.Map.Make (struct
type t = Prop.normal Specs.Jprop.t type t = Prop.normal BiabductionSummary.Jprop.t
let compare = Specs.Jprop.compare let compare = BiabductionSummary.Jprop.compare
end) end)
(** Update the specs of the current proc after the execution of one phase *) (** Update the specs of the current proc after the execution of one phase *)
let update_specs tenv prev_summary phase (new_specs: Specs.NormSpec.t list) let update_specs tenv prev_summary phase (new_specs: BiabductionSummary.NormSpec.t list)
: Specs.NormSpec.t list * bool = : BiabductionSummary.NormSpec.t list * bool =
let new_specs = Specs.normalized_specs_to_specs new_specs in let new_specs = BiabductionSummary.normalized_specs_to_specs new_specs in
let old_specs = Specs.get_specs_from_payload prev_summary in let old_specs = Tabulation.get_specs_from_payload prev_summary in
let changed = ref false in let changed = ref false in
let current_specs = let current_specs =
ref ref
(List.fold (List.fold
~f:(fun map spec -> ~f:(fun map spec ->
SpecMap.add spec.Specs.pre SpecMap.add spec.BiabductionSummary.pre
(Paths.PathSet.from_renamed_list spec.Specs.posts, spec.Specs.visited) map ) ( Paths.PathSet.from_renamed_list spec.BiabductionSummary.posts
, spec.BiabductionSummary.visited ) map )
~init:SpecMap.empty old_specs) ~init:SpecMap.empty old_specs)
in in
let re_exe_filter old_spec = let re_exe_filter old_spec =
(* filter out pres which failed re-exe *) (* filter out pres which failed re-exe *)
if if
Specs.equal_phase phase Specs.RE_EXECUTION BiabductionSummary.equal_phase phase RE_EXECUTION
&& not && not
(List.exists (List.exists
~f:(fun new_spec -> Specs.Jprop.equal new_spec.Specs.pre old_spec.Specs.pre) ~f:(fun new_spec ->
BiabductionSummary.Jprop.equal new_spec.BiabductionSummary.pre
old_spec.BiabductionSummary.pre )
new_specs) new_specs)
then ( then (
changed := true ; changed := true ;
current_specs := SpecMap.remove old_spec.Specs.pre !current_specs ) current_specs := SpecMap.remove old_spec.BiabductionSummary.pre !current_specs )
else () else ()
in in
let add_spec spec = let add_spec spec =
(* add a new spec by doing union of the posts *) (* add a new spec by doing union of the posts *)
try try
let old_post, old_visited = SpecMap.find spec.Specs.pre !current_specs in let old_post, old_visited = SpecMap.find spec.BiabductionSummary.pre !current_specs in
let new_post, new_visited = let new_post, new_visited =
( Paths.PathSet.union old_post (Paths.PathSet.from_renamed_list spec.Specs.posts) ( Paths.PathSet.union old_post
, Specs.Visitedset.union old_visited spec.Specs.visited ) (Paths.PathSet.from_renamed_list spec.BiabductionSummary.posts)
, BiabductionSummary.Visitedset.union old_visited spec.BiabductionSummary.visited )
in in
if not (Paths.PathSet.equal old_post new_post) then ( if not (Paths.PathSet.equal old_post new_post) then (
changed := true ; changed := true ;
current_specs := current_specs :=
SpecMap.add spec.Specs.pre (new_post, new_visited) SpecMap.add spec.BiabductionSummary.pre (new_post, new_visited)
(SpecMap.remove spec.Specs.pre !current_specs) ) (SpecMap.remove spec.BiabductionSummary.pre !current_specs) )
with Caml.Not_found -> with Caml.Not_found ->
changed := true ; changed := true ;
current_specs := current_specs :=
SpecMap.add spec.Specs.pre SpecMap.add spec.BiabductionSummary.pre
(Paths.PathSet.from_renamed_list spec.Specs.posts, spec.Specs.visited) !current_specs ( Paths.PathSet.from_renamed_list spec.BiabductionSummary.posts
, spec.BiabductionSummary.visited ) !current_specs
in in
let res = ref [] in let res = ref [] in
let convert pre (post_set, visited) = let convert pre (post_set, visited) =
res := res :=
Specs.spec_normalize tenv BiabductionSummary.spec_normalize tenv
{Specs.pre; Specs.posts= Paths.PathSet.elements post_set; Specs.visited} BiabductionSummary.{pre; posts= Paths.PathSet.elements post_set; visited}
:: !res :: !res
in in
List.iter ~f:re_exe_filter old_specs ; List.iter ~f:re_exe_filter old_specs ;
@ -1098,7 +1123,7 @@ let update_specs tenv prev_summary phase (new_specs: Specs.NormSpec.t list)
(** update a summary after analysing a procedure *) (** update a summary after analysing a procedure *)
let update_summary tenv prev_summary specs phase res = let update_summary tenv prev_summary specs phase res =
let normal_specs = List.map ~f:(Specs.spec_normalize tenv) specs in let normal_specs = List.map ~f:(BiabductionSummary.spec_normalize tenv) specs in
let new_specs, _ = update_specs tenv prev_summary phase normal_specs in let new_specs, _ = update_specs tenv prev_summary phase normal_specs in
let symops = prev_summary.Specs.stats.Specs.symops + SymOp.get_total () in let symops = prev_summary.Specs.stats.Specs.symops + SymOp.get_total () in
let stats_failure = let stats_failure =
@ -1107,13 +1132,15 @@ let update_summary tenv prev_summary specs phase res =
let stats = {prev_summary.Specs.stats with symops; stats_failure} in let stats = {prev_summary.Specs.stats with symops; stats_failure} in
let preposts = let preposts =
match phase with match phase with
| Specs.FOOTPRINT -> | BiabductionSummary.FOOTPRINT ->
Some new_specs new_specs
| Specs.RE_EXECUTION -> | BiabductionSummary.RE_EXECUTION ->
Some (List.map ~f:(Specs.NormSpec.erase_join_info_pre tenv) new_specs) List.map ~f:(BiabductionSummary.NormSpec.erase_join_info_pre tenv) new_specs
in
let payload =
{prev_summary.Specs.payload with Specs.biabduction= Some BiabductionSummary.{preposts; phase}}
in in
let payload = {prev_summary.Specs.payload with Specs.preposts} in {prev_summary with Specs.stats; payload}
{prev_summary with Specs.phase; stats; payload}
(** Analyze the procedure and return the resulting summary. *) (** Analyze the procedure and return the resulting summary. *)
@ -1121,7 +1148,7 @@ let analyze_proc exe_env tenv proc_cfg : Specs.summary =
let proc_desc = ProcCfg.Exceptional.proc_desc proc_cfg in let proc_desc = ProcCfg.Exceptional.proc_desc proc_cfg in
let proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
reset_global_values proc_desc ; reset_global_values proc_desc ;
let summary = Specs.get_summary_unsafe "analyze_proc" proc_name in let summary = Specs.get_summary_unsafe proc_name in
let go, get_results = perform_analysis_phase exe_env tenv summary proc_cfg in let go, get_results = perform_analysis_phase exe_env tenv summary proc_cfg in
let res = Timeout.exe_timeout go () in let res = Timeout.exe_timeout go () in
let specs, phase = get_results () in let specs, phase = get_results () in
@ -1135,19 +1162,32 @@ let analyze_proc exe_env tenv proc_cfg : Specs.summary =
(** Perform the transition from [FOOTPRINT] to [RE_EXECUTION] in spec table *) (** Perform the transition from [FOOTPRINT] to [RE_EXECUTION] in spec table *)
let transition_footprint_re_exe tenv proc_name joined_pres = let transition_footprint_re_exe tenv proc_name joined_pres =
let summary = Specs.get_summary_unsafe "transition_footprint_re_exe" proc_name in let summary = Specs.get_summary_unsafe proc_name in
let summary' = let summary' =
if Config.only_footprint then {summary with Specs.phase= Specs.RE_EXECUTION} if Config.only_footprint then
match summary.Specs.payload.biabduction with
| Some ({phase= FOOTPRINT} as biabduction) ->
{ summary with
Specs.payload=
{ summary.payload with
Specs.biabduction= Some {biabduction with BiabductionSummary.phase= RE_EXECUTION}
} }
| _ ->
summary
else else
let specs = let preposts =
List.map List.map
~f:(fun jp -> ~f:(fun jp ->
Specs.spec_normalize tenv {Specs.pre= jp; posts= []; visited= Specs.Visitedset.empty} BiabductionSummary.spec_normalize tenv
{BiabductionSummary.pre= jp; posts= []; visited= BiabductionSummary.Visitedset.empty}
) )
joined_pres joined_pres
in in
let payload = {summary.Specs.payload with Specs.preposts= Some specs} in let payload =
{summary with Specs.phase= Specs.RE_EXECUTION; payload} { summary.Specs.payload with
biabduction= Some BiabductionSummary.{preposts; phase= RE_EXECUTION} }
in
{summary with Specs.payload}
in in
Specs.add_summary proc_name summary' Specs.add_summary proc_name summary'
@ -1184,7 +1224,7 @@ let perform_transition proc_cfg tenv proc_name =
transition_footprint_re_exe tenv proc_name joined_pres transition_footprint_re_exe tenv proc_name joined_pres
in in
match Specs.get_summary proc_name with match Specs.get_summary proc_name with
| Some summary when Specs.equal_phase (Specs.get_phase summary) Specs.FOOTPRINT -> | Some summary when BiabductionSummary.equal_phase (Tabulation.get_phase summary) FOOTPRINT ->
transition summary transition summary
| _ -> | _ ->
() ()
@ -1198,8 +1238,23 @@ let analyze_procedure_aux exe_env tenv proc_desc =
Specs.add_summary proc_name summaryfp ; Specs.add_summary proc_name summaryfp ;
perform_transition proc_cfg tenv proc_name ; perform_transition proc_cfg tenv proc_name ;
let summaryre = Config.run_in_re_execution_mode (analyze_proc exe_env tenv) proc_cfg in let summaryre = Config.run_in_re_execution_mode (analyze_proc exe_env tenv) proc_cfg in
Specs.add_summary proc_name summaryre ; let summary_compact =
match summaryre.Specs.payload.biabduction with
| Some BiabductionSummary.({preposts} as biabduction) when Config.save_compact_summaries ->
let sharing_env = Sil.create_sharing_env () in
let compact_preposts =
List.map ~f:(BiabductionSummary.NormSpec.compact sharing_env) preposts
in
{ summaryre with
payload=
{ summaryre.payload with
biabduction= Some {biabduction with BiabductionSummary.preposts= compact_preposts} }
}
| _ ->
summaryre summaryre
in
Specs.add_summary proc_name summary_compact ;
summary_compact
let analyze_procedure {Callbacks.summary; proc_desc; tenv; exe_env} : Specs.summary = let analyze_procedure {Callbacks.summary; proc_desc; tenv; exe_env} : Specs.summary =
@ -1210,4 +1265,4 @@ let analyze_procedure {Callbacks.summary; proc_desc; tenv; exe_env} : Specs.summ
( try ignore (analyze_procedure_aux exe_env tenv proc_desc) with exn -> ( try ignore (analyze_procedure_aux exe_env tenv proc_desc) with exn ->
IExn.reraise_if exn ~f:(fun () -> not (Exceptions.handle_exception exn)) ; IExn.reraise_if exn ~f:(fun () -> not (Exceptions.handle_exception exn)) ;
Reporting.log_error_deprecated proc_name exn ) ; Reporting.log_error_deprecated proc_name exn ) ;
Specs.get_summary_unsafe __FILE__ proc_name Specs.get_summary_unsafe proc_name

Loading…
Cancel
Save