Skip to content

Commit a5bee96

Browse files
authored
Merge pull request #1752 from goblint/g2html-ocaml
Output g2html directly
2 parents 6c6276e + 8461c86 commit a5bee96

38 files changed

+2250
-204
lines changed

docs/user-guide/inspecting.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
# Inspecting results
22

3-
## g2html
3+
## HTML
4+
1. Run Goblint with additional `--html` argument.
5+
2. Run `python3 -m http.server --directory result 8080`
6+
or `npx http-server -c-1 result`.
7+
3. Inspect results at <http://localhost:8080/index.xml>.
8+
9+
Modern browsers' security settings forbid some file access which is necessary for the HTML output to work, hence the need for serving the results via Python's `http.server` (or similar).
10+
11+
## g2html (legacy)
12+
If there are problems with the above HTML output, the legacy g2html output (using an external Java component) is still possible.
13+
The frontend of the above HTML output is reused from g2html, so the two look the same (except for code highlighting).
14+
415
1. First time run: `make jar`.
5-
2. Run Goblint with additional `--html` argument.
16+
2. Run Goblint with additional `--set result g2html` arguments.
617
3. Run `python3 -m http.server --directory result 8080`
7-
or `npx http-server -c-1 result`.
18+
or `npx http-server -c-1 result`.
819
4. Inspect results at <http://localhost:8080/index.xml>.
920

1021
Modern browsers' security settings forbid some file access which is necessary for g2html to work, hence the need for serving the results via Python's `http.server` (or similar).

dune-project

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ Goblint includes analyses for assertions, overflows, deadlocks, etc and can be e
8383
)
8484
(sites
8585
(share lib)
86-
(share conf))
86+
(share conf)
87+
(share xslt))
8788
)
8889

8990
; (map_workspace_root false) ;uncomment to enable breakpoints

make.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ rule() {
7474
}
7575
;; setup)
7676
echo "Make sure you have the following installed: opam >= 2.0.0, git, patch, m4, autoconf, libgmp-dev, libmpfr-dev, pkg-config"
77-
echo "For the --html output you also need: javac, ant, dot (graphviz)"
77+
echo "For the --html output you also need: graphviz and python3-pygments (optional)"
7878
echo "For running the regression tests you also need: ruby, gem, curl, and the `os` gem"
7979
echo "For reference see ./Dockerfile or ./scripts/travis-ci.sh"
8080
opam_setup

src/common/framework/cfgTools.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,12 +654,12 @@ let sprint_fundec_html_dot (module Cfg : CfgBidir) live fd =
654654
fprint_fundec_html_dot (module Cfg) live fd Format.str_formatter;
655655
Format.flush_str_formatter ()
656656

