diff --git a/src/plugins/dive/build.ml b/src/plugins/dive/build.ml index 867ba64d01b9c47f97c7d8d801106101302d72e4..19d7f0d5832d2a752f1e02ab2fa90a9f30174607 100644 --- a/src/plugins/dive/build.ml +++ b/src/plugins/dive/build.ml @@ -175,6 +175,7 @@ module NodeRef = Datatype.Pair_with_collections (struct let module_name = "Build.NodeRef" end) module Graph = Imprecision_graph +module VertexTable = Datatype.Int.Hashtbl module NodeTable = FCHashtbl.Make (NodeRef) module FileTable = Datatype.String.Hashtbl module CallstackTable = Value_types.Callstack.Hashtbl @@ -183,6 +184,7 @@ module FunctionMap = Kernel_function.Map type t = { mutable graph: Graph.t; + mutable vertex_table: node VertexTable.t; mutable node_table: node NodeTable.t; mutable file_table: node FileTable.t; mutable callstack_table: node CallstackTable.t; @@ -190,20 +192,41 @@ type t = { mutable hidden_bases: BaseSet.t; mutable focus: bool FunctionMap.t; mutable roots: node list; + mutable graph_diff: graph_diff; } +let get_node context node_key = + VertexTable.find context.vertex_table node_key + +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; + context.graph_diff <- { + context.graph_diff with + added_nodes = node :: context.graph_diff.added_nodes + }; + node + +let _remove_node context node = + Graph.remove_node context.graph node; + VertexTable.remove context.vertex_table node.node_key; + 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 - Graph.create_node context.graph ~node_kind:Cluster ~node_locality + 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); - Graph.create_node context.graph ~node_kind:Cluster ~node_locality + add_node context ~node_kind:Cluster ~node_locality in match loc_callstack with | [] -> @@ -233,7 +256,7 @@ let build_node context callstack location lval = let node_locality = build_node_locality callstack node_kind in reference_node_locality context node_locality; let build_new_node _location = - Graph.create_node context.graph ~node_kind ~node_locality + add_node context ~node_kind ~node_locality in (* Compute the new location which might have changed after folding *) let location' = @@ -263,7 +286,7 @@ let build_lval context callstack kinstr lval = let build_alarm context callstack stmt alarm = let node_kind = Alarm (stmt,alarm) in let node_locality = build_node_locality callstack node_kind in - Graph.create_node context.graph ~node_kind ~node_locality + add_node context ~node_kind ~node_locality exception Too_many_deps of lval @@ -435,6 +458,7 @@ let create () = !Db.Value.compute (); { graph = Graph.create (); + vertex_table = VertexTable.create 13; node_table = NodeTable.create 13; file_table = FileTable.create 13; callstack_table = CallstackTable.create 13; @@ -442,15 +466,18 @@ let create () = hidden_bases = BaseSet.empty; focus = FunctionMap.empty; roots = []; + graph_diff = { added_nodes=[] ; removed_nodes=[] }; } let clear context = context.graph <- Graph.create (); + context.vertex_table <- VertexTable.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.roots <- []; + context.graph_diff <- { added_nodes=[] ; removed_nodes=[] } (* --- Accessors --- *) @@ -481,8 +508,7 @@ let should_auto_explore node = | Scalar _ | Composite _ | Alarm _ -> true | Scattered _ | Cluster -> false -let complete_in_depth ~depth context root = - context.roots <- root :: context.roots; +let explore ~depth context root = (* Breadth first search *) let queue : (node * int) Queue.t = Queue.create () in Queue.add (root,0) queue; @@ -502,6 +528,10 @@ let complete_in_depth ~depth context root = end; done +let complete_in_depth ~depth context root = + context.roots <- root :: context.roots; + explore ~depth context root + let add_var ?(depth=1) context varinfo = let callstack = [] in let node = build_var context callstack varinfo in @@ -519,3 +549,11 @@ let add_alarm ?(depth=1) context stmt alarm = let callstack = Callstack.init (Kernel_function.find_englobing_kf stmt) in let node = build_alarm context callstack stmt alarm in complete_in_depth ~depth context node + +let explore_from_vertex ~depth context node_key = + explore ~depth context (get_node context node_key) + +let take_last_differences context = + let diff = context.graph_diff in + context.graph_diff <- { added_nodes=[] ; removed_nodes=[] }; + diff diff --git a/src/plugins/dive/build.mli b/src/plugins/dive/build.mli index c710f021829c06b0cb4849e25cac5be4b765d6a5..961dec8856363dcafaa76fa59df6e7cb591e9f05 100644 --- a/src/plugins/dive/build.mli +++ b/src/plugins/dive/build.mli @@ -37,3 +37,5 @@ val unhide_base : t -> Cil_types.varinfo -> unit 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 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 acf759d97f84382df0fe6bd1fcb5796dd595f748..515bf05c6a45be70b228ed46f2e4b41964575fd8 100644 --- a/src/plugins/dive/graph_types.mli +++ b/src/plugins/dive/graph_types.mli @@ -57,3 +57,8 @@ type dependency = { dependency_kind : dependency_kind; mutable dependency_multiple : bool; } + +type graph_diff = { + added_nodes: node list; + removed_nodes: node list; +} diff --git a/src/plugins/dive/imprecision_graph.ml b/src/plugins/dive/imprecision_graph.ml index 81d0ecf36c0d86e802c442895f9f0d509d373c9f..167d850effde26c8f2dca77a8c1fbad51322d83c 100644 --- a/src/plugins/dive/imprecision_graph.ml +++ b/src/plugins/dive/imprecision_graph.ml @@ -62,6 +62,8 @@ let create_node ?node_values ~node_kind ~node_locality g = add_vertex g node; node +let remove_node = remove_vertex + let union_interval i1 i2 = { min = min i1.min i2.min ; max = max i1.max i2.max } @@ -195,18 +197,21 @@ let ouptput_to_dot out_channel g = in Dot.output_graph out_channel g -let to_json g = - let rec output_kinstr = function +module JsonPrinter = +struct + let output_kinstr = function | Cil_types.Kglobal -> Json.of_string "global" | Cil_types.Kstmt stmt -> Json.of_int stmt.Cil_types.sid - and output_callsite (kf,kinstr) = + + let output_callsite (kf,kinstr) = Json.of_fields [ ("fun", Json.of_string (Kernel_function.get_name kf)) ; ("instr", output_kinstr kinstr) ; ] - and output_callstack cs = + + let output_callstack cs = Json.of_list (List.map output_callsite cs) - in + let output_node_kind kind = let s = match kind with | Scalar _ -> "scalar" @@ -216,21 +221,24 @@ let to_json g = | Cluster -> "dummy" in Json.of_string s - and output_node_locality { loc_file ; loc_callstack } = + + let output_node_locality { loc_file ; loc_callstack } = let f1 = ("file", Json.of_string loc_file) in let fields = match loc_callstack with | [] -> [f1] - | cs -> [f1 ; ("fun", output_callstack cs)] + | cs -> [f1 ; ("callstack", output_callstack cs)] in Json.of_fields fields - and output_node_precision_grade grade = + + let output_node_precision_grade grade = let s = match grade with | Singleton -> "singleton" | Normal -> "normal" | Wide -> "wide" in Json.of_string s - and output_dep_kind kind = + + let output_dep_kind kind = let s = match kind with | Callee -> "callee" | Data -> "data" @@ -238,47 +246,96 @@ let to_json g = | Control -> "ctrl" in Json.of_string s - and output_float_interval interval = + + let output_float_interval interval = Json.of_fields [ ("min", Json.of_float interval.min) ; ("max", Json.of_float interval.max) ; ] - in + let output_node_values values = Json.of_fields [ - ("values", output_float_interval values.values_interval) ; + ("computed", output_float_interval values.values_interval) ; ("limits", output_float_interval values.values_limits) ; ("grade", output_node_precision_grade values.values_grade) ; ] - in - let output_node node acc = - if node.node_kind = Cluster then acc - else - let label = Pretty_utils.to_string Node_kind.pretty node.node_kind in - Json.of_fields ([ - ("id", Json.of_int node.node_key) ; - ("label", Json.of_string label) ; - ("kind", output_node_kind node.node_kind) ; - ("locality", output_node_locality node.node_locality) ; - ("explored", Json.of_bool node.node_deps_computed) - ] @ - match node.node_values with - | None -> [] - | Some node_values -> [("values", output_node_values node_values)] - ) :: acc - and output_dep (n1,dep,n2) acc = + + let output_node node = + let label = Pretty_utils.to_string Node_kind.pretty node.node_kind in + Json.of_fields ([ + ("id", Json.of_int node.node_key) ; + ("label", Json.of_string label) ; + ("kind", output_node_kind node.node_kind) ; + ("locality", output_node_locality node.node_locality) ; + ("explored", Json.of_bool node.node_deps_computed) + ] @ + match node.node_values with + | None -> [] + | Some node_values -> [("values", output_node_values node_values)] + ) + + let output_dep (n1,dep,n2) = Json.of_fields [ ("id", Json.of_int dep.dependency_key) ; ("src", Json.of_int n1.node_key) ; ("dst", Json.of_int n2.node_key) ; ("kind", output_dep_kind dep.dependency_kind) ; ("multiple", Json.of_bool dep.dependency_multiple) - ] :: acc - in - let nodes = Json.of_list (fold_vertex output_node g []) - and deps = Json.of_list (fold_edges_e output_dep g []) in - Json.of_fields [("nodes", nodes) ; ("deps", deps)] + ] + + let output_graph g = + let add_node node acc = + if node.node_kind = Cluster + then acc + else output_node node :: acc + and add_edge d acc = + output_dep d :: acc + in + Json.of_fields [ + ("nodes", Json.of_list (fold_vertex add_node g [])) ; + ("deps", Json.of_list (fold_edges_e add_edge g [])) + ] + + let output_diff g diff = + let add_node acc node = + if node.node_kind = Cluster + then acc + else output_node node :: acc + and add_dep d acc = + output_dep d :: acc + in + let added_nodes = + List.fold_left add_node [] diff.added_nodes + and added_deps = + let module Set = Set.Make (struct + type t = edge + let compare (_,d1,_) (_,d2,_) = d1.dependency_key - d2.dependency_key + end) + in + let collect_deps set node = + let set = fold_pred_e Set.add g node set in + let set = fold_succ_e Set.add g node set in + set + in + let set = List.fold_left collect_deps Set.empty diff.added_nodes in + Set.fold add_dep set [] + and removed_nodes = + List.map (fun node -> Json.of_int node.node_key) diff.removed_nodes + in + Json.of_fields [ + ("add", Json.of_fields [ + ("nodes", Json.of_list added_nodes) ; + ("deps", Json.of_list added_deps) + ]) ; + ("sub", Json.of_list removed_nodes)] +end let ouptput_to_json out_channel g = - let json = to_json g in + let json = JsonPrinter.output_graph g in Json.save_channel ~pretty:true out_channel json + +let to_json g = + JsonPrinter.output_graph g + +let diff_to_json g diff = + JsonPrinter.output_diff g diff diff --git a/src/plugins/dive/imprecision_graph.mli b/src/plugins/dive/imprecision_graph.mli index 17a5812872e99557bc9d7b219d4a25b4ee0eff44..3a88646f26df93877c7a140209b2530c1d64cfb1 100644 --- a/src/plugins/dive/imprecision_graph.mli +++ b/src/plugins/dive/imprecision_graph.mli @@ -31,11 +31,15 @@ val create_node : node_kind:node_kind -> node_locality:node_locality -> t -> node +val remove_node : t -> node -> unit + val update_node_values : node -> node_values -> unit val create_dependency : allow_folding:bool -> t -> node -> dependency_kind -> node -> unit -val to_json : t -> Json.t val ouptput_to_dot : out_channel -> t -> unit val ouptput_to_json : out_channel -> t -> unit + +val to_json : t -> Json.t +val diff_to_json : t -> graph_diff -> Json.t diff --git a/src/plugins/dive/server_interface.ml b/src/plugins/dive/server_interface.ml index 510d80955cb14477551e07c9337820001c9c01c9..b8038457f16050e2bd217e3f008ec80d86fb6dac 100644 --- a/src/plugins/dive/server_interface.ml +++ b/src/plugins/dive/server_interface.ml @@ -45,6 +45,13 @@ struct let to_json = Imprecision_graph.to_json end +module GraphDiff = +struct + type t = Imprecision_graph.t * Graph_types.graph_diff + let syntax = Syntax.any + let to_json = fun (g,d) -> Imprecision_graph.diff_to_json g d +end + module Variable = Data.Collection (struct type t = Cil_types.varinfo let syntax = Syntax.publish ~page ~name:"variable" @@ -112,11 +119,21 @@ let () = Request.register ~page let () = Request.register ~page ~kind:`EXEC ~name:"dive.add_var" ~descr:(Markdown.rm "Add a variable to the graph") - ~input:(module Variable) ~output:(module Graph) + ~input:(module Variable) ~output:(module GraphDiff) begin fun var -> let depth = Self.DepthLimit.get () in let g = get_graph () in Build.add_var ~depth g var; - Build.get_graph g + Build.get_graph g, Build.take_last_differences g end +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.explore" + ~descr:(Markdown.rm "Explore the graph starting from 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.explore_from_vertex ~depth g node_key; + Build.get_graph g, Build.take_last_differences g + end