From c95fcb4d4c2d193b395bcc28cd28c7477b7b3ad8 Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Tue, 8 Feb 2022 23:44:47 +0100 Subject: [PATCH] [Eva] api: document the Results API --- src/plugins/value/Eva.mli | 183 ++++++++++++++++++++++------ src/plugins/value/utils/results.mli | 183 ++++++++++++++++++++++------ 2 files changed, 290 insertions(+), 76 deletions(-) diff --git a/src/plugins/value/Eva.mli b/src/plugins/value/Eva.mli index 7872eb90937..8cde78579fa 100644 --- a/src/plugins/value/Eva.mli +++ b/src/plugins/value/Eva.mli @@ -19,15 +19,39 @@ module Analysis: sig end module Results: sig - (* Usage sketch : + (** Eva's result API is a work-in-progress interface to allow accessing the + analysis results once its completed. It is experimental and is very likely + to change in the future. It aims at replacing [Db.Value] but does not + completely covers all its usages yet. As for now, this interface as some + advantages over Db's : - Eva.Results.(before stmt |> in_callstack cs |> eval_var vi |> as_int |> value ~default:0) + - evaluations uses every available domains and not only Cvalue ; + - the caller may distinguish failure cases when a request is unsucessful ; + - working with callstacks is easy ; + - some common shortcuts are provided (e.g. for extracting ival directly) ; + - overall, individual functions are simpler. - or, if you prefer + The idea behind this API is that requests must be decomposed in several + steps. For instance, to evaluate an expression : - Eva.Results.(as_int (eval_var vi (in_callstack cs (before stmt)))) + 1. first, you have to state where you want to evaluate it, + 2. optionally, you may specify in which callstack, + 3. you choose the expression to evaluate, + 4. you require a destination type to evaluate into. + + Usage sketch : + + Eva.Results.( + before stmt |> in_callstack cs |> + eval_var vi |> as_int |> default 0) + + or equivalently, if you prefer + + Eva.Results.( + default O (as_int (eval_var vi (in_callstack cs (before stmt)))) *) + type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list type request @@ -39,99 +63,182 @@ module Results: sig type error = Bottom | Top | DisabledDomain type 'a result = ('a,error) Result.t + (** Results handling *) + + (** Translates an error to a human readable string. *) val string_of_error : error -> string + (** Pretty printer for errors. *) val pretty_error : Format.formatter -> error -> unit + (** Pretty printer for API's results. *) val pretty_result : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a result -> unit - (* Control point selection *) + (** [default d r] extracts the value of r if r is Ok or use the default value d + otherwise. + Equivalent to [Result.value ~default:d r] *) + val default : 'a -> 'a result -> 'a + + (** Control point selection *) + + (** At the begining of the analysis, but after the initialization of globals. *) val at_start : request + (** At the end of the analysis, after the main function has returned. *) val at_end : request + (** At the start of the given function. *) val at_start_of : Cil_types.kernel_function -> request + (** At the end of the given function. *) val at_end_of : Cil_types.kernel_function -> request + (** Just before a statement is executed. *) val before : Cil_types.stmt -> request + (** Just after a statement is executed. *) val after : Cil_types.stmt -> request + (** Just before a statement or at the begining of analysis. *) val before_kinstr : Cil_types.kinstr -> request + (** Just after a statement or at the end of analysis. *) val after_kinstr : Cil_types.kinstr -> request - (* Callstack selection *) + + (** Callstack selection *) + + (** Only consider the given callstack. Replaces previous calls to [in_callstack] + or [in_callstacks]. *) val in_callstack : callstack -> request -> request + (** Only consider all the callstacks in the given list. If previous calls to + [in_callstack] or [in_callstacks] were done, it takes the intersection. *) val in_callstacks : callstack list -> request -> request + (** Only consider callstacks satisfying the given predicate. Several filters + can be added. If callstacks are also selected with [in_callstack] or + [in_callstacks], only the selected callstacks will be filtered. *) val filter_callstack : (callstack -> bool) -> request -> request - (* Working with callstacks *) + + (** Working with callstacks *) + + (** Retrieves the list of reachable callstacks from the given request. *) val callstacks : request -> callstack list + (** Retrieves, a list of subrequest for each reachable callstack from + the given request. *) val by_callstack : request -> (callstack * request) list + (** Iterate on the reachable callstacks from the request. *) val iter_callstacks : (callstack -> request -> unit) -> request -> unit + (** Fold on the reachable callstacks from the request. *) val fold_callstacks : (callstack -> request -> 'a -> 'a) -> 'a -> request -> 'a - (* State requests *) + + (** State requests *) + + (** Returns the list of expressions which have been infered to be equal to + the given expression by the Equality domain. *) val equality_class : Cil_types.exp -> request -> Cil_types.exp list result + (** Returns the Cvalue model. *) val as_cvalue_model : request -> Cvalue.Model.t result - (* Dependencies *) - val expr_deps : Cil_types.exp -> request -> Locations.Zone.t (* The zone of bits read to evaluate the expression, including adresses computation *) - val lval_deps : Cil_types.lval -> request -> Locations.Zone.t (* The zone of bits read to evaluate the lvalue, including lvalue zone itself *) - val address_deps : Cil_types.lval -> request -> Locations.Zone.t (* The zone of bits read to evaluate the lvalue, excluding the lvalue zone itself *) - (* Evaluation *) + (** Dependencies *) + + (** Computes (an overapproximation of) the zone of each bit that must be read to + evaluate the given expression, including all adresses computations. *) + val expr_deps : Cil_types.exp -> request -> Locations.Zone.t + (** Computes (an overapproximation of) the zone of each bit that must be read to + evaluate the given lvalue, including the lvalue zone itself. *) + val lval_deps : Cil_types.lval -> request -> Locations.Zone.t + (** Computes (an overapproximation of) the zone of each bit that must be read to + evaluate the given lvalue, excluding the lvalue zone itself. *) + val address_deps : Cil_types.lval -> request -> Locations.Zone.t + + + (** Evaluation *) + + (** Returns the variable's values infered by the analysis. *) val eval_var : Cil_types.varinfo -> request -> value evaluation + (** Returns the lvalue's values infered by the analysis. *) val eval_lval : Cil_types.lval -> request -> value evaluation + (** Returns the expression's values infered by the analysis. *) val eval_exp : Cil_types.exp -> request -> value evaluation + (** Returns the lvalue's addresses infered by the analysis. *) val eval_address : Cil_types.lval -> request -> address evaluation - (* Returns the kernel functions into which the given expression may evaluate. - If the callee expression doesn't always evaluate to a function, those - spurious values are ignored. If it always evaluate to a non-function value - then the returned list is empty. - Raises [Stdlib.Invalid_argument] if the callee expression is not an lvalue - without offset. - Also see [callee] for a function which applies directly on Call - statements *) + (** Returns the kernel functions into which the given expression may evaluate. + If the callee expression doesn't always evaluate to a function, those + spurious values are ignored. If it always evaluate to a non-function value + then the returned list is empty. + Raises [Stdlib.Invalid_argument] if the callee expression is not an lvalue + without offset. + Also see [callee] for a function which applies directly on Call + statements. *) val eval_callee : Cil_types.exp -> request -> Kernel_function.t list result - (* Value conversion *) + + (** Evaluated values conversion *) + + (** In all the functions below, if Eva's infered value does not fit in the + required type, [Error Top] is returned, as Top is the only possible + over-approximation of the request. *) + + (** Convert into a singleton ocaml int *) val as_int : value evaluation -> int result + (** Convert into a singleton unbounded integer *) val as_integer : value evaluation -> Integer.t result + (** Convert into a floating point number *) val as_float : value evaluation -> float result + (** Convert into a C number abstraction *) val as_ival : value evaluation -> Ival.t result + (** Convert into an floating point abstraction *) val as_fval : value evaluation -> Fval.t result + (** Convert into a C value abstraction *) val as_cvalue : value evaluation -> Cvalue.V.t result + (** Convert into a C location abstraction *) val as_location : address evaluation -> Locations.location result + (** Convert into a Zone *) val as_zone : ?access:Locations.access -> address evaluation -> Locations.Zone.t result - (* Evaluation properties *) + + (** Evaluation properties *) + + (** Returns whether the evaluated value is initialized or not. If the value have + been evaluated from a Cil expression, it is always considered initialized. + *) val is_initialized : value evaluation -> bool + (** Returns the set of alarms emitted during the evaluation. *) val alarms : 'a evaluation -> Alarms.t list - (* Reachability *) + + (** Reachability *) + + (** Returns true if there are no reachable states for the given request. *) val is_empty : request -> bool + (** Returns true if an evaluation ended to bottom, i.e. if the given expression + or lvalue cannot be evaluated to a valid result for the given request. *) val is_bottom : 'a evaluation -> bool - val is_called : Cil_types.kernel_function -> bool (* called during the analysis, not by the actual program *) - val is_reachable : Cil_types.stmt -> bool (* reachable by the analysis, not by the actual program *) + (** Returns true if a function have been analyzed. *) + val is_called : Cil_types.kernel_function -> bool + (** Returns true if a statement have been reached by the analysis. *) + val is_reachable : Cil_types.stmt -> bool + (** Returns true if a statement have been reached by the analysis, or if + the main function have been analyzed for [Kglobal]. *) val is_reachable_kinstr : Cil_types.kinstr -> bool - (* Callers / Callees / Callsites *) + + (*** Callers / Callees / Callsites *) + + (** Returns the list of infered callers of the given function. *) val callers : Cil_types.kernel_function -> Cil_types.kernel_function list + (** Returns the list of infered callers, and for each of them, the list + of callsites (the call statements) inside. *) val callsites : Cil_types.kernel_function -> (Cil_types.kernel_function * Cil_types.stmt list) list - (* Returns the kernel functions called in the given statement. - If the callee expression doesn't always evaluate to a function, those - spurious values are ignored. If it always evaluate to a non-function value - then the returned list is empty. - Raises [Stdlib.Invalid_argument] if the statement is not a [Call] - instruction or a [Local_init] with [ConsInit] initializer. *) + (** Returns the kernel functions called in the given statement. + If the callee expression doesn't always evaluate to a function, those + spurious values are ignored. If it always evaluate to a non-function value + then the returned list is empty. + Raises [Stdlib.Invalid_argument] if the statement is not a [Call] + instruction or a [Local_init] with [ConsInit] initializer. *) val callee : Cil_types.stmt -> Kernel_function.t list - (* Result conversion *) - (** [default d r] extract the value of r if r is Ok or use the default value d - otherwise. - Equivalent to [Result.value ~default:d r] *) - val default : 'a -> 'a result -> 'a end module Value_results: sig diff --git a/src/plugins/value/utils/results.mli b/src/plugins/value/utils/results.mli index d4030ff471d..e1ade184538 100644 --- a/src/plugins/value/utils/results.mli +++ b/src/plugins/value/utils/results.mli @@ -21,15 +21,39 @@ (**************************************************************************) [@@@ api_start] -(* Usage sketch : +(** Eva's result API is a work-in-progress interface to allow accessing the + analysis results once its completed. It is experimental and is very likely + to change in the future. It aims at replacing [Db.Value] but does not + completely covers all its usages yet. As for now, this interface as some + advantages over Db's : - Eva.Results.(before stmt |> in_callstack cs |> eval_var vi |> as_int |> value ~default:0) + - evaluations uses every available domains and not only Cvalue ; + - the caller may distinguish failure cases when a request is unsucessful ; + - working with callstacks is easy ; + - some common shortcuts are provided (e.g. for extracting ival directly) ; + - overall, individual functions are simpler. - or, if you prefer + The idea behind this API is that requests must be decomposed in several + steps. For instance, to evaluate an expression : - Eva.Results.(as_int (eval_var vi (in_callstack cs (before stmt)))) + 1. first, you have to state where you want to evaluate it, + 2. optionally, you may specify in which callstack, + 3. you choose the expression to evaluate, + 4. you require a destination type to evaluate into. + + Usage sketch : + + Eva.Results.( + before stmt |> in_callstack cs |> + eval_var vi |> as_int |> default 0) + + or equivalently, if you prefer + + Eva.Results.( + default O (as_int (eval_var vi (in_callstack cs (before stmt)))) *) + type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list type request @@ -41,97 +65,180 @@ type 'a evaluation type error = Bottom | Top | DisabledDomain type 'a result = ('a,error) Result.t +(** Results handling *) + +(** Translates an error to a human readable string. *) val string_of_error : error -> string +(** Pretty printer for errors. *) val pretty_error : Format.formatter -> error -> unit +(** Pretty printer for API's results. *) val pretty_result : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a result -> unit -(* Control point selection *) +(** [default d r] extracts the value of r if r is Ok or use the default value d + otherwise. + Equivalent to [Result.value ~default:d r] *) +val default : 'a -> 'a result -> 'a + +(** Control point selection *) + +(** At the begining of the analysis, but after the initialization of globals. *) val at_start : request +(** At the end of the analysis, after the main function has returned. *) val at_end : request +(** At the start of the given function. *) val at_start_of : Cil_types.kernel_function -> request +(** At the end of the given function. *) val at_end_of : Cil_types.kernel_function -> request +(** Just before a statement is executed. *) val before : Cil_types.stmt -> request +(** Just after a statement is executed. *) val after : Cil_types.stmt -> request +(** Just before a statement or at the begining of analysis. *) val before_kinstr : Cil_types.kinstr -> request +(** Just after a statement or at the end of analysis. *) val after_kinstr : Cil_types.kinstr -> request -(* Callstack selection *) + +(** Callstack selection *) + +(** Only consider the given callstack. Replaces previous calls to [in_callstack] + or [in_callstacks]. *) val in_callstack : callstack -> request -> request +(** Only consider all the callstacks in the given list. If previous calls to + [in_callstack] or [in_callstacks] were done, it takes the intersection. *) val in_callstacks : callstack list -> request -> request +(** Only consider callstacks satisfying the given predicate. Several filters + can be added. If callstacks are also selected with [in_callstack] or + [in_callstacks], only the selected callstacks will be filtered. *) val filter_callstack : (callstack -> bool) -> request -> request -(* Working with callstacks *) + +(** Working with callstacks *) + +(** Retrieves the list of reachable callstacks from the given request. *) val callstacks : request -> callstack list +(** Retrieves, a list of subrequest for each reachable callstack from + the given request. *) val by_callstack : request -> (callstack * request) list +(** Iterate on the reachable callstacks from the request. *) val iter_callstacks : (callstack -> request -> unit) -> request -> unit +(** Fold on the reachable callstacks from the request. *) val fold_callstacks : (callstack -> request -> 'a -> 'a) -> 'a -> request -> 'a -(* State requests *) + +(** State requests *) + +(** Returns the list of expressions which have been infered to be equal to + the given expression by the Equality domain. *) val equality_class : Cil_types.exp -> request -> Cil_types.exp list result +(** Returns the Cvalue model. *) val as_cvalue_model : request -> Cvalue.Model.t result -(* Dependencies *) -val expr_deps : Cil_types.exp -> request -> Locations.Zone.t (* The zone of bits read to evaluate the expression, including adresses computation *) -val lval_deps : Cil_types.lval -> request -> Locations.Zone.t (* The zone of bits read to evaluate the lvalue, including lvalue zone itself *) -val address_deps : Cil_types.lval -> request -> Locations.Zone.t (* The zone of bits read to evaluate the lvalue, excluding the lvalue zone itself *) -(* Evaluation *) +(** Dependencies *) + +(** Computes (an overapproximation of) the zone of each bit that must be read to + evaluate the given expression, including all adresses computations. *) +val expr_deps : Cil_types.exp -> request -> Locations.Zone.t +(** Computes (an overapproximation of) the zone of each bit that must be read to + evaluate the given lvalue, including the lvalue zone itself. *) +val lval_deps : Cil_types.lval -> request -> Locations.Zone.t +(** Computes (an overapproximation of) the zone of each bit that must be read to + evaluate the given lvalue, excluding the lvalue zone itself. *) +val address_deps : Cil_types.lval -> request -> Locations.Zone.t + + +(** Evaluation *) + +(** Returns the variable's values infered by the analysis. *) val eval_var : Cil_types.varinfo -> request -> value evaluation +(** Returns the lvalue's values infered by the analysis. *) val eval_lval : Cil_types.lval -> request -> value evaluation +(** Returns the expression's values infered by the analysis. *) val eval_exp : Cil_types.exp -> request -> value evaluation +(** Returns the lvalue's addresses infered by the analysis. *) val eval_address : Cil_types.lval -> request -> address evaluation -(* Returns the kernel functions into which the given expression may evaluate. - If the callee expression doesn't always evaluate to a function, those - spurious values are ignored. If it always evaluate to a non-function value - then the returned list is empty. - Raises [Stdlib.Invalid_argument] if the callee expression is not an lvalue - without offset. - Also see [callee] for a function which applies directly on Call - statements *) +(** Returns the kernel functions into which the given expression may evaluate. + If the callee expression doesn't always evaluate to a function, those + spurious values are ignored. If it always evaluate to a non-function value + then the returned list is empty. + Raises [Stdlib.Invalid_argument] if the callee expression is not an lvalue + without offset. + Also see [callee] for a function which applies directly on Call + statements. *) val eval_callee : Cil_types.exp -> request -> Kernel_function.t list result -(* Value conversion *) + +(** Evaluated values conversion *) + +(** In all the functions below, if Eva's infered value does not fit in the + required type, [Error Top] is returned, as Top is the only possible + over-approximation of the request. *) + +(** Convert into a singleton ocaml int *) val as_int : value evaluation -> int result +(** Convert into a singleton unbounded integer *) val as_integer : value evaluation -> Integer.t result +(** Convert into a floating point number *) val as_float : value evaluation -> float result +(** Convert into a C number abstraction *) val as_ival : value evaluation -> Ival.t result +(** Convert into an floating point abstraction *) val as_fval : value evaluation -> Fval.t result +(** Convert into a C value abstraction *) val as_cvalue : value evaluation -> Cvalue.V.t result +(** Convert into a C location abstraction *) val as_location : address evaluation -> Locations.location result +(** Convert into a Zone *) val as_zone : ?access:Locations.access -> address evaluation -> Locations.Zone.t result -(* Evaluation properties *) + +(** Evaluation properties *) + +(** Returns whether the evaluated value is initialized or not. If the value have + been evaluated from a Cil expression, it is always considered initialized. +*) val is_initialized : value evaluation -> bool +(** Returns the set of alarms emitted during the evaluation. *) val alarms : 'a evaluation -> Alarms.t list -(* Reachability *) + +(** Reachability *) + +(** Returns true if there are no reachable states for the given request. *) val is_empty : request -> bool +(** Returns true if an evaluation ended to bottom, i.e. if the given expression + or lvalue cannot be evaluated to a valid result for the given request. *) val is_bottom : 'a evaluation -> bool -val is_called : Cil_types.kernel_function -> bool (* called during the analysis, not by the actual program *) -val is_reachable : Cil_types.stmt -> bool (* reachable by the analysis, not by the actual program *) +(** Returns true if a function have been analyzed. *) +val is_called : Cil_types.kernel_function -> bool +(** Returns true if a statement have been reached by the analysis. *) +val is_reachable : Cil_types.stmt -> bool +(** Returns true if a statement have been reached by the analysis, or if + the main function have been analyzed for [Kglobal]. *) val is_reachable_kinstr : Cil_types.kinstr -> bool -(* Callers / Callees / Callsites *) + +(*** Callers / Callees / Callsites *) + +(** Returns the list of infered callers of the given function. *) val callers : Cil_types.kernel_function -> Cil_types.kernel_function list +(** Returns the list of infered callers, and for each of them, the list + of callsites (the call statements) inside. *) val callsites : Cil_types.kernel_function -> (Cil_types.kernel_function * Cil_types.stmt list) list -(* Returns the kernel functions called in the given statement. - If the callee expression doesn't always evaluate to a function, those - spurious values are ignored. If it always evaluate to a non-function value - then the returned list is empty. - Raises [Stdlib.Invalid_argument] if the statement is not a [Call] - instruction or a [Local_init] with [ConsInit] initializer. *) +(** Returns the kernel functions called in the given statement. + If the callee expression doesn't always evaluate to a function, those + spurious values are ignored. If it always evaluate to a non-function value + then the returned list is empty. + Raises [Stdlib.Invalid_argument] if the statement is not a [Call] + instruction or a [Local_init] with [ConsInit] initializer. *) val callee : Cil_types.stmt -> Kernel_function.t list -(* Result conversion *) -(** [default d r] extract the value of r if r is Ok or use the default value d - otherwise. - Equivalent to [Result.value ~default:d r] *) -val default : 'a -> 'a result -> 'a [@@@ api_end] -- GitLab