From 39984416934ae5ea30890da6a8cc90a020aa85d7 Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Tue, 9 Apr 2019 18:32:18 +0200 Subject: [PATCH] [Dive] Compute dependencies per callstack --- src/plugins/dive/Makefile.in | 2 +- src/plugins/dive/build.ml | 360 ++++++++++-------- src/plugins/dive/callstack.ml | 50 +++ src/plugins/dive/callstack.mli | 36 ++ src/plugins/dive/graph_types.mli | 4 +- src/plugins/dive/imprecision_graph.ml | 58 ++- src/plugins/dive/node_kind.ml | 8 +- src/plugins/dive/tests/dive/const.i | 2 +- .../dive/tests/dive/oracle/assigned_param.dot | 26 +- src/plugins/dive/tests/dive/oracle/const.dot | 35 +- .../dive/tests/dive/oracle/pointed_param.dot | 36 ++ .../dive/oracle/pointed_param.res.oracle | 6 + .../tests/dive/oracle/unfocused_callers.dot | 38 ++ .../dive/oracle/unfocused_callers.res.oracle | 14 + .../dive/tests/dive/oracle/various.dot | 83 ++-- src/plugins/dive/tests/dive/pointed_param.i | 25 ++ .../dive/tests/dive/unfocused_callers.i | 19 + src/plugins/dive/tests/test_config | 2 +- 18 files changed, 556 insertions(+), 248 deletions(-) create mode 100644 src/plugins/dive/callstack.ml create mode 100644 src/plugins/dive/callstack.mli create mode 100644 src/plugins/dive/tests/dive/oracle/pointed_param.dot create mode 100644 src/plugins/dive/tests/dive/oracle/pointed_param.res.oracle create mode 100644 src/plugins/dive/tests/dive/oracle/unfocused_callers.dot create mode 100644 src/plugins/dive/tests/dive/oracle/unfocused_callers.res.oracle create mode 100644 src/plugins/dive/tests/dive/pointed_param.i create mode 100644 src/plugins/dive/tests/dive/unfocused_callers.i diff --git a/src/plugins/dive/Makefile.in b/src/plugins/dive/Makefile.in index 56431372bfd..f04bc8ed084 100644 --- a/src/plugins/dive/Makefile.in +++ b/src/plugins/dive/Makefile.in @@ -38,7 +38,7 @@ endif PLUGIN_DIR ?=. PLUGIN_NAME := Dive -PLUGIN_CMO := self node_kind imprecision_graph build main +PLUGIN_CMO := callstack self node_kind imprecision_graph build main PLUGIN_CMI:= graph_types PLUGIN_DEPENDENCIES:= Eva Studia PLUGIN_HAS_MLI:= yes diff --git a/src/plugins/dive/build.ml b/src/plugins/dive/build.ml index f8a9eee5dd8..2a6763f3fe7 100644 --- a/src/plugins/dive/build.ml +++ b/src/plugins/dive/build.ml @@ -114,55 +114,76 @@ let build_node_kind ~is_folded_base lval location = and matching = Bit_utils.MatchType typ in let offset', _ = Bit_utils.find_offset vi.vtype ~offset matching in Scalar (vi, typ, offset') - with Ival.Not_Singleton_Int -> + with Bit_utils.NoMatchingOffset | Ival.Not_Singleton_Int -> + (* TODO: handle Bit_utils.NoMatchingOffset (strict aliasing ?) *) Scattered (lval) end | None -> Scattered (lval) -let stmt_to_node_locality stmt = - let kf = Kernel_function.find_englobing_kf stmt in - let file = get_loc_filename (Kernel_function.get_location kf) in - { loc_file = file ; loc_function = Some kf } +let default_node_locality callstack = + match callstack with + | [] -> + (* The empty callstack can be used for global lvalues *) + { loc_file="" ; loc_callstack=[] } + | (kf,_kinstr) :: _ -> + let loc_file = get_loc_filename (Kernel_function.get_location kf) in + { loc_file ; loc_callstack=callstack } -let build_node_locality kinstr kind = - match Node_kind.get_base kind with +let build_node_locality callstack node_kind = + match Node_kind.get_base node_kind with + | None -> default_node_locality callstack | Some vi -> - let loc_function = Kernel_function.find_defining_kf vi in - (* Use the location of the defining function if it exists, as some - temporary variables do not have proper location *) - let def_vi = Extlib.may_map Kernel_function.get_vi ~dft:vi loc_function in - let loc_file = get_loc_filename def_vi.vdecl in - { loc_file ; loc_function } - | None -> - match kinstr with - | Kglobal -> assert false - | Kstmt stmt -> stmt_to_node_locality stmt + match Kernel_function.find_defining_kf vi with + | Some kf -> + let callstack = + try + Callstack.pop_downto kf callstack + with Failure _ -> + Callstack.init kf (* TODO: complete callstack *) + in + default_node_locality callstack + | None -> + { loc_file = get_loc_filename vi.vdecl ; loc_callstack = [] } (* --- Context object --- *) +module NodeRef = Datatype.Pair_with_collections + (Locations.Location) (Callstack) + (struct let module_name = "Build.NodeRef" end) + module Graph = Imprecision_graph -module NodeTable = FCHashtbl.Make (Locations.Location) +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 = { graph: Graph.t; node_table: node NodeTable.t; file_table: node FileTable.t; + callstack_table: node CallstackTable.t; mutable unfolded_bases: BaseSet.t; mutable hidden_bases: BaseSet.t; + mutable focus: bool FunctionMap.t; mutable roots: node list; } -let reference_file context loc_file = - if not (FileTable.mem context.file_table loc_file) then begin - let node_kind = File - and node_locality = { loc_file ; loc_function = None } in - let node = Graph.create_node context.graph ~node_kind ~node_locality in - FileTable.add context.file_table loc_file node - end +let reference_node_locality context node_locality = + let { loc_file ; loc_callstack } = node_locality in + let add_file _ = + let node_locality = { node_locality with loc_callstack = [] } in + Graph.create_node context.graph ~node_kind:Cluster ~node_locality + and add_callstack _ = + Graph.create_node context.graph ~node_kind:Cluster ~node_locality + in + ignore (FileTable.memo context.file_table loc_file add_file); + match loc_callstack with + | [] -> () + | cs -> + ignore (CallstackTable.memo context.callstack_table cs add_callstack) let is_folded context vi = not (BaseSet.mem vi context.unfolded_bases) @@ -176,15 +197,15 @@ let is_hidden context node_kind = (* --- Graph building --- *) (* Update or create a node *) -let build_node context kinstr location lval = +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 kinstr node_kind in + let node_locality = build_node_locality callstack node_kind in + reference_node_locality context node_locality; let build_new_node _location = - reference_file context node_locality.loc_file; Graph.create_node context.graph ~node_kind ~node_locality in (* Compute the new location which might have changed after folding *) @@ -193,19 +214,20 @@ let build_node context kinstr location lval = | None -> location | Some location' -> location' in - let node = NodeTable.memo context.node_table location' build_new_node in + let ref = (location', callstack) in + let node = NodeTable.memo context.node_table ref build_new_node in Some node end -let build_var context varinfo = +let build_var context callstack varinfo = let location = Locations.loc_of_varinfo varinfo and lval = Var varinfo, NoOffset in - build_node context Kglobal location lval + build_node context callstack location lval -let build_lval context kinstr lval = +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 kinstr location lval with + match build_node context callstack location lval with | None -> None | Some node -> (* Update the precision information *) @@ -213,138 +235,150 @@ let build_lval context kinstr lval = Graph.update_node_precision node node_precision; Some node -let build_alarm context stmt alarm = - let node_kind = Alarm (stmt,alarm) - and node_locality = stmt_to_node_locality stmt +let build_alarm context callstack stmt alarm = + let node_kind = Alarm (stmt,alarm) in + let node_locality = build_node_locality callstack node_kind and node_precision = Critical in Graph.create_node context.graph ~node_precision ~node_kind ~node_locality + exception Too_many_deps of lval -let rec build_node_deps context node = +let build_node_deps context node = + let rec build_writes_deps callstack lval = + Self.debug ~dkey "computing deps for %a" Cil_printer.pp_lval lval; + let zone = !Db.Value.lval_to_zone Kglobal lval in + let writes = Studia.Writes.compute zone + and add_deps (stmt,effects) = + match stmt.skind with + | Instr instr when effects.Studia.Writes.direct -> + build_instr_deps callstack stmt instr + | _ -> () + in + let count = List.length writes in + Self.debug ~dkey "%d found" count; + if count > 20 then + raise (Too_many_deps lval) + else + List.iter add_deps writes + + and build_arg_deps callstack vi = + assert vi.vformal; + let kf = Extlib.the (Kernel_function.find_defining_kf vi) in + let pos = Kernel_function.get_formal_position vi kf in + let callsites = + match Callstack.pop callstack with + | Some (kf',stmt,callstack) -> + assert (Kernel_function.equal kf' kf); + [(stmt,callstack)] + | None -> + let callsites = Kernel_function.find_syntactic_callsites kf in + List.map (fun (kf,stmt) -> (stmt,Callstack.init kf)) callsites + and add_deps (stmt,callstack) = + match stmt.skind with + | Instr (Call (_,_,args,_)) + | Instr (Local_init (_, ConsInit (_, args, _), _)) -> + let exp = List.nth args pos in + build_exp_deps callstack stmt Data exp + | _ -> + assert false (* Callsites can only be Call or ConsInit *) + in + List.iter add_deps callsites + + and build_return_deps callstack stmt args kf = + match Kernel_function.find_return kf with + | {skind = Return (Some {enode = Lval lval_res},_)} -> + let callstack = Callstack.push (kf,stmt) callstack in + build_lval_deps callstack stmt Data lval_res + | _ -> assert false (* Cil invariant *) + | exception Kernel_function.No_Statement -> + (* the function is only a prototype *) + (* TODO: read assigns instead *) + List.iter (build_exp_deps callstack stmt Data) args + + and build_call_deps callstack stmt callee args = + begin match callee.enode with + | Lval (Var _vi, _offset) -> () + | Lval (Mem exp, _offset) -> + build_exp_deps callstack stmt Callee exp + | _ -> + Self.warning "Cannot compute all callee dependencies for %a" + Cil_printer.pp_stmt stmt; + end; + let kinstr = Kstmt stmt in + let _,set = !Db.Value.expr_to_kernel_function kinstr ~deps:None callee in + Kernel_function.Hptset.iter (build_return_deps callstack stmt args) set + + and build_alarm_deps callstack stmt alarm = + let for_exp e = + build_exp_deps callstack stmt Data e + in + let open Alarms in + match alarm with + | Division_by_zero e | Index_out_of_bound (e, _) | Invalid_shift (e,_) + | Overflow (_,e,_,_) | Float_to_int (e,_,_) | Is_nan_or_infinite (e,_) + | Is_nan (e,_) | Function_pointer (e,_) -> for_exp e + | Pointer_comparison (opt_e1,e2) -> Extlib.may for_exp opt_e1; for_exp e2 + | Differing_blocks (e1,e2) -> for_exp e1; for_exp e2 + | Memory_access _ | Not_separated _ | Overlap _ + | Uninitialized _ | Dangling _ | Uninitialized_union _ -> () + (* TODO: adress depencies inside lval *) + | Invalid_bool lv -> build_lval_deps callstack stmt Data lv + + and build_instr_deps callstack stmt = function + | Set (_, exp, _) -> + build_exp_deps callstack stmt Data exp + | Call (_, callee, args, _) -> build_call_deps callstack stmt callee args + | Local_init (dest, ConsInit (f, args, k), loc) -> + let as_func _dest callee args _loc = + build_call_deps callstack stmt callee args + in + Cil.treat_constructor_as_func as_func dest f args k loc + | Local_init (_, AssignInit init, _) -> + build_init_deps callstack stmt init + | Asm _ | Skip _ | Code_annot _ -> () (* Cases not returned by Studia *) + + and build_init_deps callstack stmt = function + | SingleInit exp -> + build_exp_deps callstack stmt Data exp + | CompoundInit (_typ, initl) -> + List.iter (fun (_off,init) -> build_init_deps callstack stmt init) initl + + and build_exp_deps callstack stmt kind exp = + match exp.enode with + | Const _ + | SizeOf _ | SizeOfE _ | SizeOfStr _ + | AlignOf _ | AlignOfE _ + | AddrOf _ | StartOf _ -> () + | Lval lval -> + build_lval_deps callstack stmt kind lval + | UnOp (_,e,_) | CastE (_,e) | Info (e,_) -> + build_exp_deps callstack stmt kind e + | BinOp (_,e1,e2,_) -> + build_exp_deps callstack stmt kind e1; + 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 + + in + let callstack = node.node_locality.loc_callstack in match node.node_kind with | Scalar (vi,_typ,offset) -> - build_writes_deps context node (Cil_types.Var vi, offset); - if vi.vformal then build_arg_deps context node vi + build_writes_deps callstack (Cil_types.Var vi, offset); + if vi.vformal then build_arg_deps callstack vi | Composite (vi) -> - build_writes_deps context node (Cil_types.Var vi, Cil_types.NoOffset); - if vi.vformal then build_arg_deps context node 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 *) | Alarm (stmt,alarm) -> - build_alarm_deps context node stmt alarm - | File -> () - -and build_writes_deps context src lval = - Self.debug ~dkey "computing deps for %a" Cil_printer.pp_lval lval; - let zone = !Db.Value.lval_to_zone Kglobal lval in - let writes = Studia.Writes.compute zone - and add_deps (stmt,effects) = - match stmt.skind with - | Instr instr when effects.Studia.Writes.direct -> - build_instr_deps context src stmt instr - | _ -> () - in - let count = List.length writes in - Self.debug ~dkey "%d found" count; - if count > 20 then - raise (Too_many_deps lval) - else - List.iter add_deps writes - -and build_arg_deps context src vi = - assert vi.vformal; - let kf = Extlib.the (Kernel_function.find_defining_kf vi) in - let pos = Kernel_function.get_formal_position vi kf in - let callsites = Kernel_function.find_syntactic_callsites kf - and add_deps (_caller_kf, stmt) = - match stmt.skind with - | Instr (Call (_,_,args,_)) - | Instr (Local_init (_, ConsInit (_, args, _), _)) -> - let exp = List.nth args pos in - build_exp_deps context src stmt Data exp - | _ -> - assert false (* Callsites can only be Call or ConsInit *) - in - List.iter add_deps callsites - -and build_return_deps context src stmt args kf = - match Kernel_function.find_return kf with - | {skind = Return (Some {enode = Lval lval_res},_)} -> - build_lval_deps context src stmt Data lval_res - | _ -> assert false (* Cil invariant *) - | exception Kernel_function.No_Statement -> - (* the function is only a prototype *) - (* TODO: read assigns instead *) - List.iter (build_exp_deps context src stmt Data) args - -and build_call_deps context src stmt callee args = - begin match callee.enode with - | Lval (Var _vi, _offset) -> () - | Lval (Mem exp, _offset) -> - build_exp_deps context src stmt Callee exp - | _ -> - Self.warning "Cannot compute all callee dependencies for %a" - Cil_printer.pp_stmt stmt; - end; - let kinstr = Kstmt stmt in - let _,set = !Db.Value.expr_to_kernel_function kinstr ~deps:None callee in - Kernel_function.Hptset.iter (build_return_deps context src stmt args) set - -and build_alarm_deps context src stmt alarm = - let for_exp e = - build_exp_deps context src stmt Data e - in - let open Alarms in - match alarm with - | Division_by_zero e | Index_out_of_bound (e, _) | Invalid_shift (e,_) - | Overflow (_,e,_,_) | Float_to_int (e,_,_) | Is_nan_or_infinite (e,_) - | Is_nan (e,_) | Function_pointer (e,_) -> for_exp e - | Pointer_comparison (opt_e1,e2) -> Extlib.may for_exp opt_e1; for_exp e2 - | Differing_blocks (e1,e2) -> for_exp e1; for_exp e2 - | Memory_access _ | Not_separated _ | Overlap _ - | Uninitialized _ | Dangling _ | Uninitialized_union _ -> () - (* TODO: adress depencies inside lval *) - | Invalid_bool lv -> build_lval_deps context src stmt Data lv - -and build_instr_deps context src stmt = function - | Set (_, exp, _) -> - build_exp_deps context src stmt Data exp - | Call (_, callee, args, _) -> build_call_deps context src stmt callee args - | Local_init (dest, ConsInit (f, args, k), loc) -> - let as_func _dest callee args _loc = - build_call_deps context src stmt callee args - in - Cil.treat_constructor_as_func as_func dest f args k loc - | Local_init (_, AssignInit init, _) -> - build_init_deps context src stmt init - | Asm _ | Skip _ | Code_annot _ -> () (* Cases not returned by Studia *) - -and build_init_deps context src stmt = function - | SingleInit exp -> - build_exp_deps context src stmt Data exp - | CompoundInit (_typ, initl) -> - List.iter (fun (_off,init) -> build_init_deps context src stmt init) initl - -and build_exp_deps context src stmt kind exp = - match exp.enode with - | Const _ - | SizeOf _ | SizeOfE _ | SizeOfStr _ - | AlignOf _ | AlignOfE _ - | AddrOf _ | StartOf _ -> () - | Lval lval -> - build_lval_deps context src stmt kind lval - | UnOp (_,e,_) | CastE (_,e) | Info (e,_) -> - build_exp_deps context src stmt kind e - | BinOp (_,e1,e2,_) -> - build_exp_deps context src stmt kind e1; - build_exp_deps context src stmt kind e2 - -and build_lval_deps context src stmt kind lval = - match build_lval context (Kstmt stmt) lval with - | None -> () - | Some dst -> - let allow_folding = true in - Graph.create_dependency ~allow_folding context.graph dst kind src + build_alarm_deps callstack stmt alarm + | Cluster -> () + (* --- Graph initialization --- *) @@ -355,8 +389,10 @@ let create () = graph = Graph.create (); 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; roots = []; } @@ -387,7 +423,7 @@ let unhide_base context vi = let should_auto_explore node = match node.node_kind with | Scalar _ | Composite _ | Alarm _ -> true - | Scattered _ | File -> false + | Scattered _ | Cluster -> false let complete_in_depth ~depth context root = context.roots <- root :: context.roots; @@ -411,13 +447,19 @@ let complete_in_depth ~depth context root = done let add_var ?(depth=1) context varinfo = - let node = build_var context varinfo in + let callstack = [] in + let node = build_var context callstack varinfo in Extlib.may (complete_in_depth ~depth context) node let add_lval ?(depth=1) context kinstr lval = - let node = build_lval context kinstr lval in + let callstack = match kinstr with + | Kglobal -> [] + | 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 let add_alarm ?(depth=1) context stmt alarm = - let node = build_alarm context stmt alarm in + 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 diff --git a/src/plugins/dive/callstack.ml b/src/plugins/dive/callstack.ml new file mode 100644 index 00000000000..6a01ffb50e8 --- /dev/null +++ b/src/plugins/dive/callstack.ml @@ -0,0 +1,50 @@ +(**************************************************************************) +(* *) +(* 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 + +include Value_types.Callstack + +type call_site = (Cil_types.kernel_function * Cil_types.kinstr) + +let init kf = [(kf,Kglobal)] + +let pop cs = + match cs with + | [] | (_,Kglobal) :: _ :: _ | [(_,Kstmt _)] -> assert false (* Invariant *) + | [(_,Kglobal)] -> None + | (kf,Kstmt stmt) :: t -> Some (kf,stmt,t) + +let rec pop_downto top_kf = function + | [] -> failwith "the callstack doesn't contain this function" + | ((kf,_kinstr) :: tail) as cs -> + if Kernel_function.equal kf top_kf + then cs + else pop_downto top_kf tail + +let push (kf,stmt) cs = + match cs with + (* When the callstack is truncated, we ignore the first callsite *) + | [] -> [(kf,Kglobal)] + | cs -> (kf,Kstmt stmt) :: cs + + diff --git a/src/plugins/dive/callstack.mli b/src/plugins/dive/callstack.mli new file mode 100644 index 00000000000..e5d55b8255d --- /dev/null +++ b/src/plugins/dive/callstack.mli @@ -0,0 +1,36 @@ +(**************************************************************************) +(* *) +(* 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). *) +(* *) +(**************************************************************************) + +type call_site = (Cil_types.kernel_function * Cil_types.kinstr) +type t = call_site list + +include Datatype.S_with_collections with type t := t + +(* The callstacks manipulated here have the following invariant: + - the callstack is never an empty list + - the last item of the list has alwas a Kglobal + - all elements of the list except the last have a Kstmt *) + +val init : Cil_types.kernel_function -> t +val pop : t -> (Cil_types.kernel_function * Cil_types.stmt * t) option +val pop_downto : Cil_types.kernel_function -> t -> t +val push : Cil_types.kernel_function * Cil_types.stmt -> t -> t diff --git a/src/plugins/dive/graph_types.mli b/src/plugins/dive/graph_types.mli index cea36be39f2..e0b5eaf97b8 100644 --- a/src/plugins/dive/graph_types.mli +++ b/src/plugins/dive/graph_types.mli @@ -25,11 +25,11 @@ type node_kind = | Composite of Cil_types.varinfo | Scattered of Cil_types.lval | Alarm of Cil_types.stmt * Alarms.alarm - | File (* Dummy node, hack for Ocamlgraph Graphviz Dot module *) + | Cluster (* Dummy node, hack for Ocamlgraph Graphviz Dot module *) type node_locality = { loc_file : string; - loc_function : Cil_types.kernel_function option; + loc_callstack : Callstack.t; } type node_precision = Unevaluated | Singleton | Normal | Wide | Critical diff --git a/src/plugins/dive/imprecision_graph.ml b/src/plugins/dive/imprecision_graph.ml index ebb0ce5808c..c8685f2ea70 100644 --- a/src/plugins/dive/imprecision_graph.ml +++ b/src/plugins/dive/imprecision_graph.ml @@ -103,10 +103,11 @@ let ouptput_to_dot out_channel g = let build_label s = `HtmlLabel (Extlib.html_escape s) in let module FileTable = Datatype.String.Hashtbl in - let module FunctionTable = Kernel_function.Hashtbl in + let module CallstackTable = Value_types.Callstack.Hashtbl in let file_table = FileTable.create 13 - and function_table = FunctionTable.create 13 in + and callstack_table = CallstackTable.create 13 in let file_counter = ref 0 in + let callstack_counter = ref 0 in let rec build_file_subgraph filename = incr file_counter; { @@ -114,16 +115,20 @@ let ouptput_to_dot out_channel g = sg_attributes = [build_label filename]; sg_parent = None; } - and build_function_subgraph filename kf = - { - sg_name = "function_" ^ (string_of_int (Kernel_function.get_id kf)); - sg_attributes = [build_label (Kernel_function.get_name kf)]; - sg_parent = Some (get_file_subgraph filename).sg_name; - } + and build_callstack_subgraph = function + | [] -> None + | (kf,_kinstr) :: stack -> + let parent = get_callstack_subgraph stack in + incr callstack_counter; + Some { + sg_name = "cs_" ^ (string_of_int !callstack_counter); + sg_attributes = [build_label (Kernel_function.get_name kf)]; + sg_parent = Extlib.opt_map (fun sg -> sg.sg_name) parent; + } and get_file_subgraph filename = FileTable.memo file_table filename build_file_subgraph - and get_function_subgraph filename kf = - FunctionTable.memo function_table kf (build_function_subgraph filename) + and get_callstack_subgraph cs = + CallstackTable.memo callstack_table cs build_callstack_subgraph in let module Dot = Graph.Graphviz.Dot ( @@ -142,7 +147,7 @@ let ouptput_to_dot out_channel g = | Composite _ -> [ `Shape `Box3d ] | Scattered _ -> [ `Shape `Parallelogram ] | Alarm _ -> [ `Shape `Doubleoctagon ; `Style `Bold ] - | File -> [ `Style `Invis ] + | Cluster -> [ `Style `Invis ] and precision = match v.node_precision with | Unevaluated -> [] | Singleton -> [`Color 0x88aaff ; @@ -159,10 +164,10 @@ let ouptput_to_dot out_channel g = l := [ `Style `Dotted ] @ !l; !l let get_subgraph v = - let {loc_file ; loc_function} = v.node_locality in - match loc_function with - | None -> Some (get_file_subgraph loc_file) - | Some kf -> Some (get_function_subgraph loc_file kf) + let {loc_file ; loc_callstack} = v.node_locality in + match loc_callstack with + | [] -> Some (get_file_subgraph loc_file) + | cs -> get_callstack_subgraph cs let default_edge_attributes _g = [] let edge_attributes (_v1,e,_v2) = let kind_attribute = match e.dependency_kind with @@ -177,20 +182,31 @@ let ouptput_to_dot out_channel g = Dot.output_graph out_channel g let ouptput_to_json out_channel g = + let rec 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) = + Json.of_fields [ + ("fun", Json.of_string (Kernel_function.get_name kf)) ; + ("instr", output_kinstr kinstr) ; + ] + and output_callstack cs = + Json.of_list (List.map output_callsite cs) + in let output_node_kind kind = let s = match kind with | Scalar _ -> "scalar" | Composite _ -> "composite" | Scattered _ -> "scattered" | Alarm _ -> "alarm" - | File -> "dummy" + | Cluster -> "dummy" in Json.of_string s - and output_node_locality { loc_file ; loc_function } = + and output_node_locality { loc_file ; loc_callstack } = let f1 = ("file", Json.of_string loc_file) in - let fields = match loc_function with - | None -> [f1] - | Some kf -> [f1 ; ("fun", Json.of_string (Kernel_function.get_name kf))] + let fields = match loc_callstack with + | [] -> [f1] + | cs -> [f1 ; ("fun", output_callstack cs)] in Json.of_fields fields and output_node_precision precision = @@ -212,7 +228,7 @@ let ouptput_to_json out_channel g = Json.of_string s in let output_node node acc = - if node.node_kind = File then 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 [ diff --git a/src/plugins/dive/node_kind.ml b/src/plugins/dive/node_kind.ml index bbcf645bc16..2f68c93875f 100644 --- a/src/plugins/dive/node_kind.ml +++ b/src/plugins/dive/node_kind.ml @@ -24,7 +24,7 @@ open Graph_types let get_base = function | Scalar (vi,_,_) | Composite (vi) -> Some vi - | Scattered _ | Alarm _ | File -> None + | Scattered _ | Alarm _ | Cluster -> None let to_location = function | Scalar (vi,typ,offset) -> @@ -32,17 +32,17 @@ let to_location = function Some (Locations.loc_of_typoffset base typ offset) | Composite (vi) -> Some (Locations.loc_of_varinfo vi) - | Scattered _ | Alarm _ | File -> None + | Scattered _ | 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 - | Alarm (_,_) | File -> None + | Alarm (_,_) | Cluster -> None let pretty fmt = function | (Scalar _ | Composite _ | Scattered _) as kind -> Cil_printer.pp_lval fmt (Extlib.the (to_lval kind)) | Alarm (_stmt,alarm) -> Cil_printer.pp_predicate fmt (Alarms.create_predicate alarm) - | File -> () + | Cluster -> () diff --git a/src/plugins/dive/tests/dive/const.i b/src/plugins/dive/tests/dive/const.i index 3534fa8e703..84c62df4f10 100644 --- a/src/plugins/dive/tests/dive/const.i +++ b/src/plugins/dive/tests/dive/const.i @@ -1,5 +1,5 @@ /* run.config -STDOPT: +"-dive-from main::res -dive-depth-limit 5" +STDOPT: +"-dive-from main::res -dive-depth-limit 4" */ int f(int x, int y) diff --git a/src/plugins/dive/tests/dive/oracle/assigned_param.dot b/src/plugins/dive/tests/dive/oracle/assigned_param.dot index e988a3cdc4a..0f545f95df0 100644 --- a/src/plugins/dive/tests/dive/oracle/assigned_param.dot +++ b/src/plugins/dive/tests/dive/oracle/assigned_param.dot @@ -1,25 +1,27 @@ digraph G { cp0 [style="invis,dotted", ]; - cp1 [label=<w>, shape=box, ]; - cp2 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + cp1 [style="invis,dotted", ]; + cp2 [label=<w>, shape=box, ]; + cp3 [style="invis,dotted", ]; + cp4 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - cp3 [label=<z>, shape=box, fillcolor="#EEFFEE", color="#004400", + cp5 [label=<z>, shape=box, fillcolor="#EEFFEE", color="#004400", style="filled", ]; - cp4 [label=<x>, shape=box, fillcolor="#EEFFEE", color="#004400", + cp6 [label=<x>, shape=box, fillcolor="#EEFFEE", color="#004400", style="filled", ]; - cp5 [label=<z>, shape=box, fillcolor="#EEFFEE", color="#004400", + cp7 [label=<z>, shape=box, fillcolor="#EEFFEE", color="#004400", style="filled", ]; - subgraph cluster_file_1 { label=<tests/dive/assigned_param.i>; cp0; - subgraph cluster_function_19 { label=<f>; cp3;cp2; - }; - subgraph cluster_function_24 { label=<main>; cp5;cp4;cp1; + subgraph cluster_cs_1 { label=<main>; cp7;cp6;cp2;cp1; + subgraph cluster_cs_2 { label=<f>; cp5;cp4;cp3; }; }; + subgraph cluster_file_1 { label=<tests/dive/assigned_param.i>; cp0; + }; - cp2 -> cp1; - cp3 -> cp2; cp4 -> cp2; - cp5 -> cp3; + cp5 -> cp4; + cp6 -> cp4; + cp7 -> cp5; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/const.dot b/src/plugins/dive/tests/dive/oracle/const.dot index ddf007a23e7..4921853943c 100644 --- a/src/plugins/dive/tests/dive/oracle/const.dot +++ b/src/plugins/dive/tests/dive/oracle/const.dot @@ -1,35 +1,34 @@ digraph G { cp0 [style="invis,dotted", ]; - cp1 [label=<res>, shape=box, ]; - cp2 [style="invis,dotted", ]; - cp3 [label=<__retres>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + cp1 [style="invis,dotted", ]; + cp2 [label=<res>, shape=box, ]; + cp3 [style="invis,dotted", ]; + cp4 [label=<__retres>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - cp4 [label=<c>, shape=box, fillcolor="#EEFFEE", color="#004400", + cp5 [label=<c>, shape=box, fillcolor="#EEFFEE", color="#004400", style="filled", ]; - cp5 [label=<w>, shape=box, fillcolor="#EEFFEE", color="#004400", + cp6 [label=<w>, shape=box, fillcolor="#EEFFEE", color="#004400", style="filled", ]; - cp6 [label=<x>, shape=box, fillcolor="#EEFFEE", color="#004400", + cp7 [label=<x>, shape=box, fillcolor="#EEFFEE", color="#004400", style="filled", ]; - cp7 [label=<y>, shape=box, fillcolor="#EEFFEE", color="#004400", - style="filled", ]; - cp8 [label=<i>, shape=box, fillcolor="#EEFFEE", color="#004400", + cp8 [label=<y>, shape=box, fillcolor="#EEFFEE", color="#004400", style="filled", ]; + cp9 [label=<i>, shape=box, fillcolor="#EEFFEE", color="#004400", + style="filled,dotted", ]; - subgraph cluster_file_1 { label=<tests/dive/const.i>; cp0; - subgraph cluster_function_25 { label=<main>; cp8;cp1; + subgraph cluster_cs_1 { label=<main>; cp9;cp2;cp1; + subgraph cluster_cs_2 { label=<f>; cp8;cp7;cp6;cp5;cp4;cp3; }; }; - subgraph cluster_file_2 { label=<>; cp2; - subgraph cluster_function_18 { label=<f>; cp7;cp6;cp5;cp4;cp3; - }; + subgraph cluster_file_1 { label=<tests/dive/const.i>; cp0; }; - cp3 -> cp1; - cp4 -> cp3; - cp5 -> cp3; + cp4 -> cp2; + cp5 -> cp4; cp6 -> cp4; cp7 -> cp5; cp8 -> cp6; - cp8 -> cp7; + cp9 -> cp7; + cp9 -> cp8; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/pointed_param.dot b/src/plugins/dive/tests/dive/oracle/pointed_param.dot new file mode 100644 index 00000000000..cc0432dfdae --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/pointed_param.dot @@ -0,0 +1,36 @@ +digraph G { + cp0 [style="invis,dotted", ]; + cp1 [style="invis,dotted", ]; + cp2 [label=<y>, shape=box, ]; + cp3 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp4 [style="invis,dotted", ]; + cp5 [label=<tmp>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp6 [style="invis,dotted", ]; + cp7 [label=<tmp>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp8 [style="invis,dotted", ]; + cp9 [label=<__retres>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp10 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled,dotted", ]; + + subgraph cluster_cs_1 { label=<main>; cp3;cp2;cp1; + subgraph cluster_cs_2 { label=<f>; cp10;cp5;cp4; + subgraph cluster_cs_3 { label=<g>; cp7;cp6; + subgraph cluster_cs_4 { label=<h>; cp9;cp8; + }; + }; + }; + }; + subgraph cluster_file_1 { label=<tests/dive/pointed_param.i>; cp0; + }; + + cp3 -> cp2; + cp5 -> cp3; + cp7 -> cp5; + cp9 -> cp7; + cp10 -> cp9; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/pointed_param.res.oracle b/src/plugins/dive/tests/dive/oracle/pointed_param.res.oracle new file mode 100644 index 00000000000..267eca08a19 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/pointed_param.res.oracle @@ -0,0 +1,6 @@ +[kernel] Parsing tests/dive/pointed_param.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva] done for function main +[dive] output to tests/dive/result/pointed_param.dot diff --git a/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot b/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot new file mode 100644 index 00000000000..17fe2165025 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot @@ -0,0 +1,38 @@ +digraph G { + cp0 [style="invis,dotted", ]; + cp1 [style="invis,dotted", ]; + cp2 [label=<x>, shape=box, ]; + cp3 [style="invis,dotted", ]; + cp4 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [style="invis,dotted", ]; + cp6 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [style="invis,dotted", ]; + cp8 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp9 [style="invis,dotted", ]; + cp10 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + + subgraph cluster_cs_1 { label=<g>; cp2;cp1; + }; + subgraph cluster_cs_2 { label=<f3>; cp4;cp3; + }; + subgraph cluster_cs_3 { label=<f2>; cp6;cp5; + }; + subgraph cluster_cs_4 { label=<f1>; cp8;cp7; + }; + subgraph cluster_cs_5 { label=<main>; cp10;cp9; + }; + subgraph cluster_file_1 { label=<tests/dive/unfocused_callers.i>; cp0; + }; + + cp4 -> cp2; + cp6 -> cp2; + cp8 -> cp2; + cp10 -> cp4; + cp10 -> cp6; + cp10 -> cp8; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/unfocused_callers.res.oracle b/src/plugins/dive/tests/dive/oracle/unfocused_callers.res.oracle new file mode 100644 index 00000000000..afebc6fd738 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/unfocused_callers.res.oracle @@ -0,0 +1,14 @@ +[kernel] Parsing tests/dive/unfocused_callers.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva:alarm] tests/dive/unfocused_callers.i:16: Warning: + non-finite float value. + assert \is_finite((float)(tmp + tmp_0)); + (tmp from f1(x), tmp_0 from f2(x)) +[eva:alarm] tests/dive/unfocused_callers.i:16: Warning: + non-finite float value. + assert \is_finite((float)((float)(tmp + tmp_0) + tmp_1)); + (tmp from f1(x), tmp_0 from f2(x), tmp_1 from f3(x)) +[eva] done for function main +[dive] output to tests/dive/result/unfocused_callers.dot diff --git a/src/plugins/dive/tests/dive/oracle/various.dot b/src/plugins/dive/tests/dive/oracle/various.dot index d8595e7b16c..9ed41e39557 100644 --- a/src/plugins/dive/tests/dive/oracle/various.dot +++ b/src/plugins/dive/tests/dive/oracle/various.dot @@ -1,48 +1,73 @@ digraph G { cp0 [style="invis,dotted", ]; - cp1 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp1 [style="invis,dotted", ]; + cp2 [label=<x2>, shape=box, ]; + cp3 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp2 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp4 [style="invis,dotted", ]; + cp5 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - cp3 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + cp6 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp4 [label=<g>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + cp7 [label=<g>, shape=box, fillcolor="#AACCFF", color="#88AAFF", style="filled", ]; - cp5 [label=<z>, shape=box, ]; - cp6 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + cp8 [label=<z>, shape=box, ]; + cp9 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", style="filled", ]; - cp7 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; - cp8 [label=<tmp_0>, shape=box, fillcolor="#FFBBBB", color="#FF0000", - style="filled", ]; - cp9 [label=<pf>, shape=box, fillcolor="#AACCFF", color="#88AAFF", - style="filled", ]; - cp10 [label=<is_nan_or_infinite: \is_finite((float)((double)((double)y * 2.0)))>, + cp10 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp11 [style="invis,dotted", ]; + cp12 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp13 [label=<tmp_0>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp14 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp15 [label=<pf>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp16 [style="invis,dotted", ]; + cp17 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp18 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp19 [label=<is_nan_or_infinite: \is_finite((float)((double)((double)y * 2.0)))>, shape=doubleoctagon, fillcolor="#FF0000", color="#FF0000", style="bold,filled,bold", ]; - cp11 [label=<is_nan_or_infinite: \is_finite((float)(y + w))>, + cp20 [label=<is_nan_or_infinite: \is_finite((float)(y + w))>, shape=doubleoctagon, fillcolor="#FF0000", color="#FF0000", style="bold,filled,bold", ]; - subgraph cluster_file_1 { label=<tests/dive/various.i>; cp4;cp0; - subgraph cluster_function_19 { label=<f>; cp10;cp2;cp1; + subgraph cluster_cs_1 { label=<f>; cp19;cp6;cp3;cp2;cp1; + }; + subgraph cluster_cs_2 { label=<main>; cp20;cp15;cp13;cp10;cp9;cp8;cp5;cp4; + subgraph cluster_cs_3 { label=<f>; cp14;cp12;cp11; }; - subgraph cluster_function_24 { label=<main>; cp11;cp9;cp8;cp7;cp6;cp5;cp3; + subgraph cluster_cs_4 { label=<f>; cp18;cp17;cp16; }; }; + subgraph cluster_file_1 { label=<tests/dive/various.i>; cp7;cp0; + }; - cp1 -> cp2; - cp1 -> cp6; - cp1 -> cp8; - cp2 -> cp1; - cp2 -> cp10; - cp3 -> cp1; - cp4 -> cp3; - cp6 -> cp5; - cp6 -> cp11; + cp3 -> cp2; + cp3 -> cp6; + cp3 -> cp19; + cp5 -> cp2; + cp5 -> cp6; + cp5 -> cp12; + cp5 -> cp17; + cp6 -> cp3; cp7 -> cp5; - cp7 -> cp11; - cp8 -> cp7; - cp9 -> cp8 [color="#00FF00", ]; + cp9 -> cp8; + cp9 -> cp20; + cp10 -> cp8; + cp10 -> cp20; + cp12 -> cp9; + cp12 -> cp14; + cp13 -> cp10; + cp14 -> cp12; + cp15 -> cp13 [color="#00FF00", ]; + cp17 -> cp13; + cp17 -> cp18; + cp18 -> cp17; } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/pointed_param.i b/src/plugins/dive/tests/dive/pointed_param.i new file mode 100644 index 00000000000..3a47e7648f4 --- /dev/null +++ b/src/plugins/dive/tests/dive/pointed_param.i @@ -0,0 +1,25 @@ +/* run.config +STDOPT: +"-dive-from main::y -dive-depth-limit 5" +*/ + +float h(float *p) +{ + return *p - 1; +} + +float g(float *p) +{ + return h(p); +} + +float f(float x) +{ + return g(&x); +} + +float main(float x) +{ + float y = f(x) + 1; + return y; +} + diff --git a/src/plugins/dive/tests/dive/unfocused_callers.i b/src/plugins/dive/tests/dive/unfocused_callers.i new file mode 100644 index 00000000000..f61e8438737 --- /dev/null +++ b/src/plugins/dive/tests/dive/unfocused_callers.i @@ -0,0 +1,19 @@ +/* run.config +STDOPT: +"-dive-from g::x -dive-depth-limit 7" +*/ + +float g(float x) { + float y = x + 1; + return y; +} + +float f1(float x) { return g(x); } +float f2(float x) { return g(x); } +float f3(float x) { return g(x); } + +float main(float x) +{ + float z = f1(x) + f2(x) + f3(x); + return z; +} + diff --git a/src/plugins/dive/tests/test_config b/src/plugins/dive/tests/test_config index 2baab62a7d7..6a140cd0a50 100644 --- a/src/plugins/dive/tests/test_config +++ b/src/plugins/dive/tests/test_config @@ -1,2 +1,2 @@ LOG: @PTEST_NAME@.dot -OPT: -no-autoload-plugins -load-module from,inout,eva,variadic,scope,dive -check -eva-msg-key=-initial-state -eva-no-show-progress -dive-output @PTEST_RESULT@/@PTEST_NAME@ +OPT: -no-autoload-plugins -load-module from,inout,eva,variadic,scope,dive -check -eva-msg-key=-initial-state -eva-no-show-progress -dive-output-dot @PTEST_RESULT@/@PTEST_NAME@ -- GitLab