[英]Frama-C Plugin development: Getting result of value-analysis
I am working on a Plugin for Frama-C, using the Value-analysis.我正在使用价值分析为 Frama-C 开发一个插件。 I simply want to print the state of the variables (values) after each statement (I think the solution is quiet easy, but I couldn't figure it out).
我只是想在每个语句后打印变量(值)的 state(我认为解决方案很简单,但我想不出来)。
I got the current state with Db.Value.get_stmt_state
in the vstmt_aux
method in the visitor.我在访问者的
vstmt_aux
方法中使用Db.Value.get_stmt_state
获得了当前的 state。
How can I now get the values of the variables?我现在如何获取变量的值?
PS: I found this post, but it didn't help, there is no real solution, and with the help of the description I was not able to do it: How to use functions in Value.Eval_expr, Value.Eval_op etc modules of Frama-c Value plugin PS:我找到了这篇文章,但没有帮助,没有真正的解决方案,并且在描述的帮助下我无法做到: How to use functions in Value.Eval_expr, Value.Eval_op etc modules of Frama-c 值插件
Here's a concrete example of how to print, for each local and global variable, the result computed by Value before each statement in a given function (read the functions from bottom to top): 下面是一个具体的例子,说明如何为每个本地和全局变量打印在给定函数中的每个语句之前由Value计算的结果(从下到上读取函数):
open Cil_types
(* Prints the value associated to variable [vi] before [stmt]. *)
let pretty_vi fmt stmt vi =
let kinstr = Kstmt stmt in (* make a kinstr from a stmt *)
let lval = (Var vi, NoOffset) in (* make an lval from a varinfo *)
let loc = (* make a location from a kinstr + an lval *)
!Db.Value.lval_to_loc kinstr ~with_alarms:CilE.warn_none_mode lval
in
Db.Value.fold_state_callstack
(fun state () ->
(* for each state in the callstack *)
let value = Db.Value.find state loc in (* obtain value for location *)
Format.fprintf fmt "%a -> %a@." Printer.pp_varinfo vi
Locations.Location_Bytes.pretty value (* print mapping *)
) () ~after:false kinstr
(* Prints the state at statement [stmt] for each local variable in [kf],
and for each global variable. *)
let pretty_local_and_global_vars kf fmt stmt =
let locals = Kernel_function.get_locals kf in
List.iter (fun vi -> pretty_vi fmt stmt vi) locals;
Globals.Vars.iter (fun vi _ -> pretty_vi fmt stmt vi)
(* Visits each statement in [kf] and prints the result of Value before the
statement. *)
class stmt_val_visitor kf =
object (self)
inherit Visitor.frama_c_inplace
method! vstmt_aux stmt =
(match stmt.skind with
| Instr _ ->
Format.printf "state for all variables before stmt: %a@.%a@."
Printer.pp_stmt stmt (pretty_local_and_global_vars kf) stmt
| _ -> ());
Cil.DoChildren
end
(* usage: frama-c file.c -load-script print_vals.ml *)
let () =
Db.Main.extend (fun () ->
Format.printf "computing value...@.";
!Db.Value.compute ();
let fun_name = "main" in
Format.printf "visiting function: %s@." fun_name;
let kf_vis = new stmt_val_visitor in
let kf = Globals.Functions.find_by_name fun_name in
let fundec = Kernel_function.get_definition kf in
ignore (Visitor.visitFramacFunction (kf_vis kf) fundec);
Format.printf "done!@.")
This is far from ideal, and the output is uglier than simply using Cvalue.Model.pretty state
, but it could serve as base for further modifications. 这远非理想,输出比简单地使用
Cvalue.Model.pretty state
更加丑陋,但它可以作为进一步修改的基础。
This script has been tested with Frama-C Magnesium. 该脚本已经过Frama-C Magnesium测试。
To retrieve the state after a statement, simply replace the ~after:false
parameter in fold_state_callstack
with ~after:true
. 要检索语句之后的状态下,只需更换
~after:false
参数fold_state_callstack
与~after:true
。 My previous version of the code used a function which already bound that value for the pre-state, but no such function is exported for the post-state, so we must use fold_state_callstack
(which is incidentally more powerful, because it allows retrieving a specific state per callstack). 我以前的代码版本使用了一个函数,该函数已经为前置状态绑定了该值,但是没有为后置状态导出这样的函数,所以我们必须使用
fold_state_callstack
(顺便说一下,它更强大,因为它允许检索特定的每个callstack的状态)。
This is an update to the previous answer, using Eva's new API, available since Frama-C 25.0 (Magnesium);这是对先前答案的更新,使用 Eva 的新 API,自 Frama-C 25.0(镁)起可用; I left the original answer for users based on older Frama-C versions.
我根据旧的 Frama-C 版本为用户留下了原始答案。
Using Eva's new API, the above answer can be written more succinctly:使用Eva的新API,上面的答案可以写得更简洁:
(* Prints the value associated to variable [vi] before [stmt]. *)
let pretty_vi fmt stmt vi =
let req = Eva.Results.before stmt in
let cvalue = Eva.Results.(eval_var vi req |> as_cvalue) in
Format.fprintf fmt "%a -> %a@." Printer.pp_varinfo vi
Cvalue.V.pretty cvalue (* print mapping *)
(* Prints the state at statement [stmt] for each local variable in [kf],
and for each global variable. *)
let pretty_local_and_global_vars kf fmt stmt =
let locals = Kernel_function.get_locals kf in
List.iter (fun vi -> pretty_vi fmt stmt vi) locals;
Globals.Vars.iter (fun vi _ -> pretty_vi fmt stmt vi)
(* Visits each statement in [kf] and prints the result of Value before the
statement. *)
class stmt_val_visitor kf =
object
inherit Visitor.frama_c_inplace
method! vstmt_aux stmt =
(match stmt.skind with
| Instr _ ->
Format.printf "state for all variables before stmt: %a@.%a@."
Printer.pp_stmt stmt (pretty_local_and_global_vars kf) stmt
| _ -> ());
Cil.DoChildren
end
(* usage: frama-c file.c -load-script print_vals.ml *)
let () =
Db.Main.extend (fun () ->
Format.printf "computing value...@.";
Eva.Analysis.compute ();
let fun_name = "main" in
Format.printf "visiting function: %s@." fun_name;
let kf_vis = new stmt_val_visitor in
let kf = Globals.Functions.find_by_name fun_name in
let fundec = Kernel_function.get_definition kf in
ignore (Visitor.visitFramacFunction (kf_vis kf) fundec);
Format.printf "done!@.")
Note that the output is not identical;请注意,output 并不相同; it is actually more condensed, as in, instead of printing eg
score -> {{ NULL -> {0} }}
, which means, for location score
, the offset associated to the NULL base, that is, a constant value, is 0 , it simply prints score -> {0}
.它实际上更简洁,例如,而不是打印
score -> {{ NULL -> {0} }}
,这意味着,对于 location score
,与 NULL 基相关联的偏移量,即常数值,是0 ,它只是打印score -> {0}
。 It also prints minimum/maximum bounds according to the variable type (eg int __fc_errno
was printed as an unbounded interval [--..--]
with the previous code; here, it is printed as [-2147483648..2147483647]
when using a machdep with 32-bit integers).它还根据变量类型打印最小/最大边界(例如
int __fc_errno
在前面的代码中被打印为无界区间[--..--]
;这里使用时打印为[-2147483648..2147483647]
具有 32 位整数的 machdep)。
The new API also makes it easier to answer queries such as Is there also a way to get the values after the statement?新的 API 还可以更轻松地回答诸如Is there also a way to get the values after the statement? 之类的查询。 : just use
Eva.Results.after
instead of Eva.Results.before
. :只需使用
Eva.Results.after
而不是Eva.Results.before
。
Finally, for callstack-specific information, search for callstack
in the src/plugins/value/utils/results.mli
file.最后,对于特定于调用堆栈的信息,请在
src/plugins/value/utils/results.mli
文件中搜索callstack
。 This file also contains some lenghty comments explaining the API, as well as a usage sketch.该文件还包含一些解释 API 的冗长注释,以及一个用法草图。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.