657-
let dead_code_cfg (module FileCfg: MyCFG.FileCfg) live =
657+
let dead_code_cfg ~path (module FileCfg: MyCFG.FileCfg) live =
658658
iterGlobals FileCfg.file (fun glob ->
659659
match glob with
660660
| GFun (fd,loc) ->
661661
(* ignore (Printf.printf "fun: %s\n" fd.svar.vname); *)
662-
let base_dir = GobSys.mkdir_or_exists_absolute (Fpath.v "cfgs") in
662+
let base_dir = GobSys.mkdir_or_exists_absolute path in
663663
let c_file_name = Str.global_substitute (Str.regexp Filename.dir_sep) (fun _ -> "%2F") loc.file in
664664
let dot_file_name = fd.svar.vname^".dot" in
665665
let file_dir = GobSys.mkdir_or_exists_absolute Fpath.(base_dir / c_file_name) in

src/config/options.schema.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"description":
8282
"Result style: none, fast_xml, json, pretty, pretty-deterministic, json-messages, sarif.",
8383
"type": "string",
84-
"enum": ["none", "fast_xml", "json", "pretty", "pretty-deterministic", "json-messages", "sarif"],
84+
"enum": ["none", "fast_xml", "g2html", "xslt", "json", "pretty", "pretty-deterministic", "json-messages", "sarif"],
8585
"default": "none"
8686
},
8787
"solver": {
@@ -117,12 +117,6 @@
117117
"enum": ["auto", "always", "never"],
118118
"default": "auto"
119119
},
120-
"g2html": {
121-
"title": "g2html",
122-
"description": "Run g2html.jar on the generated xml.",
123-
"type": "boolean",
124-
"default": false
125-
},
126120
"save_run": {
127121
"title": "save_run",
128122
"description":

src/framework/analysisResult.ml

Lines changed: 15 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(** Analysis result output. *)
1+
(** Analysis results. *)
22

33
open GoblintCil
44
open Pretty
@@ -32,164 +32,22 @@ sig
3232
val result_name: string
3333
end
3434

35-
module Result (Range: Printable.S) (C: ResultConf) =
36-
struct
37-
include BatHashtbl.Make (ResultNode)
38-
type nonrec t = Range.t t (* specialize polymorphic type for Range values *)
39-
40-
let pretty () mapping =
41-
let f key st dok =
42-
dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st
43-
in
44-
let content () = fold f mapping nil in
45-
let defline () = dprintf "OTHERS -> Not available\n" in
46-
dprintf "@[Mapping {\n @[%t%t@]}@]" content defline
47-
48-
let pretty_deterministic () mapping =
49-
let bindings =
50-
to_list mapping
51-
|> List.sort [%ord: ResultNode.t * Range.t]
52-
in
53-
let f dok (key, st) =
54-
dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st
55-
in
56-
let content () = List.fold_left f nil bindings in
57-
let defline () = dprintf "OTHERS -> Not available\n" in
58-
dprintf "@[Mapping {\n @[%t%t@]}@]" content defline
35+
module type Result =
36+
sig
37+
include ResultConf
38+
module Range: Printable.S
39+
module H: BatHashtbl.S with type key := ResultNode.t
40+
include BatHashtbl.S with type 'a t := 'a H.t and type key := ResultNode.t
41+
type t = Range.t H.t
42+
end
5943

44+
module Result (Range: Printable.S) (C: ResultConf): Result with module Range = Range =
45+
struct
6046
include C
61-
62-
let printXml f xs =
63-
let print_one n v =
64-
(* Not using Node.location here to have updated locations in incremental analysis.
65-
See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *)
66-
let loc = UpdateCil.getLoc n in
67-
BatPrintf.fprintf f "<call id=\"%s\" file=\"%s\" line=\"%d\" order=\"%d\" column=\"%d\" endLine=\"%d\" endColumn=\"%d\" synthetic=\"%B\">\n" (Node.show_id n) loc.file loc.line loc.byte loc.column loc.endLine loc.endColumn loc.synthetic;
68-
BatPrintf.fprintf f "%a</call>\n" Range.printXml v
69-
in
70-
iter print_one xs
71-
72-
let printJson f xs =
73-
let print_one n v =
74-
(* Not using Node.location here to have updated locations in incremental analysis.
75-
See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *)
76-
let loc = UpdateCil.getLoc n in
77-
BatPrintf.fprintf f "{\n\"id\": \"%s\", \"file\": \"%s\", \"line\": \"%d\", \"byte\": \"%d\", \"column\": \"%d\", \"states\": %s\n},\n" (Node.show_id n) loc.file loc.line loc.byte loc.column (Yojson.Safe.to_string (Range.to_yojson v))
78-
in
79-
iter print_one xs
80-
81-
let printXmlWarning f () =
82-
let one_text f Messages.Piece.{loc; text = m; _} =
83-
match loc with
84-
| Some loc ->
85-
let l = Messages.Location.to_cil loc in
86-
BatPrintf.fprintf f "\n<text file=\"%s\" line=\"%d\" column=\"%d\">%s</text>" l.file l.line l.column (XmlUtil.escape m)
87-
| None ->
88-
() (* TODO: not outputting warning without location *)
89-
in
90-
let one_w f (m: Messages.Message.t) = match m.multipiece with
91-
| Single piece -> one_text f piece
92-
| Group {group_text = n; pieces = e; group_loc} ->
93-
let group_loc_text = match group_loc with
94-
| None -> ""
95-
| Some group_loc -> GobPretty.sprintf " (%a)" CilType.Location.pretty (Messages.Location.to_cil group_loc)
96-
in
97-
BatPrintf.fprintf f "<group name=\"%s%s\">%a</group>\n" n group_loc_text (BatList.print ~first:"" ~last:"" ~sep:"" one_text) e
98-
in
99-
let one_w f x = BatPrintf.fprintf f "\n<warning>%a</warning>" one_w x in
100-
List.iter (one_w f) !Messages.Table.messages_list
101-
102-
let output table gtable gtfxml (file: file) =
103-
let out = Messages.get_out result_name !Messages.out in
104-
match get_string "result" with
105-
| "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table))
106-
| "pretty-deterministic" -> ignore (fprintf out "%a\n" pretty_deterministic (Lazy.force table))
107-
| "fast_xml" ->
108-
let module SH = BatHashtbl.Make (Basetype.RawStrings) in
109-
let file2funs = SH.create 100 in
110-
let funs2node = SH.create 100 in
111-
iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table);
112-
iterGlobals file (function
113-
| GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname
114-
| _ -> ()
115-
);
116-
let p_node f n = BatPrintf.fprintf f "%s" (Node.show_id n) in
117-
let p_nodes f xs =
118-
List.iter (BatPrintf.fprintf f "<node name=\"%a\"/>\n" p_node) xs
119-
in
120-
let p_funs f xs =
121-
let one_fun n =
122-
BatPrintf.fprintf f "<function name=\"%s\">\n%a</function>\n" n p_nodes (SH.find_all funs2node n)
123-
in
124-
List.iter one_fun xs
125-
in
126-
let write_file f fn =
127-
Messages.xml_file_name := fn;
128-
Logs.info "Writing xml to temp. file: %s" fn;
129-
BatPrintf.fprintf f "<run>";
130-
BatPrintf.fprintf f "<parameters>%s</parameters>" GobSys.command_line;
131-
BatPrintf.fprintf f "<statistics>";
132-
let timing_ppf = BatFormat.formatter_of_out_channel f in
133-
Timing.Default.print timing_ppf;
134-
Format.pp_print_flush timing_ppf ();
135-
BatPrintf.fprintf f "</statistics>";
136-
BatPrintf.fprintf f "<result>\n";
137-
BatEnum.iter (fun b -> BatPrintf.fprintf f "<file name=\"%s\" path=\"%s\">\n%a</file>\n" (Filename.basename b) b p_funs (SH.find_all file2funs b)) (BatEnum.uniq @@ SH.keys file2funs); (* nosemgrep: batenum-module *)
138-
BatPrintf.fprintf f "%a" printXml (Lazy.force table);
139-
gtfxml f gtable;
140-
printXmlWarning f ();
141-
BatPrintf.fprintf f "</result></run>\n";
142-
BatPrintf.fprintf f "%!"
143-
in
144-
if get_bool "g2html" then
145-
BatFile.with_temporary_out ~mode:[`create;`text;`delete_on_exit] write_file
146-
else
147-
let f = BatIO.output_channel out in
148-
write_file f (get_string "outfile")
149-
| "json" ->
150-
let open BatPrintf in
151-
let module SH = BatHashtbl.Make (Basetype.RawStrings) in
152-
let file2funs = SH.create 100 in
153-
let funs2node = SH.create 100 in
154-
iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table);
155-
iterGlobals file (function
156-
| GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname
157-
| _ -> ()
158-
);
159-
let p_enum p f xs = BatEnum.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in (* nosemgrep: batenum-module *)
160-
let p_list p f xs = BatList.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in
161-
(*let p_kv f (k,p,v) = fprintf f "\"%s\": %a" k p v in*)
162-
(*let p_obj f xs = BatList.print ~first:"{\n " ~last:"\n}" ~sep:",\n " p_kv xs in*)
163-
let p_node f n = BatPrintf.fprintf f "\"%s\"" (Node.show_id n) in
164-
let p_fun f x = fprintf f "{\n \"name\": \"%s\",\n \"nodes\": %a\n}" x (p_list p_node) (SH.find_all funs2node x) in
165-
(*let p_fun f x = p_obj f [ "name", BatString.print, x; "nodes", p_list p_node, SH.find_all funs2node x ] in*)
166-
let p_file f x = fprintf f "{\n \"name\": \"%s\",\n \"path\": \"%s\",\n \"functions\": %a\n}" (Filename.basename x) x (p_list p_fun) (SH.find_all file2funs x) in
167-
let write_file f fn =
168-
Logs.info "Writing json to temp. file: %s" fn;
169-
fprintf f "{\n \"parameters\": \"%s\",\n " GobSys.command_line;
170-
fprintf f "\"files\": %a,\n " (p_enum p_file) (SH.keys file2funs);
171-
fprintf f "\"results\": [\n %a\n]\n" printJson (Lazy.force table);
172-
(*gtfxml f gtable;*)
173-
(*printXmlWarning f ();*)
174-
fprintf f "}\n";
175-
in
176-
if get_bool "g2html" then
177-
BatFile.with_temporary_out ~mode:[`create;`text;`delete_on_exit] write_file
178-
else
179-
let f = BatIO.output_channel out in
180-
write_file f (get_string "outfile")
181-
| "sarif" ->
182-
Logs.result "Writing Sarif to file: %s" (get_string "outfile");
183-
Yojson.Safe.to_channel ~std:true out (Sarif.to_yojson (List.rev !Messages.Table.messages_list));
184-
| "json-messages" ->
185-
let json = `Assoc [
186-
("files", Preprocessor.dependencies_to_yojson ());
187-
("messages", Messages.Table.to_yojson ());
188-
]
189-
in
190-
Yojson.Safe.to_channel ~std:true out json
191-
| "none" -> ()
192-
| s -> failwith @@ "Unsupported value for option `result`: "^s
47+
module Range = Range
48+
module H = BatHashtbl.Make (ResultNode)
49+
include H
50+
type t = Range.t H.t
19351
end
19452

19553
module ResultType2 (S: Analyses.Spec) =
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
(** Analysis result output. *)
2+
3+
open GoblintCil
4+
open Pretty
5+
open GobConfig
6+
open AnalysisResult
7+
8+
module Make (Result: Result) =
9+
struct
10+
open Result
11+
12+
let pretty () mapping =
13+
let f key st dok =
14+
dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st
15+
in
16+
let content () = fold f mapping nil in
17+
let defline () = dprintf "OTHERS -> Not available\n" in
18+
dprintf "@[Mapping {\n @[%t%t@]}@]" content defline
19+
20+
let pretty_deterministic () mapping =
21+
let bindings =
22+
to_list mapping
23+
|> List.sort [%ord: ResultNode.t * Range.t]
24+
in
25+
let f dok (key, st) =
26+
dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st
27+
in
28+
let content () = List.fold_left f nil bindings in
29+
let defline () = dprintf "OTHERS -> Not available\n" in
30+
dprintf "@[Mapping {\n @[%t%t@]}@]" content defline
31+
32+
33+
let printJson f xs =
34+
let print_one n v =
35+
(* Not using Node.location here to have updated locations in incremental analysis.
36+
See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *)
37+
let loc = UpdateCil.getLoc n in
38+
BatPrintf.fprintf f "{\n\"id\": \"%s\", \"file\": \"%s\", \"line\": \"%d\", \"byte\": \"%d\", \"column\": \"%d\", \"states\": %s\n},\n" (Node.show_id n) loc.file loc.line loc.byte loc.column (Yojson.Safe.to_string (Range.to_yojson v))
39+
in
40+
iter print_one xs
41+
42+
43+
let output table live gtable gtfxml (module FileCfg: MyCFG.FileCfg) =
44+
let file = FileCfg.file in
45+
let out = Messages.get_out result_name !Messages.out in
46+
match get_string "result" with
47+
| "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table))
48+
| "pretty-deterministic" -> ignore (fprintf out "%a\n" pretty_deterministic (Lazy.force table))
49+
| "fast_xml"
50+
| "g2html" ->
51+
let module Output = XsltResultOutput.Make (Result) in
52+
Output.output table live gtable gtfxml (module FileCfg)
53+
| "xslt" ->
54+
let module Output = XsltResultOutput.Make2 (Result) in
55+
Output.output table live gtable gtfxml (module FileCfg)
56+
| "json" ->
57+
let open BatPrintf in
58+
let module SH = BatHashtbl.Make (Basetype.RawStrings) in
59+
let file2funs = SH.create 100 in
60+
let funs2node = SH.create 100 in
61+
iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table);
62+
iterGlobals file (function
63+
| GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname
64+
| _ -> ()
65+
);
66+
let p_enum p f xs = BatEnum.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in (* nosemgrep: batenum-module *)
67+
let p_list p f xs = BatList.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in
68+
(*let p_kv f (k,p,v) = fprintf f "\"%s\": %a" k p v in*)
69+
(*let p_obj f xs = BatList.print ~first:"{\n " ~last:"\n}" ~sep:",\n " p_kv xs in*)
70+
let p_node f n = BatPrintf.fprintf f "\"%s\"" (Node.show_id n) in
71+
let p_fun f x = fprintf f "{\n \"name\": \"%s\",\n \"nodes\": %a\n}" x (p_list p_node) (SH.find_all funs2node x) in
72+
(*let p_fun f x = p_obj f [ "name", BatString.print, x; "nodes", p_list p_node, SH.find_all funs2node x ] in*)
73+
let p_file f x = fprintf f "{\n \"name\": \"%s\",\n \"path\": \"%s\",\n \"functions\": %a\n}" (Filename.basename x) x (p_list p_fun) (SH.find_all file2funs x) in
74+
let write_file f fn =
75+
Logs.info "Writing json to temp. file: %s" fn;
76+
fprintf f "{\n \"parameters\": \"%s\",\n " GobSys.command_line;
77+
fprintf f "\"files\": %a,\n " (p_enum p_file) (SH.keys file2funs);
78+
fprintf f "\"results\": [\n %a\n]\n" printJson (Lazy.force table);
79+
(*gtfxml f gtable;*)
80+
(*printXmlWarning f ();*)
81+
fprintf f "}\n";
82+
in
83+
let f = BatIO.output_channel out in
84+
write_file f (get_string "outfile")
85+
| "sarif" ->
86+
Logs.result "Writing Sarif to file: %s" (get_string "outfile");
87+
Yojson.Safe.to_channel ~std:true out (Sarif.to_yojson (List.rev !Messages.Table.messages_list));
88+
| "json-messages" ->
89+
let json = `Assoc [
90+
("files", Preprocessor.dependencies_to_yojson ());
91+
("messages", Messages.Table.to_yojson ());
92+
]
93+
in
94+
Yojson.Safe.to_channel ~std:true out json
95+
| "none" -> ()
96+
| s -> failwith @@ "Unsupported value for option `result`: "^s
97+
end

