diff --git a/src/dune b/src/dune index 599d3f7423ac6a0e1daba6de1ea8c204345e76e3..7cd55aa0b825af3628b942e63f2e0b5503c955a6 100644 --- a/src/dune +++ b/src/dune @@ -39,6 +39,7 @@ (echo " - dune-site.plugins:" %{lib-available:dune-site.plugins} "\n") (echo " - ppx_import:" %{lib-available:ppx_import} "\n") (echo " - ppx_deriving.eq:" %{lib-available:ppx_deriving.eq} "\n") + (echo " - ppx_deriving.ord:" %{lib-available:ppx_deriving.ord} "\n") (echo " - ppx_deriving_yaml:" %{lib-available:ppx_deriving_yaml} "\n") ) ) @@ -51,7 +52,7 @@ (flags :standard -w -9) (libraries frama-c.init fpath str unix zarith ocamlgraph dynlink bytes yaml.unix yojson menhirLib dune-site dune-site.plugins) (instrumentation (backend landmarks)) - (preprocess (staged_pps ppx_import ppx_deriving.eq ppx_deriving_yaml)) + (preprocess (staged_pps ppx_import ppx_deriving.eq ppx_deriving.ord ppx_deriving_yaml)) ) (generate_sites_module (module config_data) (sites frama-c) (plugins (frama-c plugins) (frama-c plugins_gui))) diff --git a/src/kernel_services/abstract_interp/eva_types.ml b/src/kernel_services/abstract_interp/eva_types.ml new file mode 100644 index 0000000000000000000000000000000000000000..229782bc556f4a22fe3107eb5a01799935788973 --- /dev/null +++ b/src/kernel_services/abstract_interp/eva_types.ml @@ -0,0 +1,188 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2023 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +let stable_hash x = Hashtbl.seeded_hash 0 x + +let dkey_callstack = Kernel.register_category "callstack" + +module Callstack = +struct + module Thread = Int (* Threads are identified by integers *) + module Kf = Kernel_function + module Stmt = Cil_datatype.Stmt + module Var = Cil_datatype.Varinfo + + module Call = Datatype.Pair_with_collections(Kf)(Stmt) + (struct let module_name = "Eva.Callstack.Call" end) + + module Calls = Datatype.List (Call) + + type callstack = { + thread: int; + entry_point: Kernel_function.t; + stack: Call.t list; + } + + module Prototype = + struct + include Datatype.Serializable_undefined + + type t = callstack = { + thread: int; + entry_point: Kernel_function.t; + stack: Call.t list; + } + [@@deriving eq, ord] + + let name = "Eva.Callstack" + + let reprs = + List.concat_map (fun stack -> + List.map (fun entry_point -> { thread = 0; entry_point; stack }) + Kernel_function.reprs) + Calls.reprs + + let pretty fmt cs = + let pp_call fmt (kf,stmt) = + Format.fprintf fmt "%a :: %a <-@ " + Kf.pretty kf + Cil_datatype.Location.pretty (Stmt.loc stmt) + in + Format.fprintf fmt "@[<hv>"; + List.iter (pp_call fmt) cs.stack; + Format.fprintf fmt "%a@]" Kernel_function.pretty cs.entry_point + + let hash cs = + Hashtbl.hash + (cs.thread, Kernel_function.hash cs.entry_point, Calls.hash cs.stack) + end + + type call = Call.t + + include Datatype.Make_with_collections (Prototype) + + let pretty_debug = pretty + + let compare_lex cs1 cs2 = + if cs1 == cs2 then 0 else + let c = Thread.compare cs1.thread cs2.thread in + if c <> 0 then c else + let c = Kernel_function.compare cs1.entry_point cs2.entry_point in + if c <> 0 then c else + Calls.compare (List.rev cs1.stack) (List.rev cs2.stack) + + (* Stack manipulation *) + + let init ?(thread=0) kf = { thread; entry_point=kf; stack = [] } + + let push kf stmt cs = + { cs with stack = (kf, stmt) :: cs.stack } + + let pop cs = + match cs.stack with + | [] -> None + | _ :: tail -> Some { cs with stack = tail } + + let top cs = + match cs.stack with + | [] -> None + | (kf, stmt) :: _ -> Some (kf, stmt) + + let top_kf cs = + match cs.stack with + | (kf, _stmt) :: _ -> kf + | [] -> cs.entry_point + + let top_callsite cs = + match cs.stack with + | [] -> Cil_types.Kglobal + | (_kf, stmt) :: _ -> Cil_types.Kstmt stmt + + let top_call cs = + match cs.stack with + | (kf, stmt) :: _ -> kf, Cil_types.Kstmt stmt + | [] -> cs.entry_point, Cil_types.Kglobal + + let top_caller cs = + match cs.stack with + | _ :: (kf, _) :: _ -> Some kf + | [_] -> Some cs.entry_point + | [] -> None + + (* Conversion *) + + let to_kf_list cs = cs.entry_point :: List.rev_map fst cs.stack + let to_stmt_list cs = List.rev_map snd cs.stack + + let to_call_list cs = + let l = + List.rev_map (fun (kf, stmt) -> (kf, Cil_types.Kstmt stmt)) cs.stack + in + (cs.entry_point, Cil_types.Kglobal) :: l + + (* Stable hash and pretty-printing *) + + let stmt_hash s = + let pos = fst (Cil_datatype.Stmt.loc s) in + stable_hash (pos.Filepath.pos_path, pos.Filepath.pos_lnum) + + let kf_hash kf = stable_hash (Kernel_function.get_name kf) + + let rec calls_hash = function + | [] -> 0 + | (kf, stmt) :: tl -> stable_hash (kf_hash kf, stmt_hash stmt, calls_hash tl) + + let stable_hash { thread; entry_point; stack } = + let p = stable_hash (thread, kf_hash entry_point, calls_hash stack) in + p mod 11_316_496 (* 58 ** 4 *) + + let base58_map = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + (* Converts [i] into a fixed-length, 4-wide string in base-58 *) + let base58_of_int n = + let buf = Bytes.create 4 in + Bytes.set buf 0 (String.get base58_map (n mod 58)); + let n = n / 58 in + Bytes.set buf 1 (String.get base58_map (n mod 58)); + let n = n / 58 in + Bytes.set buf 2 (String.get base58_map (n mod 58)); + let n = n / 58 in + Bytes.set buf 3 (String.get base58_map (n mod 58)); + Bytes.to_string buf + + let pretty_hash fmt callstack = + if Kernel.is_debug_key_enabled dkey_callstack then + Format.fprintf fmt "<%s> " (base58_of_int (stable_hash callstack)) + else Format.ifprintf fmt "" + + let pretty_short fmt callstack = + Format.fprintf fmt "%a" pretty_hash callstack; + let list = List.rev (to_kf_list callstack) in + Pretty_utils.pp_flowlist ~left:"" ~sep:" <- " ~right:"" + (fun fmt kf -> Kernel_function.pretty fmt kf) + fmt list + + let pretty fmt callstack = + Format.fprintf fmt "@[<hv>%a" pretty_hash callstack; + pretty fmt callstack; + Format.fprintf fmt "@]" +end diff --git a/src/kernel_services/abstract_interp/eva_types.mli b/src/kernel_services/abstract_interp/eva_types.mli new file mode 100644 index 0000000000000000000000000000000000000000..d5406e1d64876766c4d68ce28d7ca537e70e4539 --- /dev/null +++ b/src/kernel_services/abstract_interp/eva_types.mli @@ -0,0 +1,63 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2023 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(** This module is here for compatibility reasons only and will be removed in + future versions. Use [Eva.Callstack] instead *) + +module Callstack : +sig + type call = Cil_types.kernel_function * Cil_types.stmt + + module Call : Datatype.S with type t = call + + type callstack = { + thread: int; + entry_point: Cil_types.kernel_function; + stack: call list; + } + + include Datatype.S_with_collections with type t = callstack + + val pretty_hash : Format.formatter -> t -> unit + val pretty_short : Format.formatter -> t -> unit + val pretty_debug : Format.formatter -> t -> unit + + val compare_lex : t -> t -> int + + val init : ?thread:int -> Cil_types.kernel_function -> t + + val push : Cil_types.kernel_function -> Cil_types.stmt -> t -> t + val pop : t -> t option + val top : t -> (Cil_types.kernel_function * Cil_types.stmt) option + val top_kf : t -> Cil_types.kernel_function + val top_callsite : t -> Cil_types.kinstr + val top_call : t -> Cil_types.kernel_function * Cil_types.kinstr + val top_caller : t -> Cil_types.kernel_function option + + val to_kf_list : t -> Cil_types.kernel_function list + val to_stmt_list : t -> Cil_types.stmt list + val to_call_list : t -> (Cil_types.kernel_function * Cil_types.kinstr) list +end +[@@alert db_deprecated + "Eva_types is only provided for compatibility reason and will be removed \ + in a future version of Frama-C. Please use the Eva.Callstack in the \ + public API instead."] diff --git a/src/kernel_services/abstract_interp/value_types.ml b/src/kernel_services/abstract_interp/value_types.ml index de2fbcb8ba0f702ae0537818c03c585572dfca65..d7d070d20e3053df8e3337c8921a6b9bd037c536 100644 --- a/src/kernel_services/abstract_interp/value_types.ml +++ b/src/kernel_services/abstract_interp/value_types.ml @@ -20,95 +20,6 @@ (* *) (**************************************************************************) -module OCamlHashtbl = Hashtbl -open Cil_types - -type call_site = kernel_function * kinstr - -module Callsite = struct - include Datatype.Pair_with_collections(Kernel_function)(Cil_datatype.Kinstr) - (struct let module_name = "Value_callbacks.Callpoint" end) - - let pretty fmt (kf, ki) = - Format.fprintf fmt "%a@@%t" Kernel_function.pretty kf - (fun fmt -> - match ki with - | Kglobal -> Format.pp_print_string fmt "<main>" - | Kstmt stmt -> Format.pp_print_int fmt stmt.sid - ) -end - -let dkey_callstack = Kernel.register_category "callstack" - -type callstack = call_site list - -module Callstack = struct - include Datatype.With_collections - (Datatype.List(Callsite)) - (struct let module_name = "Value_types.Callstack" end) - - (* Use default Datatype printer for debug only *) - let pretty_debug = pretty - - let stmt_hash s = - let pos = fst (Cil_datatype.Stmt.loc s) in - OCamlHashtbl.seeded_hash 0 - (pos.Filepath.pos_path, pos.Filepath.pos_lnum) - - let kf_hash kf = - let name = Kernel_function.get_name kf in - OCamlHashtbl.seeded_hash 0 name - - let ki_hash = function - | Kglobal -> 1 - | Kstmt s -> 5 * stmt_hash s - - let rec hash = function - | [] -> 0 - | (kf, ki) :: r -> - let p = OCamlHashtbl.seeded_hash 0 (kf_hash kf, ki_hash ki, hash r) in - p mod 11_316_496 (* 58 ** 4 *) - - let base58_map = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - - (* Converts [i] into a fixed-length, 4-wide string in base-58 *) - let base58_of_int n = - let buf = Bytes.create 4 in - Bytes.set buf 0 (String.get base58_map (n mod 58)); - let n = n / 58 in - Bytes.set buf 1 (String.get base58_map (n mod 58)); - let n = n / 58 in - Bytes.set buf 2 (String.get base58_map (n mod 58)); - let n = n / 58 in - Bytes.set buf 3 (String.get base58_map (n mod 58)); - Bytes.to_string buf - - let pretty_hash fmt callstack = - if Kernel.is_debug_key_enabled dkey_callstack then - Format.fprintf fmt "<%s> " (base58_of_int (hash callstack)) - else Format.ifprintf fmt "" - - let pretty_short fmt callstack = - Format.fprintf fmt "%a" pretty_hash callstack; - Pretty_utils.pp_flowlist ~left:"" ~sep:" <- " ~right:"" - (fun fmt (kf,_) -> Kernel_function.pretty fmt kf) - fmt - callstack - - let pretty fmt callstack = - Format.fprintf fmt "@[<hv>%a" pretty_hash callstack; - List.iter (fun (kf,ki) -> - Kernel_function.pretty fmt kf; - match ki with - | Kglobal -> () - | Kstmt stmt -> Format.fprintf fmt " :: %a <-@ " - Cil_datatype.Location.pretty - (Cil_datatype.Stmt.loc stmt) - ) callstack; - Format.fprintf fmt "@]" - -end - type 'a callback_result = | Normal of 'a | NormalStore of 'a * int diff --git a/src/kernel_services/abstract_interp/value_types.mli b/src/kernel_services/abstract_interp/value_types.mli index f492097990283c38c1702685c30ce3c6fc89f5d9..811e97b24fd8645dacd03b2224d5186e007f8c6a 100644 --- a/src/kernel_services/abstract_interp/value_types.mli +++ b/src/kernel_services/abstract_interp/value_types.mli @@ -23,41 +23,6 @@ (** Declarations that are useful for plugins written on top of the results of Value. *) -open Cil_types - -(* TODO: These types are already defined in Value_util. *) -type call_site = kernel_function * kinstr -(** Value call-site. - A callsite [(f,p)] represents a call at function [f] invoked - {i from} program point [p]. -*) - -type callstack = call_site list -(** Value callstacks, as used e.g. in Db.Value hooks. - - The head call site [(f,p)] is the most recent one, - where current function [f] has been called from program point [p]. - - Therefore, the tail call site is expected to be [(main,Kglobal)] - where [main] is the global entry point. - - Moreover, given two consecutive call-sites […(_,p);(g,_)…] in a callstack, - program point [p] is then expected to live in function [g]. -*) - -module Callsite: Datatype.S_with_collections with type t = call_site -module Callstack: sig - include Datatype.S_with_collections with type t = callstack - val pretty_debug : Format.formatter -> t -> unit - - (** Print a hash of the callstack when '-kernel-msg-key callstack' - is enabled (prints nothing otherwise). *) - val pretty_hash : Format.formatter -> t -> unit - - (** Print a call stack without displaying call sites. *) - val pretty_short : Format.formatter -> t -> unit -end - type 'a callback_result = | Normal of 'a | NormalStore of 'a * int diff --git a/src/kernel_services/plugin_entry_points/db.ml b/src/kernel_services/plugin_entry_points/db.ml index 5567a74eefc59512a1aeb82db309194b16e4d71b..1e38b3a9b5af9d933c78c89446a7e60f886e1018 100644 --- a/src/kernel_services/plugin_entry_points/db.ml +++ b/src/kernel_services/plugin_entry_points/db.ml @@ -127,7 +127,8 @@ module Operational_inputs = struct let get_external = mk_fun "Operational_inputs.get_external" module Record_Inout_Callbacks = - Hook.Build (struct type t = Value_types.callstack * Inout_type.t end) + Hook.Build (struct type t = Eva_types.Callstack.t * Inout_type.t end) + [@@alert "-db_deprecated"] let pretty fmt x = Format.fprintf fmt "@[<v>"; @@ -236,8 +237,11 @@ module Value = struct let size = 256 + [@@@ alert "-db_deprecated"] + type callstack = Eva_types.Callstack.callstack + module States_by_callstack = - Value_types.Callstack.Hashtbl.Make(Cvalue.Model) + Eva_types.Callstack.Hashtbl.Make(Cvalue.Model) module Table_By_Callstack = Cil_state_builder.Stmt_hashtbl(States_by_callstack) @@ -365,19 +369,17 @@ module Value = struct Cvalue.Model.pretty v) *) - type callstack = (kernel_function * kinstr) list - module Record_Value_Callbacks = Hook.Build (struct - type t = (kernel_function * kinstr) list * (state Stmt.Hashtbl.t) Lazy.t + type t = callstack * (state Stmt.Hashtbl.t) Lazy.t end) module Record_Value_Callbacks_New = Hook.Build (struct type t = - (kernel_function * kinstr) list * + callstack * ((state Stmt.Hashtbl.t) Lazy.t * (state Stmt.Hashtbl.t) Lazy.t) Value_types.callback_result end) @@ -385,24 +387,24 @@ module Value = struct module Record_Value_After_Callbacks = Hook.Build (struct - type t = (kernel_function * kinstr) list * (state Stmt.Hashtbl.t) Lazy.t + type t = callstack * (state Stmt.Hashtbl.t) Lazy.t end) module Record_Value_Superposition_Callbacks = Hook.Build (struct - type t = (kernel_function * kinstr) list * (state list Stmt.Hashtbl.t) Lazy.t + type t = callstack * (state list Stmt.Hashtbl.t) Lazy.t end) module Call_Value_Callbacks = Hook.Build - (struct type t = state * (kernel_function * kinstr) list end) + (struct type t = state * callstack end) module Call_Type_Value_Callbacks = Hook.Build(struct type t = [`Builtin of Value_types.call_froms | `Spec of funspec | `Def | `Memexec] - * state * (kernel_function * kinstr) list end) + * state * callstack end) ;; @@ -418,7 +420,7 @@ module Value = struct let no_results = mk_fun "Value.no_results" let update_callstack_table ~after stmt callstack v = - let open Value_types in + let open Eva_types in let find,add = if after then AfterTable_By_Callstack.find, AfterTable_By_Callstack.add @@ -437,9 +439,8 @@ module Value = struct Callstack.Hashtbl.add r callstack v; add stmt r - let merge_initial_state cs state = - let open Value_types in - let kf = match cs with (kf, _) :: _ -> kf | _ -> assert false in + let merge_initial_state cs kf state = + let open Eva_types in let by_callstack = try Called_Functions_By_Callstack.find kf with Not_found -> @@ -459,7 +460,7 @@ module Value = struct with Not_found -> let state = try - let open Value_types in + let open Eva_types in let by_callstack = Called_Functions_By_Callstack.find kf in Callstack.Hashtbl.fold (fun _cs state acc -> Cvalue.Model.join acc state) @@ -507,7 +508,7 @@ module Value = struct match ho with | None -> Cvalue.Model.bottom | Some h -> - Value_types.Callstack.Hashtbl.fold (fun _cs state acc -> + Eva_types.Callstack.Hashtbl.fold (fun _cs state acc -> Cvalue.Model.join acc state ) h Cvalue.Model.bottom in @@ -539,7 +540,7 @@ module Value = struct assert (is_computed ()); (* this assertion fails during Eva analysis *) match get_stmt_state_callstack ~after stmt with | None -> acc - | Some h -> Value_types.Callstack.Hashtbl.fold (fun _ -> f) h acc + | Some h -> Eva_types.Callstack.Hashtbl.fold (fun _ -> f) h acc let fold_state_callstack f acc ~after ki = assert (is_computed ()); (* this assertion fails during Eva analysis *) @@ -559,7 +560,7 @@ module Value = struct | None -> false | Some h -> try - Value_types.Callstack.Hashtbl.iter + Eva_types.Callstack.Hashtbl.iter (fun _cs state -> if Cvalue.Model.is_reachable state then raise Is_reachable) h; diff --git a/src/kernel_services/plugin_entry_points/db.mli b/src/kernel_services/plugin_entry_points/db.mli index 5dc9c539f9b4cdccfa158fca7f7d303bc374b108..518f3ad3f8a2978179ee41c074c49676c40738e8 100644 --- a/src/kernel_services/plugin_entry_points/db.mli +++ b/src/kernel_services/plugin_entry_points/db.mli @@ -134,15 +134,19 @@ module Value : sig (** Return [true] iff the value analysis has been done. @see <https://frama-c.com/download/frama-c-plugin-development-guide.pdf> Plug-in Development Guide *) + [@@@alert "-db_deprecated"] + + type callstack = Eva_types.Callstack.callstack + module Table_By_Callstack: State_builder.Hashtbl with type key = stmt - and type data = state Value_types.Callstack.Hashtbl.t + and type data = state Eva_types.Callstack.Hashtbl.t (** Table containing the results of the value analysis, ie. the state before the evaluation of each reachable statement. *) module AfterTable_By_Callstack: State_builder.Hashtbl with type key = stmt - and type data = state Value_types.Callstack.Hashtbl.t + and type data = state Eva_types.Callstack.Hashtbl.t (** Table containing the state of the value analysis after the evaluation of each reachable and evaluable statement. Filled only if [Value_parameters.ResultsAfter] is set. *) @@ -216,12 +220,12 @@ module Value : sig val get_initial_state : kernel_function -> state val get_initial_state_callstack : - kernel_function -> state Value_types.Callstack.Hashtbl.t option + kernel_function -> state Eva_types.Callstack.Hashtbl.t option val get_state : ?after:bool -> kinstr -> state (** [after] is false by default. *) val get_stmt_state_callstack: - after:bool -> stmt -> state Value_types.Callstack.Hashtbl.t option + after:bool -> stmt -> state Eva_types.Callstack.Hashtbl.t option val get_stmt_state : ?after:bool -> stmt -> state (** [after] is false by default. @@ -406,19 +410,20 @@ module Value : sig (** {3 Callbacks} *) - type callstack = Value_types.callstack - (** Actions to perform at end of each function analysis. Not compatible with option [-memexec-all] *) module Record_Value_Callbacks: - Hook.Iter_hook with type param = callstack * (state Stmt.Hashtbl.t) Lazy.t + Hook.Iter_hook + with type param = callstack * (state Stmt.Hashtbl.t) Lazy.t module Record_Value_Superposition_Callbacks: - Hook.Iter_hook with type param = callstack * (state list Stmt.Hashtbl.t) Lazy.t + Hook.Iter_hook + with type param = callstack * (state list Stmt.Hashtbl.t) Lazy.t module Record_Value_After_Callbacks: - Hook.Iter_hook with type param = callstack * (state Stmt.Hashtbl.t) Lazy.t + Hook.Iter_hook + with type param = callstack * (state Stmt.Hashtbl.t) Lazy.t (**/**) (* Temporary API, do not use *) @@ -493,7 +498,7 @@ module Value : sig (kernel_function -> call_kinstr:kinstr -> state -> (exp*t) list -> Cvalue.V_Offsetmap.t option (** returned value of [kernel_function] *) * state) ref *) - val merge_initial_state : callstack -> state -> unit + val merge_initial_state : callstack -> kernel_function -> state -> unit (** Store an additional possible initial state for the given callstack as well as its values for actuals. *) @@ -657,8 +662,9 @@ module Operational_inputs : sig (**/**) (* Internal use *) module Record_Inout_Callbacks: - Hook.Iter_hook with type param = Value_types.callstack * Inout_type.t - (**/**) + Hook.Iter_hook with type param = Eva_types.Callstack.t * Inout_type.t + [@@alert "-db_deprecated"] + (**/**) end diff --git a/src/plugins/dive/build.ml b/src/plugins/dive/build.ml index acfa92afaa6498c25af003240e6b683123fe89b6..016d29aa3e101ea5fce05287237a543353ff1790 100644 --- a/src/plugins/dive/build.ml +++ b/src/plugins/dive/build.ml @@ -275,6 +275,7 @@ let find_compatible_callstacks stmt callstack = let callstacks = Eval.to_callstacks stmt in (* TODO: missing callstacks filtered by memexec *) let make_compatible cs = + let cs = List.rev (Eva.Callstack.to_call_list cs) in Callstack.truncate_to_sub cs callstack |> Option.value ~default:(Callstack.init kf) in diff --git a/src/plugins/dive/callstack.ml b/src/plugins/dive/callstack.ml index f9d329ef16cdd90fae218dcf7d93669d60059f2d..8f94e127823b61085b9b25315ffa0fd97d4353e9 100644 --- a/src/plugins/dive/callstack.ml +++ b/src/plugins/dive/callstack.ml @@ -22,10 +22,11 @@ open Cil_types -include Value_types.Callstack +module Callsite = Datatype.Pair (Kernel_function) (Cil_datatype.Kinstr) +type call_site = Callsite.t -type call_site = Value_types.call_site -module Callsite = Value_types.Callsite +include Datatype.With_collections (Datatype.List (Callsite)) + (struct let module_name = "Dive.Callstack" end) let init kf = [(kf,Kglobal)] diff --git a/src/plugins/dive/dive_graph.ml b/src/plugins/dive/dive_graph.ml index ef3c84076d6d3a39afe77c1c39c0786fc8602102..d6aeae490d92bb6f49896e464b6cef67087a53ec 100644 --- a/src/plugins/dive/dive_graph.ml +++ b/src/plugins/dive/dive_graph.ml @@ -177,7 +177,7 @@ let output_to_dot out_channel g = let build_label s = `HtmlLabel (Extlib.html_escape s) in let module FileTable = Datatype.String.Hashtbl in - let module CallstackTable = Value_types.Callstack.Hashtbl in + let module CallstackTable = Callstack.Hashtbl in let file_table = FileTable.create 13 and callstack_table = CallstackTable.create 13 in let file_counter = ref 0 in diff --git a/src/plugins/eva/Eva.mli b/src/plugins/eva/Eva.mli index db84313b85ad427fb3c61cacc7507d1c02809886..aa5f15a1aa332ffe18f788769d042ec12723c6b9 100644 --- a/src/plugins/eva/Eva.mli +++ b/src/plugins/eva/Eva.mli @@ -115,6 +115,75 @@ module Analysis: sig val save_results: Cil_types.kernel_function -> bool end +module Callstack: sig + [@@@ alert "-db_deprecated"] + + (** A call is identified by the function called and the call statement *) + type call = Cil_types.kernel_function * Cil_types.stmt + + module Call : Datatype.S with type t = call + + (** Eva callstacks. *) + type callstack = Eva_types.Callstack.callstack = { + thread: int; + (* An identifier of the thread's callstack. *) + entry_point: Cil_types.kernel_function; + (** The first function function of the callstack. *) + stack: call list; + (** A call stack is a list of calls. The head is the latest call. *) + } + + include Datatype.S_with_collections + with type t = callstack + and module Hashtbl = Eva_types.Callstack.Hashtbl + + (** Prints a callstack without displaying call sites. *) + val pretty_short : Format.formatter -> t -> unit + + (** Prints a hash of the callstack when '-kernel-msg-key callstack' + is enabled (prints nothing otherwise). *) + val pretty_hash : Format.formatter -> t -> unit + + (** [compare_lex] compares callstack lexicographically, slightly slower + than [compare] but in a more natural order, giving more importance + to the function at bottom of the callstack - the first functions called. *) + val compare_lex : t -> t -> int + + (*** {2 Stack manipulation} *) + + (*** Constructor *) + val init : ?thread:int -> Cil_types.kernel_function -> t + + (** Adds a new call to the top of the callstack. *) + val push : Cil_types.kernel_function -> Cil_types.stmt -> t -> t + + (** Removes the topmost call from the callstack. *) + val pop : t -> t option + + val top : t -> (Cil_types.kernel_function * Cil_types.stmt) option + val top_kf : t -> Cil_types.kernel_function + val top_callsite : t -> Cil_types.kinstr + val top_call : t -> Cil_types.kernel_function * Cil_types.kinstr + + (** Returns the function that called the topmost function of the callstack. *) + val top_caller : t -> Cil_types.kernel_function option + + (** {2 Conversion} *) + + (** Gives the list of kf in the callstack from the entry point to the top of the + callstack (i.e. reverse order of the call stack). *) + val to_kf_list : t -> Cil_types.kernel_function list + + (** Gives the list of call statements from the bottom to the top of the + callstack (i.e. reverse order of the call stack). *) + val to_stmt_list : t -> Cil_types.stmt list + + (** Gives the list of call from the bottom to the top of the callstack + (i.e. reverse order of the call stack). *) + val to_call_list : t -> (Cil_types.kernel_function * Cil_types.kinstr) list + +end + module Results: sig (** Eva's result API is a new interface to access the results of an analysis, @@ -158,8 +227,6 @@ module Results: sig all requests in the function will lead to a Top error. *) val are_available : Cil_types.kernel_function -> bool - type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list - type request type value @@ -222,16 +289,16 @@ module Results: sig (** Only consider the given callstack. Replaces previous calls to [in_callstack] or [in_callstacks]. *) - val in_callstack : callstack -> request -> request + val in_callstack : Callstack.t -> request -> request (** Only consider the callstacks from the given list. Replaces previous calls to [in_callstack] or [in_callstacks]. *) - val in_callstacks : callstack list -> request -> request + val in_callstacks : Callstack.t list -> request -> request (** Only consider callstacks satisfying the given predicate. Several filters can be added. If callstacks are also selected with [in_callstack] or [in_callstacks], only the selected callstacks will be filtered. *) - val filter_callstack : (callstack -> bool) -> request -> request + val filter_callstack : (Callstack.t -> bool) -> request -> request (** Working with callstacks *) @@ -241,11 +308,11 @@ module Results: sig reached by the analysis, or if no information has been saved at this point (for instance with the -eva-no-results option). Use [is_empty request] to distinguish these two cases. *) - val callstacks : request -> callstack list + val callstacks : request -> Callstack.t list (** Returns a list of subrequests for each reachable callstack from the given request. *) - val by_callstack : request -> (callstack * request) list + val by_callstack : request -> (Callstack.t * request) list (** State requests *) @@ -616,7 +683,6 @@ module Cvalue_callbacks: sig in a future version. Please contact us if you need to register callbacks to be executed during an Eva analysis. *) - type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list type state = Cvalue.Model.t type analysis_kind = @@ -630,7 +696,7 @@ module Cvalue_callbacks: sig the function called, the kind of analysis performed by Eva for this call, and the cvalue state at the beginning of the call. *) val register_call_hook: - (callstack -> Cil_types.kernel_function -> analysis_kind -> state -> unit) + (Callstack.t -> Cil_types.kernel_function -> analysis_kind -> state -> unit) -> unit @@ -650,7 +716,7 @@ module Cvalue_callbacks: sig function call. Arguments of the callback are the callstack of the call, the function called and the cvalue states resulting from its analysis. *) val register_call_results_hook: - (callstack -> Cil_types.kernel_function -> call_results -> unit) + (Callstack.t -> Cil_types.kernel_function -> call_results -> unit) -> unit end @@ -733,7 +799,7 @@ module Eva_results: sig For technical reasons, the top of the callstack must currently be preserved. *) val change_callstacks: - (Value_types.callstack -> Value_types.callstack) -> results -> results + (Callstack.t -> Callstack.t) -> results -> results val eval_tlval_as_location : ?result:Cil_types.varinfo -> diff --git a/src/plugins/eva/api/values_request.ml b/src/plugins/eva/api/values_request.ml index d810aa0d99919467335cedb4939e4c56408bd45a..8e0c351b98b2d9c605e8d7723dc47640200387b9 100644 --- a/src/plugins/eva/api/values_request.ml +++ b/src/plugins/eva/api/values_request.ml @@ -26,9 +26,8 @@ open Cil_types module Kmap = Kernel_function.Hashtbl module Smap = Cil_datatype.Stmt.Hashtbl -module CS = Value_types.Callstack -module CSet = CS.Set -module CSmap = CS.Hashtbl +module CSet = Callstack.Set +module CSmap = Callstack.Hashtbl module Md = Markdown module Jfct = Kernel_ast.Function @@ -49,7 +48,6 @@ type evaluation_point = General_requests.evaluation_point = (* A term and the program point where it should be evaluated. *) type probe = term * evaluation_point -type callstack = Value_types.callstack type truth = Abstract_interp.truth (* The result of an evaluation: @@ -138,7 +136,7 @@ let probe marker = module type Ranking_sig = sig val stmt : stmt -> int - val sort : callstack list -> callstack list + val sort : Callstack.t list -> Callstack.t list end module Ranking : Ranking_sig = struct @@ -198,18 +196,15 @@ module Ranking : Ranking_sig = struct let stmt = let rk = new ranker in rk#rank - let rec ranks (rks : int list) (cs : callstack) : int list = - match cs with - | [] -> rks - | (_,Kglobal)::wcs -> ranks rks wcs - | (_,Kstmt s)::wcs -> ranks (stmt s :: rks) wcs + let ranks (cs : Callstack.t) : int list = + List.map stmt (Callstack.to_stmt_list cs) let order : int list -> int list -> int = Stdlib.compare - let sort (wcs : callstack list) : callstack list = + let sort (wcs : Callstack.t list) : Callstack.t list = List.map fst @@ List.sort (fun (_,rp) (_,rq) -> order rp rq) @@ - List.map (fun cs -> cs , ranks [] cs) wcs + List.map (fun cs -> cs , ranks cs) wcs end @@ -217,9 +212,9 @@ end (* --- Domain Utilities --- *) (* -------------------------------------------------------------------------- *) -module Jcallstack : S with type t = callstack = struct +module Jcallstack : S with type t = Callstack.t = struct module I = Data.Index - (Value_types.Callstack.Map) + (Callstack.Map) (struct let name = "eva-callstack-id" end) let jtype = Data.declare ~package ~name:"callstack" I.jtype type t = I.t @@ -227,9 +222,9 @@ module Jcallstack : S with type t = callstack = struct let of_json = I.of_json end -module Jcalls : Request.Output with type t = callstack = struct +module Jcalls : Request.Output with type t = Callstack.t = struct - type t = callstack + type t = Callstack.t let jtype = Package.(Jarray (Jrecord [ "callee" , Jfct.jtype ; @@ -238,23 +233,25 @@ module Jcalls : Request.Output with type t = callstack = struct "rank" , Joption Jnumber ; ])) - let rec jcallstack jcallee ki cs : json list = - match ki , cs with - | Kglobal , _ | _ , [] -> [ `Assoc [ "callee", jcallee ] ] - | Kstmt stmt , (called,ki) :: cs -> - let jcaller = Jfct.to_json called in - let callsite = `Assoc [ - "callee", jcallee ; - "caller", jcaller ; - "stmt", Jstmt.to_json stmt ; - "rank", Jint.to_json (Ranking.stmt stmt) ; - ] - in - callsite :: jcallstack jcaller ki cs - - let to_json = function - | [] -> `List [] - | (callee,ki)::cs -> `List (jcallstack (Jfct.to_json callee) ki cs) + let jcallsite ~jcaller ~jcallee stmt = + `Assoc [ + "callee", jcallee ; + "caller", jcaller ; + "stmt", Jstmt.to_json stmt ; + "rank", Jint.to_json (Ranking.stmt stmt) ; + ] + + let to_json (cs : t) = + let aux (acc, jcaller) (callee, stmt) = + let jcallee = Jfct.to_json callee in + jcallsite ~jcaller ~jcallee stmt :: acc, jcallee + in + let entry_point = Jfct.to_json cs.entry_point in + let l, _last_callee = List.fold_left aux + ([`Assoc [ "callee", entry_point ]], entry_point) + (List.rev cs.stack) + in + `List l end @@ -353,9 +350,9 @@ let filter_variables bases = (* -------------------------------------------------------------------------- *) module type EvaProxy = sig - val kf_callstacks : kernel_function -> callstack list - val stmt_callstacks : stmt -> callstack list - val evaluate : probe -> callstack option -> evaluations + val kf_callstacks : kernel_function -> Callstack.t list + val stmt_callstacks : stmt -> Callstack.t list + val evaluate : probe -> Callstack.t option -> evaluations end module Proxy(A : Analysis.S) : EvaProxy = struct diff --git a/src/plugins/eva/domains/cvalue/builtins_malloc.ml b/src/plugins/eva/domains/cvalue/builtins_malloc.ml index 4f17d70e897baac10ce641654653c972c309e88a..77b26cbe8cc5bcb6e6cd6812c283f612ecb56eed 100644 --- a/src/plugins/eva/domains/cvalue/builtins_malloc.ml +++ b/src/plugins/eva/domains/cvalue/builtins_malloc.ml @@ -38,7 +38,7 @@ let wkey_imprecise_alloc = Self.register_warn_category module Base_hptmap = Hptmap.Make (Base.Base) - (Value_types.Callstack) + (Callstack) (Hptmap.Comp_unused) (struct let v = [ [ ] ] end) (struct let l = [ Ast.self ] end) @@ -57,9 +57,9 @@ let () = Ast.add_monotonic_state Dynamic_Alloc_Bases.self (* -------------------------- Auxiliary functions -------------------------- *) let current_call_site () = - match Eva_utils.call_stack () with - | (_kf, Kstmt stmt) :: _ -> stmt - | _ -> Cil.dummyStmt + match Callstack.top_callsite (Eva_utils.current_call_stack ()) with + | Kglobal -> Cil.dummyStmt + | Kstmt stmt -> stmt (* Remove some parts of the callstack: - Remove the bottom of the call tree until we get to the call site @@ -67,29 +67,26 @@ let current_call_site () = these call site correspond to a different use of a malloc function, so it is interesting to keep their bases separated. *) let call_stack_no_wrappers () = - let stack = Eva_utils.call_stack () in - assert (stack != []); - let wrappers = Parameters.AllocFunctions.get() in + let cs = Eva_utils.current_call_stack () in + let wrappers = Parameters.AllocFunctions.get () in let rec bottom_filter = function - | [] -> assert false - | [_] as stack -> stack (* Do not empty the stack completely *) + | [] | [_] as stack -> stack | (kf,_)::((kf', _):: _ as rest) as stack -> - if Datatype.String.Set.mem (Kernel_function.get_name kf) wrappers then - if Datatype.String.Set.mem (Kernel_function.get_name kf') wrappers then - bottom_filter rest - else - stack - else - stack + if Datatype.String.Set.mem (Kernel_function.get_name kf) wrappers + && Datatype.String.Set.mem (Kernel_function.get_name kf') wrappers + then bottom_filter rest + else stack in - bottom_filter stack + { cs with stack = bottom_filter cs.stack } -let register_malloced_base ?(stack=call_stack_no_wrappers ()) b = - let stack_without_top = List.tl stack in +let register_malloced_base ~stack b = + let stack_without_top = + Option.value ~default:stack (Callstack.pop stack) + in Dynamic_Alloc_Bases.set (Base_hptmap.add b stack_without_top (Dynamic_Alloc_Bases.get ())) -let fold_dynamic_bases (f: Base.t -> Value_types.Callstack.t -> 'a -> 'a) init = +let fold_dynamic_bases (f: Base.t -> Callstack.t -> 'a -> 'a) init = Base_hptmap.fold f (Dynamic_Alloc_Bases.get ()) init let is_automatically_deallocated base = @@ -120,20 +117,17 @@ let extract_size sizev_bytes = (* Name of the base that will be given to a malloced variable, determined using the callstack. *) -let base_name prefix stack = +let base_name prefix cs = let stmt_line stmt = (fst (Cil_datatype.Stmt.loc stmt)).Filepath.pos_lnum in - match stack with - | [] -> assert false - | [kf, Kglobal] -> (* Degenerate case *) - Format.asprintf "__%s_%a" prefix Kernel_function.pretty kf - | (_, Kglobal) :: _ :: _ -> assert false - | (_, Kstmt callsite) :: qstack -> + match cs.Callstack.stack with + | [] -> + (* Degenerate case *) + Format.asprintf "__%s_%a" prefix Kernel_function.pretty cs.entry_point + | (_, callsite) :: qstack -> (* Use the whole call-stack to generate the name *) let rec loop_full = function - | [_, Kglobal] -> Format.sprintf "_%s" (Kernel.MainFunction.get ()) - | (_, Kglobal) :: _ :: _ -> assert false - | [] -> assert false (* impossible, we should have seen a Kglobal *) - | (kf, Kstmt line)::b -> + | [] -> Format.sprintf "_%s" (Kernel_function.get_name cs.entry_point) + | (kf, line) :: b -> let line = stmt_line line in let node_str = Format.asprintf "_l%d__%a" line Kernel_function.pretty kf @@ -141,14 +135,14 @@ let base_name prefix stack = (loop_full b) ^ node_str in (* Use only the name of the caller to malloc for the name *) - let caller = function - | [] -> assert false (* caught above *) - | (kf, _) :: _ -> Format.asprintf "_%a" Kernel_function.pretty kf + let caller = + let kf = Callstack.top_kf { cs with stack = qstack } in + Format.asprintf "_%a" Kernel_function.pretty kf in let full_name = false in Format.asprintf "__%s%s_l%d" prefix - (if full_name then loop_full qstack else caller qstack) + (if full_name then loop_full qstack else caller) (stmt_line callsite) type var = Weak | Strong @@ -217,7 +211,7 @@ let guess_intended_malloc_type stack sizev constant_size = | _ -> raise Exit in try - match snd (List.hd stack) with + match Callstack.top_callsite stack with | Kstmt {skind = Instr (Call (Some lv, _, _, _))} -> mk_typed_size (Cil.typeOfLval lv) | Kstmt {skind = Instr(Local_init(vi, _, _))} -> mk_typed_size vi.vtype @@ -380,7 +374,7 @@ let string_of_region = function (* Only called when the 'weakest base' needs to be allocated. *) let create_weakest_base region = - let stack = [ fst (Globals.entry_point ()), Kglobal ] in + let stack = { (Eva_utils.current_call_stack ()) with stack = [] } in let type_base = TArray (Cil.charType, None, []) in @@ -413,8 +407,8 @@ let alloc_weakest_base region = stack. Currently, the callstacks are truncated according to [-eva-alloc-functions]. *) module MallocedByStack = (* varinfo list Callstack.hashtbl *) - State_builder.Hashtbl(Value_types.Callstack.Hashtbl) - (Datatype.List(Base)) + State_builder.Hashtbl (Callstack.Hashtbl) + (Datatype.List (Base)) (struct let name = "Value.Builtins_malloc.MallocedByStack" let size = 17 @@ -686,7 +680,7 @@ let free_automatic_bases stack state = let bases_to_free = Base_hptmap.fold (fun base stack' acc -> if is_automatically_deallocated base && - Value_types.Callstack.equal stack stack' + Callstack.equal stack stack' then Base.Hptset.add base acc else acc ) (Dynamic_Alloc_Bases.get ()) Base.Hptset.empty diff --git a/src/plugins/eva/domains/cvalue/builtins_malloc.mli b/src/plugins/eva/domains/cvalue/builtins_malloc.mli index 8cd4f9ddabf4ba775b258e6fc96fca993996285d..399b01a97c65de424fe157642df55c4b3f22c98b 100644 --- a/src/plugins/eva/domains/cvalue/builtins_malloc.mli +++ b/src/plugins/eva/domains/cvalue/builtins_malloc.mli @@ -23,7 +23,7 @@ (** Dynamic allocation related builtins. Most functionality is exported as builtins. *) -val fold_dynamic_bases: (Base.t -> Value_types.Callstack.t -> 'a -> 'a) -> 'a -> 'a +val fold_dynamic_bases: (Base.t -> Callstack.t -> 'a -> 'a) -> 'a -> 'a (** [fold_dynamic_bases f init] folds [f] to each dynamically allocated base, with initial accumulator [init]. Note that this also includes bases created by [alloca] and [VLAs]. *) @@ -34,7 +34,7 @@ val alloc_size_ok: Cvalue.V.t -> Alarmset.status small enough, [False] that the allocation is guaranteed to fail (because the size is always greater than SIZE_MAX). *) -val free_automatic_bases: Value_types.Callstack.t -> Cvalue.Model.t -> Cvalue.Model.t +val free_automatic_bases: Callstack.t -> Cvalue.Model.t -> Cvalue.Model.t (** Performs the equivalent of [free] for each location that was allocated via [alloca()] in the current function (as per [Eva_utils.call_stack ()]). This function must be called during finalization of a function call. *) @@ -44,7 +44,3 @@ val freeable: Cvalue.V.t -> Abstract_interp.truth value points to an allocated memory block that can be safely released using the C function free. Note that \freeable(\null) does not hold, despite NULL being a valid argument to the C function free. *) - -(**/**) -val register_malloced_base: ?stack:Value_types.Callstack.t -> Base.t -> unit -(* Should not be used by casual users. *) diff --git a/src/plugins/eva/domains/cvalue/builtins_memory.ml b/src/plugins/eva/domains/cvalue/builtins_memory.ml index b543ce8a78c89f9ec93388d8b0c7824fc4e464b0..4a34203eed44a62880082513b6b35d4e872b9621 100644 --- a/src/plugins/eva/domains/cvalue/builtins_memory.ml +++ b/src/plugins/eva/domains/cvalue/builtins_memory.ml @@ -92,7 +92,7 @@ let memcpy_check_indeterminate_offsetmap offsm = currently called function. *) let deps_nth_arg n = let open Function_Froms in - let (kf,_) = List.hd (Eva_utils.call_stack()) in + let kf = Callstack.top_kf (Eva_utils.current_call_stack ()) in try let vi = List.nth (Kernel_function.get_formals kf) n in Deps.add_data_dep Deps.bottom (Locations.zone_of_varinfo vi) diff --git a/src/plugins/eva/domains/cvalue/cvalue_domain.ml b/src/plugins/eva/domains/cvalue/cvalue_domain.ml index 5989aa87bfc229e1f85701ad7cc85777f18b8634..7822ad59369304d3781cdaa189f1c2abfb0fac4a 100644 --- a/src/plugins/eva/domains/cvalue/cvalue_domain.ml +++ b/src/plugins/eva/domains/cvalue/cvalue_domain.ml @@ -460,8 +460,8 @@ module State = struct end) let register_global_state b _ = Storage.set b - let register_initial_state callstack (state, _clob) = - Db.Value.merge_initial_state callstack state + let register_initial_state callstack kf (state, _clob) = + Db.Value.merge_initial_state callstack kf state let register_state_before_stmt callstack stmt (state, _clob) = Db.Value.update_callstack_table ~after:false stmt callstack state let register_state_after_stmt callstack stmt (state, _clob) = @@ -473,7 +473,6 @@ module State = struct else `Value (state, Locals_scoping.top ()) let lift_tbl tbl = - let open Value_types in let h = Callstack.Hashtbl.create 7 in let process callstack state = Callstack.Hashtbl.replace h callstack (state, Locals_scoping.top ()) @@ -485,10 +484,10 @@ module State = struct match selection with | None -> tbl | Some list -> - let new_tbl = Value_types.Callstack.Hashtbl.create (List.length list) in + let new_tbl = Callstack.Hashtbl.create (List.length list) in let add cs = - let s = Value_types.Callstack.Hashtbl.find_opt tbl cs in - Option.iter (Value_types.Callstack.Hashtbl.replace new_tbl cs) s + let s = Callstack.Hashtbl.find_opt tbl cs in + Option.iter (Callstack.Hashtbl.replace new_tbl cs) s in List.iter add list; new_tbl diff --git a/src/plugins/eva/domains/cvalue/cvalue_transfer.ml b/src/plugins/eva/domains/cvalue/cvalue_transfer.ml index 22b674e46ea768e152224753ce32f0caff4c4ad3..222f8bbe68349524dabe96f1d9b91d1ed3e1e438 100644 --- a/src/plugins/eva/domains/cvalue/cvalue_transfer.ml +++ b/src/plugins/eva/domains/cvalue/cvalue_transfer.ml @@ -205,7 +205,7 @@ let actualize_formals state arguments = let start_call _stmt call _recursion _valuation state = let with_formals = actualize_formals state call.arguments in - let stack_with_call = Eva_utils.call_stack () in + let stack_with_call = Eva_utils.current_call_stack () in Db.Value.Call_Value_Callbacks.apply (with_formals, stack_with_call); `Value with_formals @@ -214,8 +214,9 @@ let finalize_call stmt call _recursion ~pre:_ ~post:state = To minimize computations, only do it for function definitions. *) let state' = if Kernel_function.is_definition call.kf then - let stack = (call.kf, Kstmt stmt) :: (Eva_utils.call_stack ()) in - Builtins_malloc.free_automatic_bases stack state + let callstack = Eva_utils.current_call_stack () in + let callstack = Callstack.push call.kf stmt callstack in + Builtins_malloc.free_automatic_bases callstack state else state in `Value state' diff --git a/src/plugins/eva/domains/domain_builder.ml b/src/plugins/eva/domains/domain_builder.ml index 4349b82595669625eee09b6bed667205575083ee..0305ca8a35fdbb53e57fdbe620642bffc3d7151a 100644 --- a/src/plugins/eva/domains/domain_builder.ml +++ b/src/plugins/eva/domains/domain_builder.ml @@ -610,8 +610,8 @@ module Restrict let lift_register f state = f (get_state state) - let register_initial_state callstack = - lift_register (Domain.Store.register_initial_state callstack) + let register_initial_state callstack kf = + lift_register (Domain.Store.register_initial_state callstack kf) let register_state_before_stmt callstack stmt = lift_register (Domain.Store.register_state_before_stmt callstack stmt) let register_state_after_stmt callstack stmt = @@ -628,7 +628,7 @@ module Restrict | `Top -> `Top | `Bottom -> `Bottom | `Value t -> - let module Hashtbl = Value_types.Callstack.Hashtbl in + let module Hashtbl = Callstack.Hashtbl in let table = Hashtbl.create (Hashtbl.length t) in Hashtbl.iter (fun key s -> Hashtbl.add table key (inject s)) t; `Value table diff --git a/src/plugins/eva/domains/domain_product.ml b/src/plugins/eva/domains/domain_product.ml index faa5590ceb6a9ba58aa1edecb081afeec0f8a784..45b71568bd946df35b2042012551fa803f82b1b1 100644 --- a/src/plugins/eva/domains/domain_product.ml +++ b/src/plugins/eva/domains/domain_product.ml @@ -302,7 +302,6 @@ module Make Right.reuse kf bases ~current_input:right_input ~previous_output:right_output let merge_tbl left_tbl right_tbl = - let open Value_types in let tbl = Callstack.Hashtbl.create 7 in let merge callstack left = try @@ -315,7 +314,6 @@ module Make if Callstack.Hashtbl.length tbl > 0 then `Value tbl else `Bottom let lift_tbl f tbl = - let open Value_types in let new_tbl = Callstack.Hashtbl.create 7 in let lift cs t = Callstack.Hashtbl.replace new_tbl cs (f t) in Callstack.Hashtbl.iter lift tbl; @@ -333,9 +331,9 @@ module Make let register_global_state b state = Left.Store.register_global_state b (state >>-: fst); Right.Store.register_global_state b (state >>-: snd) - let register_initial_state callstack (left, right) = - Left.Store.register_initial_state callstack left; - Right.Store.register_initial_state callstack right + let register_initial_state callstack kf (left, right) = + Left.Store.register_initial_state callstack kf left; + Right.Store.register_initial_state callstack kf right let register_state_before_stmt callstack stmt (left, right) = Left.Store.register_state_before_stmt callstack stmt left; Right.Store.register_state_before_stmt callstack stmt right diff --git a/src/plugins/eva/domains/domain_store.ml b/src/plugins/eva/domains/domain_store.ml index 99d9a6c63ce295c49d2cb8bcecee3082d72e9e84..9d8f48ecd40af05f3c35dbbe25ce5540429587d9 100644 --- a/src/plugins/eva/domains/domain_store.ml +++ b/src/plugins/eva/domains/domain_store.ml @@ -32,22 +32,22 @@ end module type S = sig type t val register_global_state: bool -> t or_bottom -> unit - val register_initial_state: Value_types.callstack -> t -> unit - val register_state_before_stmt: Value_types.callstack -> stmt -> t -> unit - val register_state_after_stmt: Value_types.callstack -> stmt -> t -> unit + val register_initial_state: Callstack.t -> kernel_function -> t -> unit + val register_state_before_stmt: Callstack.t -> stmt -> t -> unit + val register_state_after_stmt: Callstack.t -> stmt -> t -> unit (** Allows accessing the states inferred by an Eva analysis after it has been computed with the domain enabled. *) val get_global_state: unit -> t or_bottom val get_initial_state: kernel_function -> t or_bottom val get_initial_state_by_callstack: - ?selection:callstack list -> - kernel_function -> t Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + kernel_function -> t Callstack.Hashtbl.t or_top_bottom val get_stmt_state: after:bool -> stmt -> t or_bottom val get_stmt_state_by_callstack: - ?selection:callstack list -> - after:bool -> stmt -> t Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + after:bool -> stmt -> t Callstack.Hashtbl.t or_top_bottom val mark_as_computed: unit -> unit val is_computed: unit -> bool @@ -105,7 +105,7 @@ module Make (Domain: InputDomain) = struct end) module States_by_callstack = - Value_types.Callstack.Hashtbl.Make (Domain) + Callstack.Hashtbl.Make (Domain) module Table_By_Callstack = Cil_state_builder.Stmt_hashtbl(States_by_callstack) @@ -158,7 +158,6 @@ module Make (Domain: InputDomain) = struct end) let update_callstack_table ~after stmt callstack v = - let open Value_types in let find,add = if after then AfterTable_By_Callstack.find, AfterTable_By_Callstack.add @@ -184,10 +183,8 @@ module Make (Domain: InputDomain) = struct | `Bottom -> () | `Value state -> Global_State.set state - let register_initial_state callstack state = + let register_initial_state callstack kf state = if Storage.get () then - let open Value_types in - let kf = match callstack with (kf, _) :: _ -> kf | _ -> assert false in let by_callstack = try Called_Functions_By_Callstack.find kf with Not_found -> @@ -217,7 +214,7 @@ module Make (Domain: InputDomain) = struct try let by_callstack = Called_Functions_By_Callstack.find kf in let state = - Value_types.Callstack.Hashtbl.fold + Callstack.Hashtbl.fold (fun _cs state acc -> Bottom.join Domain.join acc (`Value state)) by_callstack `Bottom in @@ -229,10 +226,10 @@ module Make (Domain: InputDomain) = struct match selection with | None -> tbl | Some list -> - let new_tbl = Value_types.Callstack.Hashtbl.create (List.length list) in + let new_tbl = Callstack.Hashtbl.create (List.length list) in let add cs = - let state_opt = Value_types.Callstack.Hashtbl.find_opt tbl cs in - Option.iter (Value_types.Callstack.Hashtbl.replace new_tbl cs) state_opt + let state_opt = Callstack.Hashtbl.find_opt tbl cs in + Option.iter (Callstack.Hashtbl.replace new_tbl cs) state_opt in List.iter add list; new_tbl @@ -263,7 +260,7 @@ module Make (Domain: InputDomain) = struct match ho with | None -> `Bottom | Some h -> - Value_types.Callstack.Hashtbl.fold + Callstack.Hashtbl.fold (fun _cs state acc -> Bottom.join Domain.join acc (`Value state)) h `Bottom in diff --git a/src/plugins/eva/domains/domain_store.mli b/src/plugins/eva/domains/domain_store.mli index 2eff08d35a87f5d9553b4ae615af97d04a60d065..f8779d56e301db38200b1346aa25da495c684e9a 100644 --- a/src/plugins/eva/domains/domain_store.mli +++ b/src/plugins/eva/domains/domain_store.mli @@ -39,22 +39,22 @@ module type S = sig false, register functions do nothing, and get functions return Top. *) val register_global_state: bool -> t or_bottom -> unit - val register_initial_state: Value_types.callstack -> t -> unit - val register_state_before_stmt: Value_types.callstack -> stmt -> t -> unit - val register_state_after_stmt: Value_types.callstack -> stmt -> t -> unit + val register_initial_state: Callstack.t -> kernel_function -> t -> unit + val register_state_before_stmt: Callstack.t -> stmt -> t -> unit + val register_state_after_stmt: Callstack.t -> stmt -> t -> unit (** Allows accessing the states inferred by an Eva analysis after it has been computed with the domain enabled. *) val get_global_state: unit -> t or_bottom val get_initial_state: kernel_function -> t or_bottom val get_initial_state_by_callstack: - ?selection:callstack list -> - kernel_function -> t Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + kernel_function -> t Callstack.Hashtbl.t or_top_bottom val get_stmt_state: after:bool -> stmt -> t or_bottom val get_stmt_state_by_callstack: - ?selection:callstack list -> - after:bool -> stmt -> t Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + after:bool -> stmt -> t Callstack.Hashtbl.t or_top_bottom val mark_as_computed: unit -> unit val is_computed: unit -> bool diff --git a/src/plugins/eva/dune b/src/plugins/eva/dune index 6877dfd4327e79a383f3fca9c87c3ee06e1952cb..a5aae82cd3584e41c471019a26e2bfd4477b48c1 100644 --- a/src/plugins/eva/dune +++ b/src/plugins/eva/dune @@ -105,7 +105,7 @@ (mode (promote (only Eva.mli))) (deps gen-api.sh Eva.header - engine/analysis.mli utils/results.mli parameters.mli + engine/analysis.mli types/callstack.mli utils/results.mli parameters.mli utils/eva_annotations.mli eval.mli domains/cvalue/builtins.mli utils/cvalue_callbacks.mli legacy/logic_inout.mli utils/eva_results.mli utils/unit_tests.mli) diff --git a/src/plugins/eva/engine/analysis.ml b/src/plugins/eva/engine/analysis.ml index f64faf0f84f798badce9ffb4be393f58c4dcf988..ddc557787c5bfa11923b37a338ec8741745af230 100644 --- a/src/plugins/eva/engine/analysis.ml +++ b/src/plugins/eva/engine/analysis.ml @@ -55,13 +55,13 @@ module type Results = sig val get_global_state: unit -> state or_top_bottom val get_stmt_state : after:bool -> stmt -> state or_top_bottom val get_stmt_state_by_callstack: - ?selection:callstack list -> - after:bool -> stmt -> state Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + after:bool -> stmt -> state Callstack.Hashtbl.t or_top_bottom val get_initial_state: kernel_function -> state or_top_bottom val get_initial_state_by_callstack: - ?selection:callstack list -> - kernel_function -> state Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + kernel_function -> state Callstack.Hashtbl.t or_top_bottom val eval_expr : state -> exp -> value evaluated val copy_lvalue: state -> lval -> value flagged_value evaluated diff --git a/src/plugins/eva/engine/analysis.mli b/src/plugins/eva/engine/analysis.mli index dc017343ef221e88d4369946e52ffc6463218251..8dd74f365d0902882070a351b18b404853d35eef 100644 --- a/src/plugins/eva/engine/analysis.mli +++ b/src/plugins/eva/engine/analysis.mli @@ -31,13 +31,13 @@ module type Results = sig val get_global_state: unit -> state or_top_bottom val get_stmt_state : after:bool -> stmt -> state or_top_bottom val get_stmt_state_by_callstack: - ?selection:callstack list -> - after:bool -> stmt -> state Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + after:bool -> stmt -> state Callstack.Hashtbl.t or_top_bottom val get_initial_state: kernel_function -> state or_top_bottom val get_initial_state_by_callstack: - ?selection:callstack list -> - kernel_function -> state Value_types.Callstack.Hashtbl.t or_top_bottom + ?selection:Callstack.t list -> + kernel_function -> state Callstack.Hashtbl.t or_top_bottom val eval_expr : state -> exp -> value evaluated val copy_lvalue: state -> lval -> value flagged_value evaluated diff --git a/src/plugins/eva/engine/compute_functions.ml b/src/plugins/eva/engine/compute_functions.ml index 11a90011b3aec48f415ac7bc8bfc4059ac70d5f4..58c9b2173fc58a6df8e25d14c75c92d26822ddc8 100644 --- a/src/plugins/eva/engine/compute_functions.ml +++ b/src/plugins/eva/engine/compute_functions.ml @@ -240,7 +240,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct if pp then Self.feedback "@[computing for function %a.@\nCalled from %a.@]" - Value_types.Callstack.pretty_short call.callstack + Callstack.pretty_short call.callstack Cil_datatype.Location.pretty (Cil_datatype.Kinstr.loc kinstr); let cvalue_state = get_cvalue_or_top state in let compute, kind = @@ -344,22 +344,21 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* ----- Main call -------------------------------------------------------- *) - let store_initial_state kf init_state = - Abstract.Dom.Store.register_initial_state (Eva_utils.call_stack ()) init_state; + let store_initial_state callstack kf init_state = + Abstract.Dom.Store.register_initial_state callstack kf init_state; let cvalue_state = get_cvalue_or_top init_state in - Db.Value.Call_Value_Callbacks.apply (cvalue_state, [kf, Kglobal]) + Db.Value.Call_Value_Callbacks.apply (cvalue_state, callstack) let compute kf init_state = let restore_signals = register_signal_handler () in let compute () = - Eva_utils.push_call_stack kf Kglobal; - store_initial_state kf init_state; - let callstack = [kf, Kglobal] in + let callstack = Eva_utils.init_call_stack kf in + store_initial_state callstack kf init_state; let call = { kf; callstack; arguments = []; rest = []; return = None; } in let final_result = compute_call Kglobal call None init_state in let final_states = List.map snd (final_result.Transfer.states) in let final_state = PowersetDomain.(final_states |> of_list |> join) in - Eva_utils.pop_call_stack (); + Eva_utils.clear_call_stack (); Self.feedback "done for function %a" Kernel_function.pretty kf; Abstract.Dom.Store.mark_as_computed (); Self.(set_computation_state Computed); diff --git a/src/plugins/eva/engine/function_calls.ml b/src/plugins/eva/engine/function_calls.ml index 9e641c47631d8bb00a3a77537d174723b84387de..8e0817904cab16f7cb9a1ca1b6f9acaeb6cf5466 100644 --- a/src/plugins/eva/engine/function_calls.ml +++ b/src/plugins/eva/engine/function_calls.ml @@ -49,12 +49,14 @@ module Callers = Kernel_function.Map.Make (StmtSet) module CallersTable = Kernel_function.Make_Table (Callers) (val info "Callers") let register_call kinstr kf = - match kinstr, Eva_utils.call_stack () with + let callstack = Eva_utils.current_call_stack () in + let kf', kinstr' = Callstack.top_call callstack in + assert (Kernel_function.equal kf kf'); + assert (Cil_datatype.Kinstr.equal kinstr kinstr'); + match kinstr, Callstack.top_caller callstack with | Kglobal, _ -> CallersTable.add kf Kernel_function.Map.empty - | Kstmt _, ([] | [_]) -> assert false - | Kstmt stmt, (kf', kinstr') :: (caller, _) :: _ -> - assert (Kernel_function.equal kf kf'); - assert (Cil_datatype.Kinstr.equal kinstr kinstr'); + | Kstmt _, None -> assert false + | Kstmt stmt, Some caller -> let callsite = StmtSet.singleton stmt in let change calls = let prev_stmts = Kernel_function.Map.find_opt caller calls in diff --git a/src/plugins/eva/engine/initialization.ml b/src/plugins/eva/engine/initialization.ml index ba5c5ea1c1539b2d72543981a0f827cab92722e1..ed47b68bad299b793f3b1cfc6e84dcb5d3652d8a 100644 --- a/src/plugins/eva/engine/initialization.ml +++ b/src/plugins/eva/engine/initialization.ml @@ -309,14 +309,18 @@ module Make let initialize_global_variable ~lib_entry vi init state = Cil.CurrentLoc.set vi.vdecl; let state = Domain.enter_scope Abstract_domain.Global [vi] state in - if vi.vsource then - let initialize = - if lib_entry || (vi.vstorage = Extern) - then initialize_var_lib_entry - else initialize_var_not_lib_entry ~local:false - in - initialize Kglobal vi init.init state - else state + let state = if vi.vsource then + let initialize = + if lib_entry || (vi.vstorage = Extern) + then initialize_var_lib_entry + else initialize_var_not_lib_entry ~local:false + in + initialize Kglobal vi init.init state + else state + in + state + + (* Compute the initial state with all global variable initialized. *) let compute_global_state ~lib_entry () = diff --git a/src/plugins/eva/engine/iterator.ml b/src/plugins/eva/engine/iterator.ml index 7979fd721696059972af7faf81b24327e98c4c0f..fc546c883ccd75cee7a4ca1ba01d5dfebbd4736c 100644 --- a/src/plugins/eva/engine/iterator.ml +++ b/src/plugins/eva/engine/iterator.ml @@ -450,7 +450,7 @@ module Make_Dataflow (* TODO: apply on all domains. *) let states = Partitioning.contents f in let cvalue_states = gather_cvalues states in - let callstack = Eva_utils.call_stack () in + let callstack = Eva_utils.current_call_stack () in Cvalue_callbacks.apply_statement_hooks callstack stmt cvalue_states let update_vertex ?(widening : bool = false) (v : vertex) @@ -702,7 +702,7 @@ module Make_Dataflow in let merged_pre_cvalues = lazy (lift_to_cvalues merged_pre_states) and merged_post_cvalues = lazy (lift_to_cvalues merged_post_states) in - let callstack = Eva_utils.call_stack () in + let callstack = Eva_utils.current_call_stack () in if save_results then begin let register_pre = Domain.Store.register_state_before_stmt callstack and register_post = Domain.Store.register_state_after_stmt callstack in diff --git a/src/plugins/eva/engine/recursion.ml b/src/plugins/eva/engine/recursion.ml index 30d7fb29e4c0284fe6429a221de6d49f68dd1760..b2fb66139b2ce3db51094c6f76ebd585d7d11ac3 100644 --- a/src/plugins/eva/engine/recursion.ml +++ b/src/plugins/eva/engine/recursion.ml @@ -192,8 +192,9 @@ let make_recursion call depth = { depth; substitution; base_substitution; withdrawal; base_withdrawal; } let make call = - let is_same_kf (f, _) = Kernel_function.equal f call.kf in - let previous_calls = List.filter is_same_kf call.callstack in + let is_same_kf = Kernel_function.equal call.kf in + let all_calls = Callstack.to_kf_list call.callstack in + let previous_calls = List.filter is_same_kf all_calls in let depth = List.length previous_calls - 1 in if depth > 0 then Some (make_recursion call depth) diff --git a/src/plugins/eva/engine/transfer_stmt.ml b/src/plugins/eva/engine/transfer_stmt.ml index 3d17e1177e9756eeeafe100a238db064756dc675..200ac09480a6f30ee880aeb71118a1a631be1d91 100644 --- a/src/plugins/eva/engine/transfer_stmt.ml +++ b/src/plugins/eva/engine/transfer_stmt.ml @@ -313,7 +313,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* Returns the result of a call, and a boolean that indicates whether a builtin has been used to interpret the call. *) let process_call stmt call recursion valuation state = - Eva_utils.push_call_stack call.kf (Kstmt stmt); + Eva_utils.push_call_stack call.kf stmt; let cleanup () = Eva_utils.pop_call_stack (); (* Changed by compute_call_ref, called from process_call *) @@ -325,7 +325,8 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* Process the call according to the domain decision. *) match Domain.start_call stmt call recursion domain_valuation state with | `Value state -> - Domain.Store.register_initial_state (Eva_utils.call_stack ()) state; + let callstack = Eva_utils.current_call_stack () in + Domain.Store.register_initial_state callstack call.kf state; !compute_call_ref stmt call recursion state | `Bottom -> { states = []; cacheable = Cacheable; builtin=false } @@ -534,7 +535,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* Create an Eval.call *) let create_call stmt kf args = let return = Library_functions.get_retres_vi kf in - let callstack = (kf, Kstmt stmt) :: Eva_utils.call_stack () in + let callstack = Callstack.push kf stmt (Eva_utils.current_call_stack ()) in let arguments, rest = let formals = Kernel_function.get_formals kf in let rec format_arguments acc args formals = match args, formals with @@ -728,8 +729,9 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* Legacy callbacks for the cvalue domain, usually called by {Cvalue_transfer.start_call}. *) - let apply_cvalue_callback kf ki_call state = - let stack_with_call = (kf, ki_call) :: Eva_utils.call_stack () in + let apply_cvalue_callback kf stmt state = + let call_stack = Eva_utils.current_call_stack () in + let stack_with_call = Callstack.push kf stmt call_stack in let cvalue_state = get_cvalue_or_top state in Db.Value.Call_Value_Callbacks.apply (cvalue_state, stack_with_call); let kind = `Builtin None in @@ -752,7 +754,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* The special Frama_C_ functions to print states are handled here. *) if apply_special_directives ~subdivnb kf args state then - let () = apply_cvalue_callback kf ki_call state in + let () = apply_cvalue_callback kf stmt state in [(Partition.Key.empty, state)] else (* Create the call. *) diff --git a/src/plugins/eva/eval.ml b/src/plugins/eva/eval.ml index f0c0403a672540d8c414d63a8cc17e95f64b69c4..c08bf95bb6c864b0ca6bf378c554ed7eb5fced51 100644 --- a/src/plugins/eva/eval.ml +++ b/src/plugins/eva/eval.ml @@ -239,13 +239,9 @@ type ('loc, 'value) argument = { avalue: ('loc, 'value) assigned; } - -type call_site = kernel_function * kinstr -type callstack = call_site list - type ('loc, 'value) call = { kf: kernel_function; - callstack: callstack; + callstack: Callstack.t; arguments: ('loc, 'value) argument list; rest: (exp * ('loc, 'value) assigned) list; return: varinfo option; diff --git a/src/plugins/eva/eval.mli b/src/plugins/eva/eval.mli index 08793e093b818ab4da6b4cdb5cceb3695db5d70f..cee30db5319525e4a12c72c0a2ad0e6a09d15824 100644 --- a/src/plugins/eva/eval.mli +++ b/src/plugins/eva/eval.mli @@ -223,21 +223,10 @@ type ('loc, 'value) argument = { avalue: ('loc, 'value) assigned; (** The value of the concrete argument. *) } -(** A call_stack is a list, telling which function was called at which - site. The head of the list tells about the latest call. *) - -(** A call site: the function called, and the call statement - (or [Kglobal] for the main function. *) -type call_site = kernel_function * kinstr - -(* A call stack is a list of call sites. The head is the latest call. - The last element is the main function. *) -type callstack = call_site list - (** A function call. *) type ('loc, 'value) call = { kf: kernel_function; (** The called function. *) - callstack: callstack; (** The current callstack + callstack: Callstack.t; (** The current callstack (with this call on top). *) arguments: ('loc, 'value) argument list; (** The arguments of the call. *) rest: (exp * ('loc, 'value) assigned) list; (** Extra-arguments. *) diff --git a/src/plugins/eva/gui/gui_callstacks_filters.ml b/src/plugins/eva/gui/gui_callstacks_filters.ml index c8817ff00aaa9c2f5b04f82d830492109e8ca0dd..f4af6ba1b8016f7529c2a4329364297781c4d420 100644 --- a/src/plugins/eva/gui/gui_callstacks_filters.ml +++ b/src/plugins/eva/gui/gui_callstacks_filters.ml @@ -22,17 +22,19 @@ open Cil_types -type rcallstack = Value_types.callstack +type rcallstack = (Cil_types.kernel_function * Cil_types.kinstr) list let empty = [] -let from_callstack = List.rev +let from_callstack cs = Callstack.to_call_list cs let callstack_matches_callstack (rcs1:rcallstack) (rcs2:rcallstack) = let rec aux q1 q2 = match q1, q2 with | [], _ | _, [] -> true - | call1 :: q1, call2 :: q2 -> - Value_types.Callsite.equal call1 call2 && aux q1 q2 + | (kf1, kinstr1) :: q1, (kf2, kinstr2) :: q2 -> + Kernel_function.equal kf1 kf2 + && Cil_datatype.Kinstr.equal kinstr1 kinstr2 + && aux q1 q2 in aux rcs1 rcs2 @@ -62,7 +64,7 @@ let has_matching_callstack ~after csf stmt = | `Bottom -> false | `Value h -> try - Value_types.Callstack.Hashtbl.iter + Callstack.Hashtbl.iter (fun cs' _state -> let rcs' = from_callstack cs' in if callstack_matches csf rcs' then raise Exit @@ -95,7 +97,7 @@ let register_to_zone_functions (module Eval: Gui_eval.S) = let eval_filter csf stmt ev v = match Eval.Analysis.get_stmt_state_by_callstack ~after:false stmt with | `Value h -> - Value_types.Callstack.Hashtbl.fold + Callstack.Hashtbl.fold (fun cs state acc -> let rcs' = from_callstack cs in if callstack_matches csf rcs' then diff --git a/src/plugins/eva/gui/gui_callstacks_filters.mli b/src/plugins/eva/gui/gui_callstacks_filters.mli index b5bb5924d36f560eafa26d4061520c2823145da0..39da15edb11ae559db931ffb99a84682c07c8da7 100644 --- a/src/plugins/eva/gui/gui_callstacks_filters.mli +++ b/src/plugins/eva/gui/gui_callstacks_filters.mli @@ -26,7 +26,7 @@ type rcallstack val empty: rcallstack -val from_callstack: Value_types.callstack -> rcallstack +val from_callstack: Callstack.t -> rcallstack (** Filters on callstacks. [None] means that all callstacks are active *) diff --git a/src/plugins/eva/gui/gui_eval.ml b/src/plugins/eva/gui/gui_eval.ml index 25d985d9e4d77693633421bc8e3567105bd11f26..bc338ad9cd9f916018566fb902f00affa3b8defe 100644 --- a/src/plugins/eva/gui/gui_eval.ml +++ b/src/plugins/eva/gui/gui_eval.ml @@ -93,7 +93,7 @@ module type S = sig type ('env, 'expr, 'v) evaluation_functions = { eval_and_warn: 'env -> 'expr -> 'v * bool (* alarm *) * bool (* red *); - env: Analysis.Dom.t -> Value_types.callstack -> 'env; + env: Analysis.Dom.t -> Callstack.t -> 'env; equal: 'v -> 'v -> bool; bottom: 'v; join: 'v -> 'v -> 'v; @@ -124,7 +124,7 @@ module type S = sig val predicate_with_red: gui_loc -> - (Eval_terms.eval_env * (kinstr * Value_types.callstack), + (Eval_terms.eval_env * (kinstr * Callstack.t), Red_statuses.alarm_or_property * predicate, Eval_terms.predicate_status or_bottom ) evaluation_functions @@ -152,7 +152,7 @@ module Make (X: Analysis.S) = struct type ('env, 'expr, 'v) evaluation_functions = { eval_and_warn: 'env -> 'expr -> 'v * bool * bool; - env: X.Dom.t -> Value_types.callstack -> 'env; + env: X.Dom.t -> Callstack.t -> 'env; equal: 'v -> 'v -> bool; bottom: 'v; join: 'v -> 'v -> 'v; @@ -323,7 +323,7 @@ module Make (X: Analysis.S) = struct match Db.Value.get_initial_state_callstack kf with | None -> Cvalue.Model.top (* should not happen *) | Some h -> - try Value_types.Callstack.Hashtbl.find h callstack + try Callstack.Hashtbl.find h callstack with Not_found -> Cvalue.Model.top (* should not happen either *) let env_here kf here callstack = @@ -355,8 +355,8 @@ module Make (X: Analysis.S) = struct (* Maps from callstacks to Value states before and after a GUI location. The 'after' map is not always available. *) type states_by_callstack = { - states_before: X.Dom.t Value_types.Callstack.Hashtbl.t or_top_bottom; - states_after: X.Dom.t Value_types.Callstack.Hashtbl.t or_top_bottom; + states_before: X.Dom.t Callstack.Hashtbl.t or_top_bottom; + states_after: X.Dom.t Callstack.Hashtbl.t or_top_bottom; } let top_states_by_callstacks = { states_before = `Top; states_after = `Top } @@ -542,7 +542,7 @@ module Make (X: Analysis.S) = struct let make_data_all_callstacks_from_states ev ~before ~after expr = let exn = ref [] in - let single_callstack = (Value_types.Callstack.Hashtbl.length before) = 1 in + let single_callstack = (Callstack.Hashtbl.length before) = 1 in let v_join_before = ref ev.bottom in let v_join_after = ref ev.bottom in let ok_join = ref true in @@ -565,14 +565,14 @@ module Make (X: Analysis.S) = struct let ev = { ev with eval_and_warn } in (* Rows by callstack *) let list = - Value_types.Callstack.Hashtbl.fold + Callstack.Hashtbl.fold (fun callstack before acc -> let before = ev.env before callstack in let after = match after with | `Top | `Bottom as x -> x | `Value after -> try - let after = Value_types.Callstack.Hashtbl.find after callstack in + let after = Callstack.Hashtbl.find after callstack in `Value (ev.env after callstack) (* If a callstack exists before the statement but is not found after, then the post state for this callstack is bottom. *) diff --git a/src/plugins/eva/gui/gui_eval.mli b/src/plugins/eva/gui/gui_eval.mli index 31916a24c65376e536cebd2d9796e21e203f0b44..52be333c333b9f80a9fe5adf38fb87f9a486b5fb 100644 --- a/src/plugins/eva/gui/gui_eval.mli +++ b/src/plugins/eva/gui/gui_eval.mli @@ -61,7 +61,7 @@ module type S = sig (** This is the record that encapsulates all evaluation functions *) type ('env, 'expr, 'v) evaluation_functions = { eval_and_warn: 'env -> 'expr -> 'v * bool (* alarm *) * bool (* red *); - env: Analysis.Dom.t -> Value_types.callstack -> 'env; + env: Analysis.Dom.t -> Callstack.t -> 'env; equal: 'v -> 'v -> bool; bottom: 'v; join: 'v -> 'v -> 'v; @@ -108,7 +108,7 @@ module type S = sig val predicate_with_red: gui_loc -> - (Eval_terms.eval_env * (kinstr * Value_types.callstack), + (Eval_terms.eval_env * (kinstr * Callstack.t), Red_statuses.alarm_or_property * predicate, Eval_terms.predicate_status Lattice_bounds.or_bottom ) evaluation_functions diff --git a/src/plugins/eva/gui/gui_types.ml b/src/plugins/eva/gui/gui_types.ml index 94f675343aa8ecb17515ad6137ae9596a5a033d3..3f989adaf5db8eb63d43507cab31ea63783d8d19 100644 --- a/src/plugins/eva/gui/gui_types.ml +++ b/src/plugins/eva/gui/gui_types.ml @@ -25,20 +25,20 @@ open Cil_types type gui_callstack = | GC_Filtered (* Some results have been hidden by a filter *) | GC_Consolidated (* Join of all possible callstacks *) - | GC_Single of Value_types.callstack (* Only one callstack possible here *) - | GC_Callstack of Value_types.callstack (* One of multiple callstacks *) + | GC_Single of Callstack.t (* Only one callstack possible here *) + | GC_Callstack of Callstack.t (* One of multiple callstacks *) let hash_gui_callstack = function | GC_Filtered -> 0 | GC_Consolidated -> 1 - | GC_Single cs -> 2 * Value_types.Callstack.hash cs - | GC_Callstack cs -> 4 * Value_types.Callstack.hash cs + | GC_Single cs -> 2 * Callstack.hash cs + | GC_Callstack cs -> 4 * Callstack.hash cs let compare_gui_callstack cs1 cs2 = match cs1, cs2 with | GC_Filtered, GC_Filtered -> 0 | GC_Consolidated, GC_Consolidated -> 0 | GC_Single cs1, GC_Single cs2 | GC_Callstack cs1, GC_Callstack cs2 -> - Value_types.Callstack.compare cs1 cs2 + Callstack.compare cs1 cs2 | _, GC_Filtered -> 1 | GC_Filtered, _ -> -1 | _, GC_Consolidated -> 1 @@ -227,44 +227,45 @@ let gui_loc_loc = function let kf_of_gui_loc = function | GL_Stmt (kf, _) | GL_Pre kf | GL_Post kf -> kf +let pop_last_call cs = + match cs.Callstack.stack with + | (_, stmt) :: ((kf, _) :: _ as stack)-> Some (stmt, kf, { cs with stack }) + | [_, stmt] -> Some (stmt, cs.entry_point, { cs with stack = [] }) + | [] -> None + (* This pretty-printer drops the toplevel kf, which is always the function in which we are pretty-printing the expression/term *) let pretty_callstack fmt cs = - match cs with - | [_, Kglobal] -> () - | (_kf_cur, Kstmt callsite) :: q -> begin - let rec aux callsite = function - | (kf, callsite') :: q -> begin - Format.fprintf fmt "%a (%a%t)" - Kernel_function.pretty kf - Cil_datatype.Location.pretty (Cil_datatype.Stmt.loc callsite) - (fun fmt -> - if Gui_parameters.debug_atleast 1 then - Format.fprintf fmt ", %d" callsite.sid); - match callsite' with - | Kglobal -> () - | Kstmt callsite' -> - Format.fprintf fmt " â†@ "; - aux callsite' q - end - | _ -> assert false - in - Format.fprintf fmt "@[<hv>%a" Value_types.Callstack.pretty_hash cs; - aux callsite q; - Format.fprintf fmt "@]" - end - | _ -> assert false + match pop_last_call cs with + | None -> () + | Some (stmt, caller, q) -> + let rec aux stmt caller cs = + Format.fprintf fmt "%a (%a%t)" + Kernel_function.pretty caller + Cil_datatype.Location.pretty (Cil_datatype.Stmt.loc stmt) + (fun fmt -> + if Gui_parameters.debug_atleast 1 then + Format.fprintf fmt ", %d" stmt.sid); + match pop_last_call cs with + | None -> () + | Some (stmt, caller, q) -> + Format.fprintf fmt " â†@ "; + aux stmt caller q + in + Format.fprintf fmt "@[<hv>%a" Callstack.pretty_hash cs; + aux stmt caller q; + Format.fprintf fmt "@]" (* This pretty-printer prints only the lists of the functions, not the locations *) let pretty_callstack_short fmt cs = - match cs with - | [_, Kglobal] -> () - | (_kf_cur, Kstmt _callsite) :: q -> - Format.fprintf fmt "%a" Value_types.Callstack.pretty_hash cs; + match Callstack.pop cs with + | None -> () + | Some q -> + Format.fprintf fmt "%a" Callstack.pretty_hash cs; + let list = List.rev (Callstack.to_kf_list q) in Pretty_utils.pp_flowlist ~left:"@[" ~sep:" â†@ " ~right:"@]" - (fun fmt (kf, _) -> Kernel_function.pretty fmt kf) fmt q - | _ -> assert false + Kernel_function.pretty fmt list (* diff --git a/src/plugins/eva/gui/gui_types.mli b/src/plugins/eva/gui/gui_types.mli index e4d5018d9c10a678c9a4c719d470f9280ff5d719..04b713cd82aa5d8f73ccb4d14baa58a38cbeddfe 100644 --- a/src/plugins/eva/gui/gui_types.mli +++ b/src/plugins/eva/gui/gui_types.mli @@ -23,8 +23,8 @@ type gui_callstack = | GC_Filtered | GC_Consolidated - | GC_Single of Value_types.callstack - | GC_Callstack of Value_types.callstack + | GC_Single of Callstack.t + | GC_Callstack of Callstack.t val hash_gui_callstack : gui_callstack -> int val compare_gui_callstack : gui_callstack -> gui_callstack -> int @@ -69,9 +69,9 @@ val gui_loc_loc : gui_loc -> Cil_types.location val kf_of_gui_loc : gui_loc -> Cil_types.kernel_function val pretty_callstack : - Format.formatter -> Value_types.callstack -> unit + Format.formatter -> Callstack.t -> unit val pretty_callstack_short : - Format.formatter -> Value_types.callstack -> unit + Format.formatter -> Callstack.t -> unit type 'a gui_res = | GR_Empty diff --git a/src/plugins/eva/legacy/eval_annots.ml b/src/plugins/eva/legacy/eval_annots.ml index 6ce1404d2491364dfcf653b5435a65fe4e865778..ca814b26cfbc3252f2e1a491436c22efa230b1e5 100644 --- a/src/plugins/eva/legacy/eval_annots.ml +++ b/src/plugins/eva/legacy/eval_annots.ml @@ -117,7 +117,7 @@ let c_labels kf cs = if stmt.labels != [] then try let hstate = Db.Value.Table_By_Callstack.find stmt in - let state = Value_types.Callstack.Hashtbl.find hstate cs in + let state = Callstack.Hashtbl.find hstate cs in Cil_datatype.Logic_label.Map.add (StmtLabel (ref stmt)) state acc with Not_found -> acc else acc @@ -143,7 +143,7 @@ let eval_by_callstack kf stmt p = Unknown | Some states -> try - match Value_types.Callstack.Hashtbl.fold aux_callstack states `Bottom with + match Callstack.Hashtbl.fold aux_callstack states `Bottom with | `Bottom -> Eval_terms.Unknown (* probably never reached *) | `Value status -> status with Exit -> Eval_terms.Unknown diff --git a/src/plugins/eva/legacy/eval_annots.mli b/src/plugins/eva/legacy/eval_annots.mli index e6e9691cce40c3c99ee614e85f5adad33f51651d..bb4b33cac7a70b9ffb9fa0d2fb474bff0b2ee8aa 100644 --- a/src/plugins/eva/legacy/eval_annots.mli +++ b/src/plugins/eva/legacy/eval_annots.mli @@ -26,4 +26,4 @@ val has_requires: spec -> bool val mark_invalid_initializers: unit -> unit val mark_unreachable: unit -> unit val mark_green_and_red: unit -> unit -val c_labels: kernel_function -> Value_types.callstack -> Eval_terms.labels_states +val c_labels: kernel_function -> Callstack.t -> Eval_terms.labels_states diff --git a/src/plugins/eva/types/callstack.ml b/src/plugins/eva/types/callstack.ml new file mode 100644 index 0000000000000000000000000000000000000000..160bfd2d48e272a3ee48bbc0de225a1994ea53f5 --- /dev/null +++ b/src/plugins/eva/types/callstack.ml @@ -0,0 +1,23 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2023 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +include Eva_types.Callstack diff --git a/src/plugins/eva/types/callstack.mli b/src/plugins/eva/types/callstack.mli new file mode 100644 index 0000000000000000000000000000000000000000..9e4a7654bbf600985ea25750c94a9dbd0ccc8767 --- /dev/null +++ b/src/plugins/eva/types/callstack.mli @@ -0,0 +1,92 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2023 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +[@@@ api_start] +[@@@ alert "-db_deprecated"] + +(** A call is identified by the function called and the call statement *) +type call = Cil_types.kernel_function * Cil_types.stmt + +module Call : Datatype.S with type t = call + +(** Eva callstacks. *) +type callstack = Eva_types.Callstack.callstack = { + thread: int; + (* An identifier of the thread's callstack. *) + entry_point: Cil_types.kernel_function; + (** The first function function of the callstack. *) + stack: call list; + (** A call stack is a list of calls. The head is the latest call. *) +} + +include Datatype.S_with_collections + with type t = callstack + and module Hashtbl = Eva_types.Callstack.Hashtbl + +(** Prints a callstack without displaying call sites. *) +val pretty_short : Format.formatter -> t -> unit + +(** Prints a hash of the callstack when '-kernel-msg-key callstack' + is enabled (prints nothing otherwise). *) +val pretty_hash : Format.formatter -> t -> unit + +(** [compare_lex] compares callstack lexicographically, slightly slower + than [compare] but in a more natural order, giving more importance + to the function at bottom of the callstack - the first functions called. *) +val compare_lex : t -> t -> int + +(*** {2 Stack manipulation} *) + +(*** Constructor *) +val init : ?thread:int -> Cil_types.kernel_function -> t + +(** Adds a new call to the top of the callstack. *) +val push : Cil_types.kernel_function -> Cil_types.stmt -> t -> t + +(** Removes the topmost call from the callstack. *) +val pop : t -> t option + +val top : t -> (Cil_types.kernel_function * Cil_types.stmt) option +val top_kf : t -> Cil_types.kernel_function +val top_callsite : t -> Cil_types.kinstr +val top_call : t -> Cil_types.kernel_function * Cil_types.kinstr + +(** Returns the function that called the topmost function of the callstack. *) +val top_caller : t -> Cil_types.kernel_function option + +(** {2 Conversion} *) + +(** Gives the list of kf in the callstack from the entry point to the top of the + callstack (i.e. reverse order of the call stack). *) +val to_kf_list : t -> Cil_types.kernel_function list + +(** Gives the list of call statements from the bottom to the top of the + callstack (i.e. reverse order of the call stack). *) +val to_stmt_list : t -> Cil_types.stmt list + +(** Gives the list of call from the bottom to the top of the callstack + (i.e. reverse order of the call stack). *) +val to_call_list : t -> (Cil_types.kernel_function * Cil_types.kinstr) list + +[@@@ api_end] + +val pretty_debug : Format.formatter -> t -> unit diff --git a/src/plugins/eva/utils/cvalue_callbacks.ml b/src/plugins/eva/utils/cvalue_callbacks.ml index c8fcd6074d88d87f531d5cbff03c3d9d205b0d0c..e1843e4c1f7c09e2264b40778cc66f0cf90c3602 100644 --- a/src/plugins/eva/utils/cvalue_callbacks.ml +++ b/src/plugins/eva/utils/cvalue_callbacks.ml @@ -24,7 +24,6 @@ open Cil_types let dkey = Self.dkey_callbacks -type callstack = (kernel_function * kinstr) list type state = Cvalue.Model.t type analysis_kind = @@ -35,7 +34,7 @@ type analysis_kind = module Call = Hook.Build - (struct type t = callstack * kernel_function * analysis_kind * state end) + (struct type t = Callstack.t * kernel_function * analysis_kind * state end) let register_call_hook f = Call.extend (fun (callstack, kf, kind, state) -> f callstack kf kind state) @@ -53,7 +52,7 @@ type call_results = | Reuse of int module Call_Results = - Hook.Build (struct type t = callstack * kernel_function * call_results end) + Hook.Build (struct type t = Callstack.t * kernel_function * call_results end) let register_call_results_hook f = Call_Results.extend (fun (callstack, kf, results) -> f callstack kf results) @@ -74,7 +73,7 @@ let apply_call_results_hooks callstack kf call_results = module Statement = - Hook.Build (struct type t = callstack * stmt * state list end) + Hook.Build (struct type t = Callstack.t * stmt * state list end) let register_statement_hook f = Statement.extend (fun (callstack, stmt, states) -> f callstack stmt states) diff --git a/src/plugins/eva/utils/cvalue_callbacks.mli b/src/plugins/eva/utils/cvalue_callbacks.mli index abc2408b9ec12f4e3b05aa3e0a1eab2a55d6c873..2d93d634a4d0f18dbce55cce70826734c0377c2a 100644 --- a/src/plugins/eva/utils/cvalue_callbacks.mli +++ b/src/plugins/eva/utils/cvalue_callbacks.mli @@ -28,7 +28,6 @@ in a future version. Please contact us if you need to register callbacks to be executed during an Eva analysis. *) -type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list type state = Cvalue.Model.t type analysis_kind = @@ -42,7 +41,7 @@ type analysis_kind = the function called, the kind of analysis performed by Eva for this call, and the cvalue state at the beginning of the call. *) val register_call_hook: - (callstack -> Cil_types.kernel_function -> analysis_kind -> state -> unit) + (Callstack.t -> Cil_types.kernel_function -> analysis_kind -> state -> unit) -> unit @@ -62,17 +61,17 @@ type call_results = function call. Arguments of the callback are the callstack of the call, the function called and the cvalue states resulting from its analysis. *) val register_call_results_hook: - (callstack -> Cil_types.kernel_function -> call_results -> unit) + (Callstack.t -> Cil_types.kernel_function -> call_results -> unit) -> unit [@@@ api_end] val register_statement_hook: - (callstack -> Cil_types.stmt -> state list -> unit) -> unit + (Callstack.t -> Cil_types.stmt -> state list -> unit) -> unit val apply_call_hooks: - callstack -> Cil_types.kernel_function -> analysis_kind -> state -> unit + Callstack.t -> Cil_types.kernel_function -> analysis_kind -> state -> unit val apply_call_results_hooks: - callstack -> Cil_types.kernel_function -> call_results -> unit + Callstack.t -> Cil_types.kernel_function -> call_results -> unit val apply_statement_hooks: - callstack -> Cil_types.stmt -> state list -> unit + Callstack.t -> Cil_types.stmt -> state list -> unit diff --git a/src/plugins/eva/utils/eva_perf.ml b/src/plugins/eva/utils/eva_perf.ml index 1efc3d7c32ab56ef25903adfc736f74add3278af..4c0ae010c6ff1309e2e1fd7e93d48f702691b2bf 100644 --- a/src/plugins/eva/utils/eva_perf.ml +++ b/src/plugins/eva/utils/eva_perf.ml @@ -218,7 +218,8 @@ module Imperative_callstack_trie(M:sig type t val default:unit -> t end) = struc n in find_subtree subnode.subtree b (Some subnode) - let find_subtree t callstack = find_subtree t (List.rev callstack) None + let find_subtree t callstack = + find_subtree t (Callstack.to_call_list callstack) None let find t callstack = (find_subtree t callstack).self @@ -285,25 +286,25 @@ let display fmt = end ;; -let caller_callee_callinfo = function - | (callee_kf,_)::(caller_kf,_)::_ -> - (let caller_flat = Kernel_function.Hashtbl.find flat caller_kf in - try +let caller_callee_callinfo callstack = + match Callstack.top_caller callstack with + | Some caller_kf -> + let callee_kf = Callstack.top_kf callstack in + let caller_flat = Kernel_function.Hashtbl.find flat caller_kf in + (try Kernel_function.Hashtbl.find caller_flat.called_functions callee_kf with Not_found -> let call_info = Call_info.create() in Kernel_function.Hashtbl.add caller_flat.called_functions callee_kf call_info; call_info) - | [_] -> Call_info.main - | [] -> assert false + | None -> Call_info.main ;; let start_doing_perf callstack = if Parameters.ValShowPerf.get() then begin let time = Sys.time() in - assert (callstack != []); - let kf = fst (List.hd callstack) in + let kf = Callstack.top_kf callstack in let flat_info = try Kernel_function.Hashtbl.find flat kf with Not_found -> @@ -325,7 +326,7 @@ let stop_doing_perf callstack = if Parameters.ValShowPerf.get() then begin let time = Sys.time() in - let kf = fst (List.hd callstack) in + let kf = Callstack.top_kf callstack in let flat_info = Kernel_function.Hashtbl.find flat kf in Call_info.after_call flat_info.call_info time; let node = Perf_by_callstack.find perf callstack in @@ -365,14 +366,13 @@ let stack_flamegraph = ref [] (* pretty-prints the functions in a Value callstack, starting by main (i.e. in reverse order). *) -let pretty_callstack oc l = +let pretty_callstack oc callstack = let rec aux oc = function | [] -> () (* does not happen in theory *) - | [main, _] -> Printf.fprintf oc "%s" (Kernel_function.get_name main) - | (f, _) :: q -> - Printf.fprintf oc "%a;%s" aux q (Kernel_function.get_name f) + | [main] -> Printf.fprintf oc "%s" (Kernel_function.get_name main) + | kf :: q -> Printf.fprintf oc "%s;%a" (Kernel_function.get_name kf) aux q in - aux oc l + aux oc (Callstack.to_kf_list callstack) (* update the [self_total_time] information for the function being analyzed, assuming that the current time is [time] *) @@ -385,9 +385,8 @@ let update_self_total_time time = (* called when a new function is being analyzed *) let start_doing_flamegraph callstack = - match callstack with - | [] -> assert false - | [_] -> + match callstack.Callstack.stack with + | [] -> (* Analysis of main *) if not (Parameters.ValPerfFlamegraphs.is_empty ()) then begin let file = Parameters.ValPerfFlamegraphs.get () in @@ -401,7 +400,7 @@ let start_doing_flamegraph callstack = (Printexc.to_string e); oc_flamegraph := None (* to be on the safe side *) end - | _ :: _ :: _ -> + | _ :: _ -> if !oc_flamegraph <> None then (* Flamegraphs are being computed. Update time spent in current function so far, then push a slot for the analysis of the new function *) diff --git a/src/plugins/eva/utils/eva_perf.mli b/src/plugins/eva/utils/eva_perf.mli index 2c0a2edd183946a7d6970bd7139e2560a7f62ec5..cb4a2af9a1eb768e56c5e454594206e1ef4b0fdf 100644 --- a/src/plugins/eva/utils/eva_perf.mli +++ b/src/plugins/eva/utils/eva_perf.mli @@ -22,11 +22,11 @@ (** Call [start_doing] when starting analyzing a new function. The new function is on the top of the call stack.*) -val start_doing: Value_types.callstack -> unit +val start_doing: Callstack.t -> unit (** Call [start_doing] when finishing analyzing a function. The function must still be on the top of the call stack. *) -val stop_doing: Value_types.callstack -> unit +val stop_doing: Callstack.t -> unit (** Display a complete summary of performance informations. Can be called during the analysis. *) diff --git a/src/plugins/eva/utils/eva_results.ml b/src/plugins/eva/utils/eva_results.ml index 7bd81b39928f5550e41c6a0a6d3a2265afe3fc2d..7b7d2b98ddf18c4f3ae8d33b2b647abbebba07b1 100644 --- a/src/plugins/eva/utils/eva_results.ml +++ b/src/plugins/eva/utils/eva_results.ml @@ -35,7 +35,7 @@ let partition_terminating_instr stmt = let terminating = ref [] in let non_terminating = ref [] in let add x xs = xs := x :: !xs in - Value_types.Callstack.Hashtbl.iter (fun cs state -> + Callstack.Hashtbl.iter (fun cs state -> if Db.Value.is_reachable state then add cs terminating else add cs non_terminating) h; @@ -49,7 +49,7 @@ let is_non_terminating_instr stmt = (* {2 Saving and restoring state} *) -type stmt_by_callstack = Cvalue.Model.t Value_types.Callstack.Hashtbl.t +type stmt_by_callstack = Cvalue.Model.t Callstack.Hashtbl.t module AlarmsStmt = Datatype.Pair_with_collections (Alarms) (Stmt) @@ -72,7 +72,7 @@ type results = { let get_results () = let vue = Emitter.get Eva_utils.emitter in let main = Some (fst (Globals.entry_point ())) in - let module CS = Value_types.Callstack in + let module CS = Callstack in let copy_states iter = let h = Stmt.Hashtbl.create 128 in let copy stmt hstack = Stmt.Hashtbl.add h stmt (CS.Hashtbl.copy hstack) in @@ -144,16 +144,16 @@ let set_results results = let aux_callstack callstack state = Db.Value.update_callstack_table ~after stmt callstack state; in - Value_types.Callstack.Hashtbl.iter aux_callstack h + Callstack.Hashtbl.iter aux_callstack h in Stmt.Hashtbl.iter (aux_states ~after:false) results.before_states; Stmt.Hashtbl.iter (aux_states ~after:true) results.after_states; (* Kf initial state *) - let aux_initial_state _kf h = + let aux_initial_state kf h = let aux_callstack callstack state = - Db.Value.merge_initial_state callstack state + Db.Value.merge_initial_state callstack kf state in - Value_types.Callstack.Hashtbl.iter aux_callstack h + Callstack.Hashtbl.iter aux_callstack h in Kernel_function.Hashtbl.iter aux_initial_state results.kf_initial_states; Function_calls.set_results results.kf_callers; @@ -204,7 +204,7 @@ struct end -module CallstackH = HExt(Value_types.Callstack.Hashtbl) +module CallstackH = HExt(Callstack.Hashtbl) module StmtH = HExt(Stmt.Hashtbl) module KfH = HExt(Kernel_function.Hashtbl) module PropertyH = HExt(Property.Hashtbl) diff --git a/src/plugins/eva/utils/eva_results.mli b/src/plugins/eva/utils/eva_results.mli index bc5d4c7d8cd7cf276098184a9abb3b4a94b27bf3..ee688e7b7d7dc4e997c3fa22aaf592d847bcf79c 100644 --- a/src/plugins/eva/utils/eva_results.mli +++ b/src/plugins/eva/utils/eva_results.mli @@ -44,7 +44,7 @@ val merge: results -> results -> results For technical reasons, the top of the callstack must currently be preserved. *) val change_callstacks: - (Value_types.callstack -> Value_types.callstack) -> results -> results + (Callstack.t -> Callstack.t) -> results -> results val eval_tlval_as_location : ?result:Cil_types.varinfo -> diff --git a/src/plugins/eva/utils/eva_utils.ml b/src/plugins/eva/utils/eva_utils.ml index 7dbc948a1e05968b67aeee3d0ae3c17ccae81c3e..73dc03924ed0497f7e611994aeb9631803134333 100644 --- a/src/plugins/eva/utils/eva_utils.ml +++ b/src/plugins/eva/utils/eva_utils.ml @@ -22,35 +22,53 @@ open Cil_types -(* Callstacks related types and functions *) +(* Callstacks related functions *) -let call_stack : Value_types.callstack ref = ref [] -(* let call_stack_for_callbacks : (kernel_function * kinstr) list ref = ref [] *) +let current_callstack : Callstack.t option ref = ref None let clear_call_stack () = - call_stack := [] - -let pop_call_stack () = - Eva_perf.stop_doing !call_stack; - call_stack := List.tl !call_stack -;; - -let push_call_stack kf ki = - call_stack := (kf,ki) :: !call_stack; - Eva_perf.start_doing !call_stack -;; - + match !current_callstack with + | None -> () + | Some cs -> + Eva_perf.stop_doing cs; + current_callstack := None + +let init_call_stack kf = + assert (!current_callstack = None); + let cs = Callstack.init kf in + current_callstack := Some cs; + Eva_perf.start_doing cs; + cs + +let current_call_stack_opt () = !current_callstack + +let current_call_stack () = + match !current_callstack with + | None -> Self.fatal "Callstack not initialized" + | Some cs -> cs let current_kf () = - let (kf,_) = (List.hd !call_stack) in kf;; + let cs = current_call_stack () in + Callstack.top_kf cs -let call_stack () = !call_stack +let push_call_stack kf stmt = + let cs = current_call_stack () in + let new_cs = Callstack.push kf stmt cs in + current_callstack := Some new_cs; + Eva_perf.start_doing new_cs + +let pop_call_stack () = + let cs = current_call_stack () in + Eva_perf.stop_doing cs; + current_callstack := Callstack.pop cs let pp_callstack fmt = if Parameters.PrintCallstacks.get () then - Format.fprintf fmt "@ stack: %a" - Value_types.Callstack.pretty (call_stack()) -;; + match !current_callstack with + | None -> () (* Stack not initialized; happens when handling global initializations *) + | Some cs -> + Format.fprintf fmt "@ stack: %a" Callstack.pretty cs + (* Assertions emitted during the analysis *) diff --git a/src/plugins/eva/utils/eva_utils.mli b/src/plugins/eva/utils/eva_utils.mli index e02e47c866017db14b252a76791b8f7cf046af87..2e3ab131c5c85d921a2eecf7bc1d6fa42912b002 100644 --- a/src/plugins/eva/utils/eva_utils.mli +++ b/src/plugins/eva/utils/eva_utils.mli @@ -25,13 +25,31 @@ open Cil_types (** {2 Callstacks related types and functions} *) (** Functions dealing with call stacks. *) + +(** Clears the current callstack: future accesses to the current callstack + will fail. *) val clear_call_stack : unit -> unit + +(** Initializes the current callstack with the main entry point. *) +val init_call_stack : kernel_function -> Callstack.t + +(** Push a new call to the current callstack. *) +val push_call_stack : kernel_function -> stmt -> unit + +(** Removes the topmost call from the current callstack. *) val pop_call_stack : unit -> unit -val push_call_stack : kernel_function -> kinstr -> unit -(** The current function is the one on top of the call stack. *) +(** Returns the current function, at the top of the current callstack. + Fails if no callstack has been initialized. This should only be called + during the analysis of a function. *) val current_kf : unit -> kernel_function -val call_stack : unit -> Value_types.callstack + +(** Returns the current callstack; fails if it has not been initialized. + This should only be called during the analysis of a function. *) +val current_call_stack : unit -> Callstack.t + +(** Returns the current callstack, or [None] if it has not been initialized. *) +val current_call_stack_opt : unit -> Callstack.t option (** Prints the current callstack. *) val pp_callstack : Format.formatter -> unit diff --git a/src/plugins/eva/utils/private.ml b/src/plugins/eva/utils/private.ml index ee1b6d4caa868e1bf5e81dd531e13f9ef9403472..bb7d45ac49ff7e8a7bad98b07171ae33ef87585b 100644 --- a/src/plugins/eva/utils/private.ml +++ b/src/plugins/eva/utils/private.ml @@ -28,6 +28,7 @@ module Abstractions = Abstractions module Active_behaviors = Active_behaviors module Alarmset = Alarmset module Analysis = Analysis +module Callstack = Callstack module Cvalue_domain = Cvalue_domain module Domain_builder = Domain_builder module Eva_dynamic = Eva_dynamic diff --git a/src/plugins/eva/utils/private.mli b/src/plugins/eva/utils/private.mli index 36d0eb3b8ce5419317d81c0adbe116529dbec7f3..8fb57decf5b6c805f6776917c96f537c42709bc1 100644 --- a/src/plugins/eva/utils/private.mli +++ b/src/plugins/eva/utils/private.mli @@ -32,6 +32,7 @@ module Abstractions = Abstractions module Active_behaviors = Active_behaviors module Alarmset = Alarmset module Analysis = Analysis +module Callstack = Callstack module Cvalue_domain = Cvalue_domain module Domain_builder = Domain_builder module Eva_dynamic = Eva_dynamic diff --git a/src/plugins/eva/utils/red_statuses.ml b/src/plugins/eva/utils/red_statuses.ml index dac25fbeb277d1236cb19281984c73c50a17f611..af80f2255c20882ec87a421a26b08ac8a7ddebc2 100644 --- a/src/plugins/eva/utils/red_statuses.ml +++ b/src/plugins/eva/utils/red_statuses.ml @@ -51,7 +51,9 @@ module AlarmOrProp = Datatype.Make_with_collections(struct | Prop p -> 175 + Property.hash p end) -module Callstacks = Value_types.Callstack.Set +module Info = struct let module_name = "CallstackOption" end +module CallstackOption = Datatype.Option_with_collections (Callstack) (Info) +module Callstacks = CallstackOption.Set (* For each alarm or predicate, stores the set of callstacks for which it was evaluated to False. *) @@ -81,7 +83,8 @@ let add_red_ap kinstr ap = try AlarmOrProp.Map.find ap current_map with Not_found -> Callstacks.empty in - let new_callstacks = Callstacks.add (Eva_utils.call_stack ()) callstacks in + let new_callstacks = + Callstacks.add (Eva_utils.current_call_stack_opt ()) callstacks in let new_map = AlarmOrProp.Map.add ap new_callstacks current_map in RedStatusesTable.replace kinstr new_map; Hook.apply ap @@ -122,7 +125,7 @@ let is_red_in_callstack kinstr ap callstack = try let map = RedStatusesTable.find kinstr in let callstacks = AlarmOrProp.Map.find ap map in - Callstacks.mem callstack callstacks + Callstacks.mem (Some callstack) callstacks with Not_found -> false let get_all () = diff --git a/src/plugins/eva/utils/red_statuses.mli b/src/plugins/eva/utils/red_statuses.mli index d18cb14e1eb2b111a201654375f5d72a56540170..12ad7c0588ce8a549193d153bb8f8f5e8d17d807 100644 --- a/src/plugins/eva/utils/red_statuses.mli +++ b/src/plugins/eva/utils/red_statuses.mli @@ -40,7 +40,7 @@ val is_red: Property.t -> bool (* Whether a red status has been emitted for an alarm or a property at the given kinstr in the given callstack. *) val is_red_in_callstack: - kinstr -> alarm_or_property -> Value_types.callstack -> bool + kinstr -> alarm_or_property -> Callstack.t -> bool (* Returns the unsorted list of all alarms and properties for which a red status has been emitted during the analysis. Also returns the kinstr of the alarm or diff --git a/src/plugins/eva/utils/results.ml b/src/plugins/eva/utils/results.ml index 2d048a8995e6b381a6c223c74ea543cf108c16be..bc78a896d00c381e9616a2a675b4f7a6b3152029 100644 --- a/src/plugins/eva/utils/results.ml +++ b/src/plugins/eva/utils/results.ml @@ -28,10 +28,7 @@ let are_available kf = | Analyzed (Complete | Partial) -> true | SpecUsed | Builtin _ | Unreachable | Analyzed NoResults -> false -module Callstack = Value_types.Callstack - -type callstack = Callstack.t -type 'a by_callstack = (callstack * 'a) list +type 'a by_callstack = (Callstack.t * 'a) list type control_point = | Initial @@ -41,8 +38,8 @@ type control_point = type context = { control_point : control_point; - selector : callstack list option; - filter: (callstack -> bool) list; + selector : Callstack.t list option; + filter: (Callstack.t -> bool) list; } type request = @@ -133,7 +130,7 @@ struct | `Value state -> ByCallstack [cs,state] let by_callstack : context -> - [< `Bottom | `Top | `Value of 'a Value_types.Callstack.Hashtbl.t ] -> + [< `Bottom | `Top | `Value of 'a Callstack.Hashtbl.t ] -> ('a, restricted_to_callstack) t = fun req -> function | `Top -> Top @@ -149,13 +146,13 @@ struct (* Accessors *) - let callstacks : ('a, restricted_to_callstack) t -> callstack list = function + let callstacks: ('a, restricted_to_callstack) t -> Callstack.t list = function | Top | Bottom -> [] (* What else to do when Top is given ? *) | ByCallstack l -> List.map fst l (* Fold *) - let fold (f : callstack -> 'a -> 'b -> 'b) (acc : 'b) : + let fold (f : Callstack.t -> 'a -> 'b -> 'b) (acc : 'b) : ('a, restricted_to_callstack) t -> 'b = function | Top | Bottom -> acc (* What else to do when Top is given ? *) @@ -245,7 +242,8 @@ struct A.get_stmt_state_by_callstack ?selection ~after:true stmt |> by_callstack ctx | Initial -> - A.get_global_state () |> singleton [] + let cs = Callstack.init (fst (Globals.entry_point ())) in + A.get_global_state () |> singleton cs | Start kf -> A.get_initial_state_by_callstack ?selection kf |> by_callstack ctx diff --git a/src/plugins/eva/utils/results.mli b/src/plugins/eva/utils/results.mli index eb362d580b9c100f1c60c2057e8dd5d866e5415b..122dfe95a676937703d5ddf4261f7a88147923c1 100644 --- a/src/plugins/eva/utils/results.mli +++ b/src/plugins/eva/utils/results.mli @@ -63,8 +63,6 @@ all requests in the function will lead to a Top error. *) val are_available : Cil_types.kernel_function -> bool -type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list - type request type value @@ -127,16 +125,16 @@ val in_cvalue_state : Cvalue.Model.t -> request (** Only consider the given callstack. Replaces previous calls to [in_callstack] or [in_callstacks]. *) -val in_callstack : callstack -> request -> request +val in_callstack : Callstack.t -> request -> request (** Only consider the callstacks from the given list. Replaces previous calls to [in_callstack] or [in_callstacks]. *) -val in_callstacks : callstack list -> request -> request +val in_callstacks : Callstack.t list -> request -> request (** Only consider callstacks satisfying the given predicate. Several filters can be added. If callstacks are also selected with [in_callstack] or [in_callstacks], only the selected callstacks will be filtered. *) -val filter_callstack : (callstack -> bool) -> request -> request +val filter_callstack : (Callstack.t -> bool) -> request -> request (** Working with callstacks *) @@ -146,11 +144,11 @@ val filter_callstack : (callstack -> bool) -> request -> request reached by the analysis, or if no information has been saved at this point (for instance with the -eva-no-results option). Use [is_empty request] to distinguish these two cases. *) -val callstacks : request -> callstack list +val callstacks : request -> Callstack.t list (** Returns a list of subrequests for each reachable callstack from the given request. *) -val by_callstack : request -> (callstack * request) list +val by_callstack : request -> (Callstack.t * request) list (** State requests *) diff --git a/src/plugins/from/callwise.ml b/src/plugins/from/callwise.ml index 3d84dfa8af17e98be504a70776c3cd68e2fc023c..791a86676b1e06340b141955ee0a9d4c7ac72bc7 100644 --- a/src/plugins/from/callwise.ml +++ b/src/plugins/from/callwise.ml @@ -66,7 +66,7 @@ let record_callwise_dependencies_in_db call_site froms = let call_for_individual_froms callstack _kf call_type value_initial_state = if From_parameters.ForceCallDeps.get () then begin - let current_function, call_site = List.hd callstack in + let current_function, call_site = Eva.Callstack.top_call callstack in let register_from froms = record_callwise_dependencies_in_db call_site froms; match !call_froms_stack with @@ -102,7 +102,7 @@ let call_for_individual_froms callstack _kf call_type value_initial_state = end let end_record call_stack froms = - let (current_function_value, call_site) = List.hd call_stack in + let current_function_value, call_site = Eva.Callstack.top_call call_stack in record_callwise_dependencies_in_db call_site froms; (* pop + record in top of stack the froms of function that just finished *) match !call_froms_stack with diff --git a/src/plugins/inout/cumulative_analysis.ml b/src/plugins/inout/cumulative_analysis.ml index 02490caad65bd6a940c807d6c1ee97abda12ecfb..9fd6d8dcfe08947ead6ec1cc3ed539dfc867a6cc 100644 --- a/src/plugins/inout/cumulative_analysis.ml +++ b/src/plugins/inout/cumulative_analysis.ml @@ -39,9 +39,9 @@ let specialize_state_on_call ?stmt kf = match stmt with | None -> Eva.Results.(at_start_of kf |> get_cvalue_model) | Some stmt -> - let filter = function - | (_, Kstmt s) :: _ -> Cil_datatype.Stmt.equal s stmt - | _ -> false + let filter = fun cs -> match Eva.Callstack.top_callsite cs with + | Kstmt s -> Cil_datatype.Stmt.equal s stmt + | Kglobal -> false in Eva.Results.(at_start_of kf |> filter_callstack filter |> get_cvalue_model) diff --git a/src/plugins/inout/operational_inputs.ml b/src/plugins/inout/operational_inputs.ml index d3240b5f5a6713dccf230a2a31951d017fd9c6b1..055cedeea1a352bad1fae4b51ee301bf9ce99943 100644 --- a/src/plugins/inout/operational_inputs.ml +++ b/src/plugins/inout/operational_inputs.ml @@ -207,13 +207,16 @@ module Internals = let size = 17 end) -module CallsiteHash = Value_types.Callsite.Hashtbl +module Callsite = + Datatype.Pair_with_collections (Kernel_function) (Cil_datatype.Kinstr) + (struct let module_name = "From.Callsite" end) +module CallsiteHash = Callsite.Hashtbl (* Results of an an entire call, represented by a pair (stmt, kernel_function). *) module CallwiseResults = State_builder.Hashtbl - (Value_types.Callsite.Hashtbl) + (Callsite.Hashtbl) (Inout_type) (struct let size = 17 @@ -569,7 +572,7 @@ module Callwise = struct let call_inout_stack = ref [] let call_for_callwise_inout callstack _kf call_type state = - let (current_function, ki as call_site) = List.hd callstack in + let current_function, ki as call_site = Eva.Callstack.top_call callstack in let merge_inout inout = Db.Operational_inputs.Record_Inout_Callbacks.apply (callstack, inout); if ki = Kglobal @@ -618,8 +621,7 @@ module Callwise = struct let end_record call_stack inout = merge_local_table_in_global_ones (snd (List.hd !call_inout_stack)); - - let (current_function, _ as call_site) = List.hd call_stack in + let (current_function, _) as call = Eva.Callstack.top_call call_stack in (* pop + record in top of stack the inout of function that just finished*) match !call_inout_stack with | (current_function2, _) :: (((_caller, table) :: _) as tail) -> @@ -628,10 +630,10 @@ module Callwise = struct Kernel_function.pretty current_function (* g *) Kernel_function.pretty current_function2 (* f *); call_inout_stack := tail; - merge_call_in_local_table call_site table inout; + merge_call_in_local_table call table inout; | _ -> (* the entry point, probably *) - merge_call_in_global_tables call_site inout; + merge_call_in_global_tables call inout; call_inout_stack := []; CallwiseResults.mark_as_computed () diff --git a/src/plugins/nonterm/nonterm_run.ml b/src/plugins/nonterm/nonterm_run.ml index 3d35dd91f99e0642024a0478f144c252abeb9464..176678b5e498016794add8061f174db1042246fd 100644 --- a/src/plugins/nonterm/nonterm_run.ml +++ b/src/plugins/nonterm/nonterm_run.ml @@ -91,17 +91,16 @@ let pretty_stmt_kind fmt stmt = let pp_numbered_stacks fmt callstacks = if List.length callstacks < 2 then Format.fprintf fmt "stack: %a" - (Pretty_utils.pp_list ~sep:": " Value_types.Callstack.pretty) callstacks + (Pretty_utils.pp_list ~sep:": " Eva.Callstack.pretty) callstacks else (* number callstacks *) let numbered_callstacks = - let count = ref 0 in - List.map (fun cs -> incr count; (!count, cs)) callstacks + List.mapi (fun i cs -> (i+1, cs)) callstacks in Format.fprintf fmt "%a" (Pretty_utils.pp_list ~sep:"@\n" (Pretty_utils.pp_pair ~pre:"stack " ~sep:": " - Format.pp_print_int Value_types.Callstack.pretty)) + Format.pp_print_int Eva.Callstack.pretty)) numbered_callstacks let wkey_stmt = Self.register_warn_category "stmt" @@ -306,21 +305,6 @@ let collect_nonterminating_statements fd nonterm_stacks = ) vis#get_instr_stmts; !new_nonterm_stmts -let rec cmp_callstacks_aux cs1 cs2 = - match cs1, cs2 with - | [], [] -> 0 - | [], _ -> -1 - | _, [] -> 1 - | (kf1, ki1) :: r1, (kf2, ki2) :: r2 -> - let c = Cil_datatype.Kinstr.compare ki1 ki2 in - if c <> 0 then c else - let c = Kernel_function.compare kf1 kf2 in - if c <> 0 then c else - cmp_callstacks_aux r1 r2 - -let cmp_callstacks cs1 cs2 = - if cs1 == cs2 then 0 else cmp_callstacks_aux (List.rev cs1) (List.rev cs2) - let run () = if not (Ast.is_computed ()) then Self.abort "nonterm requires a computed AST"; @@ -344,7 +328,7 @@ let run () = let warned_kfs = Stmt.Hptset.fold (fun stmt acc -> let cs = Hashtbl.find nonterm_stacks stmt in - let cs = List.sort cmp_callstacks cs in + let cs = List.sort Eva.Callstack.compare_lex cs in warn_nonterminating_statement stmt cs; Kernel_function.Set.add (Kernel_function.find_englobing_kf stmt) acc ) new_nonterm_stmts Kernel_function.Set.empty diff --git a/src/plugins/users/users_register.ml b/src/plugins/users/users_register.ml index d4ec19e6516847d2d1c3d979e46be3c5ae2bf37b..25fd111322f8f8abfc1fc2e866dc0fd388b8e707 100644 --- a/src/plugins/users/users_register.ml +++ b/src/plugins/users/users_register.ml @@ -49,15 +49,16 @@ let compute_users _ = if Eva.Results.is_called kf then let callstacks = Eva.Results.(at_start_of kf |> callstacks) in - let process_callstack list = - let process_element (user, _call_site) = + let process_callstack callstack = + let users = List.tl (List.rev (Eva.Callstack.to_kf_list callstack)) in + let process_element user = ignore (Users.memo ~change:(Kernel_function.Hptset.add kf) (fun _ -> Kernel_function.Hptset.singleton kf) user) in - List.iter process_element (List.tl list) + List.iter process_element users in List.iter process_callstack callstacks in