From c8c45ee928d5c72bb587c77507706c06f15ac805 Mon Sep 17 00:00:00 2001
From: Maxime Jacquemin <maxime.jacquemin@cea.fr>
Date: Thu, 4 Nov 2021 13:31:52 +0100
Subject: [PATCH] [ivette] Displaying Offsetmap for structures and arrays

We have the same behavior as in the previous GUI. We are going to
discuss about potential improvements.
---
 src/plugins/value/api/values_request.ml | 107 +++++++++++++++++++++---
 1 file changed, 94 insertions(+), 13 deletions(-)

diff --git a/src/plugins/value/api/values_request.ml b/src/plugins/value/api/values_request.ml
index b94f43d4b1e..6701e807f55 100644
--- a/src/plugins/value/api/values_request.ml
+++ b/src/plugins/value/api/values_request.ml
@@ -276,12 +276,90 @@ module Proxy(A : Analysis.S) : EvaProxy = struct
       | (`Top | `Bottom) as res -> res
       | `Value cmap -> try `Value (CSmap.find cmap cs) with Not_found -> `Bottom
 
-  let eval_lval lval state =
-    let value, alarms = A.copy_lvalue state lval in
-    let default = Eval.Flagged_Value.bottom in
-    (Bottom.default ~default value).v, alarms
+  type to_offsetmap =
+    | Bottom
+    | Offsetmap of Cvalue.V_Offsetmap.t
+    | Empty
+    | Top
+    | InvalidLoc
+
+  let extract_single_var vi state =
+    let b = Base.of_varinfo vi in
+    try
+      match Cvalue.Model.find_base b state with
+      | `Bottom -> InvalidLoc
+      | `Value m -> Offsetmap m
+      | `Top -> Top
+    with Not_found -> InvalidLoc
+
+  let reduce_loc_and_eval loc state =
+    if Cvalue.Model.is_top state then Top
+    else if not @@ Cvalue.Model.is_reachable state then Bottom
+    else if Int_Base.(equal loc.Locations.size zero) then Empty
+    else
+      let loc' = Locations.(valid_part Read loc) in
+      if Locations.is_bottom_loc loc' then InvalidLoc
+      else
+        try
+          let size = Int_Base.project loc'.Locations.size in
+          match Cvalue.Model.copy_offsetmap loc'.Locations.loc size state with
+          | `Bottom -> Bottom
+          | `Value offsm -> Offsetmap offsm
+        with Abstract_interp.Error_Top -> Top
+
+  let extract_or_reduce_lval lval state =
+    match lval with
+    | Var vi, NoOffset -> let r = extract_single_var vi state in fun _ -> r
+    | _ -> fun loc -> reduce_loc_and_eval loc state
+
+  let lval_to_offsetmap lval state =
+    let loc, alarms = A.eval_lval_to_loc state lval in
+    let state = A.Dom.get_cvalue_or_top state in
+    let extract = extract_or_reduce_lval lval state in
+    let precise_loc =
+      match A.Loc.get Main_locations.PLoc.key with
+      | None -> `Value (Precise_locs.loc_top)
+      | Some get -> loc >>-: get
+    and f loc acc =
+      match acc, extract loc with
+      | Offsetmap o1, Offsetmap o2 -> Offsetmap (Cvalue.V_Offsetmap.join o1 o2)
+      | Bottom, v | v, Bottom -> v
+      | Empty, v | v, Empty -> v
+      | Top, Top -> Top
+      | InvalidLoc, InvalidLoc -> InvalidLoc
+      | InvalidLoc, (Offsetmap _ as res) -> res
+      | Offsetmap _, InvalidLoc -> acc
+      | Top, r | r, Top -> r (* cannot happen, we should get Top everywhere *)
+    in precise_loc >>-: (fun loc -> Precise_locs.fold f loc Bottom), alarms
+
+  type evaluation =
+    | ToValue of A.Val.t
+    | ToOffsetmap of to_offsetmap
+
+  let pp_evaluation typ fmt = function
+    | ToValue v -> A.Val.pretty fmt v
+    | ToOffsetmap Bottom -> Format.fprintf fmt "<BOTTOM>"
+    | ToOffsetmap Empty -> Format.fprintf fmt "<EMPTY>"
+    | ToOffsetmap Top -> Format.fprintf fmt "<NO INFORMATION>"
+    | ToOffsetmap InvalidLoc -> Format.fprintf fmt "<INVALID LOCATION>"
+    | ToOffsetmap (Offsetmap o) ->
+      Cvalue.V_Offsetmap.pretty_generic ?typ:(Some typ) () fmt o ;
+      Eval_op.pretty_stitched_offsetmap fmt typ o
 
-  let eval_expr expr state = A.eval_expr state expr
+  let eval_lval lval state =
+    match Cil.(unrollType @@ typeOfLval lval) with
+    | TInt _ | TEnum _ | TPtr _ | TFloat _ ->
+      let value, alarms = A.copy_lvalue state lval in
+      let _ = lval_to_offsetmap lval state in
+      let default = Eval.Flagged_Value.bottom in
+      (Bottom.default ~default value).v >>-: (fun v -> ToValue v), alarms
+    | _ ->
+      let offsetmap, alarms = lval_to_offsetmap lval state in
+      offsetmap >>-: (fun r -> ToOffsetmap r), alarms
+
+  let eval_expr expr state =
+    let value, alarms = A.eval_expr state expr in
+    value >>-: (fun v -> ToValue v), alarms
 
   let dalarms alarms =
     let descr = Format.asprintf "@[<hov 2>%a@]" Alarms.pretty in
@@ -297,15 +375,15 @@ module Proxy(A : Analysis.S) : EvaProxy = struct
       | `Else cond -> (A.assume_cond stmt state cond false :> dstate)
     in List.fold_left (fun acc step -> f acc step @@ add_state step) acc steps
 
