Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add arg/lookup request to server mode #1001

Merged
merged 25 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3e2caa7
Extract ARG creation from witness
sim642 Feb 22, 2023
6ccf76e
Add option exp.arg for constructing ARG outside witness
sim642 Feb 22, 2023
4152d05
Add iter_nodes to BiArg
sim642 Feb 23, 2023
dd0e3fd
Add arg/lookup request to server
sim642 Feb 23, 2023
fe00bc9
Make ARG in server type safe
sim642 Feb 23, 2023
cbcaaeb
Make arg/lookup return entry if no params
sim642 Feb 23, 2023
f9c50a3
Add ARG node ID to arg/lookup response
sim642 Feb 23, 2023
0822be5
Make arg/lookup return all results
sim642 Feb 23, 2023
6606c88
Optimize arg/lookup by node ID
sim642 Feb 23, 2023
f1ef2b0
Remove exp.arg debug printing
sim642 Feb 23, 2023
253b962
Clean up ArgTools
sim642 Feb 23, 2023
e6ace71
Fix arg/lookup crash when exp.arg disabled
sim642 Feb 23, 2023
4019627
Add context and path fields to arg/lookup response
sim642 Feb 27, 2023
3fc991a
Add arg/lookup TODOs
sim642 Feb 27, 2023
534184f
Make arg/lookup request return empty lists instead of errors
sim642 Feb 28, 2023
90464b5
Extract edge_node record for arg/lookup request
sim642 Feb 28, 2023
5daf9c0
Add arg/lookup by CFG node
sim642 Feb 28, 2023
d9fc534
Add option exp.argdot
sim642 Feb 28, 2023
265d63b
Add InlinedEdge to ARG edges
sim642 Feb 28, 2023
548bb06
Collect InlinedEdge in witness lifter
sim642 Feb 28, 2023
c3cc469
Improve edge to_yojson
sim642 Feb 28, 2023
c97f8fb
Add more data to ARG edges
sim642 Feb 28, 2023
cafbcc0
Remove completed TODO in WitnessConstraints
sim642 Feb 28, 2023
cb9c8d0
Add ARG node details to arg/lookup next and prev
sim642 Mar 1, 2023
4e3cbf9
Use UpdateCil.getLoc instead of Node.location in server for increment…
sim642 Mar 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ _opam/
cfgs/
cfg.dot
cilcfg.*.dot
arg.dot

*.graphml
goblint.bc.js
Expand Down
26 changes: 22 additions & 4 deletions src/framework/control.ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ module type S2S = functor (X : Spec) -> Spec
(* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *)
let spec_module: (module Spec) Lazy.t = lazy (
GobConfig.building_spec := true;
let arg_enabled = get_bool "ana.sv-comp.enabled" || get_bool "exp.arg" in
let open Batteries in
(* apply functor F on module X if opt is true *)
let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in
let module S1 = (val
(module MCP.MCP2 : Spec)
|> lift true (module WidenContextLifterSide) (* option checked in functor *)
(* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *)
|> lift (get_bool "ana.opt.hashcons" || get_bool "ana.sv-comp.enabled") (module HashconsContextLifter)
|> lift (get_bool "ana.sv-comp.enabled") (module HashconsLifter)
|> lift (get_bool "ana.sv-comp.enabled") (module WitnessConstraints.PathSensitive3)
|> lift (not (get_bool "ana.sv-comp.enabled")) (module PathSensitive2)
|> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter)
|> lift arg_enabled (module HashconsLifter)
|> lift arg_enabled (module WitnessConstraints.PathSensitive3)
|> lift (not arg_enabled) (module PathSensitive2)
|> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter)
|> lift true (module DeadCodeLifter)
|> lift (get_bool "dbg.slice.on") (module LevelSliceLifter)
Expand Down Expand Up @@ -658,6 +659,23 @@ struct
in
Timing.wrap "warn_global" (GHT.iter warn_global) gh;

if get_bool "exp.arg" then (
let module ArgTool = ArgTools.Make (R) in
let module Arg = (val ArgTool.create entrystates) in
if get_bool "exp.argdot" then (
let module ArgDot = ArgTools.Dot (Arg) in
let oc = Stdlib.open_out "arg.dot" in
Fun.protect (fun () ->
let ppf = Format.formatter_of_out_channel oc in
ArgDot.dot ppf;
Format.pp_print_flush ppf ()
) ~finally:(fun () ->
Stdlib.close_out oc
)
);
ArgTools.current_arg := Some (module Arg);
);

