diff --git a/headers/header_spec.txt b/headers/header_spec.txt index 32832478186a2582d8a38acfd8cee26c3b43367c..7fdecae8316b409d5dc49098688c2cf5cd52f1a9 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -763,6 +763,23 @@ src/plugins/constant_propagation/propagationParameters.ml: CEA_LGPL_OR_PROPRIETA src/plugins/constant_propagation/propagationParameters.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/constant_propagation/api.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/constant_propagation/api.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/build.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/build.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/callstack.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/callstack.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/configure.ac: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/Dive.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/graph_types.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/imprecision_graph.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/imprecision_graph.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/main.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/Makefile.in: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/node_kind.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/node_kind.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/self.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/self.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/server_interface.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/dive/server_interface.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/from/From.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/from/callwise.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/from/callwise.mli: CEA_LGPL_OR_PROPRIETARY diff --git a/src/plugins/dive/.gitignore b/src/plugins/dive/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b9f44a02f3c684cc2b3080dc8d5e96a4e758cf1a --- /dev/null +++ b/src/plugins/dive/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/tests/*/result +/tests/ptests_config diff --git a/src/plugins/dive/Dive.mli b/src/plugins/dive/Dive.mli new file mode 100644 index 0000000000000000000000000000000000000000..182bc40a8e1af00f6d1329952d449d0f2e6808e1 --- /dev/null +++ b/src/plugins/dive/Dive.mli @@ -0,0 +1,21 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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). *) +(* *) +(**************************************************************************) diff --git a/src/plugins/dive/Makefile.in b/src/plugins/dive/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..1609a95d3178f2a00f289b029af915220d16f2e6 --- /dev/null +++ b/src/plugins/dive/Makefile.in @@ -0,0 +1,71 @@ +########################################################################## +# # +# This file is part of Frama-C. # +# # +# Copyright (C) 2007-2020 # +# 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). # +# # +########################################################################## + +####################### +# Frama-C Environment # +####################### + +ifndef FRAMAC_SHARE +FRAMAC_SHARE := $(shell frama-c-config -print-share-path) +endif + +ifndef FRAMAC_LIBDIR +FRAMAC_LIBDIR := $(shell frama-c-config -print-libpath) +endif + +######################### +# Plug-in configuration # +######################### + +PLUGIN_DIR ?=. +PLUGIN_ENABLE:=@ENABLE_DIVE@ +PLUGIN_NAME := Dive +PLUGIN_CMO := self callstack node_kind imprecision_graph build main \ + server_interface +PLUGIN_CMI:= graph_types +PLUGIN_DEPENDENCIES:= Eva Studia Server +PLUGIN_HAS_MLI:= yes +PLUGIN_TESTS_DIRS:=dive +PLUGIN_GENERATED:= +PLUGIN_DISTRIB_EXTERNAL:= Makefile.in configure.ac configure +PLUGIN_DISTRIBUTED:=$(PLUGIN_ENABLE) + +################ +# Generic part # +################ + +include $(FRAMAC_SHARE)/Makefile.dynamic + +##################################### +# Regenerating the Makefile on need # +##################################### + +ifeq ("$(FRAMAC_INTERNAL)","yes") +CONFIG_STATUS_DIR:=$(FRAMAC_SRC) +CONFIG_STATUS_DIR_DEP:= +else +CONFIG_STATUS_DIR:=$(Dive_DIR) +CONFIG_STATUS_DIR_DEP:=$(CONFIG_STATUS_DIR)/config.status +endif + +$(Dive_DIR)/Makefile: $(Dive_DIR)/Makefile.in $(CONFIG_STATUS_DIR_DEP) + cd $(CONFIG_STATUS_DIR) && ./config.status --file $@ diff --git a/src/plugins/dive/README.md b/src/plugins/dive/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b90cf22dfb9738630b201e97e25d7e6867fd170a --- /dev/null +++ b/src/plugins/dive/README.md @@ -0,0 +1,17 @@ +Dive is a Frama-C plugin specialized in the visualization of data dependencies. +It is intended to help the user find the origin of an imprecision in an Eva +analysis. + + +Dependencies +============ + +The front-end of Dive relies on several external tools and libraries : + +- Node.js: a runtime environment for javascript to run programs outside a + browser +- yarn: a package manager for javascript modules written for Node.js +- electron: a javascript framework for gui applications +- Ivette: the future Frama-C GUI +- Cytoscape: a javascript library to display graphs and interact with them +- Zmq: a multilanguage framework for common simple networking patterns diff --git a/src/plugins/dive/build.ml b/src/plugins/dive/build.ml new file mode 100644 index 0000000000000000000000000000000000000000..236582ae1ad1e06d21eac2f065347021e7c06d7e --- /dev/null +++ b/src/plugins/dive/build.ml @@ -0,0 +1,645 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 +open Graph_types + +let dkey = Self.register_category "build" + +exception Too_many_deps + + +(* --- Precision evaluation --- *) + +let _fval_contains_maximal_bounds fkind fval = + let top = Fval.top_finite (Fval.kind fkind) in + Fval.has_greater_min_bound top fval >= 0 || + Fval.has_smaller_max_bound top fval >= 0 + +let fkind_limits = + let max_single = float_of_string "0x1.fffffep+127" + and max_double = float_of_string "0x1.fffffffffffffp+1023" in + let single_limits = { min = -. max_single ; max = max_single } + and double_limits = { min = -. max_double ; max = max_double } in + function + | FFloat -> single_limits + | FDouble -> double_limits + | FLongDouble -> assert false + +let float_grade_limits = + let single = float_of_string "0x1p+120" + and double = float_of_string "0x1p+960" + and long_double = float_of_string "0x1p+15360" + in function + | FFloat -> single + | FDouble -> double + | FLongDouble -> long_double + +let is_large_float_range fkind (min,max) = + let limit = float_grade_limits fkind in + if (min < 0.0) = (max < 0.0) then (* if bounds have same sign *) + max -. min >= limit + else + min <= -.limit || max >= limit + +let float_grade fkind (min,max) = + if min = max then + Singleton + else if is_large_float_range fkind (min,max) then + Wide + else + Normal + +let ikind_limits ikind = + let open Cil in + let bits = bitsSizeOfInt ikind in + if isSigned ikind then + { min=min_signed_number bits; max=max_signed_number bits } + else + { min=Integer.zero; max=max_unsigned_number bits } + +let int_grade_limits ikind = + let bits = Cil.bitsSizeOfInt ikind in + Integer.(pred (two_power_of_int (bits - bits / 8))) + +let is_large_int_range ikind (l,u) = + let limit = int_grade_limits ikind in + if Integer.(lt l zero) = Integer.(lt u zero) then (* if bounds have same sign *) + Integer.(ge (sub u l) limit) + else + Integer.(le l (neg limit)) || Integer.(ge u limit) + +let int_grade ikind (min,max) = + if min = max then + Singleton + else if is_large_int_range ikind (min,max) then + Wide + else + Normal + + +let update_node_values node kinstr lval = + let typ = Cil.typeOfLval lval in + let state = Db.Value.get_state kinstr in + let _,cvalue = !Db.Value.eval_lval None state lval in + try + let ival = Cvalue.V.project_ival cvalue in + match typ with + | TInt (ikind,_) -> + let size = Integer.of_int (Cil.bitsSizeOfInt ikind) + and signed = Cil.isSigned ikind in + let ival = Ival.reinterpret_as_int ~size ~signed ival in + let min, max = match Ival.min_and_max ival with + | Some min, Some max -> min, max + | _, _ -> assert false (* ival have been reinterpreted *) + in + Imprecision_graph.update_node_int_values node { + values_interval = {min;max}; + values_limits = ikind_limits ikind; + values_grade = int_grade ikind (min,max) + } + + | TFloat (fkind,_) -> + begin match Ival.min_and_max_float ival with + | None, _can_be_nan -> () + | Some (min, max), _can_be_nan -> + let min = Fval.F.to_float min and max = Fval.F.to_float max in + Imprecision_graph.update_node_float_values node { + values_interval = {min;max}; + values_limits = fkind_limits fkind; + values_grade = float_grade fkind (min,max); + } + end + | _ -> () + with Cvalue.V.Not_based_on_null -> () + + +(* --- Locations handling --- *) + +let get_loc_filename loc = + Filepath.(Normalized.to_pretty_string (fst loc).pos_path) + +let is_foldable_type typ = + match Cil.unrollType typ with + | TArray _ | TComp _ -> true + | TVoid _ | TInt _ | TEnum _ | TFloat _ | TPtr _ | TFun _ + | TBuiltin_va_list _ -> false + | TNamed _ -> assert false (* the type have been unrolled *) + +exception NoMatchingOffset + +let cell_to_scalar typ vi offset = + (* TODO: exceptions must be shown to the user somehow *) + try + let matching = Bit_utils.MatchType typ in + let offset', _ = Bit_utils.find_offset vi.vtype ~offset matching in + Scalar (vi, typ, offset') + with Bit_utils.NoMatchingOffset -> raise NoMatchingOffset + +exception NotACell + +let enumerate_cells ~is_folded_base ~limit lval kinstr = + (* TODO: non-variable bases must be shown to the user somehow *) + (* TODO: exceptions must be shown to the user somehow *) + (* If possible, refine the lval to a non-symbolic one *) + let location = !Db.Value.lval_to_loc kinstr lval + and typ = Cil.typeOfLval lval in + let open Locations in + let add (acc,count) node_kind = + if count > limit then + raise Too_many_deps; + (node_kind :: acc, count+1) + in + let add_base base ival (acc,count) = + match base with + | Base.Var (vi,_) -> + begin + if is_foldable_type vi.vtype && is_folded_base vi then + add (acc,count) (Composite (vi)) + else + let add_cells offset (acc,count) = + add (acc,count) (cell_to_scalar typ vi offset) + in + try + Ival.fold_int add_cells ival (acc,count) + with Abstract_interp.Error_Top -> raise NotACell + end + | _ -> raise NotACell + in + try + fst (Location_Bits.fold_i add_base location.loc ([],0)) + with Abstract_interp.Error_Top | NoMatchingOffset -> raise NotACell + +let build_node_kind ~is_folded_base lval kinstr = + match enumerate_cells ~is_folded_base ~limit:1 lval kinstr with + | [node_kind] -> node_kind + | _ -> Scattered (lval, kinstr) + | exception NotACell -> Scattered (lval, kinstr) + +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 callstack node_kind = + match Node_kind.get_base node_kind with + | None -> default_node_locality callstack + | Some vi -> + 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 + (Node_kind) (Callstack) + (struct let module_name = "Build.NodeRef" end) + +module Graph = Imprecision_graph +module Index = Datatype.Int.Hashtbl +module NodeTable = FCHashtbl.Make (NodeRef) +module BaseSet = Cil_datatype.Varinfo.Set +module FunctionMap = Kernel_function.Map + +type t = { + mutable graph: Graph.t; + mutable vertex_table: node Index.t; + mutable node_table: node NodeTable.t; + mutable unfolded_bases: BaseSet.t; + mutable hidden_bases: BaseSet.t; + mutable focus: bool FunctionMap.t; + mutable roots: node list; + 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 find_node context node_key = + Index.find context.vertex_table node_key + +let update_node context node = + if + not (List.exists (Graph.Node.equal node) context.graph_diff.added_nodes) + then + context.graph_diff <- { + context.graph_diff with + added_nodes = node :: context.graph_diff.added_nodes + } + +let add_node context ~node_kind ~node_locality = + 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 node_ref = (node.node_kind, node.node_locality.loc_callstack) in + Graph.remove_node context.graph node; + 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 + } + + +(* --- Graph building --- *) + +let add_or_update_node context callstack node_kind = + let node_locality = build_node_locality callstack node_kind in + add_node context ~node_kind ~node_locality + +let build_node context callstack lval kinstr = + let is_folded_base = is_folded context in + let node_kind = build_node_kind ~is_folded_base lval kinstr in + add_or_update_node context callstack node_kind + +let build_all_scattered_node context callstack kinstr lval = + let is_folded_base = is_folded context in + try + let cells = enumerate_cells ~is_folded_base ~limit:20 lval kinstr in + let add node_kind = + let node = add_or_update_node context callstack node_kind in + let new_lval = Extlib.the (Node_kind.to_lval node_kind) in + update_node_values node kinstr new_lval; + node + in + List.map add cells + with NotACell -> + Self.warning "Unable to enumerate cells for %a" + Cil_printer.pp_lval lval; + [] + +let build_var context callstack varinfo = + let lval = Var varinfo, NoOffset in + build_node context callstack lval Kglobal + +let build_lval context callstack kinstr lval = + let node = build_node context callstack lval kinstr in + update_node_values node kinstr lval; + node + +let build_alarm context callstack stmt alarm = + let node_kind = Alarm (stmt,alarm) in + let node_locality = build_node_locality callstack node_kind in + add_node context ~node_kind ~node_locality + +let build_node_deps context node = + let rec build_lval_write_deps callstack kinstr lval = + let zone = !Db.Value.lval_to_zone kinstr lval in + build_write_deps callstack zone + + and build_write_deps callstack zone = + Self.debug ~dkey "computing deps for %a" Node_kind.pretty node.node_kind; + let writes = Studia.Writes.compute zone + and add_deps (stmt,effects) = + match stmt.skind with + | Instr _ when not effects.Studia.Writes.direct -> () + | Instr instr -> + let callstacks = + if callstack <> [] && + Kernel_function.(equal + (find_englobing_kf stmt) + (Callstack.top_kf callstack)) + then + (* slight improvement which only work when there is no recursion + and which is only usefull because you currently can't have + all callstacks due to memexec -> in this particular case + we are sure not to miss the only admissible callstack *) + [callstack] + else + (* Keep only callstacks which are a compatible with the current one *) + let states = Db.Value.get_stmt_state_callstack ~after:false stmt in + let callstacks = match states with + | None -> assert false + | Some table -> + let module Table = Value_types.Callstack.Hashtbl in + Table.fold (fun cs _ acc -> cs :: acc) table [] + in + (* TODO: missing callstacks filtered by memexec *) + Callstack.filter_truncate callstacks callstack + in + (* Create a dependency for each of them *) + List.iter (fun cs -> build_instr_deps cs stmt instr) callstacks + | _ -> assert false + in + let count = List.length writes in + Self.debug ~dkey "%d found" count; + if count > 20 then + raise Too_many_deps + 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},_)} as return_stmt -> + let callstack = Callstack.push (kf,stmt) callstack in + build_lval_deps callstack return_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,_) | Invalid_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 = + let dst = build_lval context callstack (Kstmt stmt) lval in + Graph.create_dependency ~allow_folding:true context.graph dst kind node + + and build_scattered_deps callstack kinstr lval = + let nodes = build_all_scattered_node context callstack kinstr lval in + let kind = Composition in + let add_dep dst = + Graph.create_dependency ~allow_folding:true context.graph dst kind node + in + List.iter add_dep nodes + + in + update_node context node; + let callstack = node.node_locality.loc_callstack in + begin match node.node_kind with + | Scalar (vi,_typ,offset) -> + let lval = (Cil_types.Var vi, offset) in + build_lval_write_deps callstack Kglobal lval + | Composite (vi) -> + let lval = (Cil_types.Var vi, Cil_types.NoOffset) in + build_lval_write_deps callstack Kglobal lval + | Scattered (lval,kinstr) -> + build_scattered_deps callstack kinstr lval + | Alarm (stmt,alarm) -> + build_alarm_deps callstack stmt alarm + end; + begin match Node_kind.get_base node.node_kind with + (* TODO refine formal dependency computation for non-scalar formals *) + | Some vi when vi.vformal -> build_arg_deps callstack vi + | _ -> () + end + + +(* --- Graph initialization --- *) + +let create () = + !Db.Value.compute (); + { + graph = Graph.create (); + vertex_table = Index.create 13; + node_table = NodeTable.create 13; + unfolded_bases = BaseSet.empty; + hidden_bases = BaseSet.empty; + focus = FunctionMap.empty; + roots = []; + graph_diff = { added_nodes=[] ; removed_nodes=[] }; + } + +let clear context = + context.graph <- Graph.create (); + context.vertex_table <- Index.create 13; + context.node_table <- NodeTable.create 13; + context.focus <- FunctionMap.empty; + context.roots <- []; + context.graph_diff <- { added_nodes=[] ; removed_nodes=[] } + + +(* --- Accessors --- *) + +let get_graph context = + context.graph + +let get_roots context = + context.roots + + +(* --- Mutators --- *) + +let unfold_base context vi = + context.unfolded_bases <- BaseSet.add vi context.unfolded_bases + +let fold_base context vi = + context.unfolded_bases <- BaseSet.remove vi context.unfolded_bases + +let hide_base context vi = + context.hidden_bases <- BaseSet.add vi context.hidden_bases + +let unhide_base context vi = + context.hidden_bases <- BaseSet.add vi context.hidden_bases + +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 _ -> 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 + begin try + build_node_deps context n; + with Too_many_deps -> + (* TODO: give a mean to explore more dependencies *) + Self.warning "Too many dependencies for %a ; throwing them out" + Node_kind.pretty n.node_kind; + end; + n.node_deps_computed <- true; + end; + Graph.iter_pred (fun n' -> Queue.add (n',d+1) queue) context.graph n + 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 + complete_in_depth ~depth context node + +let add_lval ?(depth=1) context kinstr lval = + 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 + 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 + let node = build_alarm context callstack stmt alarm in + complete_in_depth ~depth context node + +let add_function_alarms ?(depth=1) context kf = + let add_one_alarm _emitter kf' stmt ~rank:_ alarm _code_annot = + if Kernel_function.equal kf' kf then + add_alarm ~depth context stmt alarm + in + Alarms.iter add_one_alarm + +let explore_from_node ~depth context node = + explore ~depth context node + +let show ?(depth=1) context node = + node.node_hidden <- false; + explore ~depth context node + +let hide context node = + 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 new file mode 100644 index 0000000000000000000000000000000000000000..990e3e6f89cf5916b7abd11f2acbb360a16cc448 --- /dev/null +++ b/src/plugins/dive/build.mli @@ -0,0 +1,48 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 t + +val create : unit -> t +val clear : t -> unit (* reset to almost an empty context, + but keeps folded and hidden bases *) + +val get_graph : t -> Imprecision_graph.t +val get_roots : t -> Graph_types.node list + +val unfold_base : t -> Cil_types.varinfo -> unit +val fold_base : t -> Cil_types.varinfo -> unit +val hide_base : t -> Cil_types.varinfo -> unit +val unhide_base : t -> Cil_types.varinfo -> unit + +val find_node : t -> int -> Graph_types.node + +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 add_function_alarms : ?depth:int -> t -> Cil_types.kernel_function -> unit +val explore_from_node : depth:int -> t -> Graph_types.node -> unit + +val show : ?depth:int -> t -> Graph_types.node -> unit +val hide : t -> Graph_types.node -> unit + +val take_last_differences : t -> Graph_types.graph_diff diff --git a/src/plugins/dive/callstack.ml b/src/plugins/dive/callstack.ml new file mode 100644 index 0000000000000000000000000000000000000000..8b25837a11357d83f0dd791d749baf2e11225b77 --- /dev/null +++ b/src/plugins/dive/callstack.ml @@ -0,0 +1,86 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 = Value_types.call_site +module Callsite = Value_types.Callsite + +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 top_kf cs = + match cs with + | [] | (_,Kglobal) :: _ :: _ | [(_,Kstmt _)] -> assert false (* Invariant *) + | (kf,_) :: _ -> kf + +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 + +let rec is_prefix cs1 cs2 = + match cs1, cs2 with + | [], _ -> true + | _, [] -> false + | [(kf,Kglobal)], (kf',_)::_ -> Kernel_function.equal kf kf' + | _, [(_,Kglobal)] -> false + | s1 :: t1, s2 :: t2 -> + if Callsite.equal s1 s2 + then is_prefix t1 t2 + else false + +let truncate_to_sub full_cs sub_cs = + let rec aux acc = function + | [] -> None + | (s :: t) as cs -> + if is_prefix sub_cs cs + then Some (List.rev acc @ sub_cs) + else aux (s :: acc) t + in + aux [] full_cs + +let list_filter_map f l = + let aux acc x = + match f x with + | None -> acc + | Some y -> y :: acc + in + List.rev (List.fold_left aux [] l) + +let filter_truncate l sub_cs = + list_filter_map (fun cs -> truncate_to_sub cs sub_cs) l diff --git a/src/plugins/dive/callstack.mli b/src/plugins/dive/callstack.mli new file mode 100644 index 0000000000000000000000000000000000000000..48d3a417b9614086235ce292a695fc084efd16b0 --- /dev/null +++ b/src/plugins/dive/callstack.mli @@ -0,0 +1,40 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 always 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 top_kf : t -> Cil_types.kernel_function +val push : Cil_types.kernel_function * Cil_types.stmt -> t -> t +val is_prefix : t -> t -> bool +val truncate_to_sub : t -> t -> t option +val filter_truncate : t list -> t -> t list diff --git a/src/plugins/dive/configure.ac b/src/plugins/dive/configure.ac new file mode 100644 index 0000000000000000000000000000000000000000..dddb5ed24e2092387c4c801ab35f0a3d061f8b41 --- /dev/null +++ b/src/plugins/dive/configure.ac @@ -0,0 +1,50 @@ +########################################################################## +# # +# This file is part of Frama-C. # +# # +# Copyright (C) 2007-2020 # +# 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). # +# # +########################################################################## + +m4_define([plugin_file],Makefile.in) + +m4_define([FRAMAC_SHARE_ENV], + [m4_normalize(m4_esyscmd([echo $FRAMAC_SHARE]))]) + +m4_define([FRAMAC_SHARE], + [m4_ifval(FRAMAC_SHARE_ENV,[FRAMAC_SHARE_ENV], + [m4_esyscmd(frama-c -print-path)])]) + +m4_ifndef([FRAMAC_M4_MACROS], [m4_include(FRAMAC_SHARE/configure.ac)]) + +check_plugin(dive,PLUGIN_RELATIVE_PATH(plugin_file), + [support for Dive plug-in],yes) + +# Plug-in dependencies +###################### + +plugin_require(dive,eva) +plugin_require(dive,studia) +#plugin_use(dive,usable_plugin_but_not_mandatory) + +check_plugin_dependencies + +####################### +# Generating Makefile # +####################### + +write_plugin_config(Makefile) diff --git a/src/plugins/dive/graph_types.mli b/src/plugins/dive/graph_types.mli new file mode 100644 index 0000000000000000000000000000000000000000..f1920b35026e847eaa92a1921861890d3087a590 --- /dev/null +++ b/src/plugins/dive/graph_types.mli @@ -0,0 +1,65 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 node_kind = + | Scalar of Cil_types.varinfo * Cil_types.typ * Cil_types.offset + | Composite of Cil_types.varinfo + | Scattered of Cil_types.lval * Cil_types.kinstr + | Alarm of Cil_types.stmt * Alarms.alarm + +type node_locality = { + loc_file : string; + loc_callstack : Callstack.t; +} + +type 'a interval = {min: 'a; max: 'a} + +type precision_grade = Singleton | Normal | Wide + +type 'a node_values = { + values_interval : 'a interval; + values_limits : 'a interval; + values_grade : precision_grade; +} + +type node = { + node_key : int; + node_kind : node_kind; + node_locality : node_locality; + mutable node_hidden : bool; + mutable node_int_values : (Integer.t node_values) option; + mutable node_float_values : (float node_values) option; + mutable node_deps_computed : bool; +} + +type dependency_kind = Callee | Data | Address | Control | Composition + +type dependency = { + dependency_key : int; + 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 new file mode 100644 index 0000000000000000000000000000000000000000..2ebd12cf182b051b49c551f7a1dd71d7b3b7c7b4 --- /dev/null +++ b/src/plugins/dive/imprecision_graph.ml @@ -0,0 +1,401 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 + +module Node = +struct + type t = node + let compare v1 v2 = v1.node_key - v2.node_key + let hash v = v.node_key + let equal v1 v2 = v1.node_key = v2.node_key +end + +module Dependency = +struct + type t = dependency + let compare e1 e2 = e1.dependency_key - e2.dependency_key + let hash e = e.dependency_key + let equal e1 e2 = e1.dependency_key = e2.dependency_key + let default = { + dependency_key = -1; + dependency_kind = Data; + dependency_multiple = false; + } +end + +module G = + Graph.Imperative.Digraph.ConcreteBidirectionalLabeled (Node) (Dependency) +include G + +let vertices g = + fold_vertex (fun n acc -> n ::acc) g [] + +let edges g = + fold_edges_e (fun d acc -> d ::acc) g [] + +let next_key = ref 0 + +let create_node ~node_kind ~node_locality g = + let node = { + node_key = !next_key; + node_kind; + node_locality; + node_hidden = false; + node_int_values = None; + node_float_values = None; + node_deps_computed = false; + } + in + incr next_key; + add_vertex g node; + node + +let remove_node = remove_vertex + +let union_int_interval i1 i2 = + { min = Integer.min i1.min i2.min ; max = Integer.max i1.max i2.max } + +let union_float_interval i1 i2 = + { min = min i1.min i2.min ; max = max i1.max i2.max } + +let worst_precision_grade q1 q2 = + match q1, q2 with + | Wide, _ | _, Wide -> Wide + | Normal, _ | _, Normal -> Normal + | Singleton, Singleton -> Singleton + +let merge_int_values p1 p2 = + (* TODO: prevent assertion failure *) + assert (Integer.equal p1.values_limits.min p2.values_limits.min); + assert (Integer.equal p1.values_limits.max p2.values_limits.max); + { + values_interval = union_int_interval p1.values_interval p2.values_interval; + values_limits = p1.values_limits; + values_grade = worst_precision_grade p1.values_grade p2.values_grade; + } + +let merge_float_values p1 p2 = + (* TODO: prevent assertion failure *) + assert (p1.values_limits = p2.values_limits); + { + values_interval = union_float_interval p1.values_interval p2.values_interval; + values_limits = p1.values_limits; + values_grade = worst_precision_grade p1.values_grade p2.values_grade; + } + +let update_node_int_values node new_values = + node.node_int_values <- + Some (Extlib.opt_fold merge_int_values node.node_int_values new_values) + +let update_node_float_values node new_values = + node.node_float_values <- + Some (Extlib.opt_fold merge_float_values node.node_float_values new_values) + + +let create_dependency ~allow_folding g v1 dependency_kind v2 = + let same_kind (_,e,_) = + e.dependency_kind = dependency_kind + in + let matching_edge = + try + if allow_folding then + Some (List.find same_kind (G.find_all_edges g v1 v2)) + else + None + with Not_found -> None + in + match matching_edge with + | Some (_,e,_) -> + e.dependency_multiple <- true + | None -> + let e = { + dependency_key = !next_key; + dependency_kind; + dependency_multiple = false; + } + in + 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; + fold_vertex (fun n acc -> if Table.mem table n then acc else n :: acc) 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 + + let module FileTable = Datatype.String.Hashtbl in + let module CallstackTable = Value_types.Callstack.Hashtbl in + let file_table = FileTable.create 13 + 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; + { + sg_name = "file_" ^ (string_of_int !file_counter); + sg_attributes = [build_label filename]; + sg_parent = None; + } + 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_callstack_subgraph cs = + CallstackTable.memo callstack_table cs build_callstack_subgraph + in + + let module Dot = Graph.Graphviz.Dot ( + struct + include G + let graph_attributes _g = [] + let default_vertex_attributes _g = [] + let vertex_name v = "cp" ^ (string_of_int v.node_key) + let vertex_attributes v = + let grade = match v.node_int_values, v.node_float_values with + | Some v1, Some v2 -> + Some (worst_precision_grade v1.values_grade v2.values_grade) + | Some v, _ -> Some v.values_grade + | _, Some v -> Some v.values_grade + | None, None -> None + in + let l = ref [] in + let text = Pretty_utils.to_string Node_kind.pretty v.node_kind in + if text <> "" then + l := build_label text :: !l; + let kind = match v.node_kind with + | Scalar _ -> [`Shape `Box] + | Composite _ -> [ `Shape `Box3d ] + | Scattered _ -> [ `Shape `Parallelogram ] + | Alarm _ -> [ `Shape `Doubleoctagon ; + `Style `Bold ; `Color 0xff0000 ; + `Style `Filled ; `Fillcolor 0xff0000 ] + and values = match grade with + | None -> [] + | Some Singleton -> + [`Color 0x88aaff ; `Style `Filled ; `Fillcolor 0xaaccff ] + | Some Normal -> + [ `Color 0x004400 ; `Style `Filled ; `Fillcolor 0xeeffee ] + | Some Wide -> + [ `Color 0xff0000 ; `Style `Filled ; `Fillcolor 0xffbbbb ] + in + l := values @ kind @ !l; + if not v.node_deps_computed then + l := [ `Style `Dotted ] @ !l; + !l + let get_subgraph v = + 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 + | Callee -> [`Color 0x00ff00 ] + | _ -> [] + and folding_attribute = match e.dependency_multiple with + | true -> [ `Style `Bold ] + | false -> [] + in kind_attribute @ folding_attribute + end) + in + Dot.output_graph out_channel g + +module JsonPrinter = +struct + let output_kinstr = function + | Cil_types.Kglobal -> `String "global" + | Cil_types.Kstmt stmt -> `Int stmt.Cil_types.sid + + let output_callsite (kf,kinstr) = + `Assoc [ + ("fun", `String (Kernel_function.get_name kf)) ; + ("instr", output_kinstr kinstr) ; + ] + + let output_callstack cs = + `List (List.map output_callsite cs) + + let output_node_kind kind = + let s = match kind with + | Scalar _ -> "scalar" + | Composite _ -> "composite" + | Scattered _ -> "scattered" + | Alarm _ -> "alarm" + in + `String s + + let output_node_locality { loc_file ; loc_callstack } = + let f1 = ("file", `String loc_file) in + let fields = match loc_callstack with + | [] -> [f1] + | cs -> [f1 ; ("callstack", output_callstack cs)] + in + `Assoc fields + + let output_node_precision_grade grade = + let s = match grade with + | Singleton -> "singleton" + | Normal -> "normal" + | Wide -> "wide" + in + `String s + + let output_dep_kind kind = + let s = match kind with + | Callee -> "callee" + | Data -> "data" + | Address -> "addr" + | Control -> "ctrl" + | Composition -> "comp" + in + `String s + + let output_int_interval interval = + (* TODO: handle overflow *) + `Assoc [ + ("min", `Int (Integer.to_int interval.min)) ; + ("max", `Int (Integer.to_int interval.max)) ; + ] + + let output_float_interval interval = + `Assoc [ + ("min", `Float interval.min) ; + ("max", `Float interval.max) ; + ] + + let output_node_int_values values = + `Assoc [ + ("computed", output_int_interval values.values_interval) ; + ("limits", output_int_interval values.values_limits) ; + ("grade", output_node_precision_grade values.values_grade) ; + ] + + let output_node_float_values values = + `Assoc [ + ("computed", output_float_interval values.values_interval) ; + ("limits", output_float_interval values.values_limits) ; + ("grade", output_node_precision_grade values.values_grade) ; + ] + + let output_node node = + let label = Pretty_utils.to_string Node_kind.pretty node.node_kind in + `Assoc ([ + ("id", `Int node.node_key) ; + ("label", `String label) ; + ("kind", output_node_kind node.node_kind) ; + ("locality", output_node_locality node.node_locality) ; + ("explored", `Bool node.node_deps_computed) ; + ] @ + begin match node.node_int_values with + | None -> [] + | Some node_values -> + [("int_values", output_node_int_values node_values)] + end @ + begin match node.node_float_values with + | None -> [] + | Some node_values -> + [("float_values", output_node_float_values node_values)] + end @ + begin match Node_kind.to_lval node.node_kind with + | None -> [] + | Some lval -> + let typ = Cil.typeOfLval lval in + let str = Pretty_utils.to_string Cil_printer.pp_typ typ in + [("type", `String str)] + end) + + let output_dep (n1,dep,n2) = + `Assoc [ + ("id", `Int dep.dependency_key) ; + ("src", `Int n1.node_key) ; + ("dst", `Int n2.node_key) ; + ("kind", output_dep_kind dep.dependency_kind) ; + ("multiple", `Bool dep.dependency_multiple) + ] + + let output_graph g = + `Assoc [ + ("nodes", `List (List.map output_node (vertices g))) ; + ("deps", `List (List.map output_dep (edges g))) + ] + + let output_diff g diff = + let added_nodes = List.map output_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 + List.map output_dep (Set.elements set) + and removed_nodes = + List.map (fun node -> `Int node.node_key) diff.removed_nodes + in + `Assoc [ + ("add", `Assoc [ + ("nodes", `List added_nodes) ; + ("deps", `List added_deps) + ]) ; + ("sub", `List removed_nodes)] +end + +let ouptput_to_json out_channel g = + let json = JsonPrinter.output_graph g in + Yojson.Basic.to_channel 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 new file mode 100644 index 0000000000000000000000000000000000000000..dae5176aff12bbd6e7ead1c57dfd62394775f788 --- /dev/null +++ b/src/plugins/dive/imprecision_graph.mli @@ -0,0 +1,55 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 Graph.Sig.G + with type V.t = node + and type E.t = node * dependency * node + +module Node : Graph.Sig.COMPARABLE with type t = node + +module Dependency : Graph.Sig.COMPARABLE with type t = dependency + +val create : ?size:int -> unit -> t + +val create_node : + node_kind:node_kind -> + node_locality:node_locality -> t -> node + +val remove_node : t -> node -> unit + +val update_node_int_values : node -> Integer.t node_values -> unit +val update_node_float_values : node -> float 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 + +val to_json : t -> Json.t +val diff_to_json : t -> graph_diff -> Json.t diff --git a/src/plugins/dive/main.ml b/src/plugins/dive/main.ml new file mode 100644 index 0000000000000000000000000000000000000000..7638783ac208063427b5dcbdfed54ac28913dcb4 --- /dev/null +++ b/src/plugins/dive/main.ml @@ -0,0 +1,61 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 format = Dot | Json + +let output format context basename = + let filename, output_function = match format with + | Dot -> basename ^ ".dot", Imprecision_graph.ouptput_to_dot + | Json -> basename ^ ".json", Imprecision_graph.ouptput_to_json + in + Self.result "output to %s" filename; + let out_channel = open_out filename in + output_function out_channel (Build.get_graph context); + close_out out_channel + +let main () = + if not (Self.FromBases.is_empty () && + Self.FromFunctionAlarms.is_empty ()) then begin + (* Create the initial graph *) + let context = Build.create () in + (* Handle parameters *) + Self.UnfoldedBases.iter (Build.unfold_base context); + Self.HiddenBases.iter (Build.hide_base context); + let depth = Self.DepthLimit.get () in + (* Add targeted vars to it *) + Self.FromBases.iter (Build.add_var ~depth context); + (* Add alarms *) + let add_alarm _emitter kf stmt ~rank:_ alarm _code_annot = + if Self.FromFunctionAlarms.mem kf then + Build.add_alarm ~depth context stmt alarm + in + if not (Self.FromFunctionAlarms.is_empty ()) then + Alarms.iter add_alarm; + (* Output it *) + if Self.OutputDot.get () <> "" then + output Dot context (Self.OutputDot.get ()); + if Self.OutputJson.get () <> "" then + output Json context (Self.OutputJson.get ()); + end + +let () = + Db.Main.extend main diff --git a/src/plugins/dive/node_kind.ml b/src/plugins/dive/node_kind.ml new file mode 100644 index 0000000000000000000000000000000000000000..5c6b5979cf3e4eba1ddb71edcf4f073fe240ea62 --- /dev/null +++ b/src/plugins/dive/node_kind.ml @@ -0,0 +1,100 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 + +module DatatypeInput = +struct + include Datatype.Undefined + + open Cil_datatype + + type t = node_kind + + let (<?>) c (cmp,x,y) = + if c = 0 + then cmp x y + else c + + let name = "Node_kind" + + let reprs = [ Scalar ( + List.hd Varinfo.reprs, + List.hd Typ.reprs, + List.hd Offset.reprs) ] + + let compare k1 k2 = + match k1, k2 with + | 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,ki1), Scattered (lv2,ki2) -> + LvalStructEq.compare lv1 lv2 <?> (Kinstr.compare, ki1, ki2) + | Scattered _, _ -> 1 + | _, Scattered _ -> -1 + | Alarm (stmt1, alarm1), Alarm (stmt2, alarm2) -> + Stmt.compare stmt1 stmt2 <?> (Alarms.compare, alarm1, alarm2) + + let equal k1 k2 = + match k1, k2 with + | Scalar (vi1, _, offset1), Scalar (vi2, _, offset2) -> + Varinfo.equal vi1 vi2 && OffsetStructEq.equal offset1 offset2 + | Composite vi1, Composite vi2 -> Varinfo.equal vi1 vi2 + | Scattered (lv1, ki1), Scattered (lv2, ki2) -> + LvalStructEq.equal lv1 lv2 && Kinstr.equal ki1 ki2 + | Alarm (stmt1, alarm1), Alarm (stmt2, alarm2) -> + Stmt.equal stmt1 stmt2 && Alarms.equal alarm1 alarm2 + | _ -> false + + let hash k = + match k with + | Scalar (vi, _, offset) -> + Hashtbl.hash (1, Varinfo.hash vi, OffsetStructEq.hash offset) + | Composite vi -> Hashtbl.hash (2, Varinfo.hash vi) + | Scattered (lv, ki) -> + Hashtbl.hash (3, LvalStructEq.hash lv, Kinstr.hash ki) + | 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 _ -> 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 (_,_) -> 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) diff --git a/src/plugins/dive/node_kind.mli b/src/plugins/dive/node_kind.mli new file mode 100644 index 0000000000000000000000000000000000000000..a65055305c6980a5f09e07fe79305cbded635561 --- /dev/null +++ b/src/plugins/dive/node_kind.mli @@ -0,0 +1,26 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +include Datatype.S with type t = Graph_types.node_kind + +val get_base : t -> Cil_types.varinfo option +val to_lval : t -> Cil_types.lval option diff --git a/src/plugins/dive/self.ml b/src/plugins/dive/self.ml new file mode 100644 index 0000000000000000000000000000000000000000..75bc7d1cce238aad412ea6d2bb83842f8b65b297 --- /dev/null +++ b/src/plugins/dive/self.ml @@ -0,0 +1,132 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 Plugin.Register + (struct + let name = "dive" + let shortname = "dive" + let help = "An interactive imprecision graph generator for Eva." + end) + +module OutputDot = Empty_string + (struct + let option_name = "-dive-output-dot" + let help = "Outputs the built graph into a dot file with this basename." + let arg_name = "basename" + end) + +module OutputJson = Empty_string + (struct + let option_name = "-dive-output-json" + let help = "Outputs the built graph into a json file with this basename." + let arg_name = "basename" + end) + +module DepthLimit = Int + (struct + let option_name = "-dive-depth-limit" + let help = "Build dependencies up to a depth of N." + let arg_name = "N" + let default = 2 + end) + +module FromFunctionAlarms = Kernel_function_set + (struct + let option_name = "-dive-from-alarms" + let help = "Build the graph from the alarms emitted in the given functions." + let arg_name = "f1,..." + end) + +module Varinfo = +struct + include Cil_datatype.Varinfo + + let of_string s = + let regexp = Str.regexp "^\\([_a-zA-Z0-9]+\\)::\\([_a-zA-Z0-9]+\\)$" in + if Str.string_match regexp s 0 then + let function_name = Str.matched_group 1 s + and variable_name = Str.matched_group 2 s in + let kf = try Globals.Functions.find_by_name function_name + with Not_found -> + raise (Cannot_build ("no function '" ^ function_name ^ "'")) + in + try Globals.Vars.find_from_astinfo variable_name (VLocal kf) + with Not_found -> + try Globals.Vars.find_from_astinfo variable_name (VFormal kf) + with Not_found -> + raise (Cannot_build ("no variable '" ^ variable_name ^ "' in function " + ^ function_name)) + else + let regexp = Str.regexp "^[_a-zA-Z0-9]+$" in + if not (Str.string_match regexp s 0) then + raise (Cannot_build ("wrong syntax: '" ^ s ^ "'")); + match Globals.Syntactic_search.find_in_scope s Cil_types.Program with + | Some vi -> vi + | None -> + raise (Cannot_build ("no global variable '" ^ s ^ "'")) + + let to_string vi = + match Kernel_function.find_defining_kf vi with + | None -> vi.vname + | Some kf -> Kernel_function.get_name kf ^ "::" ^ vi.vname + + let of_singleton_string = no_element_of_string +end + +module type Varinfo_set = Parameter_sig.Set + with type elt = Cil_types.varinfo + and type t = Cil_datatype.Varinfo.Set.t + +module Varinfo_set (X: Parameter_sig.Input_with_arg) = + Make_set + (Varinfo) + (struct + include X + let dependencies = [] + let default = Cil_datatype.Varinfo.Set.empty + end) + +module FromBases = Varinfo_set + (struct + let option_name = "-dive-from-variables" + let help = "Build the graph from these local variables." + let arg_name = "f::v,..." + end) + +module UnfoldedBases = Varinfo_set + (struct + let option_name = "-dive-unfold" + let help = "Defines the composite variables which should be \ + unfolded into individual cells." + let arg_name = "f::v,..." + end) + +module HiddenBases = Varinfo_set + (struct + let option_name = "-dive-hide" + let help = "Defines the variables which must not be \ + displayed in the graph. The dependencies for these bases \ + are not computed either." + let arg_name = "f::v,..." + end) diff --git a/src/plugins/dive/self.mli b/src/plugins/dive/self.mli new file mode 100644 index 0000000000000000000000000000000000000000..f15ce3c96bd820641bf5a421de36bc8181ae046a --- /dev/null +++ b/src/plugins/dive/self.mli @@ -0,0 +1,35 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +include Plugin.S + +module type Varinfo_set = Parameter_sig.Set + with type elt = Cil_types.varinfo + and type t = Cil_datatype.Varinfo.Set.t + +module OutputDot : Parameter_sig.String +module OutputJson : Parameter_sig.String +module DepthLimit : Parameter_sig.Int +module FromFunctionAlarms : Parameter_sig.Kernel_function_set +module FromBases : Varinfo_set +module UnfoldedBases : Varinfo_set +module HiddenBases : Varinfo_set diff --git a/src/plugins/dive/server_interface.ml b/src/plugins/dive/server_interface.ml new file mode 100644 index 0000000000000000000000000000000000000000..c80a64405a8e1a0ef15b57487b4429728382618f --- /dev/null +++ b/src/plugins/dive/server_interface.ml @@ -0,0 +1,219 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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 Server + +(* TODO: state *) +let get_graph = + let graph = ref None in + fun () -> + match !graph with + | Some g -> g + | None -> + let g = Build.create () in + graph := Some g; + g + + +let page = Doc.page (`Plugin "dive") + ~title:"Dive Services" + ~filename:"dive.md" + +module Graph = +struct + type t = Imprecision_graph.t + let syntax = Syntax.any + 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 + let name = "dive-variable-name" + let descr = Markdown.plain "The name of variable of the program" + + let signature = Data.Record.signature ~page ~name ~descr () + + let _fun_field = Data.Record.option signature + ~descr:(Markdown.plain "owner function for a local variable") + (module Data.Jstring) + + let _var_field = Data.Record.field signature + ~descr:(Markdown.plain "variable name") + (module Data.Jstring) + + type t = Cil_types.varinfo + module R = + (val (Data.Record.publish signature): Data.Record.S with type r = t) + + let syntax = R.syntax + + let to_json v = + let varname = v.Cil_types.vname in + let fields = [ "var", `String varname ] in + let fields = match Kernel_function.find_defining_kf v with + | Some kf -> ("fun", `String (Kernel_function.get_name kf)) :: fields + | None -> fields + in + `Assoc fields + + let of_json json = + let open Yojson.Basic.Util in + let funname = + try Some (json |> member "fun" |> to_string) + with Not_found -> None + and varname = json |> member "var" |> to_string in + match funname with + | Some name -> + let kf = + try + Globals.Functions.find_by_name name + with Not_found -> + Data.failure "no function '%s'" name + in + let vi = + try Globals.Vars.find_from_astinfo varname (Cil_types.VLocal kf) + with Not_found -> + try Globals.Vars.find_from_astinfo varname (Cil_types.VFormal kf) + with Not_found -> + Data.failure "no variable '%s' in function '%s'" + varname name + in + vi + | None -> + match + Globals.Syntactic_search.find_in_scope varname Cil_types.Program + with + | Some vi -> vi + | None -> + Data.failure "no global variable '%s'" varname + end) + +module Function = Data.Collection (struct + type t = Cil_types.kernel_function + + let syntax = Syntax.publish ~page ~name:"dive-function-name" + ~synopsis:Syntax.string + ~descr:(Markdown.plain "The name of a function of the program") () + + let to_json kf = + `String (Kernel_function.get_name kf) + + let of_json json = + let open Yojson.Basic.Util in + let name = to_string json in + try + Globals.Functions.find_by_name name + with Not_found -> + Data.failure "no function '%s'" name + end) + +module Node = Data.Collection (struct + type t = Graph_types.node + + let syntax = Syntax.publish ~page ~name:"dive-node" + ~synopsis:Syntax.int + ~descr:(Markdown.plain "A node identifier in the graph") () + + let to_json node = + `Int node.Graph_types.node_key + + let of_json json = + let open Yojson.Basic.Util in + let node_key = to_int json in + try + Build.find_node (get_graph ()) node_key + with Not_found -> + Data.failure "no node '%d' in the current graph" node_key + end) + + +let () = Request.register ~page + ~kind:`GET ~name:"dive.graph" + ~descr:(Markdown.plain "Retrieve the whole graph") + ~input:(module Data.Junit) ~output:(module Graph) + (fun () -> Build.get_graph (get_graph ())) + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.clear" + ~descr:(Markdown.plain "Erase the graph and start over with an empty one") + ~input:(module Data.Junit) ~output:(module Data.Junit) + (fun () -> Build.clear (get_graph ())) + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.add_var" + ~descr:(Markdown.plain "Add a variable to the 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.take_last_differences g + end + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.add_function_alarms" + ~descr:(Markdown.plain "Add all alarms of the given function") + ~input:(module Function) ~output:(module GraphDiff) + begin fun kf -> + let depth = Self.DepthLimit.get () in + let g = get_graph () in + Build.add_function_alarms ~depth g kf; + Build.get_graph g, Build.take_last_differences g + end + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.explore" + ~descr:(Markdown.plain "Explore the graph starting from an existing vertex") + ~input:(module Node) ~output:(module GraphDiff) + begin fun node -> + let depth = Self.DepthLimit.get () in + let g = get_graph () in + Build.explore_from_node ~depth g node; + Build.get_graph g, Build.take_last_differences g + end + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.show" + ~descr:(Markdown.plain "Show the dependencies of an existing vertex") + ~input:(module Node) ~output:(module GraphDiff) + begin fun node -> + let depth = Self.DepthLimit.get () in + let g = get_graph () in + Build.show ~depth g node; + Build.get_graph g, Build.take_last_differences g + end + +let () = Request.register ~page + ~kind:`EXEC ~name:"dive.hide" + ~descr:(Markdown.plain "Hide the dependencies of an existing vertex") + ~input:(module Node) ~output:(module GraphDiff) + begin fun node -> + let g = get_graph () in + Build.hide g node; + Build.get_graph g, Build.take_last_differences g + end diff --git a/src/plugins/dive/server_interface.mli b/src/plugins/dive/server_interface.mli new file mode 100644 index 0000000000000000000000000000000000000000..6b8cd770d93ac0573027b76620aa88ab4cf0316b --- /dev/null +++ b/src/plugins/dive/server_interface.mli @@ -0,0 +1,23 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* 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). *) +(* *) +(**************************************************************************) + +module Variable : Server.Data.S_collection with type t = Cil_types.varinfo diff --git a/src/plugins/dive/tests/dive/assigned_param.i b/src/plugins/dive/tests/dive/assigned_param.i new file mode 100644 index 0000000000000000000000000000000000000000..fb2876d7010ba35b4a66b5c510f4c5b70cf9143f --- /dev/null +++ b/src/plugins/dive/tests/dive/assigned_param.i @@ -0,0 +1,18 @@ +/* run.config +STDOPT: +"-dive-from-variables main::w -dive-depth-limit 5" +*/ + +volatile int nondet; + +int f(int x, int z) +{ + if (nondet) + x = z; + return x; +} + +int main(int x, int z) +{ + int w = f(x,z); + return w + 1; +} diff --git a/src/plugins/dive/tests/dive/callstack_strategy.i b/src/plugins/dive/tests/dive/callstack_strategy.i new file mode 100644 index 0000000000000000000000000000000000000000..b309e51cfc28261a73aedd2f06e2bf8c06668486 --- /dev/null +++ b/src/plugins/dive/tests/dive/callstack_strategy.i @@ -0,0 +1,31 @@ +/* run.config +STDOPT: +"-dive-from-variables g::r -dive-depth-limit 20" +*/ + +float f(float z) { + return z; +} + +float g(float y) { + float z = f(y); + float r = y + z; + return r; +} + +float h(float x) { + float y = f(x); + float r = g(y); + return r; +} + +float i(float x) { + return g(x); +} + + +void main(float a) +{ + h(a); + i(a); +} + diff --git a/src/plugins/dive/tests/dive/const.i b/src/plugins/dive/tests/dive/const.i new file mode 100644 index 0000000000000000000000000000000000000000..7001746bea33cc57f5d2f0d0a678118be3606e08 --- /dev/null +++ b/src/plugins/dive/tests/dive/const.i @@ -0,0 +1,16 @@ +/* run.config +STDOPT: +"-dive-from-variables main::res -dive-depth-limit 4" +*/ + +int f(int x, int y) +{ + const c = x + 1; + int w = y + 1; + return c + w; +} + +int main(int i) +{ + int res = f(i, 2*i); + return res; +} diff --git a/src/plugins/dive/tests/dive/global.i b/src/plugins/dive/tests/dive/global.i new file mode 100644 index 0000000000000000000000000000000000000000..ca08f89790d56aef256c8a8d67fe553d065bbb5d --- /dev/null +++ b/src/plugins/dive/tests/dive/global.i @@ -0,0 +1,14 @@ +/* run.config +STDOPT: +"-dive-from-variables main::z" +*/ + +float g; + +float main(float x) +{ + float y = x + 1; + g = y; + float z = g + x; + return z; +} + diff --git a/src/plugins/dive/tests/dive/oracle/assigned_param.dot b/src/plugins/dive/tests/dive/oracle/assigned_param.dot new file mode 100644 index 0000000000000000000000000000000000000000..ad2fec86899271793323922c055d7bda9074c910 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/assigned_param.dot @@ -0,0 +1,22 @@ +digraph G { + cp0 [label=<w>, shape=box, ]; + cp1 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + + subgraph cluster_cs_1 { label=<main>; cp7;cp5;cp0; + subgraph cluster_cs_2 { label=<f>; cp3;cp1; + }; + }; + + cp1 -> cp0; + cp3 -> cp1; + cp5 -> cp1; + cp7 -> cp3; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/assigned_param.res.oracle b/src/plugins/dive/tests/dive/oracle/assigned_param.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..af19ea9f6ba5fe5cb6d0774249bbb2d09eaf2b97 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/assigned_param.res.oracle @@ -0,0 +1,20 @@ +[kernel] Parsing tests/dive/assigned_param.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva:alarm] tests/dive/assigned_param.i:17: Warning: + signed overflow. assert w + 1 ≤ 2147483647; +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 2 functions analyzed (out of 2): 100% coverage. + In these functions, 6 statements reached (out of 6): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 1 alarm generated by the analysis: + 1 integer overflow + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/assigned_param.dot diff --git a/src/plugins/dive/tests/dive/oracle/callstack_strategy.dot b/src/plugins/dive/tests/dive/oracle/callstack_strategy.dot new file mode 100644 index 0000000000000000000000000000000000000000..2df030b641903b7f93211d1f082a334ffa6e3182 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/callstack_strategy.dot @@ -0,0 +1,44 @@ +digraph G { + cp0 [label=<r>, shape=box, ]; + cp1 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp9 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp11 [label=<a>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp13 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp16 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + + subgraph cluster_cs_1 { label=<g>; cp3;cp1;cp0; + subgraph cluster_cs_4 { label=<f>; cp9; + }; + }; + subgraph cluster_cs_2 { label=<i>; cp5; + }; + subgraph cluster_cs_3 { label=<h>; cp16;cp7; + subgraph cluster_cs_6 { label=<f>; cp13; + }; + }; + subgraph cluster_cs_5 { label=<main>; cp11; + }; + + cp1 -> cp0; + cp1 -> cp9; + cp3 -> cp0; + cp5 -> cp1; + cp7 -> cp1; + cp9 -> cp3; + cp11 -> cp5; + cp11 -> cp16; + cp13 -> cp7; + cp16 -> cp13; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/callstack_strategy.res.oracle b/src/plugins/dive/tests/dive/oracle/callstack_strategy.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..de74d0e71cbc624ac141f2ac234818c9303e05be --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/callstack_strategy.res.oracle @@ -0,0 +1,20 @@ +[kernel] Parsing tests/dive/callstack_strategy.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva:alarm] tests/dive/callstack_strategy.i:11: Warning: + non-finite float value. assert \is_finite(\add_float(y, z)); +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 5 functions analyzed (out of 5): 100% coverage. + In these functions, 12 statements reached (out of 12): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 1 alarm generated by the analysis: + 1 nan or infinite floating-point value + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/callstack_strategy.dot diff --git a/src/plugins/dive/tests/dive/oracle/const.dot b/src/plugins/dive/tests/dive/oracle/const.dot new file mode 100644 index 0000000000000000000000000000000000000000..db4416d80c8653914d98d9052e3e73972659c567 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/const.dot @@ -0,0 +1,29 @@ +digraph G { + cp0 [label=<res>, shape=box, ]; + cp1 [label=<__retres>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<c>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp9 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp11 [label=<i>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled,dotted", ]; + + subgraph cluster_cs_1 { label=<main>; cp11;cp0; + subgraph cluster_cs_2 { label=<f>; cp9;cp7;cp5;cp3;cp1; + }; + }; + + cp1 -> cp0; + cp3 -> cp1; + cp5 -> cp1; + cp7 -> cp3; + cp9 -> cp5; + cp11 -> cp7; + cp11 -> cp9; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/const.res.oracle b/src/plugins/dive/tests/dive/oracle/const.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..d4e8c9e857c1d7380cf4ae5576b221fb12349996 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/const.res.oracle @@ -0,0 +1,28 @@ +[kernel] Parsing tests/dive/const.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva:alarm] tests/dive/const.i:14: Warning: + signed overflow. assert -2147483648 ≤ 2 * i; +[eva:alarm] tests/dive/const.i:14: Warning: + signed overflow. assert 2 * i ≤ 2147483647; +[eva:alarm] tests/dive/const.i:7: Warning: + signed overflow. assert x + 1 ≤ 2147483647; +[eva:alarm] tests/dive/const.i:9: Warning: + signed overflow. assert -2147483648 ≤ c + w; +[eva:alarm] tests/dive/const.i:9: Warning: + signed overflow. assert c + w ≤ 2147483647; +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 2 functions analyzed (out of 2): 100% coverage. + In these functions, 6 statements reached (out of 6): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 5 alarms generated by the analysis: + 5 integer overflows + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/const.dot diff --git a/src/plugins/dive/tests/dive/oracle/global.dot b/src/plugins/dive/tests/dive/oracle/global.dot new file mode 100644 index 0000000000000000000000000000000000000000..b64b502f0e52418d362f35612dfdadb6b03786fe --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/global.dot @@ -0,0 +1,19 @@ +digraph G { + cp0 [label=<z>, shape=box, ]; + cp1 [label=<g>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled,dotted", ]; + + subgraph cluster_cs_1 { label=<main>; cp5;cp3;cp0; + }; + subgraph cluster_file_1 { label=<tests/dive/global.i>; cp1; + }; + + cp1 -> cp0; + cp3 -> cp0; + cp5 -> cp1; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/global.res.oracle b/src/plugins/dive/tests/dive/oracle/global.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..d46beb72b6935e11987bfde52dfdcb6906017be3 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/global.res.oracle @@ -0,0 +1,20 @@ +[kernel] Parsing tests/dive/global.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva:alarm] tests/dive/global.i:11: Warning: + non-finite float value. assert \is_finite(\add_float(g, x)); +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 1 function analyzed (out of 1): 100% coverage. + In this function, 4 statements reached (out of 4): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 1 alarm generated by the analysis: + 1 nan or infinite floating-point value + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/global.dot diff --git a/src/plugins/dive/tests/dive/oracle/per_callstack.dot b/src/plugins/dive/tests/dive/oracle/per_callstack.dot new file mode 100644 index 0000000000000000000000000000000000000000..f2c61f4f447e4496dd5764f14775ac53956e6af3 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/per_callstack.dot @@ -0,0 +1,50 @@ +digraph G { + cp0 [label=<w>, shape=box, ]; + cp1 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<z>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp9 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp11 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp13 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp15 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp17 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp19 [label=<a>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp21 [label=<b>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp23 [label=<c>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + + subgraph cluster_cs_1 { label=<main>; cp23;cp21;cp19;cp5;cp3;cp1;cp0; + subgraph cluster_cs_2 { label=<f>; cp13;cp7; + }; + subgraph cluster_cs_3 { label=<f>; cp15;cp9; + }; + subgraph cluster_cs_4 { label=<f>; cp17;cp11; + }; + }; + + cp1 -> cp0; + cp3 -> cp0; + cp5 -> cp0; + cp7 -> cp1; + cp9 -> cp3; + cp11 -> cp5; + cp13 -> cp7; + cp15 -> cp9; + cp17 -> cp11; + cp19 -> cp13; + cp21 -> cp15; + cp23 -> cp17; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/per_callstack.res.oracle b/src/plugins/dive/tests/dive/oracle/per_callstack.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..bd8ebb87aa38c0d1b4a7b7eef17528671dad13af --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/per_callstack.res.oracle @@ -0,0 +1,22 @@ +[kernel] Parsing tests/dive/per_callstack.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva:alarm] tests/dive/per_callstack.i:15: Warning: + non-finite float value. assert \is_finite(\add_float(x, y)); +[eva:alarm] tests/dive/per_callstack.i:15: Warning: + non-finite float value. assert \is_finite(\add_float(\add_float(x, y), z)); +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 2 functions analyzed (out of 2): 100% coverage. + In these functions, 7 statements reached (out of 7): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 2 alarms generated by the analysis: + 2 nan or infinite floating-point values + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/per_callstack.dot 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 0000000000000000000000000000000000000000..29d345ca69b9a03cab6c7453244add21d00feec4 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/pointed_param.dot @@ -0,0 +1,29 @@ +digraph G { + cp0 [label=<y>, shape=box, ]; + cp1 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<tmp>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<__retres>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp9 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled,dotted", ]; + + subgraph cluster_cs_1 { label=<main>; cp1;cp0; + subgraph cluster_cs_2 { label=<f>; cp9;cp3; + subgraph cluster_cs_3 { label=<g>; cp5; + subgraph cluster_cs_4 { label=<h>; cp7; + }; + }; + }; + }; + + cp1 -> cp0; + cp3 -> cp1; + cp5 -> cp3; + cp7 -> cp5; + cp9 -> cp7; + + } \ 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 0000000000000000000000000000000000000000..986e0eb6f92e87e39f33cc18422b9704f7bf666f --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/pointed_param.res.oracle @@ -0,0 +1,17 @@ +[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 +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 4 functions analyzed (out of 4): 100% coverage. + In these functions, 9 statements reached (out of 9): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 0 alarms generated by the analysis. + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/pointed_param.dot diff --git a/src/plugins/dive/tests/dive/oracle/pointers_to_local.dot b/src/plugins/dive/tests/dive/oracle/pointers_to_local.dot new file mode 100644 index 0000000000000000000000000000000000000000..714f41eccb2a776fad23082a7d007832a0700647 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/pointers_to_local.dot @@ -0,0 +1,20 @@ +digraph G { + cp0 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp1 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp4 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + + subgraph cluster_cs_1 { label=<main>; cp0; + subgraph cluster_cs_4 { label=<f2>; cp4; + }; + }; + + cp0 -> cp0; + cp0 -> cp1; + cp0 -> cp4; + cp1 -> cp0; + cp4 -> cp0; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/pointers_to_local.res.oracle b/src/plugins/dive/tests/dive/oracle/pointers_to_local.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..9e4f75453d868643fa70c8371194aec69ca4f72b --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/pointers_to_local.res.oracle @@ -0,0 +1,17 @@ +[kernel] Parsing tests/dive/pointers_to_local.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 4 functions analyzed (out of 4): 100% coverage. + In these functions, 12 statements reached (out of 12): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 0 alarms generated by the analysis. + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/pointers_to_local.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 0000000000000000000000000000000000000000..1009dfdd36fcdc4c5b0c148f4f40caca4cfde142 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/unfocused_callers.dot @@ -0,0 +1,30 @@ +digraph G { + cp0 [label=<x>, shape=box, ]; + cp1 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp5 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp7 [label=<x>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + + subgraph cluster_cs_1 { label=<g>; cp0; + }; + subgraph cluster_cs_2 { label=<f3>; cp1; + }; + subgraph cluster_cs_3 { label=<f2>; cp3; + }; + subgraph cluster_cs_4 { label=<f1>; cp5; + }; + subgraph cluster_cs_5 { label=<main>; cp7; + }; + + cp1 -> cp0; + cp3 -> cp0; + cp5 -> cp0; + cp7 -> cp1; + cp7 -> cp3; + cp7 -> cp5; + + } \ 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 0000000000000000000000000000000000000000..8e9668ded4a93d97d4cd4671892ea6ad8dd14280 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/unfocused_callers.res.oracle @@ -0,0 +1,26 @@ +[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(\add_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(\add_float(\add_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 +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 5 functions analyzed (out of 5): 100% coverage. + In these functions, 14 statements reached (out of 14): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 2 alarms generated by the analysis: + 2 nan or infinite floating-point values + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[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 new file mode 100644 index 0000000000000000000000000000000000000000..14afa37a197041f2c10b38f50cf371264a6fbdd0 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/various.dot @@ -0,0 +1,65 @@ +digraph G { + cp0 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp1 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp3 [label=<x>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp7 [label=<g>, shape=box, fillcolor="#AACCFF", color="#88AAFF", + style="filled", ]; + cp9 [label=<z>, shape=box, ]; + cp10 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp12 [label=<w>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp14 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp16 [label=<tmp_0>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp18 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp21 [label=<pf>, shape=box, ]; + cp23 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp26 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000", + style="filled", ]; + cp30 [label=<is_nan_or_infinite: \is_finite((float)\mul_double((double)y, (double)2.0))>, + fillcolor="#FF0000", color="#FF0000", shape=doubleoctagon, + style="filled,bold", ]; + cp32 [label=<is_nan_or_infinite: \is_finite(\add_float(y, w))>, + fillcolor="#FF0000", color="#FF0000", shape=doubleoctagon, + style="filled,bold", ]; + + subgraph cluster_cs_1 { label=<f>; cp30;cp1;cp0; + }; + subgraph cluster_cs_2 { label=<main>; cp32;cp21;cp16;cp12;cp10;cp9;cp3; + subgraph cluster_cs_3 { label=<f>; cp18;cp14; + }; + subgraph cluster_cs_4 { label=<f>; cp26;cp23; + }; + }; + subgraph cluster_file_1 { label=<tests/dive/various.i>; cp7; + }; + + cp0 -> cp1; + cp1 -> cp0; + cp1 -> cp30; + cp3 -> cp0; + cp3 -> cp3; + cp3 -> cp14; + cp3 -> cp23; + cp7 -> cp3; + cp10 -> cp9; + cp10 -> cp32; + cp12 -> cp9; + cp12 -> cp32; + cp14 -> cp10; + cp14 -> cp18; + cp16 -> cp12; + cp18 -> cp14; + cp21 -> cp16 [color="#00FF00", ]; + cp23 -> cp16; + cp23 -> cp26; + cp26 -> cp23; + + } \ No newline at end of file diff --git a/src/plugins/dive/tests/dive/oracle/various.res.oracle b/src/plugins/dive/tests/dive/oracle/various.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..f85c474b7ad82da6ab2816618e7c8d41a65ff3e2 --- /dev/null +++ b/src/plugins/dive/tests/dive/oracle/various.res.oracle @@ -0,0 +1,24 @@ +[kernel] Parsing tests/dive/various.i (no preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva] tests/dive/various.i:10: starting to merge loop iterations +[eva:alarm] tests/dive/various.i:13: Warning: + non-finite float value. + assert \is_finite((float)\mul_double((double)y, (double)2.0)); +[eva:alarm] tests/dive/various.i:27: Warning: + non-finite float value. assert \is_finite(\add_float(y, w)); +[eva] done for function main +[eva:summary] ====== ANALYSIS SUMMARY ====== + ---------------------------------------------------------------------------- + 2 functions analyzed (out of 2): 100% coverage. + In these functions, 18 statements reached (out of 18): 100% coverage. + ---------------------------------------------------------------------------- + No errors or warnings raised during the analysis. + ---------------------------------------------------------------------------- + 2 alarms generated by the analysis: + 2 nan or infinite floating-point values + ---------------------------------------------------------------------------- + No logical properties have been reached by the analysis. + ---------------------------------------------------------------------------- +[dive] output to tests/dive/result/various.dot diff --git a/src/plugins/dive/tests/dive/per_callstack.i b/src/plugins/dive/tests/dive/per_callstack.i new file mode 100644 index 0000000000000000000000000000000000000000..5a5d81b22cc9469962a5696f6816f540a7ece1b6 --- /dev/null +++ b/src/plugins/dive/tests/dive/per_callstack.i @@ -0,0 +1,18 @@ +/* run.config +STDOPT: +"-dive-from-variables main::w -dive-depth-limit 20" +*/ + +float f(float x) { + float y = x + 1; + return y; +} + +float main(float a, float b, float c) +{ + float x = f(a); + float y = f(b); + float z = f(c); + float w = x + y + z; + return w; +} + 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 0000000000000000000000000000000000000000..e6bd4f47db024a2e99baa09be7a14905108f0fe2 --- /dev/null +++ b/src/plugins/dive/tests/dive/pointed_param.i @@ -0,0 +1,25 @@ +/* run.config +STDOPT: +"-dive-from-variables 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/pointers_to_local.i b/src/plugins/dive/tests/dive/pointers_to_local.i new file mode 100644 index 0000000000000000000000000000000000000000..a1f5c7af079921b0e106469c4d1cf09a4c4d0725 --- /dev/null +++ b/src/plugins/dive/tests/dive/pointers_to_local.i @@ -0,0 +1,25 @@ +/* run.config +STDOPT: +"-dive-from-variables main::x -dive-depth-limit 5" +*/ + +void g(float *p) { + float x = *p; + *p = x + 1.0f; +} + +void f1(float *p) { + g(p); +} + +void f2(float *p) { + float x = *p; + *p += x + 2.0f; +} + +float main() +{ + float x = 0.0f; + f1(&x); + f2(&x); + return x; +} 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 0000000000000000000000000000000000000000..44723b2c92f6c930b240cf92c2e8c9e7225c7c75 --- /dev/null +++ b/src/plugins/dive/tests/dive/unfocused_callers.i @@ -0,0 +1,19 @@ +/* run.config +STDOPT: +"-dive-from-variables 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/dive/various.i b/src/plugins/dive/tests/dive/various.i new file mode 100644 index 0000000000000000000000000000000000000000..62468d375e7ffa0f73f17935d09e60d152884a81 --- /dev/null +++ b/src/plugins/dive/tests/dive/various.i @@ -0,0 +1,29 @@ +/* run.config +STDOPT: +"-dive-from-variables main::z,f::x2 -dive-from-alarms f,main -dive-depth-limit 5" +*/ + +float g; + +float f(float x2) +{ + float y; + for (int i = 0 ; i < 10 ; i++) + { + y = x2 + 1.0; + x2 = y * 2.0; + } + + return x2; +} + +void main() +{ + float (*pf)(float) = &f; + + float x = 3.0 + g; + float y = f(x); + x = x + 0.5f; + float w = (*pf)(x); + float z = y + w + 1.0; +} + diff --git a/src/plugins/dive/tests/test_config b/src/plugins/dive/tests/test_config new file mode 100644 index 0000000000000000000000000000000000000000..9ff9387337545ed97c88920ed5c79a722351eaa8 --- /dev/null +++ b/src/plugins/dive/tests/test_config @@ -0,0 +1,3 @@ +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-dot @PTEST_RESULT@/@PTEST_NAME@ +FILTER: perl -0777 -pe 's/\[server[^[]*//g'