diff --git a/ivette/src/dome/renderer/layout/style.css b/ivette/src/dome/renderer/layout/style.css index a2e0fca13db2095fde266199646103b6c8264367..41dfbcc3254a14b6e4c9ff107da9d50735193f12 100644 --- a/ivette/src/dome/renderer/layout/style.css +++ b/ivette/src/dome/renderer/layout/style.css @@ -103,7 +103,6 @@ .dome-xSplitter-block { display: block; - overflow: hidden; position: relative; width: 100%; height: 100%; diff --git a/ivette/src/frama-c/plugins/eva/DomainStates.tsx b/ivette/src/frama-c/plugins/eva/DomainStates.tsx new file mode 100644 index 0000000000000000000000000000000000000000..dab3218c24ad0398e44e8b525e703ed1508fcf7b --- /dev/null +++ b/ivette/src/frama-c/plugins/eva/DomainStates.tsx @@ -0,0 +1,129 @@ +/* ************************************************************************ */ +/* */ +/* This file is part of Frama-C. */ +/* */ +/* Copyright (C) 2007-2022 */ +/* 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). */ +/* */ +/* ************************************************************************ */ + +// React & Dome +import React from 'react'; +import * as Ivette from 'ivette'; +import * as States from 'frama-c/states'; +import { GlobalState, useGlobalState } from 'dome/data/states'; +import * as Eva from 'frama-c/plugins/eva/api/general'; +import * as Boxes from 'dome/layout/boxes'; +import { HSplit } from 'dome/layout/splitters'; +import { Text } from 'frama-c/richtext'; +import { Checkbox, Select } from 'dome/controls/buttons'; +import { Label } from 'dome/controls/labels'; + +const globalSelectedDomain = new GlobalState(""); +const globalFilter = new GlobalState(true); + +export function EvaStates(): JSX.Element { + const [selection] = States.useSelection(); + const selectedLoc = selection?.current; + const marker = selectedLoc?.marker; + const [domains, setDomains] = React.useState<string[]>([]); + const [selected, setSelected] = useGlobalState(globalSelectedDomain); + const [stateBefore, setStateBefore] = React.useState(""); + const [stateAfter, setStateAfter] = React.useState(""); + const [filter, setFilter] = useGlobalState(globalFilter); + + const requestArg = marker ? [marker, filter] : undefined; + const states = States.useRequest(Eva.getStates, requestArg); + + React.useEffect(() => { + if (states && states.length > 0) { + const names = states.map((d) => d[0]); + setDomains(names); + if (!names.includes(selected)) + setSelected(names[0]); + const selectedDomain = states.find((d) => d[0] === selected); + if (selectedDomain) { + setStateBefore(selectedDomain[1]); + setStateAfter(selectedDomain[2]); + } + } else + setDomains([]); + }, [states, selected, setSelected]); + + if (domains.length === 0) + return (<></>); + + function makeOption(name: string): React.ReactNode { + return <option value={name}>{name}</option>; + } + const list = React.Children.toArray(domains.map(makeOption)); + + return ( + <> + <Boxes.Hbox className="domain-state-box"> + <Label>Domain: </Label> + <Select + title="Select the analysis domain to be shown" + value={selected} + onChange={(domain) => setSelected(domain ?? "")} + > + {list} + </Select> + <Boxes.Filler/> + <Checkbox + label="Filtered state" + title="If enabled, only the part of the states relevant to the + selected marker are shown, for domains supporting this feature. + For other domains or if disabled, entire domain states are printed. + Beware that entire domain states can be very large." + value={filter} + onChange={setFilter} + /> + </Boxes.Hbox> + <Boxes.Scroll> + <HSplit + settings="ivette.eva.domainStates.beforeAfterSplit" + > + <div className="domain-state-box"> + State before the selected statement: + <Text className="domain-state" text={stateBefore} /> + </div> + <div className="domain-state-box"> + State after the selected statement: + <Text className="domain-state" text={stateAfter} /> + </div> + </HSplit> + </Boxes.Scroll> + </>); +} + +function EvaStatesComponent(): JSX.Element { + return ( + <> + <Ivette.TitleBar /> + <EvaStates /> + </> + ); +} + +Ivette.registerComponent({ + id: 'frama-c.plugins.domain_states', + group: 'frama-c.plugins', + rank: 10, + label: 'Eva States', + title: 'States of the Eva analysis', + children: <EvaStatesComponent />, +}); diff --git a/ivette/src/frama-c/plugins/eva/api/general/index.ts b/ivette/src/frama-c/plugins/eva/api/general/index.ts index 55d8b6b7768e3e1717a91f126b192ce459c819bd..3ed2ea6b595d436fd82035130a5f13647d828103 100644 --- a/ivette/src/frama-c/plugins/eva/api/general/index.ts +++ b/ivette/src/frama-c/plugins/eva/api/general/index.ts @@ -580,4 +580,30 @@ export const functionStats: State.Array< functionStatsData > = functionStats_internal; +const getStates_internal: Server.GetRequest< + [ marker, boolean ], + [ string, string, string ][] + > = { + kind: Server.RqKind.GET, + name: 'plugins.eva.general.getStates', + input: Json.jTry( + Json.jPair( + jMarkerSafe, + Json.jFail(Json.jBoolean,'Boolean expected'), + )), + output: Json.jList( + Json.jTry( + Json.jTriple( + Json.jFail(Json.jString,'String expected'), + Json.jFail(Json.jString,'String expected'), + Json.jFail(Json.jString,'String expected'), + ))), + signals: [], +}; +/** Get the domain states about the given marker */ +export const getStates: Server.GetRequest< + [ marker, boolean ], + [ string, string, string ][] + >= getStates_internal; + /* ------------------------------------- */ diff --git a/ivette/src/frama-c/plugins/eva/index.tsx b/ivette/src/frama-c/plugins/eva/index.tsx index 8aeb22bb817b5b6b873944d8b6f18ab43d2c4dff..36b6dc1d5ba54d5aede3104b1b40d767a38c784d 100644 --- a/ivette/src/frama-c/plugins/eva/index.tsx +++ b/ivette/src/frama-c/plugins/eva/index.tsx @@ -28,6 +28,7 @@ import * as Ivette from 'ivette'; import { } from 'frama-c/plugins/eva/valuetable'; import { } from './Summary'; import { } from './Coverage'; +import { } from './DomainStates'; import './style.css'; // -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/plugins/eva/style.css b/ivette/src/frama-c/plugins/eva/style.css index e4a17e65e23a5e3baf3af13f49d6f319b4c86f35..2d6367d5845267d7a8f76eaca15084b32936e1e0 100644 --- a/ivette/src/frama-c/plugins/eva/style.css +++ b/ivette/src/frama-c/plugins/eva/style.css @@ -317,3 +317,20 @@ tr:first-of-type > .eva-table-callsite-box { } /* -------------------------------------------------------------------------- */ +/* --- Domains state print --- */ +/* -------------------------------------------------------------------------- */ + +.domain-state-box { + margin: 6px; +} + +.domain-state { + cursor: auto; + user-select: text; + overflow: auto; + padding: 6px; + margin-top: 6px; + background-color: var(--background-report); +} + +/* -------------------------------------------------------------------------- */ diff --git a/src/kernel_services/abstract_interp/locations.ml b/src/kernel_services/abstract_interp/locations.ml index d330023067eb7aa336fad8020cabdb397368f45c..1a19ba3926ebeb591bac02fb259657b22c9332d1 100644 --- a/src/kernel_services/abstract_interp/locations.ml +++ b/src/kernel_services/abstract_interp/locations.ml @@ -476,6 +476,8 @@ module Zone = struct Base.SetLattice.mem b top_param | Map m -> M.mem b m + let get_bases = get_keys + let shape x = x let fold2_join_heterogeneous ~cache ~empty_left ~empty_right ~both ~join ~empty = diff --git a/src/kernel_services/abstract_interp/locations.mli b/src/kernel_services/abstract_interp/locations.mli index e071ac4420fcfa507a7e8c648830a58e0d15bc2a..982221c633d915dfc66a3f6c62ab94c7c76fa2b4 100644 --- a/src/kernel_services/abstract_interp/locations.mli +++ b/src/kernel_services/abstract_interp/locations.mli @@ -265,6 +265,10 @@ module Zone : sig @since Carbon-20101201 *) + val get_bases : t -> Base.SetLattice.t + (** Returns the bases contained by the given zone. Never fails, but + may return [Base.SetLattice.Top]. *) + val intersects : t -> t -> bool (** Assuming that [z1] and [z2] only contain valid bases, diff --git a/src/plugins/value/api/general_requests.ml b/src/plugins/value/api/general_requests.ml index 8a6068698cb4dc5568e7d35a761ccc781bc291a9..eb76164294fca8a88a3e39f3c1937a952a9308e1 100644 --- a/src/plugins/value/api/general_requests.ml +++ b/src/plugins/value/api/general_requests.ml @@ -261,7 +261,7 @@ let _array = model -(* ----- Analysis statistics -------------------------------------------- *) +(* ----- Analysis statistics ------------------------------------------------ *) module AlarmCategory = struct open Server.Data @@ -470,4 +470,76 @@ let _array = model -(**************************************************************************) +(* ----- Domains states ----------------------------------------------------- *) + +let compute_lval_deps request lval = + let zone = Results.lval_deps lval request in + Locations.Zone.get_bases zone + +let compute_expr_deps request expr = + let zone = Results.expr_deps expr request in + Locations.Zone.get_bases zone + +let compute_instr_deps request = function + | Set (lval, expr, _) -> + Base.SetLattice.join + (compute_lval_deps request lval) + (compute_expr_deps request expr) + | Local_init (vi, AssignInit (SingleInit expr), _) -> + Base.SetLattice.join + (Base.SetLattice.inject_singleton (Base.of_varinfo vi)) + (compute_expr_deps request expr) + | _ -> Base.SetLattice.empty + +let compute_stmt_deps request stmt = + match stmt.skind with + | Instr (instr) -> compute_instr_deps request instr + | If (expr, _, _, _) -> compute_expr_deps request expr + | _ -> Base.SetLattice.empty + +let compute_marker_deps request = function + | Printer_tag.PStmt (_, stmt) + | PStmtStart (_, stmt) -> compute_stmt_deps request stmt + | PLval (_, _, lval) -> compute_lval_deps request lval + | PExp (_, _, expr) -> compute_expr_deps request expr + | PVDecl (_, _, vi) -> Base.SetLattice.inject_singleton (Base.of_varinfo vi) + | _ -> Base.SetLattice.empty + +let get_filtered_state request marker = + let bases = compute_marker_deps request marker in + match bases with + | Base.SetLattice.Top -> Results.print_states request + | Base.SetLattice.Set bases -> + if Base.Hptset.is_empty bases + then [] + else Results.print_states ~filter:bases request + +let get_state filter request marker = + if filter + then get_filtered_state request marker + else Results.print_states request + +let get_states (marker, filter) = + let kinstr = Printer_tag.ki_of_localizable marker in + match kinstr with + | Kglobal -> [] + | Kstmt stmt -> + let states_before = get_state filter (Results.before stmt) marker in + let states_after = get_state filter (Results.after stmt) marker in + match states_before, states_after with + | [], _ -> List.map (fun (name, after) -> name, "", after) states_after + | _, [] -> List.map (fun (name, before) -> name, before, "") states_before + | _, _ -> + let join (name, before) (name', after) = + assert (name = name'); + name, before, after + in + List.rev_map2 join states_before states_after + +let () = Request.register ~package + ~kind:`GET ~name:"getStates" + ~descr:(Markdown.plain "Get the domain states about the given marker") + ~input:(module Data.Jpair (Kernel_ast.Marker) (Data.Jbool)) + ~output:(module Data.Jlist + (Data.Jtriple (Data.Jstring) (Data.Jstring) (Data.Jstring))) + get_states diff --git a/src/plugins/value/domains/abstract_domain.mli b/src/plugins/value/domains/abstract_domain.mli index e8536eb1f6e16c262d723f096cf919917e47eb29..6e36e58388bb196ca4fad8487d8c4a87a3863c2b 100644 --- a/src/plugins/value/domains/abstract_domain.mli +++ b/src/plugins/value/domains/abstract_domain.mli @@ -338,19 +338,24 @@ module type Reuse = sig disables MemExec. *) val relate: kernel_function -> Base.Hptset.t -> t -> Base.SetLattice.t - (** [filter kf kind bases states] reduces the state [state] to only keep + (** [filter kind bases states] reduces the state [state] to only keep properties about [bases] — it is a projection on the set of [bases]. It allows reusing an analysis of [kf] from an initial state [pre] to a final state [post]. - If [kind] is `Pre, [state] is the initial state [pre], and [bases] - includes all inputs of [kf] and satisfies [relate kf bases state = bases]. - If [kind] is `Post, [state] is the final state [post], and [bases] + If [kind] is [`Pre kf], [state] is the initial state of function [kf], + and [bases] includes all inputs of [kf] and satisfies + [relate kf bases state = bases]. + If [kind] is [`Post kf], [state] is the final state of [kf], and [bases] includes all inputs and outputs of [kf]. Afterwards, the two resulting states [reduced_pre] and [reduced_post] are used as follow: when [kf] should be analyzed with the initial state [s], if [filter kf `Pre s = reduced_pre], then the analysis is skipped, and - [reuse kf s reduced_post] is used as its final state instead. *) - val filter: kernel_function -> [`Pre | `Post] -> Base.Hptset.t -> t -> t + [reuse kf s reduced_post] is used as its final state instead. + If [kind] is [`Print], the state is reduced before printing it for the + end-user. *) + val filter: + [`Pre of kernel_function | `Post of kernel_function | `Print ] -> + Base.Hptset.t -> t -> t (** [reuse kf bases current_input previous_output] merges the initial state [current_input] with a final state [previous_output] from a previous @@ -376,6 +381,10 @@ module type S = sig type state include Datatype.S_with_collections with type t = state + (* The domain name, shown in some logs and in the GUI. Be sure to redefine a + clear and simple name instead of the default one coming from Datatype. *) + val name: string + (** {3 Lattice Structure } *) include Lattice with type state := t diff --git a/src/plugins/value/domains/cvalue/cvalue_domain.ml b/src/plugins/value/domains/cvalue/cvalue_domain.ml index 77f90be12ff69cc314f8da40cba6f39990e344c1..68d1f194f995758cef798ecbed61fbfd4c22ce52 100644 --- a/src/plugins/value/domains/cvalue/cvalue_domain.ml +++ b/src/plugins/value/domains/cvalue/cvalue_domain.ml @@ -181,7 +181,8 @@ module State = struct let mem_project = Datatype.never_any_project end ) - let key = Structure.Key_Domain.create_key "cvalue_domain" + let name = "cvalue" + let key = Structure.Key_Domain.create_key name type value = Model.value type location = Model.location @@ -287,7 +288,7 @@ module State = struct let relate _kf _bases _state = Base.SetLattice.empty (* Auxiliary function that keeps only some bases inside a memory state *) - let filter _kf _kind bases (state, clob) = + let filter _kind bases (state, clob) = Cvalue.Model.filter_by_shape bases state, clob let reuse _ _ ~current_input:(state, _) ~previous_output:(output, clob) = diff --git a/src/plugins/value/domains/domain_builder.ml b/src/plugins/value/domains/domain_builder.ml index 141c643cc703d84b082afa5a7af9e5b6355a0b92..bea7217028e5f618e54498e1c2d54c4bf404fc6e 100644 --- a/src/plugins/value/domains/domain_builder.ml +++ b/src/plugins/value/domains/domain_builder.ml @@ -46,7 +46,9 @@ module type LeafDomain = sig val incr_loop_counter: stmt -> t -> t val leave_loop: stmt -> t -> t - val filter: kernel_function -> [`Pre | `Post] -> Base.Hptset.t -> t -> t + val filter: + [`Pre of kernel_function | `Post of kernel_function | `Print ] -> + Base.Hptset.t -> t -> t val reuse: kernel_function -> Base.Hptset.t -> current_input:t -> previous_output:t -> t @@ -72,7 +74,7 @@ module Complete (Domain: InputDomain) = struct let incr_loop_counter _stmt state = state let leave_loop _stmt state = state - let filter _kf _kind _bases state = state + let filter _kind _bases state = state let reuse _kf _bases ~current_input:_ ~previous_output = previous_output let show_expr _valuation _state fmt _expr = @@ -325,6 +327,7 @@ module Restrict module I = struct let module_name = Domain.name ^ " option" end include Datatype.Option_with_collections (D) (I) + let name = Domain.name let default = Domain.top, Mode.all let structure: t Abstract.Domain.structure = @@ -575,9 +578,9 @@ module Restrict | None -> Base.SetLattice.empty | Some (state, _mode) -> Domain.relate kf bases state - let filter kf kind bases = function + let filter kind bases = function | None -> None - | Some (state, mode) -> Some (Domain.filter kf kind bases state, mode) + | Some (state, mode) -> Some (Domain.filter kind bases state, mode) let reuse kf bases ~current_input ~previous_output = match current_input, previous_output with diff --git a/src/plugins/value/domains/domain_builder.mli b/src/plugins/value/domains/domain_builder.mli index bf3c05b8f0cc79efed1e2e4ec30131f737bd49df..d4356a571fd6e3ed178c3f7d62037167d36816ae 100644 --- a/src/plugins/value/domains/domain_builder.mli +++ b/src/plugins/value/domains/domain_builder.mli @@ -27,6 +27,8 @@ open Cil_types open Eval module type InputDomain = sig + (* The [name] value from Datatype should be the domain name and will be used + in some logs and in the GUI for the end-user. *) include Datatype.S val top: t val join: t -> t -> t @@ -52,7 +54,9 @@ module type LeafDomain = sig val incr_loop_counter: stmt -> t -> t val leave_loop: stmt -> t -> t - val filter: kernel_function -> [`Pre | `Post] -> Base.Hptset.t -> t -> t + val filter: + [`Pre of kernel_function | `Post of kernel_function | `Print ] -> + Base.Hptset.t -> t -> t val reuse: kernel_function -> Base.Hptset.t -> current_input:t -> previous_output:t -> t diff --git a/src/plugins/value/domains/domain_product.ml b/src/plugins/value/domains/domain_product.ml index 550a86f3d45fd987f474e297732ee21553b1f157..52abf820dee9979a0b9b0fc711dfccce911fa939 100644 --- a/src/plugins/value/domains/domain_product.ml +++ b/src/plugins/value/domains/domain_product.ml @@ -42,14 +42,15 @@ module Make } let () = incr counter - let name = Left.name ^ "*" ^ Right.name ^ - "(" ^ string_of_int !counter ^ ")" + let unique_name = Left.name ^ "*" ^ Right.name ^ + "(" ^ string_of_int !counter ^ ")" include Datatype.Pair_with_collections (Left) (Right) - (struct let module_name = name end) + (struct let module_name = unique_name end) type state = t + let name = Left.name ^ " * " ^ Right.name let structure = Abstract.Domain.Node (Left.structure, Right.structure) @@ -292,8 +293,8 @@ module Make let relate kf bases (left, right) = Base.SetLattice.join (Left.relate kf bases left) (Right.relate kf bases right) - let filter kf kind bases (left, right) = - Left.filter kf kind bases left, Right.filter kf kind bases right + let filter kind bases (left, right) = + Left.filter kind bases left, Right.filter kind bases right let reuse kf bases ~current_input ~previous_output = let left_input, right_input = current_input and left_output, right_output = previous_output in diff --git a/src/plugins/value/domains/equality/equality_domain.ml b/src/plugins/value/domains/equality/equality_domain.ml index 44604b43c15df1ff2c12a3940b0d0d745d580dff..542d48cc2ce424ece24b54e1cdfc07a6428bab75 100644 --- a/src/plugins/value/domains/equality/equality_domain.ml +++ b/src/plugins/value/domains/equality/equality_domain.ml @@ -121,6 +121,8 @@ module Internal = struct type state = t + let name = "equality" + let log_category = dkey let pretty fmt (eqs, _, _) = Equality.Set.pretty fmt eqs diff --git a/src/plugins/value/domains/gauges/gauges_domain.ml b/src/plugins/value/domains/gauges/gauges_domain.ml index 7a91a6af3f8e2dc22c04e88287d12316f2853dd0..829afa083647c8d8e174c8e8c3a1303b840df1c9 100644 --- a/src/plugins/value/domains/gauges/gauges_domain.ml +++ b/src/plugins/value/domains/gauges/gauges_domain.ml @@ -532,6 +532,8 @@ module G = struct (Datatype.List(Datatype.Pair(Cil_datatype.Stmt)(IterationInfo))) (struct let module_name = "Values.Gauges_domain.G" end) + let name = "gauges" + let empty = MV.empty, [] let top (state: t) : t = let top_iteration_info = function diff --git a/src/plugins/value/domains/inout_domain.ml b/src/plugins/value/domains/inout_domain.ml index 8028b618a1d7e5df61659ebd87c72c7921f8ab96..63af3599eafb50965cc660d38e85473fa0d0a957 100644 --- a/src/plugins/value/domains/inout_domain.ml +++ b/src/plugins/value/domains/inout_domain.ml @@ -44,7 +44,7 @@ module LatticeInout = struct include Datatype.Serializable_undefined type t = inout - let name = "Value.Inout.t" + let name = "inout" let reprs = [ { over_outputs = List.hd Zone.reprs; diff --git a/src/plugins/value/domains/multidim/multidim_domain.ml b/src/plugins/value/domains/multidim/multidim_domain.ml index 2f6459d9c5c63631f202e43bf47504a6dfb34690..e1300c7d470e968079b423f3a7a7e5ab2ac2476e 100644 --- a/src/plugins/value/domains/multidim/multidim_domain.ml +++ b/src/plugins/value/domains/multidim/multidim_domain.ml @@ -363,6 +363,8 @@ struct (Datatype.Option (Tracking)) (struct let module_name = "DomainLattice" end) + let name = "multidim" + type state = t type value = Value.t type value_or_uninitialized = Value_or_Uninitialized.t @@ -885,7 +887,7 @@ struct let relate _kf _bases _state = Base.SetLattice.empty - let filter _kf _kind bases (base_map,tracked : t) = + let filter _kind bases (base_map,tracked : t) = BaseMap.filter (fun elt -> Base.Hptset.mem elt bases) base_map, Option.map (Tracking.inter bases) tracked diff --git a/src/plugins/value/domains/octagons.ml b/src/plugins/value/domains/octagons.ml index 757ad4d87ecadaba7e9b0bfe9fa985c413fba7f6..b463daa3ac3b49fe3278cbb5e441cc55547f1616 100644 --- a/src/plugins/value/domains/octagons.ml +++ b/src/plugins/value/domains/octagons.ml @@ -701,7 +701,7 @@ module State = struct type t = state include Datatype.Serializable_undefined - let name = "Octagons.State" + let name = "octagon" let structural_descr = Structural_descr.t_record [| Octagons.packed_descr; @@ -1275,7 +1275,7 @@ module Domain = struct in Base.Hptset.fold aux bases Base.SetLattice.empty - let filter _kf _kind bases state = + let filter _kind bases state = if intraprocedural () then state else diff --git a/src/plugins/value/domains/offsm_domain.ml b/src/plugins/value/domains/offsm_domain.ml index 66ffd1337c2b6e88dd72b51d5fbc08a9ccea7c64..396598578c7eb76547068d810671bdde1078ab3b 100644 --- a/src/plugins/value/domains/offsm_domain.ml +++ b/src/plugins/value/domains/offsm_domain.ml @@ -81,6 +81,8 @@ module Memory = struct include Lmap.Make_LOffset(V_Or_Uninitialized)(V_Offsetmap)(Default_offsetmap) + let name = "bitwise" + let widen kf stmt s1 s2 = let wh = Widen.getWidenHints kf stmt in widen wh s1 s2 @@ -197,7 +199,7 @@ module D : Abstract_domain.Leaf (* Memexec *) let relate _kf _bases _state = Base.SetLattice.empty - let filter _kf _kind bases state = + let filter _kind bases state = Memory.filter_by_shape bases state let reuse _kf bases ~current_input:input ~previous_output:output = diff --git a/src/plugins/value/domains/printer_domain.ml b/src/plugins/value/domains/printer_domain.ml index ab4c24f45a6d23b5a03fe815b68502fb7bfd454c..93abea72d3e62b33148dbcc5ede6db82b730a992 100644 --- a/src/plugins/value/domains/printer_domain.ml +++ b/src/plugins/value/domains/printer_domain.ml @@ -34,6 +34,7 @@ module Simple : Simpler_domains.Simple_Cvalue = struct (* In this domain, the states contain nothing. We use [unit] as type formal the state and we reuse [Datatype.Unit] as a base for our domain. *) include Datatype.Unit + let name = "printer" (* --- Lattice operators --- *) diff --git a/src/plugins/value/domains/simple_memory.ml b/src/plugins/value/domains/simple_memory.ml index 33bae1ed3a01b54fba5d06cbbfcb0fdf77549c77..a0ba820a46fc63cde8dd2449ba2787a536ac2c76 100644 --- a/src/plugins/value/domains/simple_memory.ml +++ b/src/plugins/value/domains/simple_memory.ml @@ -47,7 +47,7 @@ module type S = sig val fold: (Base.t -> value -> 'a -> 'a) -> t -> 'a -> 'a end -module Make_Memory (Value: Value) = struct +module Make_Memory (Info: sig val name: string end) (Value: Value) = struct module Initial_Values = struct let v = [] end module Deps = struct let l = [Ast.self] end @@ -55,8 +55,10 @@ module Make_Memory (Value: Value) = struct include Hptmap.Make (Base.Base) (Value) (Hptmap.Comp_unused) (Initial_Values) (Deps) + let name = Info.name + let cache_name s = - Hptmap_sig.PersistentCache ("Value." ^ Value.name ^ "." ^ s) + Hptmap_sig.PersistentCache ("Value." ^ name ^ "." ^ s) let disjoint_union = let cache = cache_name "union" in @@ -186,7 +188,7 @@ module Make_Domain (Info: sig val name: string end) (Value: Value) = struct try Some (Hashtbl.find table name) with Not_found -> None - module M = Make_Memory (Value) + module M = Make_Memory (Info) (Value) include M include Domain_builder.Complete (M) @@ -325,7 +327,7 @@ module Make_Domain (Info: sig val name: string end) (Value: Value) = struct let relate _kf _bases _state = Base.SetLattice.empty - let filter _kf _kind bases state = + let filter _kind bases state = M.filter (fun elt -> Base.Hptset.mem elt bases) state let reuse _kf bases ~current_input ~previous_output = diff --git a/src/plugins/value/domains/simple_memory.mli b/src/plugins/value/domains/simple_memory.mli index a194b4fe98797e8aa3438a4109d488bcf1cdb048..bbbd93735a956fa1ca8c9f9b9cb14a30a2d562b4 100644 --- a/src/plugins/value/domains/simple_memory.mli +++ b/src/plugins/value/domains/simple_memory.mli @@ -86,7 +86,7 @@ module type S = sig end (* Builds a memory from a value abstraction. *) -module Make_Memory (Value: Value) : sig +module Make_Memory (Info: sig val name: string end) (Value: Value) : sig include Datatype.S_with_collections include S with type t := t and type value := Value.t diff --git a/src/plugins/value/domains/symbolic_locs.ml b/src/plugins/value/domains/symbolic_locs.ml index 0d95237c6476678c6147b3ece9ad12a34fda9616..5ab410de611174dfee0f7a2ec1e9c1b20f128cea 100644 --- a/src/plugins/value/domains/symbolic_locs.ml +++ b/src/plugins/value/domains/symbolic_locs.ml @@ -182,7 +182,7 @@ module Memory = struct include Datatype.Serializable_undefined type t = memory - let name = "Value.Symbolic_locs.Memory.t" + let name = "symbolic-locations" let reprs = [ { values = List.hd K2V.M.reprs; zones = List.hd K2Z.reprs; @@ -603,7 +603,7 @@ module D : Abstract_domain.Leaf by itself a variable read or written by f to a variable that is not. *) let relate _kf _bases _state = Base.SetLattice.empty - let filter _kf _kind = Memory.filter + let filter _kind = Memory.filter (* Efficient version of [reuse], but the resulting state does not satisfy the [_check state], as some extra dependenies of keys removed from the diff --git a/src/plugins/value/domains/taint_domain.ml b/src/plugins/value/domains/taint_domain.ml index 7d46ca1e8094212d0653b9db9d465f8deb6b053d..f32bc939b956f7adb1b141cb95258b418d0bb4fb 100644 --- a/src/plugins/value/domains/taint_domain.ml +++ b/src/plugins/value/domains/taint_domain.ml @@ -75,7 +75,7 @@ module LatticeTaint = struct type t = taint - let name = "Value.Taint.t" + let name = "taint" let reprs = [ { locs_data = List.hd Zone.reprs; @@ -369,7 +369,7 @@ module TaintDomain = struct (* MemExec cache. *) let relate _kf _bases _state = Base.SetLattice.empty - let filter _kf _kind bases state = + let filter _kind bases state = let filter_base = Zone.filter_base (fun b -> Base.Hptset.mem b bases) in { state with locs_data = filter_base state.locs_data; locs_control = filter_base state.locs_control; diff --git a/src/plugins/value/domains/traces_domain.ml b/src/plugins/value/domains/traces_domain.ml index 52e14219be6b7f3d87404deb6e778f55890cc0a2..98cd954008c82b4b7f0fd80fbe920b5bbea07c5c 100644 --- a/src/plugins/value/domains/traces_domain.ml +++ b/src/plugins/value/domains/traces_domain.ml @@ -532,7 +532,7 @@ module Traces = struct include Datatype.Serializable_undefined type t = state - let name = "Value.Traces_domain.Traces.state" + let name = "traces" let reprs = [empty] diff --git a/src/plugins/value/domains/unit_domain.ml b/src/plugins/value/domains/unit_domain.ml index 5307ef978d60d4ceeaf3a94501935175e8cfabc8..d7f7bb0a24495a638422370353bf576bbfee0e63 100644 --- a/src/plugins/value/domains/unit_domain.ml +++ b/src/plugins/value/domains/unit_domain.ml @@ -27,7 +27,7 @@ module Static = struct include Datatype.Unit type state = t - let name = "Unit domain" + let name = "unit" let log_category = log_key let structure = Abstract.Domain.Unit diff --git a/src/plugins/value/engine/abstractions.ml b/src/plugins/value/engine/abstractions.ml index fedceb3f36ff2cd185349b8eb1b5f5303ce05829..c2221af95912657e3c1c5537a18c7ec5d8b94b5e 100644 --- a/src/plugins/value/engine/abstractions.ml +++ b/src/plugins/value/engine/abstractions.ml @@ -342,7 +342,6 @@ let add_domain (type v) dname mode (abstraction: v abstraction) (module Acc: Acc (* Set the name of the domain. *) let module Domain = struct include (val domain) - let name = dname module Store = struct include Store let register_global_state storage state = diff --git a/src/plugins/value/engine/mem_exec.ml b/src/plugins/value/engine/mem_exec.ml index ecd7a20c34ac6a793fb64e296ecdde05306f9006..cf8268830c4cdfa967b46f8527067d1a27423d03 100644 --- a/src/plugins/value/engine/mem_exec.ml +++ b/src/plugins/value/engine/mem_exec.ml @@ -181,7 +181,7 @@ module Make | Base.SetLattice.Top -> raise TooImprecise | Base.SetLattice.Set bases -> bases in - let state_input = Domain.filter kf `Pre input_bases input_state in + let state_input = Domain.filter (`Pre kf) input_bases input_state in (* Outputs bases, that is bases that are copy-pasted, also include input bases. Indeed, those may get reduced during the call. *) let all_output_bases = @@ -197,7 +197,7 @@ module Make Extlib.opt_fold Base.Hptset.add return_base all_output_bases in let clear (key,state) = - key, Domain.filter kf `Post all_output_bases state + key, Domain.filter (`Post kf) all_output_bases state in let outputs = List.map clear call_result in let call_number = current_counter () in @@ -241,7 +241,7 @@ module Make then () else (* restrict [state] to the inputs of this call *) - let st_filtered = Domain.filter kf `Pre binputs state in + let st_filtered = Domain.filter (`Pre kf) binputs state in try let bases, outputs, i = Domain.Hashtbl.find hstates st_filtered in (* We have found a previous execution, in which the outputs are diff --git a/src/plugins/value/utils/results.ml b/src/plugins/value/utils/results.ml index 540bc32142af6c42de9b85c1dbca17bc36ab2504..56935b6b2281b2aaef1ea25d1ccc65ad6624b27c 100644 --- a/src/plugins/value/utils/results.ml +++ b/src/plugins/value/utils/results.ml @@ -293,6 +293,26 @@ struct | Some extract -> convert (Response.map_join extract Cvalue.Model.join (get req)) + let print_states ?filter req = + let state = Response.map_join (fun x -> x) A.Dom.join (get req) in + let print (type s) + (key: s Abstract.Domain.key) + (module Domain: Abstract_domain.S with type state = s) + (state: s) acc = + let name = Structure.Key_Domain.name key in + let state = + match filter with + | None -> state + | Some bases -> Domain.filter `Print bases state + in + let str = Format.asprintf "%a" Domain.pretty state in + (name, str) :: acc + in + let polymorphic_fold_fun = A.Dom.{ fold = print } in + match state with + | `Top | `Bottom -> [] + | `Value state -> A.Dom.fold polymorphic_fold_fun state [] + (* Evaluation *) let eval_lval lval req = @@ -487,6 +507,10 @@ let get_cvalue_model req = | Error Bottom -> Cvalue.Model.bottom | Error (Top | DisabledDomain) -> Cvalue.Model.top +let print_states ?filter req = + let module E = Make () in + E.print_states ?filter req + (* Depedencies *) diff --git a/src/plugins/value/utils/results.mli b/src/plugins/value/utils/results.mli index e6c0678569aa2f5e7077e6780f285ac667560a5f..e18f41a032527bbde86a2c4b4122423e202afb9d 100644 --- a/src/plugins/value/utils/results.mli +++ b/src/plugins/value/utils/results.mli @@ -161,6 +161,11 @@ val get_cvalue_model : request -> Cvalue.Model.t (** Returns the Cvalue model. *) val get_cvalue_model_result : request -> Cvalue.Model.t result +(** Returns a textual representation of the internal domain states for the given + request. If [filter] is provided, states are filtered on the given bases + (for domains that support this feature). + Returns a list of pair (name, state) for all available domains. *) +val print_states: ?filter:Base.Hptset.t -> request -> (string * string) list (** Dependencies *) diff --git a/src/plugins/value/utils/structure.ml b/src/plugins/value/utils/structure.ml index 72cda0e26683743613099657926c9643456bb9f9..40733bca78a1e227aad8b2d39b822b546c94bf83 100644 --- a/src/plugins/value/utils/structure.ml +++ b/src/plugins/value/utils/structure.ml @@ -28,6 +28,7 @@ module type Key = sig val create_key: string -> 'a key val eq_type : 'a key -> 'b key -> ('a, 'b) eq option + val name: 'a key -> string val print: 'a key Pretty_utils.formatter val compare: 'a key -> 'b key -> int val equal: 'a key -> 'b key -> bool @@ -82,6 +83,7 @@ module Make () = struct let hash x = x.tag let tag x = x.tag + let name x = x.name let print fmt x = Format.pp_print_string fmt x.name end diff --git a/src/plugins/value/utils/structure.mli b/src/plugins/value/utils/structure.mli index 792d6a8376e50877c30eb25b760484db5f42b29d..f0c6ed245ddc9a7f9e7e3d8659a8a44e9448fb82 100644 --- a/src/plugins/value/utils/structure.mli +++ b/src/plugins/value/utils/structure.mli @@ -34,6 +34,7 @@ module type Key = sig val create_key: string -> 'a key val eq_type : 'a key -> 'b key -> ('a, 'b) eq option + val name: 'a key -> string val print: 'a key Pretty_utils.formatter val compare: 'a key -> 'b key -> int val equal: 'a key -> 'b key -> bool