From 96fcdf6be4216c24909ba08c33ca9413176c73bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr> Date: Fri, 21 May 2021 10:41:35 +0200 Subject: [PATCH] [Eva] Change Domain_builder.Complete signature. This functor automatically builds some abstract domain functions. It must now be called at the beginning of the abstract domain module, so that the built functions can be redefined by the domain if needed. --- src/plugins/value/domains/abstract_domain.mli | 8 +- .../value/domains/apron/apron_domain.ml | 13 +- src/plugins/value/domains/domain_builder.ml | 19 +- src/plugins/value/domains/domain_builder.mli | 17 +- .../value/domains/gauges/gauges_domain.ml | 8 +- src/plugins/value/domains/inout_domain.ml | 10 +- src/plugins/value/domains/multidim_domain.ml | 34 +- src/plugins/value/domains/octagons.ml | 8 +- src/plugins/value/domains/offsm_domain.ml | 10 +- src/plugins/value/domains/simple_memory.ml | 13 +- src/plugins/value/domains/symbolic_locs.ml | 9 +- src/plugins/value/domains/taint_domain.ml | 10 +- src/plugins/value/domains/traces_domain.ml | 352 +++++++++--------- src/plugins/value/utils/abstract.ml | 4 +- src/plugins/value/utils/abstract.mli | 4 +- 15 files changed, 271 insertions(+), 248 deletions(-) diff --git a/src/plugins/value/domains/abstract_domain.mli b/src/plugins/value/domains/abstract_domain.mli index 232f8e12eec..a71352d34ce 100644 --- a/src/plugins/value/domains/abstract_domain.mli +++ b/src/plugins/value/domains/abstract_domain.mli @@ -464,12 +464,6 @@ module type S = sig (** Category for the messages about the domain. Must be created through {!Value_parameters.register_category}. *) val log_category : Value_parameters.category -end - -(** Full implementation of domains. Automatically built by - {!Domain_builder.Complete} from an {!S_with_Structure} domain. *) -module type Internal = sig - include S (** This function is called after the analysis. The argument is the state computed at the return statement of the main function. The function can @@ -485,7 +479,7 @@ type 't key = 't Structure.Key_Domain.key (** Signature for a leaf module of a domain. *) module type Leaf = sig - include Internal + include S (** The key identifies the domain and the type [t] of its states. *) val key: t key diff --git a/src/plugins/value/domains/apron/apron_domain.ml b/src/plugins/value/domains/apron/apron_domain.ml index 1f3b2fdede7..3b8321d98f7 100644 --- a/src/plugins/value/domains/apron/apron_domain.ml +++ b/src/plugins/value/domains/apron/apron_domain.ml @@ -370,7 +370,7 @@ module Make (Man : Input) = struct let top = Abstract1.top man empty_env let make_top env = Abstract1.top man env - include Datatype.Make_with_collections ( + module D = Datatype.Make_with_collections ( struct include Datatype.Undefined type t = state @@ -402,6 +402,7 @@ module Make (Man : Input) = struct let mem_project = Datatype.never_any_project end ) + include D let name = Man.name @@ -428,6 +429,14 @@ module Make (Man : Input) = struct let s = Abstract1.meet man s1 s2 in if Abstract1.is_bottom man s then `Bottom else `Value s + include Domain_builder.Complete + (struct + include D + let name = name + let top = top + let join = join + end) + let make_eval state = let env = Abstract1.env state in fun e -> @@ -728,7 +737,7 @@ end let () = Floating_point.set_round_nearest_even () let make name (module Man: Input) = - let module Domain = Domain_builder.Complete (Make (Man)) in + let module Domain = Make (Man) in let descr = "Binding to the " ^ name ^ " domain of the Apron library. " ^ "See http://apron.cri.ensmp.fr/library for more details." diff --git a/src/plugins/value/domains/domain_builder.ml b/src/plugins/value/domains/domain_builder.ml index ac0278cd3d4..0d48207dbda 100644 --- a/src/plugins/value/domains/domain_builder.ml +++ b/src/plugins/value/domains/domain_builder.ml @@ -21,14 +21,20 @@ (**************************************************************************) module type InputDomain = sig - include Abstract_domain.S + include Datatype.S + val top: t + val join: t -> t -> t end -module Complete - (Domain: InputDomain) -= struct +module type LeafDomain = sig + type t + val post_analysis: t Bottom.or_bottom -> unit + module Store: Domain_store.S with type t := t + val key: t Abstract_domain.key +end + +module Complete (Domain: InputDomain) = struct - include Domain module Store = Domain_store.Make (Domain) let key: Domain.t Structure.Key_Domain.key = @@ -140,6 +146,7 @@ module Complete_Minimal : Datatype.S_with_collections with type t := t) end + include D include Complete (D) end @@ -161,6 +168,7 @@ module Complete_Minimal_with_datatype : Datatype.S_with_collections with type t := t) end + include D include Complete (D) end @@ -253,6 +261,7 @@ module Complete_Simple_Cvalue (Domain: Simpler_domains.Simple_Cvalue) let reuse _kf _bases ~current_input:_ ~previous_output = previous_output end + include D include Complete (D) end diff --git a/src/plugins/value/domains/domain_builder.mli b/src/plugins/value/domains/domain_builder.mli index e27fcc16829..5d76e370494 100644 --- a/src/plugins/value/domains/domain_builder.mli +++ b/src/plugins/value/domains/domain_builder.mli @@ -24,14 +24,19 @@ simplified interfaces. *) module type InputDomain = sig - include Abstract_domain.S + include Datatype.S + val top: t + val join: t -> t -> t end -module Complete - (Domain: InputDomain) - : Abstract_domain.Leaf with type state = Domain.state - and type value = Domain.value - and type location = Domain.location +module type LeafDomain = sig + type t + val post_analysis: t Bottom.or_bottom -> unit + module Store: Domain_store.S with type t := t + val key: t Abstract_domain.key +end + +module Complete (Domain: InputDomain) : LeafDomain with type t := Domain.t module Complete_Minimal (Value: Abstract_value.S) diff --git a/src/plugins/value/domains/gauges/gauges_domain.ml b/src/plugins/value/domains/gauges/gauges_domain.ml index f14c28a4ec5..ced00ea1bab 100644 --- a/src/plugins/value/domains/gauges/gauges_domain.ml +++ b/src/plugins/value/domains/gauges/gauges_domain.ml @@ -538,6 +538,8 @@ module G = struct (Datatype.List(Datatype.Pair(Cil_datatype.Stmt)(IterationInfo))) (struct let module_name = "Values.Gauges_domain.G" end) + let name = "gauges" + let empty = MV.empty, [] let top (state: t) : t = let top_iteration_info = function @@ -1117,7 +1119,7 @@ end let dkey = Value_parameters.register_category "d-gauges" -module D_Impl : Abstract_domain.S +module D : Abstract_domain.Leaf with type state = G.t and type value = Cvalue.V.t and type location = Precise_locs.precise_location @@ -1128,8 +1130,8 @@ module D_Impl : Abstract_domain.S type origin include G + include Domain_builder.Complete (struct include G let top = empty end) - let name = "gauges" let log_category = dkey let empty _ = G.empty @@ -1304,5 +1306,3 @@ module D_Impl : Abstract_domain.S let top = G.empty (* must not be used, not neutral w.r.t. join (because join crashes...)!! *) end - -module D = Domain_builder.Complete (D_Impl) diff --git a/src/plugins/value/domains/inout_domain.ml b/src/plugins/value/domains/inout_domain.ml index bbcad31558f..3d99d563f6c 100644 --- a/src/plugins/value/domains/inout_domain.ml +++ b/src/plugins/value/domains/inout_domain.ml @@ -95,6 +95,8 @@ module LatticeInout = struct end) + let name = "inout" + (* Initial abstract at the beginning of the computation: nothing written or read so far. *) let empty = { @@ -204,7 +206,7 @@ module Transfer = struct end -module Internal +module D (*: Domain_builder.InputDomain with type state = inout and type value = Cvalue.V.t @@ -220,7 +222,8 @@ module Internal include Abstract_domain.Lattice with type state := state end) - let name = "inout" + include Domain_builder.Complete (LatticeInout) + let log_category = Value_parameters.register_category "d-inout" let enter_scope _kind _vars state = state @@ -284,9 +287,6 @@ module Internal end -module D = Domain_builder.Complete (Internal) - - (* Local Variables: compile-command: "make -C ../../.." diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml index fb5764a5151..e9928248897 100644 --- a/src/plugins/value/domains/multidim_domain.ml +++ b/src/plugins/value/domains/multidim_domain.ml @@ -231,7 +231,7 @@ struct end -module DomainPrototype = +module DomainLattice = struct (* The domain is essentially a map from bases to individual memory abstractions *) module Initial_Values = struct let v = [[]] end @@ -241,16 +241,6 @@ struct (Base.Base) (Memory) (Hptmap.Comp_unused) (Initial_Values) (Deps) - type state = t - type value = Value.t - type base = Base.t - type offset = Location.offset - type memory = Memory.t - type location = Precise_locs.precise_location - type mdlocation = Location.t (* should be = to location *) - type origin - - let name = "multidim" let log_category = dkey @@ -295,6 +285,23 @@ struct in inter ~cache:Hptmap_sig.NoCache ~symmetric:false ~idempotent:true ~decide +end + +module Domain = +struct + + include DomainLattice + include Domain_builder.Complete (DomainLattice) + + type state = t + type value = Value.t + type base = Base.t + type offset = Location.offset + type memory = Memory.t + type location = Precise_locs.precise_location + type mdlocation = Location.t (* should be = to location *) + type origin + (* Bases handling *) @@ -556,7 +563,7 @@ struct let relate _kf _bases _state = Base.SetLattice.empty let filter _kf _kind bases state = - filter (fun elt -> Base.Hptset.mem elt bases) state + DomainLattice.filter (fun elt -> Base.Hptset.mem elt bases) state let reuse _kf bases ~current_input ~previous_output = let cache = Hptmap_sig.NoCache in @@ -569,5 +576,4 @@ struct current_input previous_output end - -include Domain_builder.Complete (DomainPrototype) +include Domain diff --git a/src/plugins/value/domains/octagons.ml b/src/plugins/value/domains/octagons.ml index cdccda505e9..5b0fa411508 100644 --- a/src/plugins/value/domains/octagons.ml +++ b/src/plugins/value/domains/octagons.ml @@ -753,6 +753,9 @@ module State = struct Format.fprintf fmt "@[%a@]" Octagons.pretty octagons end) + let name = "octagon" + let log_category = Value_parameters.register_category "d-octagon" + let pretty_debug fmt { octagons; intervals; relations } = Format.fprintf fmt "@[<v> Octagons: %a@; Intervals: %a@; Relations: %a@]" Octagons.pretty octagons Intervals.pretty intervals @@ -1034,6 +1037,7 @@ end module Domain = struct include State + include Domain_builder.Complete (State) type value = Cvalue.V.t type location = Precise_locs.precise_location @@ -1320,8 +1324,6 @@ module Domain = struct relations = join_rel prev_output.relations current_input.relations; modified = current_input.modified } - let name = "octagon" - let log_category = Value_parameters.register_category "d-octagon" end -include Domain_builder.Complete (Domain) +include Domain diff --git a/src/plugins/value/domains/offsm_domain.ml b/src/plugins/value/domains/offsm_domain.ml index 2818f14c91f..7f4e4b4846b 100644 --- a/src/plugins/value/domains/offsm_domain.ml +++ b/src/plugins/value/domains/offsm_domain.ml @@ -86,10 +86,12 @@ module Memory = struct widen wh s1 s2 let narrow x _y = `Value x + + let name = "bitwise" end -module Internal : Domain_builder.InputDomain +module D : Abstract_domain.Leaf with type state = Memory.t and type value = offsm_or_top and type location = Precise_locs.precise_location @@ -104,7 +106,8 @@ module Internal : Domain_builder.InputDomain include Abstract_domain.Lattice with type state := state end) - let name = "bitwise" + include Domain_builder.Complete (Memory) + let log_category = dkey let empty _ = Memory.empty_map @@ -233,6 +236,3 @@ module Internal : Domain_builder.InputDomain let evaluate_predicate _ _ _ = Alarmset.Unknown let reduce_by_predicate _ state _ _ = `Value state end - - -module D = Domain_builder.Complete (Internal) diff --git a/src/plugins/value/domains/simple_memory.ml b/src/plugins/value/domains/simple_memory.ml index b8260e5834d..80c5dbce55e 100644 --- a/src/plugins/value/domains/simple_memory.ml +++ b/src/plugins/value/domains/simple_memory.ml @@ -176,7 +176,7 @@ module Make_Memory (Value: Value) = struct end -module Make_Internal (Info: sig val name: string end) (Value: Value) = struct +module Make_Domain (Info: sig val name: string end) (Value: Value) = struct let table = Hashtbl.create 17 let () = @@ -186,7 +186,10 @@ module Make_Internal (Info: sig val name: string end) (Value: Value) = struct try Some (Hashtbl.find table name) with Not_found -> None - include Make_Memory (Value) + module M = Make_Memory (Value) + include M + + include Domain_builder.Complete (M) let name = Info.name @@ -344,13 +347,7 @@ module Make_Internal (Info: sig val name: string end) (Value: Value) = struct merge ~cache ~symmetric:false ~idempotent:true ~decide_both ~decide_left:(Traversing decide_left) ~decide_right:Neutral current_input previous_output -end - -module Make_Domain (Info: sig val name: string end) (Value: Value) = -struct - module M = Make_Internal (Info) (Value) - include Domain_builder.Complete (M) let add = M.add let find = M.find let remove = M.remove diff --git a/src/plugins/value/domains/symbolic_locs.ml b/src/plugins/value/domains/symbolic_locs.ml index 840e4fdbb09..8e49fd93cf7 100644 --- a/src/plugins/value/domains/symbolic_locs.ml +++ b/src/plugins/value/domains/symbolic_locs.ml @@ -215,6 +215,8 @@ module Memory = struct end) + let name = "symbolic-locations" + let top = { values = K2V.M.empty; zones = K2Z.empty; @@ -454,7 +456,7 @@ module Memory = struct syntactic_deps = B2K.union into.syntactic_deps t.syntactic_deps } end -module Internal : Domain_builder.InputDomain +module D : Abstract_domain.Leaf with type state = Memory.t and type value = V.t and type location = Precise_locs.precise_location @@ -469,7 +471,8 @@ module Internal : Domain_builder.InputDomain include Abstract_domain.Lattice with type state := state end) - let name = "symbolic-locations" + include Domain_builder.Complete (Memory) + let log_category = dkey let empty _ = Memory.empty_map @@ -640,5 +643,3 @@ module Internal : Domain_builder.InputDomain let evaluate_predicate _ _ _ = Alarmset.Unknown let reduce_by_predicate _ state _ _ = `Value state end - -module D = Domain_builder.Complete (Internal) diff --git a/src/plugins/value/domains/taint_domain.ml b/src/plugins/value/domains/taint_domain.ml index 6bf2ad5f6e7..6ac9fe86559 100644 --- a/src/plugins/value/domains/taint_domain.ml +++ b/src/plugins/value/domains/taint_domain.ml @@ -106,6 +106,8 @@ module LatticeTaint = struct end) + let name = "taint" + (* Initial state at the start of the computation: nothing is tainted yet. *) let empty = { locs_data = Zone.bottom; @@ -329,7 +331,7 @@ module ReuseTaint = struct end -module InternalTaint = struct +module TaintDomain = struct type state = taint type value = Cvalue.V.t type location = Precise_locs.precise_location @@ -340,6 +342,8 @@ module InternalTaint = struct include Abstract_domain.Lattice with type state := state end) + include Domain_builder.Complete (LatticeTaint) + include (QueriesTaint: Abstract_domain.Queries with type state := state and type value := value @@ -354,7 +358,6 @@ module InternalTaint = struct include (ReuseTaint: Abstract_domain.Reuse with type t := state) - let name = "taint" let log_category = dkey @@ -415,5 +418,4 @@ module InternalTaint = struct let leave_loop _ state = state end - -include Domain_builder.Complete (InternalTaint) +include TaintDomain diff --git a/src/plugins/value/domains/traces_domain.ml b/src/plugins/value/domains/traces_domain.ml index e29351acf71..25e01df3368 100644 --- a/src/plugins/value/domains/traces_domain.ml +++ b/src/plugins/value/domains/traces_domain.ml @@ -571,6 +571,8 @@ module Traces = struct end) + let name = "traces" + let view m = if m == top then `Top else `Other m @@ -844,174 +846,6 @@ let rec complete_graph (graph:Graph.t) = ) graph Graph.empty -module Internal = struct - type nonrec state = state - type value = Cvalue.V.t - type location = Precise_locs.precise_location - type origin - - include (Traces: sig - include Datatype.S_with_collections with type t = state - include Abstract_domain.Lattice with type state := state - end) - - let name = "traces" - let log_category = Value_parameters.register_category "d-traces" - - let assign ki lv e _v _valuation state = - let trans = Assign (ki, lv.Eval.lval, lv.Eval.ltyp, e) in - `Value (Traces.add_trans state trans) - - let assume stmt e pos _valuation state = - let trans = Assume (stmt, e, pos) in - `Value (Traces.add_trans state trans) - - let start_call stmt call _recursion _valuation state = - let kf = call.Eval.kf in - if Kernel_function.is_definition kf then - let msg = Format.asprintf "start_call: %s (%b)" (Kernel_function.get_name call.Eval.kf) - (Kernel_function.is_definition call.Eval.kf) in - let state = Traces.add_trans state (Msg msg) in - let formals = List.map (fun arg -> arg.Eval.formal) call.Eval.arguments in - let state = Traces.add_trans state (EnterScope (kf, formals)) in - let state = List.fold_left (fun state arg -> - Traces.add_trans state - (Assign (Kstmt stmt, Cil.var arg.Eval.formal, - arg.Eval.formal.Cil_types.vtype, - arg.Eval.concrete))) state call.Eval.arguments in - `Value state - else - (** enter the scope of the dumb result variable *) - let var = call.Eval.return in - let state = match var with - | Some var -> Traces.add_trans state (EnterScope (kf, [var])) - | None -> state in - let exps = List.map (fun arg -> arg.Eval.concrete) call.Eval.arguments in - let state = Traces.add_trans state - (CallDeclared (call.Eval.kf, exps, Option.map Cil.var var)) - in `Value {state with call_declared_function = true} - - let finalize_call _stmt call _recursion ~pre:_ ~post = - if post.call_declared_function - then `Value {post with call_declared_function = false} - else - let msg = Format.asprintf "finalize_call: %s" (Kernel_function.get_name call.Eval.kf) in - let state = Traces.add_trans post (Msg msg) in - `Value state - - let update _valuation state = `Value state - - let show_expr _valuation state fmt _expr = Traces.pretty fmt state - - (* Memexec *) - (* This domains infers no relation between variables. *) - let relate _kf _bases _state = Base.SetLattice.bottom - (* Do not filter the state: the memexec cache will be applied only on function - calls for which the entry states are equal. This almost completely - disable memexec, but is always sound. *) - let filter _kf _kind _bases state = state - (* As memexec cache is only applied on equal entry states, the previous - output state is a correct output for the current input state. *) - let reuse _kf _bases ~current_input:_ ~previous_output:state = state - - let empty () = Traces.empty - let initialize_variable lv _ ~initialized:_ _ state = - Traces.add_trans state (Msg(Format.asprintf "initialize variable: %a" Printer.pp_lval lv )) - let initialize_variable_using_type var_kind varinfo state = - let kind_str = - match var_kind with - | Abstract_domain.Global -> "global" - | Abstract_domain.Local _ -> "local" - | Abstract_domain.Formal _ -> "formal" - | Abstract_domain.Result _ -> "result" - in - let msg = - Format.asprintf "initialize@ %s variable@ using@ type@ %a" - kind_str Varinfo.pretty varinfo - in - Traces.add_trans state (Msg msg) - - (* TODO *) - let logic_assign _assign _location state = - Traces.add_trans state (Msg "logic assign") - - (* Logic *) - let evaluate_predicate _ _ _ = Alarmset.Unknown - let reduce_by_predicate _ state _ _ = `Value state - - let top_query = `Value (Cvalue.V.top, None), Alarmset.all - - let extract_expr ~oracle:_ _context _state _expr = top_query - let extract_lval ~oracle:_ _context _state _lv _typ _locs = top_query - - let backward_location _state _lval _typ loc value = - `Value (loc, value) - - let enter_loop stmt state = - let state = Traces.add_trans state (Msg "enter_loop") in - let state = if not (Value_parameters.TracesUnrollLoop.get ()) - then Traces.add_loop stmt state - else { state with current = UnrollLoop(stmt,state.current) } in - state - - let incr_loop_counter _ state = - match state.current with - | Base _ -> assert false - | UnrollLoop(_,_) -> state - | OpenLoop(stmt,s,last,_,g,l) -> - let last = Graph.join last g in - let last = if Value_parameters.TracesUnifyLoop.get () then - let s',old_last = Stmt.Hashtbl.find state.all_loop_start stmt in - let last = Graph.join last old_last in - assert (Node.equal s s'); - Stmt.Hashtbl.add state.all_loop_start stmt (s,last); - last - else last - in - let current = OpenLoop(stmt,s,last,s,Graph.empty,l) in - let state = { state with current } in - (* Traces.add_trans state (Msg("incr_loop_counter")) *) - state - - let leave_loop stmt' state = - match state.current with - | Base _ -> assert false (* absurd: we are in at least a loop *) - | UnrollLoop(_,l) -> { state with current = l } - | OpenLoop(stmt,s,last,old_current_node,g,current) -> - assert (Stmt.equal stmt stmt'); - let state = { state with current } in - let last = if Value_parameters.TracesUnifyLoop.get () then - let s',old_last = Stmt.Hashtbl.find state.all_loop_start stmt in - let last = Graph.join last old_last in - assert (Node.equal s s'); - Stmt.Hashtbl.add state.all_loop_start stmt (s,last); - last - else last - in - let state = if Graph.is_empty last then state - else Traces.add_trans state (Loop(stmt,s,last)) in - let state = Traces.copy_edges s old_current_node g state in - Traces.add_trans state (Msg "leave_loop") - - - let enter_scope kind vars state = - match kind with - | Abstract_domain.Global -> - { state with globals = vars @ state.globals } - | Abstract_domain.Formal kf -> - if Kernel_function.equal kf (fst (Globals.entry_point ())) - then { state with main_formals = vars @ state.main_formals } - else state - | Abstract_domain.(Local kf | Result kf) -> - Traces.add_trans state (EnterScope (kf, vars)) - - let leave_scope kf vars state = - Traces.add_trans state (LeaveScope (kf, vars)) - - let reduce_further _state _expr _value = [] (*Nothing intelligent to suggest*) - -end - let dummy_loc = Location.unknown let subst_in_full var_mapping = @@ -1250,15 +1084,179 @@ let project_of_cfg vreturn s = (* ) () *) -let output_dot filename state = - let out = open_out filename in - Value_parameters.feedback ~dkey:Internal.log_category "@[Output dot produced to %s.@]" filename; - (** *) - GraphDot.output_graph out (complete_graph (snd (Traces.get_current state))); - close_out out - module D = struct - include Domain_builder.Complete (Internal) + type nonrec state = state + type value = Cvalue.V.t + type location = Precise_locs.precise_location + type origin + + include (Traces: sig + include Datatype.S_with_collections with type t = state + include Abstract_domain.Lattice with type state := state + end) + + include Domain_builder.Complete (Traces) + + let log_category = Value_parameters.register_category "d-traces" + + let assign ki lv e _v _valuation state = + let trans = Assign (ki, lv.Eval.lval, lv.Eval.ltyp, e) in + `Value (Traces.add_trans state trans) + + let assume stmt e pos _valuation state = + let trans = Assume (stmt, e, pos) in + `Value (Traces.add_trans state trans) + + let start_call stmt call _recursion _valuation state = + let kf = call.Eval.kf in + if Kernel_function.is_definition kf then + let msg = Format.asprintf "start_call: %s (%b)" (Kernel_function.get_name call.Eval.kf) + (Kernel_function.is_definition call.Eval.kf) in + let state = Traces.add_trans state (Msg msg) in + let formals = List.map (fun arg -> arg.Eval.formal) call.Eval.arguments in + let state = Traces.add_trans state (EnterScope (kf, formals)) in + let state = List.fold_left (fun state arg -> + Traces.add_trans state + (Assign (Kstmt stmt, Cil.var arg.Eval.formal, + arg.Eval.formal.Cil_types.vtype, + arg.Eval.concrete))) state call.Eval.arguments in + `Value state + else + (** enter the scope of the dumb result variable *) + let var = call.Eval.return in + let state = match var with + | Some var -> Traces.add_trans state (EnterScope (kf, [var])) + | None -> state in + let exps = List.map (fun arg -> arg.Eval.concrete) call.Eval.arguments in + let state = Traces.add_trans state + (CallDeclared (call.Eval.kf, exps, Option.map Cil.var var)) + in `Value {state with call_declared_function = true} + + let finalize_call _stmt call _recursion ~pre:_ ~post = + if post.call_declared_function + then `Value {post with call_declared_function = false} + else + let msg = Format.asprintf "finalize_call: %s" (Kernel_function.get_name call.Eval.kf) in + let state = Traces.add_trans post (Msg msg) in + `Value state + + let update _valuation state = `Value state + + let show_expr _valuation state fmt _expr = Traces.pretty fmt state + + (* Memexec *) + (* This domains infers no relation between variables. *) + let relate _kf _bases _state = Base.SetLattice.bottom + (* Do not filter the state: the memexec cache will be applied only on function + calls for which the entry states are equal. This almost completely + disable memexec, but is always sound. *) + let filter _kf _kind _bases state = state + (* As memexec cache is only applied on equal entry states, the previous + output state is a correct output for the current input state. *) + let reuse _kf _bases ~current_input:_ ~previous_output:state = state + + let empty () = Traces.empty + let initialize_variable lv _ ~initialized:_ _ state = + Traces.add_trans state (Msg(Format.asprintf "initialize variable: %a" Printer.pp_lval lv )) + let initialize_variable_using_type var_kind varinfo state = + let kind_str = + match var_kind with + | Abstract_domain.Global -> "global" + | Abstract_domain.Local _ -> "local" + | Abstract_domain.Formal _ -> "formal" + | Abstract_domain.Result _ -> "result" + in + let msg = + Format.asprintf "initialize@ %s variable@ using@ type@ %a" + kind_str Varinfo.pretty varinfo + in + Traces.add_trans state (Msg msg) + + (* TODO *) + let logic_assign _assign _location state = + Traces.add_trans state (Msg "logic assign") + + (* Logic *) + let evaluate_predicate _ _ _ = Alarmset.Unknown + let reduce_by_predicate _ state _ _ = `Value state + + let top_query = `Value (Cvalue.V.top, None), Alarmset.all + + let extract_expr ~oracle:_ _context _state _expr = top_query + let extract_lval ~oracle:_ _context _state _lv _typ _locs = top_query + + let backward_location _state _lval _typ loc value = + `Value (loc, value) + + let enter_loop stmt state = + let state = Traces.add_trans state (Msg "enter_loop") in + let state = if not (Value_parameters.TracesUnrollLoop.get ()) + then Traces.add_loop stmt state + else { state with current = UnrollLoop(stmt,state.current) } in + state + + let incr_loop_counter _ state = + match state.current with + | Base _ -> assert false + | UnrollLoop(_,_) -> state + | OpenLoop(stmt,s,last,_,g,l) -> + let last = Graph.join last g in + let last = if Value_parameters.TracesUnifyLoop.get () then + let s',old_last = Stmt.Hashtbl.find state.all_loop_start stmt in + let last = Graph.join last old_last in + assert (Node.equal s s'); + Stmt.Hashtbl.add state.all_loop_start stmt (s,last); + last + else last + in + let current = OpenLoop(stmt,s,last,s,Graph.empty,l) in + let state = { state with current } in + (* Traces.add_trans state (Msg("incr_loop_counter")) *) + state + + let leave_loop stmt' state = + match state.current with + | Base _ -> assert false (* absurd: we are in at least a loop *) + | UnrollLoop(_,l) -> { state with current = l } + | OpenLoop(stmt,s,last,old_current_node,g,current) -> + assert (Stmt.equal stmt stmt'); + let state = { state with current } in + let last = if Value_parameters.TracesUnifyLoop.get () then + let s',old_last = Stmt.Hashtbl.find state.all_loop_start stmt in + let last = Graph.join last old_last in + assert (Node.equal s s'); + Stmt.Hashtbl.add state.all_loop_start stmt (s,last); + last + else last + in + let state = if Graph.is_empty last then state + else Traces.add_trans state (Loop(stmt,s,last)) in + let state = Traces.copy_edges s old_current_node g state in + Traces.add_trans state (Msg "leave_loop") + + + let enter_scope kind vars state = + match kind with + | Abstract_domain.Global -> + { state with globals = vars @ state.globals } + | Abstract_domain.Formal kf -> + if Kernel_function.equal kf (fst (Globals.entry_point ())) + then { state with main_formals = vars @ state.main_formals } + else state + | Abstract_domain.(Local kf | Result kf) -> + Traces.add_trans state (EnterScope (kf, vars)) + + let leave_scope kf vars state = + Traces.add_trans state (LeaveScope (kf, vars)) + + let reduce_further _state _expr _value = [] (*Nothing intelligent to suggest*) + + let output_dot filename state = + let out = open_out filename in + Value_parameters.feedback ~dkey:log_category "@[Output dot produced to %s.@]" filename; + (** *) + GraphDot.output_graph out (complete_graph (snd (Traces.get_current state))); + close_out out let post_analysis state = let return_stmt = Kernel_function.find_return (fst (Globals.entry_point ())) in @@ -1267,7 +1265,7 @@ module D = struct | _ -> assert false in let header fmt = Format.fprintf fmt "Trace domains:" in let body = Bottom.pretty Traces.pretty in - Value_parameters.printf ~dkey:Internal.log_category ~header " @[%a@]" body state; + Value_parameters.printf ~dkey:log_category ~header " @[%a@]" body state; if Value_parameters.TracesProject.get () || not (Value_parameters.TracesDot.is_default ()) then match state with diff --git a/src/plugins/value/utils/abstract.ml b/src/plugins/value/utils/abstract.ml index b7158ae6e3f..84c9df46890 100644 --- a/src/plugins/value/utils/abstract.ml +++ b/src/plugins/value/utils/abstract.ml @@ -72,13 +72,13 @@ end module Domain = struct module D = struct - type 'a t = (module Abstract_domain.Internal with type state = 'a) + type 'a t = (module Abstract_domain.S with type state = 'a) end include Structure.Shape (Structure.Key_Domain) (D) module type Internal = sig - include Abstract_domain.Internal + include Abstract_domain.S val structure: t structure end diff --git a/src/plugins/value/utils/abstract.mli b/src/plugins/value/utils/abstract.mli index 643159a7b00..5ea4d7e8531 100644 --- a/src/plugins/value/utils/abstract.mli +++ b/src/plugins/value/utils/abstract.mli @@ -98,10 +98,10 @@ end module Domain : sig include Structure.Shape with type 'a key = 'a Structure.Key_Domain.key - and type 'a data = (module Abstract_domain.Internal with type state = 'a) + and type 'a data = (module Abstract_domain.S with type state = 'a) module type Internal = sig - include Abstract_domain.Internal + include Abstract_domain.S val structure: t structure end -- GitLab