-  let domain_step eval ((values, alarms) as acc) step =
-    let to_string = Pretty_utils.to_string (Bottom.pretty A.Val.pretty) in
+  let domain_step typ eval ((values, alarms) as acc) step =
+    let to_str = Pretty_utils.to_string (Bottom.pretty (pp_evaluation typ)) in
     handle_top_or_bottom ~top:acc ~bottom:acc @@ fun state ->
     let step_value, step_alarms = eval state in
     let alarms = match step with `Here -> step_alarms | _ -> alarms in
-    (step, to_string step_value) :: values, alarms
+    (step, to_str step_value) :: values, alarms
 
-  let domain_eval eval stmt callstack =
-    let fold = fold_steps (domain_step eval) stmt callstack in
+  let domain_eval typ eval stmt callstack =
+    let fold = fold_steps (domain_step typ eval) stmt callstack in
     let build (vs, als) = { values = List.rev vs ; alarms = dalarms als } in
     let compute_domain state = fold state ([], Alarmset.none) |> build in
     handle_top_or_bottom ~top:dtop ~bottom:dbottom compute_domain @@
@@ -313,8 +391,10 @@ module Proxy(A : Analysis.S) : EvaProxy = struct
 
   let domain p callstack =
     match p with
-    | Plval (lval, stmt) -> domain_eval (eval_lval lval) stmt callstack
-    | Pexpr (expr, stmt) -> domain_eval (eval_expr expr) stmt callstack
+    | Plval (lval, stmt) ->
+      domain_eval (Cil.typeOfLval lval) (eval_lval lval) stmt callstack
+    | Pexpr (expr, stmt) ->
+      domain_eval (Cil.typeOf expr) (eval_expr expr) stmt callstack
     | Pnone -> dnone
 
   let var_of_base base acc =
@@ -323,7 +403,8 @@ module Proxy(A : Analysis.S) : EvaProxy = struct
 
   let compute_pointed_lvalues eval_lval stmt callstack =
     Option.(bind (A.Val.get Main_values.CVal.key)) @@ fun get ->
-    let loc state = eval_lval state |> fst >>-: get in
+    let get_eval = function ToValue v -> `Value (get v) | _ -> `Bottom in
+    let loc state = eval_lval state |> fst >>- get_eval in
     let bases value = Cvalue.V.fold_bases var_of_base value [] in
     let lvalues state = loc state >>-: bases >>-: List.map Cil.var in
     let compute state = lvalues state |> Bottom.to_option in
-- 
GitLab