diff --git a/src/plugins/dive/build.ml b/src/plugins/dive/build.ml index fa8bd1e797a12f25e05eda59fe46d2c0c9947551..5a5f88fc82d42bddef0c71ad0bd83df0a3ec5a93 100644 --- a/src/plugins/dive/build.ml +++ b/src/plugins/dive/build.ml @@ -137,10 +137,10 @@ let build_node_kind ~is_folded_base lval location = Scalar (vi, typ, offset') with Bit_utils.NoMatchingOffset | Ival.Not_Singleton_Int -> (* TODO: handle Bit_utils.NoMatchingOffset (strict aliasing ?) *) - Scattered (lval) + Scattered (lval, location) end | None -> - Scattered (lval) + Scattered (lval, location) let default_node_locality callstack = match callstack with @@ -171,23 +171,19 @@ let build_node_locality callstack node_kind = (* --- Context object --- *) module NodeRef = Datatype.Pair_with_collections - (Locations.Location) (Callstack) + (Node_kind) (Callstack) (struct let module_name = "Build.NodeRef" end) module Graph = Imprecision_graph -module VertexTable = Datatype.Int.Hashtbl +module Index = Datatype.Int.Hashtbl module NodeTable = FCHashtbl.Make (NodeRef) -module FileTable = Datatype.String.Hashtbl -module CallstackTable = Value_types.Callstack.Hashtbl module BaseSet = Cil_datatype.Varinfo.Set module FunctionMap = Kernel_function.Map type t = { mutable graph: Graph.t; - mutable vertex_table: node VertexTable.t; + mutable vertex_table: node Index.t; mutable node_table: node NodeTable.t; - mutable file_table: node FileTable.t; - mutable callstack_table: node CallstackTable.t; mutable unfolded_bases: BaseSet.t; mutable hidden_bases: BaseSet.t; mutable focus: bool FunctionMap.t; @@ -195,10 +191,18 @@ type t = { mutable graph_diff: graph_diff; } +let is_folded context vi = + not (BaseSet.mem vi context.unfolded_bases) + +let is_hidden context node_kind = + match Node_kind.get_base node_kind with + | Some vi when BaseSet.mem vi context.hidden_bases -> true + | _ -> false + let get_node context node_key = - VertexTable.find context.vertex_table node_key + Index.find context.vertex_table node_key -let update_node context node = +let update_node context node = if not (List.exists (Graph.Node.equal node) context.graph_diff.added_nodes) then @@ -208,47 +212,26 @@ let update_node context node = } let add_node context ~node_kind ~node_locality = - let node = Graph.create_node context.graph ~node_kind ~node_locality in - VertexTable.add context.vertex_table node.node_key node; - update_node context node; - node + let node_ref = (node_kind, node_locality.loc_callstack) in + let add_new _ = + let node = Graph.create_node context.graph ~node_kind ~node_locality in + node.node_hidden <- is_hidden context node.node_kind; + Index.add context.vertex_table node.node_key node; + update_node context node; + node + in + NodeTable.memo context.node_table node_ref add_new -let _remove_node context node = +let remove_node context node = + let node_ref = (node.node_kind, node.node_locality.loc_callstack) in Graph.remove_node context.graph node; - VertexTable.remove context.vertex_table node.node_key; + Index.remove context.vertex_table node.node_key; + NodeTable.remove context.node_table node_ref; context.graph_diff <- { context.graph_diff with removed_nodes = node :: context.graph_diff.removed_nodes } -let reference_node_locality context node_locality = - let { loc_file ; loc_callstack } = node_locality in - let rec add_file _ = - let node_locality = { node_locality with loc_callstack = [] } in - add_node context ~node_kind:Cluster ~node_locality - and add_callstack = function - | [] -> assert false - | _ :: t as loc_callstack -> - let node_locality = { node_locality with loc_callstack } in - if t <> [] then - ignore (CallstackTable.memo context.callstack_table t add_callstack); - add_node context ~node_kind:Cluster ~node_locality - in - match loc_callstack with - | [] -> - ignore (FileTable.memo context.file_table loc_file add_file) - | cs -> - ignore (CallstackTable.memo context.callstack_table cs add_callstack) - - -let is_folded context vi = - not (BaseSet.mem vi context.unfolded_bases) - -let is_hidden context node_kind = - match Node_kind.get_base node_kind with - | Some vi when BaseSet.mem vi context.hidden_bases -> true - | _ -> false - (* --- Graph building --- *) @@ -256,24 +239,8 @@ let is_hidden context node_kind = let build_node context callstack location lval = let is_folded_base = is_folded context in let node_kind = build_node_kind ~is_folded_base lval location in - if is_hidden context node_kind then - None - else begin - let node_locality = build_node_locality callstack node_kind in - reference_node_locality context node_locality; - let build_new_node _location = - add_node context ~node_kind ~node_locality - in - (* Compute the new location which might have changed after folding *) - let location' = - match Node_kind.to_location node_kind with - | None -> location - | Some location' -> location' - in - let ref = (location', node_locality.loc_callstack) in - let node = NodeTable.memo context.node_table ref build_new_node in - Some node - end + let node_locality = build_node_locality callstack node_kind in + add_node context ~node_kind ~node_locality let build_var context callstack varinfo = let location = Locations.loc_of_varinfo varinfo @@ -283,11 +250,9 @@ let build_var context callstack varinfo = let build_lval context callstack kinstr lval = (* If possible, refine the lval to a non-symbolic one *) let location = !Db.Value.lval_to_loc kinstr lval in - match build_node context callstack location lval with - | None -> None - | Some node -> - update_node_values node kinstr lval; - Some node + let node = build_node context callstack location lval in + update_node_values node kinstr lval; + node let build_alarm context callstack stmt alarm = let node_kind = Alarm (stmt,alarm) in @@ -437,11 +402,9 @@ let build_node_deps context node = build_exp_deps callstack stmt kind e2 and build_lval_deps callstack stmt kind lval = - match build_lval context callstack (Kstmt stmt) lval with - | None -> () - | Some dst -> - let allow_folding = true in - Graph.create_dependency ~allow_folding context.graph dst kind node + let dst = build_lval context callstack (Kstmt stmt) lval in + let allow_folding = true in + Graph.create_dependency ~allow_folding context.graph dst kind node in update_node context node; @@ -453,7 +416,7 @@ let build_node_deps context node = | Composite (vi) -> build_writes_deps callstack (Cil_types.Var vi, Cil_types.NoOffset); if vi.vformal then build_arg_deps callstack vi - | Scattered (_lval) -> () (* TODO: implements *) + | Scattered (_lval, _location) -> () (* TODO: implements *) | Alarm (stmt,alarm) -> build_alarm_deps callstack stmt alarm | Cluster -> () @@ -465,10 +428,8 @@ let create () = !Db.Value.compute (); { graph = Graph.create (); - vertex_table = VertexTable.create 13; + vertex_table = Index.create 13; node_table = NodeTable.create 13; - file_table = FileTable.create 13; - callstack_table = CallstackTable.create 13; unfolded_bases = BaseSet.empty; hidden_bases = BaseSet.empty; focus = FunctionMap.empty; @@ -478,10 +439,8 @@ let create () = let clear context = context.graph <- Graph.create (); - context.vertex_table <- VertexTable.create 13; + context.vertex_table <- Index.create 13; context.node_table <- NodeTable.create 13; - context.file_table <- FileTable.create 13; - context.callstack_table <- CallstackTable.create 13; context.focus <- FunctionMap.empty; context.roots <- []; context.graph_diff <- { added_nodes=[] ; removed_nodes=[] } @@ -510,22 +469,27 @@ let hide_base context vi = let unhide_base context vi = context.hidden_bases <- BaseSet.add vi context.hidden_bases -let should_auto_explore node = - match node.node_kind with - | Scalar _ | Composite _ | Alarm _ -> true - | Scattered _ | Cluster -> false - let explore ~depth context root = + let should_auto_explore node = + let is_root = Graph.Node.equal node root (* the root is always explored *) + and is_intersting_kind = match node.node_kind with + | Scalar _ | Composite _ | Alarm _ -> true + | Scattered _ | Cluster -> false + in + is_root || (not node.node_hidden && is_intersting_kind) + in (* Breadth first search *) let queue : (node * int) Queue.t = Queue.create () in Queue.add (root,0) queue; while not (Queue.is_empty queue) do let (n,d) = Queue.take queue in if d < depth then begin - if not (n.node_deps_computed) && should_auto_explore n then begin + if not (n.node_deps_computed) && should_auto_explore n then + begin begin try build_node_deps context n; with Too_many_deps lval -> + (* TODO: give a mean to explore more dependencies *) Self.warning "Too many dependencies for %a ; throwing them out" Cil_printer.pp_lval lval; end; @@ -542,7 +506,7 @@ let complete_in_depth ~depth context root = let add_var ?(depth=1) context varinfo = let callstack = [] in let node = build_var context callstack varinfo in - Extlib.may (complete_in_depth ~depth context) node + complete_in_depth ~depth context node let add_lval ?(depth=1) context kinstr lval = let callstack = match kinstr with @@ -550,7 +514,7 @@ let add_lval ?(depth=1) context kinstr lval = | Kstmt stmt -> Callstack.init (Kernel_function.find_englobing_kf stmt) in let node = build_lval context callstack kinstr lval in - Extlib.may (complete_in_depth ~depth context) node + complete_in_depth ~depth context node let add_alarm ?(depth=1) context stmt alarm = let callstack = Callstack.init (Kernel_function.find_englobing_kf stmt) in @@ -560,7 +524,36 @@ let add_alarm ?(depth=1) context stmt alarm = let explore_from_vertex ~depth context node_key = explore ~depth context (get_node context node_key) +let show ?(depth=1) context node_key = + let node = get_node context node_key in + node.node_hidden <- false; + explore ~depth context node + +let hide context node_key = + let node = get_node context node_key in + if not node.node_hidden then + begin + let g = get_graph context in + (* Set the node as hidden *) + node.node_hidden <- true; + (* Remove incomming edges *) + let incomming_edges = Graph.pred_e g node in + List.iter (Graph.remove_dependency g) incomming_edges; + (* Remove disconnected vertices *) + let disconnected_nodes = Graph.find_independant_nodes g context.roots in + List.iter (remove_node context) disconnected_nodes; + (* Dependencies are not there anymore *) + node.node_deps_computed <- false; + (* Notify node update *) + update_node context node + end + let take_last_differences context = + let pp_node fmt n = Format.pp_print_int fmt n.node_key in + let pp_node_list = Pretty_utils.pp_list ~sep:",@, " pp_node in let diff = context.graph_diff in + Self.debug ~dkey "added: %a,@, subbed: %a" + pp_node_list diff.added_nodes + pp_node_list diff.removed_nodes; context.graph_diff <- { added_nodes=[] ; removed_nodes=[] }; diff diff --git a/src/plugins/dive/build.mli b/src/plugins/dive/build.mli index 961dec8856363dcafaa76fa59df6e7cb591e9f05..e3e345a3e8209bf634dbf0dc076e2e82b1f4d165 100644 --- a/src/plugins/dive/build.mli +++ b/src/plugins/dive/build.mli @@ -38,4 +38,8 @@ val add_lval : ?depth:int -> t -> Cil_types.kinstr -> Cil_types.lval -> unit val add_var : ?depth:int -> t -> Cil_types.varinfo -> unit val add_alarm : ?depth:int -> t -> Cil_types.stmt -> Alarms.alarm -> unit val explore_from_vertex : depth:int -> t -> int -> unit + +val show : ?depth:int -> t -> int -> unit +val hide : t -> int -> unit + val take_last_differences : t -> Graph_types.graph_diff diff --git a/src/plugins/dive/graph_types.mli b/src/plugins/dive/graph_types.mli index 515bf05c6a45be70b228ed46f2e4b41964575fd8..e8f4943518996149c7bb6a0118e7b0565876892d 100644 --- a/src/plugins/dive/graph_types.mli +++ b/src/plugins/dive/graph_types.mli @@ -23,7 +23,7 @@ type node_kind = | Scalar of Cil_types.varinfo * Cil_types.typ * Cil_types.offset | Composite of Cil_types.varinfo - | Scattered of Cil_types.lval + | Scattered of Cil_types.lval * Locations.location | Alarm of Cil_types.stmt * Alarms.alarm | Cluster (* Dummy node, hack for Ocamlgraph Graphviz Dot module *) @@ -46,6 +46,7 @@ type node = { node_key : int; node_kind : node_kind; node_locality : node_locality; + mutable node_hidden : bool; mutable node_values : node_values option; mutable node_deps_computed : bool; } diff --git a/src/plugins/dive/imprecision_graph.ml b/src/plugins/dive/imprecision_graph.ml index 6fee241289eb5e78ebb491057196e26059a8156d..4816eb263b0d48e782f29e9ddab03ab2df0b5f04 100644 --- a/src/plugins/dive/imprecision_graph.ml +++ b/src/plugins/dive/imprecision_graph.ml @@ -54,6 +54,7 @@ let create_node ?node_values ~node_kind ~node_locality g = node_key = !next_key; node_kind; node_locality; + node_hidden = false; node_values; node_deps_computed = false; } @@ -112,9 +113,59 @@ let create_dependency ~allow_folding g v1 dependency_kind v2 = incr next_key; add_edge_e g (v1,e,v2) +let remove_dependency g edge = + remove_edge_e g edge + + +let find_independant_nodes g roots = + let module Dfs = Graph.Traverse.Dfs (struct + include G + let iter_succ = G.iter_pred + let fold_succ = G.fold_pred + end) + in + let module Table = Hashtbl.Make (Node) in + let table = Table.create 13 in + List.iter (Dfs.prefix_component (fun n -> Table.add table n true) g) roots; + let keep_node n = + Table.mem table n || n.node_kind = Cluster + in + fold_vertex (fun n acc -> if keep_node n then acc else n :: acc) g [] + + +let add_dummy_nodes g = + let module FileTable = Datatype.String.Hashtbl in + let module CallstackTable = Value_types.Callstack.Hashtbl in + let file_table = FileTable.create 13 in + let callstack_table = CallstackTable.create 13 in + let g = copy g in + + let create_locality_vertex { node_locality } = + let { loc_file ; loc_callstack } = node_locality in + let rec add_file _ = + let node_locality = { node_locality with loc_callstack = [] } in + create_node g ~node_kind:Cluster ~node_locality + and add_callstack = function + | [] -> assert false + | _ :: t as loc_callstack -> + let node_locality = { node_locality with loc_callstack } in + if t <> [] then + ignore (CallstackTable.memo callstack_table t add_callstack); + create_node g ~node_kind:Cluster ~node_locality + in + match loc_callstack with + | [] -> + ignore (FileTable.memo file_table loc_file add_file) + | cs -> + ignore (CallstackTable.memo callstack_table cs add_callstack) + in + iter_vertex create_locality_vertex g; + g + let ouptput_to_dot out_channel g = let open Graph.Graphviz.DotAttributes in + let g = add_dummy_nodes g in let build_label s = `HtmlLabel (Extlib.html_escape s) in diff --git a/src/plugins/dive/imprecision_graph.mli b/src/plugins/dive/imprecision_graph.mli index 7e1162beb32be99d45313d65ff8c0928be5d7433..890cee867a98922eb840d1927623cd2a020ea6f7 100644 --- a/src/plugins/dive/imprecision_graph.mli +++ b/src/plugins/dive/imprecision_graph.mli @@ -44,6 +44,10 @@ val update_node_values : node -> node_values -> unit val create_dependency : allow_folding:bool -> t -> node -> dependency_kind -> node -> unit +val remove_dependency : t -> node * dependency * node -> unit + +val find_independant_nodes : t -> node list -> node list + val ouptput_to_dot : out_channel -> t -> unit val ouptput_to_json : out_channel -> t -> unit diff --git a/src/plugins/dive/node_kind.ml b/src/plugins/dive/node_kind.ml index 73e3ec50a30b3da17a0f6d02bc8d178b3402c767..396119c63e5ccf6b3a782ffbafe49531f5c646de 100644 --- a/src/plugins/dive/node_kind.ml +++ b/src/plugins/dive/node_kind.ml @@ -22,6 +22,70 @@ open Graph_types +module DatatypeInput = +struct + include Datatype.Undefined + + type t = node_kind + + let (<?>) c (cmp,x,y) = + if c = 0 + then cmp x y + else c + + let name = "Node_kind" + + let reprs = [ Cluster ] + + let compare k1 k2 = + let open Cil_datatype in + match k1, k2 with + | Cluster, Cluster -> 0 + | Cluster, _ -> 1 + | _, Cluster -> -1 + | Scalar (vi1, _, offset1), Scalar (vi2, _, offset2) -> + Varinfo.compare vi1 vi2 <?> (OffsetStructEq.compare, offset1, offset2) + | Scalar _, _ -> 1 + | _, Scalar _ -> -1 + | Composite vi1, Composite vi2 -> Varinfo.compare vi1 vi2 + | Composite _, _ -> 1 + | _, Composite _ -> -1 + | Scattered (lv1,loc1), Scattered (lv2,loc2) -> + LvalStructEq.compare lv1 lv2 <?> (Locations.Location.compare, loc1, loc2) + | Scattered _, _ -> 1 + | _, Scattered _ -> -1 + | Alarm (stmt1, alarm1), Alarm (stmt2, alarm2) -> + Stmt.compare stmt1 stmt2 <?> (Alarms.compare, alarm1, alarm2) + + let equal k1 k2 = + let open Cil_datatype in + match k1, k2 with + | Cluster, Cluster -> true + | Scalar (vi1, _, offset1), Scalar (vi2, _, offset2) -> + Varinfo.equal vi1 vi2 && OffsetStructEq.equal offset1 offset2 + | Composite vi1, Composite vi2 -> Varinfo.equal vi1 vi2 + | Scattered (lv1, loc1), Scattered (lv2, loc2) -> + LvalStructEq.equal lv1 lv2 && Locations.Location.equal loc1 loc2 + | Alarm (stmt1, alarm1), Alarm (stmt2, alarm2) -> + Stmt.equal stmt1 stmt2 && Alarms.equal alarm1 alarm2 + | _ -> false + + let hash k = + let open Cil_datatype in + match k with + | Cluster -> Hashtbl.hash 0 + | Scalar (vi, _, offset) -> + Hashtbl.hash (1, Varinfo.hash vi, OffsetStructEq.hash offset) + | Composite vi -> Hashtbl.hash (2, Varinfo.hash vi) + | Scattered (lv, loc) -> + Hashtbl.hash (3, LvalStructEq.hash lv, Locations.Location.hash loc) + | Alarm (stmt, alarm) -> + Hashtbl.hash (4, Stmt.hash stmt, Alarms.hash alarm) +end + +include Datatype.Make (DatatypeInput) + + let get_base = function | Scalar (vi,_,_) | Composite (vi) -> Some vi | Scattered _ | Alarm _ | Cluster -> None @@ -32,12 +96,14 @@ let to_location = function Some (Locations.loc_of_typoffset base typ offset) | Composite (vi) -> Some (Locations.loc_of_varinfo vi) - | Scattered _ | Alarm _ | Cluster -> None + | Scattered (_,loc) -> + Some loc + | Alarm _ | Cluster -> None let to_lval = function | Scalar (vi,_typ,offset) -> Some (Cil_types.Var vi, offset) | Composite (vi) -> Some (Cil_types.Var vi, Cil_types.NoOffset) - | Scattered (lval) -> Some lval + | Scattered (lval,_) -> Some lval | Alarm (_,_) | Cluster -> None let pretty fmt = function diff --git a/src/plugins/dive/node_kind.mli b/src/plugins/dive/node_kind.mli new file mode 100644 index 0000000000000000000000000000000000000000..923348cf197c809bcd41688ca4b970fe71e54014 --- /dev/null +++ b/src/plugins/dive/node_kind.mli @@ -0,0 +1,29 @@ +(**************************************************************************) +(* *) +(* This file is part of the Frama-C plug-in `Dive'. *) +(* *) +(* Copyright (C) 2007-2019 *) +(* 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). *) +(* *) +(**************************************************************************) + +open Graph_types + +include Datatype.S with type t = Graph_types.node_kind + +val get_base : t -> Cil_types.varinfo option +val to_location : t -> Locations.location option +val to_lval : t -> Cil_types.lval option diff --git a/src/plugins/dive/server_interface.ml b/src/plugins/dive/server_interface.ml index b8038457f16050e2bd217e3f008ec80d86fb6dac..9c4a175c2b11d884170a806da9459d852255f49e 100644 --- a/src/plugins/dive/server_interface.ml +++ b/src/plugins/dive/server_interface.ml @@ -137,3 +137,24 @@ let () = Request.register ~page Build.explore_from_vertex ~depth g node_key; Build.get_graph g, Build.take_last_differences g end + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.show" + ~descr:(Markdown.rm "Show the dependencies of an existing vertex") + ~input:(module Data.Jint) ~output:(module GraphDiff) + begin fun node_key -> + let depth = Self.DepthLimit.get () in + let g = get_graph () in + Build.show ~depth g node_key; + Build.get_graph g, Build.take_last_differences g + end + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.hide" + ~descr:(Markdown.rm "Hide the dependencies of an existing vertex") + ~input:(module Data.Jint) ~output:(module GraphDiff) + begin fun node_key -> + let g = get_graph () in + Build.hide g node_key; + Build.get_graph g, Build.take_last_differences g + end \ No newline at end of file diff --git a/src/plugins/dive/simple_deps.ml b/src/plugins/dive/simple_deps.ml new file mode 100644 index 0000000000000000000000000000000000000000..8baa89672fc3b8e173529be322bedb121200facf --- /dev/null +++ b/src/plugins/dive/simple_deps.ml @@ -0,0 +1,65 @@ +(**************************************************************************) +(* *) +(* This file is part of the Frama-C plug-in `Dive'. *) +(* *) +(* Copyright (C) 2018 *) +(* 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). *) +(* *) +(**************************************************************************) + +open Cil_types + +class find_write target_vi = object (self) + inherit Visitor.frama_c_inplace + + val mutable res = ([] : stmt list) + + method add_current_stmt () = + res <- Extlib.the self#current_stmt :: res + + method! vinst i = + begin match i with + | Call (None,_,_,_) | Asm _ | Skip _ | Code_annot _ -> () (* No effect *) + | Local_init(vi,_,_) -> + if Cil_datatype.Varinfo.equal vi target_vi then + self#add_current_stmt () + | Call (Some dest,_,_,_) + | Set (dest,_,_) -> + match dest with + | Var vi, _ when vi = target_vi -> self#add_current_stmt () + | _ -> () + end; + Cil.SkipChildren + + method result = res +end + +let find_assignments kf vi = + let fundec = Kernel_function.get_definition kf + and vis = new find_write vi in + ignore (Visitor.visitFramacFunction (vis :> Visitor.frama_c_visitor) fundec); + vis#result + +let compute lval = + match lval with + | Var vi, NoOffset when not vi.vaddrof -> + Self.feedback "%a %B" Cil_printer.pp_varinfo vi vi.vaddrof; + begin try + let kf = Kernel_function.find_defining_kf vi in + Extlib.opt_map (fun kf -> find_assignments kf vi) kf + with Not_found -> None + end + | _ -> None