You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

818 lines
29 KiB

(*
* 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.
*)
open! Utils
(** Specifications and spec table *)
module L = Logging
module F = Format
(* =============== START of support for spec tables =============== *)
(** Module for joined props *)
module Jprop = struct
(** 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
let to_prop = function
| Prop (_, p) -> p
| Joined (_, p, _, _) -> p
let to_number = function
| Prop (n, _) -> n
| Joined (n, _, _, _) -> n
let rec fav_add_dfs fav = function
| Prop (_, p) -> Prop.prop_fav_add_dfs fav p
| Joined (_, p, jp1, jp2) ->
Prop.prop_fav_add_dfs fav p;
fav_add_dfs fav jp1;
fav_add_dfs fav jp2
let rec normalize = function
| Prop (n, p) -> Prop (n, Prop.normalize p)
| Joined (n, p, jp1, jp2) -> Joined (n, Prop.normalize p, normalize jp1, normalize 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))
(** Comparison for joined_prop *)
let rec compare jp1 jp2 = match jp1, jp2 with
| Prop (_, p1), Prop (_, p2) ->
Prop.prop_compare p1 p2
| Prop _, _ -> - 1
| _, Prop _ -> 1
| Joined (_, p1, jp1, jq1), Joined (_, p2, jp2, jq2) ->
let n = Prop.prop_compare p1 p2 in
if n <> 0 then n
else
let n = compare jp1 jp2 in
if n <> 0 then n else compare jq1 jq2
(** Return true if the two join_prop's are equal *)
let equal jp1 jp2 =
compare jp1 jp2 == 0
let rec fav_add fav = function
| Prop (_, p) -> Prop.prop_fav_add fav p
| Joined (_, p, jp1, jp2) ->
Prop.prop_fav_add fav p;
fav_add fav jp1;
fav_add fav jp2
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 =
Set.Make (struct
type t = Cfg.Node.id * int list
let compare (node_id1, _) (node_id2, _) = Cfg.Node.id_compare node_id1 node_id2
end)
let visited_str vis =
let s = ref "" in
let lines = ref IntSet.empty in
let do_one (_, ns) =
(* if IList.length ns > 1 then
begin
let ss = ref "" in
IList.iter (fun n -> ss := !ss ^ " " ^ string_of_int n) ns;
L.err "Node %d has lines %s@." node !ss
end; *)
IList.iter (fun n -> lines := IntSet.add n !lines) ns in
Visitedset.iter do_one vis;
IntSet.iter (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 }
module NormSpec : sig (* encapsulate type for normalized specs *)
type t
val normalize : 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 : t -> t (** Erase join info from pre of spec *)
end = struct
type t = Prop.normal spec
let tospecs specs = specs
let spec_fav (spec: Prop.normal spec) : Sil.fav =
let fav = Sil.fav_new () in
Jprop.fav_add_dfs fav spec.pre;
IList.iter (fun (p, _) -> Prop.prop_fav_add_dfs fav p) spec.posts;
fav
let spec_sub sub spec =
{ pre = Jprop.normalize (Jprop.jprop_sub sub spec.pre);
posts = IList.map (fun (p, path) -> (Prop.normalize (Prop.prop_sub sub p), path)) spec.posts;
visited = spec.visited }
(** Convert spec into normal form w.r.t. variable renaming *)
let normalize (spec: Prop.normal spec) : Prop.normal spec =
let fav = spec_fav spec in
let idlist = Sil.fav_to_list fav in
let count = ref 0 in
let sub = Sil.sub_of_list (IList.map (fun id -> incr count; (id, Sil.Var (Ident.create_normal Ident.name_spec !count))) idlist) in
spec_sub sub spec
(** Return a compact representation of the spec *)
let compact sh spec =
let pre = Jprop.compact sh spec.pre in
let posts = IList.map (fun (p, path) -> (Prop.prop_compact sh p, path)) spec.posts in
{ pre = pre; posts = posts; visited = spec.visited }
(** Erase join info from pre of spec *)
let erase_join_info_pre spec =
let spec' = { spec with pre = Jprop.Prop (1, Jprop.to_prop spec.pre) } in
normalize 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
module CallStats = struct (** module for tracing stats of function calls *)
module PnameLocHash = Hashtbl.Make (struct
type t = Procname.t * Location.t
let hash (pname, loc) = Hashtbl.hash (Procname.hash_pname pname, loc.Location.line)
let equal (pname1, loc1) (pname2, loc2) =
Location.equal loc1 loc2 && Procname.equal pname1 pname2
end)
type call_result = (** kind of result of a procedure call *)
| CR_success (** successful call *)
| CR_not_met (** precondition not met *)
| CR_not_found (** the callee has no specs *)
| CR_skip (** the callee was skipped *)
type trace = (call_result * bool) list
type t = trace PnameLocHash.t
let trace_add tr (res : call_result) in_footprint = (res, in_footprint) :: tr
let empty_trace : trace = []
let init calls =
let hash = PnameLocHash.create 1 in
let do_call pn_loc = PnameLocHash.add hash pn_loc empty_trace in
IList.iter do_call calls;
hash
let trace t proc_name loc res in_footprint =
let tr_old = try PnameLocHash.find t (proc_name, loc) with
| Not_found ->
PnameLocHash.add t (proc_name, loc) empty_trace;
empty_trace in
let tr_new = trace_add tr_old res in_footprint in
PnameLocHash.replace t (proc_name, loc) tr_new
let tr_elem_str (cr, in_footprint) =
let s1 = match cr with
| CR_success -> "OK"
| CR_not_met -> "NotMet"
| CR_not_found -> "NotFound"
| CR_skip -> "Skip" in
let s2 = if in_footprint then "FP" else "RE" in
s1 ^ ":" ^ s2
let pp_trace fmt tr =
pp_seq
(fun fmt x -> F.fprintf fmt "%s" (tr_elem_str x))
fmt (IList.rev tr)
let iter f t =
let elems = ref [] in
PnameLocHash.iter (fun x tr -> elems := (x, tr) :: !elems) t;
let sorted_elems =
let compare ((pname1, loc1), _) ((pname2, loc2), _) =
let n = Procname.compare pname1 pname2 in
if n <> 0 then n else Location.compare loc1 loc2 in
IList.sort compare !elems in
IList.iter (fun (x, tr) -> f x tr) sorted_elems
(*
let pp fmt t =
let do_call (pname, loc) tr =
F.fprintf fmt "%a %a: %a@\n" Procname.pp pname Location.pp loc pp_trace tr in
iter do_call t
*)
end
(** stats of the calls performed during the analysis *)
type call_stats = CallStats.t
(** Execution statistics *)
type stats =
{ stats_time: float; (** Analysis time for the procedure *)
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 *)
mutable nodes_visited_re : IntSet.t; (** Nodes visited during the re-execution phase *)
call_stats : call_stats;
}
type status = ACTIVE | INACTIVE | STALE
type phase = FOOTPRINT | RE_EXECUTION
type dependency_map_t = int Procname.Map.t
(* name of callee + location of the call *)
type call_site = Procname.t * Location.t
let compare_call_site (pname1, loc1) (pname2, loc2) =
let n = Procname.compare pname1 pname2 in
if n <> 0
then n
else Location.compare loc1 loc2
let pp_call_site fmt (pname, loc) = F.fprintf fmt "%a at %a" Procname.pp pname Location.pp loc
module CallSiteSet = PrettyPrintable.MakePPSet(struct
type t = call_site
let compare = compare_call_site
let pp_element = pp_call_site
end)
type call_summary = CallSiteSet.t Typ.AnnotMap.t
(** Payload: results of some analysis *)
type payload =
{
preposts : NormSpec.t list option; (** list of specs *)
typestate : unit TypeState.t option; (** final typestate *)
calls: call_summary option;
}
type summary =
{ dependency_map: dependency_map_t; (** maps children procs to timestamp as last seen at the start of an analysys phase for this proc *)
nodes: Cfg.Node.id list; (** ids of cfg nodes of the procedure *)
phase: phase; (** in FOOTPRINT phase or in RE_EXECUTION PHASE *)
payload: payload; (** payload containing the result of some analysis *)
sessions: int ref; (** Session number: how many nodes went trough symbolic execution *)
stats: stats; (** statistics: execution time and list of errors *)
status: status; (** ACTIVE when the proc is being analyzed *)
timestamp: int; (** Timestamp of the specs, >= 0, increased every time the specs change *)
attributes : ProcAttributes.t; (** Attributes of the procedure *)
}
type spec_tbl = (summary * DB.origin) Procname.Hash.t
let spec_tbl: spec_tbl = Procname.Hash.create 128
let clear_spec_tbl () = Procname.Hash.clear spec_tbl
(** pretty print analysis time; if [whole_seconds] is true, only print time in seconds *)
let pp_time whole_seconds fmt t =
if whole_seconds then F.fprintf fmt "%3.0f s" 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 -> SymOp.pp_failure_kind fmt failure_kind
| None -> F.fprintf fmt "NONE"
let pp_stats err_log whole_seconds fmt stats =
F.fprintf fmt "TIME:%a FAILURE:%a SYMOPS:%d CALLS:%d,%d@\n" (pp_time whole_seconds)
stats.stats_time pp_failure_kind_opt stats.stats_failure stats.symops
stats.stats_calls.Cg.in_calls stats.stats_calls.Cg.out_calls;
F.fprintf fmt "ERRORS: @[<h>%a@]@." Errlog.pp_errors err_log;
F.fprintf fmt "WARNINGS: @[<h>%a@]" Errlog.pp_warnings err_log
(** 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 = IList.map fst spec.posts in
match pe.pe_kind with
| PP_TEXT ->
F.fprintf fmt "--------------------------- %s ---------------------------@\n" num_str;
F.fprintf fmt "PRE:@\n%a@\n" (Prop.pp_prop pe_text) pre;
F.fprintf fmt "%a@\n" (Propgraph.pp_proplist pe_post "POST" (pre, true)) post_list;
F.fprintf fmt "----------------------------------------------------------------"
| PP_HTML ->
F.fprintf fmt "--------------------------- %s ---------------------------@\n" num_str;
F.fprintf fmt "PRE:@\n%a%a%a@\n" Io_infer.Html.pp_start_color Blue (Prop.pp_prop (pe_html Blue)) pre Io_infer.Html.pp_end_color ();
F.fprintf fmt "%a" (Propgraph.pp_proplist pe_post "POST" (Jprop.to_prop spec.pre, true)) post_list;
F.fprintf fmt "----------------------------------------------------------------"
| PP_LATEX ->
F.fprintf fmt "\\textbf{\\large Requires}\\\\@\n@[%a%a%a@]\\\\@\n" Latex.pp_color Blue (Prop.pp_prop (pe_latex Blue)) pre Latex.pp_color pe.pe_color;
F.fprintf fmt "\\textbf{\\large Ensures}\\\\@\n@[%a@]" (Propgraph.pp_proplist pe_post "POST" (pre, true)) post_list
(** 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 = IList.length specs in
let cnt = ref 0 in
match pe.pe_kind with
| PP_TEXT ->
IList.iter (fun spec -> incr cnt; F.fprintf fmt "%a@\n" (pp_spec pe (Some (!cnt, total))) spec) specs
| PP_HTML ->
IList.iter (fun spec -> incr cnt; F.fprintf fmt "%a<br>@\n" (pp_spec pe (Some (!cnt, total))) spec) specs
| PP_LATEX ->
IList.iter (fun spec -> incr cnt; F.fprintf fmt "\\subsection*{Spec %d of %d}@\n\\(%a\\)@\n" !cnt total (pp_spec pe None) spec) specs
(** Print the decpendency map *)
let pp_dependency_map fmt dependency_map =
let pp_entry fmt proc_name n = F.fprintf fmt "%a=%d " Procname.pp proc_name n in
Procname.Map.iter (pp_entry fmt) dependency_map
let describe_timestamp summary =
("Timestamp", Printf.sprintf "%d" summary.timestamp)
let describe_status summary =
("Status", if summary.status == ACTIVE then "ACTIVE" else "INACTIVE")
let describe_phase summary =
("Phase", if summary.phase == FOOTPRINT then "FOOTPRINT" else "RE_EXECUTION")
(** Return the signature of a procedure declaration as a string *)
let get_signature summary =
let s = ref "" in
IList.iter
(fun (p, typ) ->
let pp_name f () = F.fprintf f "%a" Mangled.pp p in
let pp f () = Typ.pp_decl pe_text pp_name f typ in
let decl = pp_to_string pp () in
s := if !s = "" then decl else !s ^ ", " ^ decl)
summary.attributes.ProcAttributes.formals;
let pp_procname f () = F.fprintf f "%a"
Procname.pp summary.attributes.ProcAttributes.proc_name in
let pp f () =
Typ.pp_decl pe_text pp_procname f summary.attributes.ProcAttributes.ret_type in
let decl = pp_to_string pp () in
decl ^ "(" ^ !s ^ ")"
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 "%a@\n" pp_pair (describe_timestamp summary);
F.fprintf fmt "%a@\n" pp_pair (describe_status summary);
F.fprintf fmt "%a@\n" pp_pair (describe_phase summary);
F.fprintf fmt "Dependency_map: @[%a@]@\n" pp_dependency_map summary.dependency_map
let pp_stats_html err_log fmt =
Errlog.pp_html [] fmt err_log
let get_specs_from_payload summary =
match summary.payload.preposts with
| Some specs -> NormSpec.tospecs specs
| None -> []
(** Print the summary *)
let pp_summary pe whole_seconds fmt summary =
let err_log = summary.attributes.ProcAttributes.err_log in
match pe.pe_kind with
| PP_TEXT ->
pp_summary_no_stats_specs fmt summary;
F.fprintf fmt "%a@\n" (pp_stats err_log whole_seconds) summary.stats;
F.fprintf fmt "%a" (pp_specs pe) (get_specs_from_payload summary)
| PP_HTML ->
Io_infer.Html.pp_start_color fmt Black;
F.fprintf fmt "@\n%a" pp_summary_no_stats_specs summary;
Io_infer.Html.pp_end_color fmt ();
pp_stats_html err_log fmt;
Io_infer.Html.pp_hline fmt ();
F.fprintf fmt "<LISTING>@\n";
pp_specs pe fmt (get_specs_from_payload summary);
F.fprintf fmt "</LISTING>@\n"
| PP_LATEX ->
F.fprintf fmt "\\begin{verbatim}@\n";
pp_summary_no_stats_specs fmt summary;
F.fprintf fmt "%a@\n" (pp_stats err_log whole_seconds) summary.stats;
F.fprintf fmt "\\end{verbatim}@\n";
F.fprintf fmt "%a@\n" (pp_specs pe) (get_specs_from_payload summary)
(** Print the spec table *)
let pp_spec_table pe whole_seconds fmt () =
Procname.Hash.iter (fun proc_name (summ, _) ->
F.fprintf fmt "PROC %a@\n%a@\n" Procname.pp proc_name (pp_summary pe whole_seconds) summ
) spec_tbl
let empty_stats calls in_out_calls_opt =
{ stats_time = 0.0;
stats_failure = None;
stats_calls =
(match in_out_calls_opt with
| Some in_out_calls -> in_out_calls
| None -> { Cg.in_calls = 0; Cg.out_calls = 0 });
symops = 0;
nodes_visited_fp = IntSet.empty;
nodes_visited_re = IntSet.empty;
call_stats = CallStats.init calls;
}
let payload_compact sh payload =
match payload.preposts with
| Some specs ->
{ payload with
preposts = Some (IList.map (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 }
let set_summary_origin proc_name summary origin =
Procname.Hash.replace spec_tbl proc_name (summary, origin)
let add_summary_origin (proc_name : Procname.t) (summary: summary) (origin: DB.origin) : unit =
L.out "Adding summary for %a@\n@[<v 2> %a@]@." Procname.pp proc_name (pp_summary pe_text false) summary;
set_summary_origin proc_name summary origin
(** Add the summary to the table for the given function *)
let add_summary (proc_name : Procname.t) (summary: summary) : unit =
add_summary_origin proc_name summary DB.Res_dir
let specs_filename pname =
let pname_file = Procname.to_filename pname in
pname_file ^ Config.specs_files_suffix
(** path to the .specs file for the given procedure in the current results directory *)
let res_dir_specs_filename pname =
DB.Results_dir.path_to_filename DB.Results_dir.Abs_root [Config.specs_dir_name; specs_filename pname]
(** paths to the .specs file for the given procedure in the current spec libraries *)
let specs_library_filenames pname =
IList.map
(fun specs_dir -> DB.filename_from_string (Filename.concat specs_dir (specs_filename pname)))
Config.specs_library
(** paths to the .specs file for the given procedure in the models folder *)
let specs_models_filename pname =
DB.filename_from_string (Filename.concat Config.models_dir (specs_filename pname))
let summary_exists_in_models pname =
Sys.file_exists (DB.filename_to_string (specs_models_filename pname))
let summary_serializer : summary Serialization.serializer =
Serialization.create_serializer Serialization.summary_key
(** Save summary for the procedure into the spec database *)
let store_summary pname (summ: summary) =
let process_payload payload = match payload.preposts with
| Some specs ->
{ payload with
preposts = Some (IList.map NormSpec.erase_join_info_pre specs);
}
| None -> payload in
let summ1 = { summ with payload = process_payload summ.payload } in
let summ2 = if Config.save_compact_summaries
then summary_compact (Sil.create_sharing_env ()) summ1
else summ1 in
let summ3 = if Config.save_time_in_summaries
then summ2
else
{ summ2 with
stats = { summ1.stats with stats_time = 0.0} } in
Serialization.to_file summary_serializer (res_dir_specs_filename pname) summ3
(** Load procedure summary from the given file *)
let load_summary specs_file =
Serialization.from_file summary_serializer specs_file
(** Load procedure summary for the given procedure name and update spec table *)
let load_summary_to_spec_table proc_name =
let add summ origin =
add_summary_origin proc_name summ origin;
true in
let load_summary_models models_dir =
match load_summary models_dir with
| None -> false
| Some summ -> add summ DB.Models in
let rec load_summary_libs = function (* try to load the summary from a list of libs *)
| [] -> false
| spec_path :: spec_paths ->
(match load_summary spec_path with
| None -> load_summary_libs spec_paths
| Some summ ->
add summ Spec_lib) in
let load_summary_ziplibs zip_specs_filename =
let zip_specs_path = Filename.concat Config.specs_dir_name zip_specs_filename in
match ZipLib.load summary_serializer zip_specs_path with
| None -> false
| Some (summary, origin) -> add summary origin in
let default_spec_dir = res_dir_specs_filename proc_name in
match load_summary default_spec_dir with
| None ->
(* search on models, libzips, and libs *)
load_summary_models (specs_models_filename proc_name) ||
load_summary_ziplibs (specs_filename proc_name) ||
load_summary_libs (specs_library_filenames proc_name)
| Some summ ->
add summ DB.Res_dir
let rec get_summary_origin proc_name =
try
Some (Procname.Hash.find spec_tbl proc_name)
with Not_found ->
if load_summary_to_spec_table proc_name then
get_summary_origin proc_name
else None
let get_summary proc_name =
match get_summary_origin proc_name with
| Some (summary, _) -> Some summary
| None -> None
let get_summary_unsafe s proc_name =
match get_summary proc_name with
| None ->
raise (Failure (
"[" ^ s ^ "] Specs.get_summary_unsafe: " ^ (Procname.to_string proc_name) ^ "Not_found"))
| Some summary -> summary
(** Check if the procedure is from a library:
It's not defined, and there is no spec file for it. *)
let proc_is_library proc_attributes =
if not proc_attributes.ProcAttributes.is_defined then
match get_summary proc_attributes.ProcAttributes.proc_name with
| None -> true
| Some _ -> false
else false
(** Try to find the attributes for a defined proc.
First look at specs (to get attributes computed by analysis)
then look at the attributes table.
If no attributes can be found, return None.
*)
let proc_resolve_attributes proc_name =
let from_attributes_table () =
AttributesTable.load_attributes proc_name in
let from_specs () = match get_summary proc_name with
| Some summary ->
Some summary.attributes
| None -> None in
match from_specs () with
| Some attributes ->
if attributes.ProcAttributes.is_defined
then Some attributes
else begin
match from_attributes_table () with
| Some attributes' ->
Some attributes'
| None ->
Some attributes
end
| None ->
from_attributes_table ()
(** Like proc_resolve_attributes but start from a proc_desc. *)
let pdesc_resolve_attributes proc_desc =
let proc_name = Cfg.Procdesc.get_proc_name proc_desc in
match proc_resolve_attributes proc_name with
| Some proc_attributes ->
proc_attributes
| None ->
(* this should not happen *)
assert false
let get_origin proc_name =
match get_summary_origin proc_name with
| Some (_, origin) -> origin
| None -> DB.Res_dir
let summary_exists proc_name =
match get_summary proc_name with
| Some _ -> true
| None -> false
let get_status summary =
summary.status
let is_active proc_name =
get_status (get_summary_unsafe "is_active" proc_name) = ACTIVE
let is_inactive proc_name =
get_status (get_summary_unsafe "is_active" proc_name) = INACTIVE
let get_timestamp summary =
summary.timestamp
let get_proc_name summary =
summary.attributes.ProcAttributes.proc_name
let get_ret_type summary =
summary.attributes.ProcAttributes.ret_type
let get_formals summary =
summary.attributes.ProcAttributes.formals
let get_attributes summary =
summary.attributes
(** Get the flag with the given key for the procedure, if any *)
(* TODO get_flag should get a summary as parameter *)
let get_flag proc_name key =
match get_summary proc_name with
| None -> None
| Some summary ->
let proc_flags = summary.attributes.ProcAttributes.proc_flags in
try
Some (Hashtbl.find proc_flags key)
with Not_found -> None
(** Return the specs and parameters for the proc in the spec table *)
let get_specs_formals proc_name =
match get_summary proc_name with
| None ->
raise (Failure ("Specs.get_specs_formals: " ^ (Procname.to_string proc_name) ^ "Not_found"))
| Some summary ->
let specs = get_specs_from_payload summary in
let formals = get_formals summary in
(specs, formals)
(** Return the specs for the proc in the spec table *)
let get_specs proc_name =
fst (get_specs_formals proc_name)
(** Return the current phase for the proc *)
let get_phase proc_name =
match get_summary_origin proc_name with
| None -> raise (Failure ("Specs.get_phase: " ^ (Procname.to_string proc_name) ^ " Not_found"))
| Some (summary, _) -> summary.phase
(** Set the current status for the proc *)
let set_status proc_name status =
match get_summary_origin proc_name with
| None -> raise (Failure ("Specs.set_status: " ^ (Procname.to_string proc_name) ^ " Not_found"))
| Some (summary, origin) -> set_summary_origin proc_name { summary with status = status } origin
(** Create the initial dependency map with the given list of dependencies *)
let mk_initial_dependency_map proc_list : dependency_map_t =
IList.fold_left (fun map pname -> Procname.Map.add pname (- 1) map) Procname.Map.empty proc_list
(** Re-initialize a dependency map *)
let re_initialize_dependency_map dependency_map =
Procname.Map.map (fun _ -> - 1) dependency_map
(** Update the dependency map of [proc_name] with the current
timestamps of the dependents *)
let update_dependency_map proc_name =
match get_summary_origin proc_name with
| None ->
raise
(Failure ("Specs.update_dependency_map: " ^ (Procname.to_string proc_name) ^ " Not_found"))
| Some (summary, origin) ->
let current_dependency_map =
Procname.Map.mapi
(fun _ _ -> get_timestamp summary)
summary.dependency_map in
set_summary_origin proc_name { summary with dependency_map = current_dependency_map } origin
let empty_payload =
{
preposts = None;
typestate = None;
calls = None;
}
(** [init_summary (depend_list, nodes,
proc_flags, calls, in_out_calls_opt, proc_attributes)]
initializes the summary for [proc_name] given dependent procs in list [depend_list]. *)
let init_summary
(depend_list, nodes,
proc_flags, calls, in_out_calls_opt,
proc_attributes) =
let dependency_map = mk_initial_dependency_map depend_list in
let summary =
{
dependency_map = dependency_map;
nodes = nodes;
phase = FOOTPRINT;
sessions = ref 0;
payload = empty_payload;
stats = empty_stats calls in_out_calls_opt;
status = INACTIVE;
timestamp = 0;
attributes =
{ proc_attributes with
ProcAttributes.proc_flags = proc_flags; };
} in
Procname.Hash.replace spec_tbl proc_attributes.ProcAttributes.proc_name (summary, DB.Res_dir)
(** Reset a summary rebuilding the dependents and preserving the proc attributes if present. *)
let reset_summary call_graph proc_name attributes_opt =
let dependents = Cg.get_defined_children call_graph proc_name in
let proc_attributes = match attributes_opt with
| Some attributes ->
attributes
| None ->
ProcAttributes.default proc_name !Config.curr_language in
init_summary (
Procname.Set.elements dependents,
[],
proc_flags_empty (),
[],
Some (Cg.get_calls call_graph proc_name),
proc_attributes
)
(* =============== END of support for spec tables =============== *)
(*
let rec post_equal pl1 pl2 = match pl1, pl2 with
| [],[] -> true
| [], _:: _ -> false
| _:: _,[] -> false
| p1:: pl1', p2:: pl2' ->
if Prop.prop_equal p1 p2 then post_equal pl1' pl2'
else false
*)