(* Before SV-COMP, so result can depend on YAML witness validation. *)
if get_string "witness.yaml.validate" <> "" then (
let module YWitness = YamlWitness.Validator (R) in
Expand Down
55 changes: 54 additions & 1 deletion src/framework/edge.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type t =
* appeared *)
| Skip
(** This is here for historical reasons. I never use Skip edges! *)
[@@deriving eq, ord, hash, to_yojson]
[@@deriving eq, ord, hash]


let pretty () = function
Expand All @@ -55,3 +55,56 @@ let pretty_plain () = function
| ASM _ -> text "ASM ..."
| Skip -> text "Skip"
| VDecl v -> dprintf "VDecl '%a %s;'" d_type v.vtype v.vname

let to_yojson e =
let fields = match e with
| Assign (lval, exp) ->
[
("type", `String "assign");
("lval", CilType.Lval.to_yojson lval);
("exp", CilType.Exp.to_yojson exp);
]
| Test (exp, branch) ->
[
("type", `String "branch");
("exp", CilType.Exp.to_yojson exp);
("branch", `Bool branch);
]
| Proc (lval, function_, args) ->
[
("type", `String "call");
("lval", [%to_yojson: CilType.Lval.t option] lval);
("function", CilType.Exp.to_yojson function_);
("args", [%to_yojson: CilType.Exp.t list] args);
]
| Entry function_ ->
[
("type", `String "entry");
("function", CilType.Fundec.to_yojson function_);
]
| Ret (exp, function_) ->
[
("type", `String "return");
("function", CilType.Fundec.to_yojson function_);
("exp", [%to_yojson: CilType.Exp.t option] exp);
]
| ASM (instructions, output, input) ->
[
("type", `String "asm");
("instructions", [%to_yojson: string list] instructions);
("output", asm_out_to_yojson output);
("input", asm_in_to_yojson input);
]
| VDecl variable ->
[
("type", `String "declare");
("variable", CilType.Varinfo.to_yojson variable);
]
| Skip ->
[
("type", `String "nop");
]
in
`Assoc ([
("string", `String (Pretty.sprint ~width:max_int (pretty () e)))
] @ fields)
12 changes: 12 additions & 0 deletions src/util/options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,18 @@
"description": "Hide standard extern globals (e.g. `stdout`) from cluttering local states.",
"type": "boolean",
"default": true
},
"arg": {
"title": "exp.arg",
"description": "Construct abstract reachability graph (ARG).",
"type": "boolean",
"default": false
},
"argdot": {
"title": "exp.argdot",
"description": "Output ARG as dot file.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false
Expand Down
140 changes: 138 additions & 2 deletions src/util/server.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ open Batteries
open Jsonrpc
open GoblintCil

module type ArgWrapper =
sig
module Arg: ArgTools.BiArg
module Locator: module type of WitnessUtil.Locator (Arg.Node)
val locator: Locator.t
val find_node: string -> Arg.Node.t
val find_cfg_node: string -> Arg.Node.t list
end

type t = {
mutable file: Cil.file option;
mutable max_ids: MaxIdUtil.max_ids;
arg_wrapper: (module ArgWrapper) ResettableLazy.t;
input: IO.input;
output: unit IO.output;
}
Expand Down Expand Up @@ -106,6 +116,36 @@ let serve serv =
|> Seq.map Packet.t_of_yojson
|> Seq.iter (handle_packet serv)

let arg_wrapper: (module ArgWrapper) ResettableLazy.t =
ResettableLazy.from_fun (fun () ->
let module Arg = (val (Option.get_exn !ArgTools.current_arg Response.Error.(E (make ~code:RequestFailed ~message:"not analyzed or arg disabled" ())))) in
let module Locator = WitnessUtil.Locator (Arg.Node) in
let module StringH = Hashtbl.Make (Printable.Strings) in

let locator = Locator.create () in
let ids = StringH.create 113 in
let cfg_nodes = StringH.create 113 in
Arg.iter_nodes (fun n ->
let cfgnode = Arg.Node.cfgnode n in
let loc = UpdateCil.getLoc cfgnode in
if not loc.synthetic then
Locator.add locator loc n;
StringH.replace ids (Arg.Node.to_string n) n;
StringH.add cfg_nodes (Node.show_id cfgnode) n (* add for find_all *)
);

let module ArgWrapper =
struct
module Arg = Arg
module Locator = Locator
let locator = locator
let find_node = StringH.find ids
let find_cfg_node = StringH.find_all cfg_nodes
end
in
(module ArgWrapper: ArgWrapper)
)

let make ?(input=stdin) ?(output=stdout) file : t =
let max_ids =
match file with
Expand All @@ -115,6 +155,7 @@ let make ?(input=stdin) ?(output=stdout) file : t =
{
file;
max_ids;
arg_wrapper;
input;
output
}
Expand Down Expand Up @@ -189,7 +230,7 @@ let node_locator: Locator.t ResettableLazy.t =
let rec iter_node node =
if not (NH.mem reachable node) then begin
NH.replace reachable node ();
let loc = Node.location node in
let loc = UpdateCil.getLoc node in
if not loc.synthetic then
Locator.add locator loc node;
List.iter (fun (_, prev_node) ->
Expand Down Expand Up @@ -219,6 +260,7 @@ let analyze ?(reset=false) (s: t) =
Serialize.Cache.reset_data AnalysisData);
let increment_data, fresh = increment_data s file reparsed in
ResettableLazy.reset node_locator;
ResettableLazy.reset s.arg_wrapper;
Cilfacade.reset_lazy ();
InvariantCil.reset_lazy ();
WideningThresholds.reset_lazy ();
Expand Down Expand Up @@ -389,7 +431,7 @@ let () =
Response.Error.(raise (make ~code:RequestFailed ~message:"requires node xor location" ()))
in
let node_id = Node.show_id node in
let location = Node.location node in
let location = UpdateCil.getLoc node in
let module Cfg = (val !MyCFG.current_cfg) in
let next =
Cfg.next node
Expand All @@ -406,6 +448,100 @@ let () =
{node = node_id; location; next; prev}
end);

register (module struct
let name = "arg/lookup"
type params = {
node: string option [@default None];
location: CilType.Location.t option [@default None];
cfg_node: string option [@default None];
} [@@deriving of_yojson]

type edge_node = {
edge: MyARG.inline_edge;
node: string;
cfg_node: string;
context: string;
path: string;
location: CilType.Location.t;
} [@@deriving to_yojson]
type one_response = {
node: string;
cfg_node: string;
context: string;
path: string;
location: CilType.Location.t;
next: edge_node list;
prev: edge_node list;
} [@@deriving to_yojson]
type response = one_response list [@@deriving to_yojson]

let process (params: params) serv =
let module ArgWrapper = (val (ResettableLazy.force serv.arg_wrapper)) in
let open ArgWrapper in
let open GobList.Syntax in
let+ n: Arg.Node.t = match params.node, params.location, params.cfg_node with
| None, None, None ->
[Arg.main_entry]
| Some node_id, None, None ->
begin try
[ArgWrapper.find_node node_id]
with Not_found ->
[] (* non-existent node *)
end
| None, Some location, None ->
let nodes_opt =
let open GobOption.Syntax in
let+ nodes = Locator.find_opt locator location in
Locator.ES.elements nodes
in
Option.default [] nodes_opt (* cannot find node for location *)
| None, None, Some cfg_node ->
ArgWrapper.find_cfg_node cfg_node
| _, _, _ ->
Response.Error.(raise (make ~code:RequestFailed ~message:"requires at most one of node, location and cfg_node" ()))
in
let cfg_node = Arg.Node.cfgnode n in
let cfg_node_id = Node.show_id cfg_node in
let location = UpdateCil.getLoc cfg_node in
let next =
Arg.next n
|> List.map (fun (edge, to_node) ->
let cfg_to_node = Arg.Node.cfgnode to_node in
{
edge;
node = Arg.Node.to_string to_node;
cfg_node = Node.show_id cfg_to_node;
context = string_of_int (Arg.Node.context_id to_node);
path = string_of_int (Arg.Node.path_id to_node);
location = UpdateCil.getLoc cfg_to_node;
}
)
in
let prev =
Arg.prev n
|> List.map (fun (edge, to_node) ->
let cfg_to_node = Arg.Node.cfgnode to_node in
{
edge;
node = Arg.Node.to_string to_node;
cfg_node = Node.show_id cfg_to_node;
context = string_of_int (Arg.Node.context_id to_node);
path = string_of_int (Arg.Node.path_id to_node);
location = UpdateCil.getLoc cfg_to_node;
}
)
in
{
node = Arg.Node.to_string n;
cfg_node = cfg_node_id;
context = string_of_int (Arg.Node.context_id n);
path = string_of_int (Arg.Node.path_id n);
location;
next;
prev
}
end);

register (module struct
let name = "node_state"
type params = { nid: string } [@@deriving of_yojson]
Expand Down
Loading