src/framework/control.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ struct
102102
module LT = SetDomain.HeadlessSet (RT)
103103
(* Analysis result structure---a hashtable from program points to [LT] *)
104104
module Result = AnalysisResult.Result (LT) (struct let result_name = "analysis" end)
105+
module ResultOutput = AnalysisResultOutput.Make (Result)
105106

106107
module Query = ResultQuery.Query (SpecSys)
107108

@@ -760,7 +761,7 @@ struct
760761
in
761762

762763
if get_bool "exp.cfgdot" then
763-
CfgTools.dead_code_cfg (module FileCfg) liveness;
764+
CfgTools.dead_code_cfg ~path:(Fpath.v "cfgs") (module FileCfg) liveness;
764765

765766
let warn_global g v =
766767
(* Logs.debug "warn_global %a %a" EQSys.GVar.pretty_trace g EQSys.G.pretty v; *)
@@ -840,7 +841,7 @@ struct
840841
if get_string "result" <> "none" then Logs.debug "Generating output: %s" (get_string "result");
841842

842843
Messages.finalize ();
843-
Timing.wrap "result output" (Result.output (lazy local_xml) gh make_global_fast_xml) file
844+
Timing.wrap "result output" (ResultOutput.output (lazy local_xml) liveness gh make_global_fast_xml) (module FileCfg)
844845
end
845846

846847
(* This function was originally a part of the [AnalyzeCFG] module, but

0 commit comments

Comments
 (0)