Cleanup toplevel InferAnalyze and include procedures in the progress bar.

Summary:public

Cleanup toplevel of InferAnalyze:
- Make the toplevel flow of InferAnalyze more explicit (no exit).
- Always tear down the logging at the end.
- Fix printing of stats to include only the files actually analyzed with --reactive.
- In the progress bar, print F for files and . for procedures.

Example outputs:
Starting analysis (Infer version v0.7.0-b2fb8fc)
F.....
Analyzed 1 file

where it can say 0 if no file was modified.
Or F without dots if a file was modified but no procedure was.

Reviewed By: sblackshear, jvillard

Differential Revision: D3016934

fb-gh-sync-id: 32cf89c
shipit-source-id: 32cf89c
master
Cristiano Calcagno 9 years ago committed by Facebook Github Bot 8
parent 73d5a355f6
commit 7c464c5bac

@ -15,8 +15,8 @@ module F = Format
(** a cluster is a file *) (** a cluster is a file *)
type t = DB.source_dir type t = DB.source_dir
(** type stored in .cluster file: (n,m,cl) indicates cl is cluster n out of m *) (** type stored in .cluster file: (n,cl) indicates cl is cluster n *)
type serializer_t = int * int * t type serializer_t = int * t
(** Serializer for clusters *) (** Serializer for clusters *)
let serializer : serializer_t Serialization.serializer = let serializer : serializer_t Serialization.serializer =
@ -34,15 +34,10 @@ let cl_name n = "cl" ^ string_of_int n
let cl_file n = "x" ^ (cl_name n) ^ ".cluster" let cl_file n = "x" ^ (cl_name n) ^ ".cluster"
let pp_cluster_name fmt n = Format.fprintf fmt "%s" (cl_name n) let pp_cluster_name fmt n = Format.fprintf fmt "%s" (cl_name n)
let pp_cluster nr tot_nr cluster fmt () = let pp_cluster fmt (nr, cluster) =
let fname = cl_file nr in let fname = cl_file nr in
let pp_cl fmt n = Format.fprintf fmt "%s" (cl_name n) in let pp_cl fmt n = Format.fprintf fmt "%s" (cl_name n) in
store_to_file (DB.filename_from_string fname) (nr, tot_nr, cluster); store_to_file (DB.filename_from_string fname) (nr, cluster);
F.fprintf fmt "%a: @\n" pp_cl nr; F.fprintf fmt "%a: @\n" pp_cl nr;
F.fprintf fmt "\t$(INFERANALYZE) -cluster %s >%a@\n" fname pp_cl nr; F.fprintf fmt "\t$(INFERANALYZE) -cluster %s >%a@\n" fname pp_cl nr;
F.fprintf fmt "@\n" F.fprintf fmt "@\n"
let print_clusters clusters =
let pp_cluster num =
L.err "cluster #%d @." num in
IList.iteri (fun i _ -> pp_cluster i) clusters

@ -7,7 +7,6 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*) *)
module L = Logging
module F = Format module F = Format
(** Module to process clusters of procedures. *) (** Module to process clusters of procedures. *)
@ -15,17 +14,14 @@ module F = Format
(** a cluster is a file *) (** a cluster is a file *)
type t = DB.source_dir type t = DB.source_dir
(** type stored in .cluster file: (n,m,cl) indicates cl is cluster n out of m *) (** type stored in .cluster file: (n,cl) indicates cl is cluster n *)
type serializer_t = int * int * t type serializer_t = int * t
(** Load a cluster from a file *) (** Load a cluster from a file *)
val load_from_file : DB.filename -> serializer_t option val load_from_file : DB.filename -> serializer_t option
(** Print a cluster *) (** Print a cluster *)
val pp_cluster : int -> int -> t -> F.formatter -> unit -> unit val pp_cluster : F.formatter -> serializer_t -> unit
(** Print a cluster name *) (** Print a cluster name *)
val pp_cluster_name : F.formatter -> int -> unit val pp_cluster_name : F.formatter -> int -> unit
(** Print clusters *)
val print_clusters : t list -> unit

@ -20,15 +20,10 @@ let source_file_from_pname pname =
let source_file_to_pname fname = let source_file_to_pname fname =
Procname.from_string_c_fun (DB.source_file_to_string fname) Procname.from_string_c_fun (DB.source_file_to_string fname)
let pp_prolog fmt clusters = let cluster_should_be_analyzed cluster =
F.fprintf fmt "INFERANALYZE= %s $(INFER_OPTIONS) -results_dir '%s'\n@." let fname = DB.source_dir_to_string cluster in
Sys.executable_name
(Escape.escape_map (fun c -> if c = '#' then Some "\\#" else None) !Config.results_dir);
F.fprintf fmt "OBJECTS=";
let filter source_dir =
let fname = DB.source_dir_to_string source_dir in
let in_ondemand_config = let in_ondemand_config =
match Ondemand.read_dirs_to_analyze () with match Lazy.force Ondemand.dirs_to_analyze with
| None -> | None ->
None None
| Some set -> | Some set ->
@ -48,31 +43,36 @@ let pp_prolog fmt clusters =
check_modified () check_modified ()
| None -> | None ->
true true
end in end
let pp_prolog fmt clusters =
F.fprintf fmt "INFERANALYZE= %s $(INFER_OPTIONS) -results_dir '%s'\n@."
Sys.executable_name
(Escape.escape_map
(fun c -> if c = '#' then Some "\\#" else None)
!Config.results_dir);
F.fprintf fmt "CLUSTERS=";
IList.iteri IList.iteri
(fun i cl -> (fun i cl ->
if filter cl then F.fprintf fmt "%a " Cluster.pp_cluster_name (i+1)) if cluster_should_be_analyzed cl
then F.fprintf fmt "%a " Cluster.pp_cluster_name (i+1))
clusters; clusters;
F.fprintf fmt "@.@.default: test@.@.all: test@.@."; F.fprintf fmt "@.@.default: test@.@.all: test@.@.";
F.fprintf fmt "test: $(OBJECTS)@."; F.fprintf fmt "test: $(CLUSTERS)@.";
if !Config.show_progress_bar then F.fprintf fmt "\techo \"\"@." if !Config.show_progress_bar then F.fprintf fmt "\techo \"\"@."
let pp_epilog fmt () = let pp_epilog fmt () =
F.fprintf fmt "@.clean:@.\trm -f $(OBJECTS)@." F.fprintf fmt "@.clean:@.\trm -f $(CLUSTERS)@."
let create_cluster_makefile_and_exit let create_cluster_makefile (clusters: Cluster.t list) (fname: string) =
(clusters: Cluster.t list) (fname: string) =
let outc = open_out fname in let outc = open_out fname in
let fmt = Format.formatter_of_out_channel outc in let fmt = Format.formatter_of_out_channel outc in
let cluster_nr = ref 0 in let do_cluster cluster_nr cluster =
let tot_clusters_nr = IList.length clusters in F.fprintf fmt "#%s@\n" (DB.source_dir_to_string cluster);
let do_cluster cluster = Cluster.pp_cluster fmt (cluster_nr + 1, cluster) in
incr cluster_nr;
let do_file source_dir =
F.fprintf fmt "#%s@\n" (DB.source_dir_to_string source_dir) in
do_file cluster;
Cluster.pp_cluster !cluster_nr tot_clusters_nr cluster fmt () in
pp_prolog fmt clusters; pp_prolog fmt clusters;
IList.iter do_cluster clusters; IList.iteri do_cluster clusters;
pp_epilog fmt (); pp_epilog fmt ()
exit 0

@ -13,35 +13,6 @@
module L = Logging module L = Logging
module F = Format module F = Format
(* This module, unused by default, generates random c files with procedure calls *)
module Codegen = struct
let num_files = 5000
let num_functions = 1000
let calls_per_fun = 5
let fun_nr = ref 0
let gen () =
for file_nr = 1 to num_files do
let fname = Printf.sprintf "file%04d.c" file_nr in
let fmt = open_out fname in
for _ = 1 to num_functions do
incr fun_nr;
let num_calls = if !fun_nr = 1 then 0 else Random.int calls_per_fun in
Printf.fprintf fmt "void f%04d() {\n" !fun_nr;
for _ = 1 to num_calls do
let callee_nr = 1 + Random.int (max 1 (num_calls - 1)) in
Printf.fprintf fmt "f%04d();\n" callee_nr
done;
Printf.fprintf fmt "}\n";
done;
close_out fmt
done
let dont () =
gen ();
exit 0
end
(** command line option: if true, run the analysis in checker mode *) (** command line option: if true, run the analysis in checker mode *)
let checkers = ref false let checkers = ref false
@ -255,7 +226,7 @@ let () = (* parse command-line arguments *)
let analyze_exe_env exe_env = let analyze_exe_env exe_env =
let init_time = Unix.gettimeofday () in let init_time = Unix.gettimeofday () in
L.log_progress_simple "."; L.log_progress_file ();
Specs.clear_spec_tbl (); Specs.clear_spec_tbl ();
Random.self_init (); Random.self_init ();
let line_reader = Printer.LineReader.create () in let line_reader = Printer.LineReader.create () in
@ -273,16 +244,6 @@ let analyze_exe_env exe_env =
L.out "Interprocedural footprint analysis terminated in %f sec@." elapsed L.out "Interprocedural footprint analysis terminated in %f sec@." elapsed
end end
let output_json_file_stats num_files num_procs num_lines =
let file_stats =
`Assoc [ ("files", `Int num_files);
("procedures", `Int num_procs);
("lines", `Int num_lines) ] in
(* write stats file to disk, intentionally overwriting old file if it already exists *)
let f = open_out (Filename.concat !Config.results_dir Config.proc_stats_filename) in
Yojson.Basic.pretty_to_channel f file_stats
(** Create an exe_env from a cluster. *) (** Create an exe_env from a cluster. *)
let exe_env_from_cluster cluster = let exe_env_from_cluster cluster =
let _exe_env = Exe_env.create () in let _exe_env = Exe_env.create () in
@ -293,28 +254,14 @@ let exe_env_from_cluster cluster =
exe_env exe_env
(** Analyze a cluster of files *) (** Analyze a cluster of files *)
let analyze_cluster cluster_num tot_clusters (cluster : Cluster.t) = let analyze_cluster cluster_num (cluster : Cluster.t) =
let cluster_num_ref = ref cluster_num in
incr cluster_num_ref;
let exe_env = exe_env_from_cluster cluster in let exe_env = exe_env_from_cluster cluster in
let defined_procs = Cg.get_defined_nodes (Exe_env.get_cg exe_env) in let defined_procs = Cg.get_defined_nodes (Exe_env.get_cg exe_env) in
let num_procs = IList.length defined_procs in let num_procs = IList.length defined_procs in
L.err "@.Processing cluster #%d/%d with %d procedures@." L.err "@.Processing cluster #%d with %d procedures@."
!cluster_num_ref tot_clusters num_procs; (cluster_num + 1) num_procs;
analyze_exe_env exe_env analyze_exe_env exe_env
let process_cluster_cmdline_exit () =
match !cluster_cmdline with
| None -> ()
| Some fname ->
(match Cluster.load_from_file (DB.filename_from_string fname) with
| None ->
L.err "Cannot find cluster file %s@." fname;
exit 0
| Some (nr, tot_nr, cluster) ->
analyze_cluster (nr - 1) tot_nr cluster;
exit 0)
let open_output_file f fname = let open_output_file f fname =
try try
let cout = open_out fname in let cout = open_out fname in
@ -355,17 +302,19 @@ let teardown_logging analyzer_out_of analyzer_err_of =
close_output_file analyzer_err_of; close_output_file analyzer_err_of;
end end
(** Compute clusters. let output_json_makefile_stats clusters =
Each cluster will contain only the name of the directory for a file. *) let clusters_to_analyze =
let compute_clusters clusters = IList.filter ClusterMakefile.cluster_should_be_analyzed clusters in
Cluster.print_clusters clusters; let num_files = IList.length clusters_to_analyze in
let num_files = IList.length clusters in
let num_procs = 0 (* can't compute it at this stage *) in let num_procs = 0 (* can't compute it at this stage *) in
let num_lines = 0 in let num_lines = 0 in
output_json_file_stats num_files num_procs num_lines; let file_stats =
if !makefile_cmdline <> "" `Assoc [ ("files", `Int num_files);
then ClusterMakefile.create_cluster_makefile_and_exit clusters !makefile_cmdline; ("procedures", `Int num_procs);
clusters ("lines", `Int num_lines) ] in
(* write stats file to disk, intentionally overwriting old file if it already exists *)
let f = open_out (Filename.concat !Config.results_dir Config.proc_stats_filename) in
Yojson.Basic.pretty_to_channel f file_stats
let print_prolog () = let print_prolog () =
match !cluster_cmdline with match !cluster_cmdline with
@ -373,6 +322,13 @@ let print_prolog () =
L.stdout "Starting analysis (Infer version %s)@." Version.versionString; L.stdout "Starting analysis (Infer version %s)@." Version.versionString;
| Some clname -> L.stdout "Cluster %s@." clname | Some clname -> L.stdout "Cluster %s@." clname
let process_cluster_cmdline fname =
match Cluster.load_from_file (DB.filename_from_string fname) with
| None ->
L.err "Cannot find cluster file %s@." fname
| Some (nr, cluster) ->
analyze_cluster (nr - 1) cluster
let () = let () =
print_prolog (); print_prolog ();
RegisterCheckers.register (); RegisterCheckers.register ();
@ -383,15 +339,28 @@ let () =
let analyzer_out_of, analyzer_err_of = setup_logging () in let analyzer_out_of, analyzer_err_of = setup_logging () in
if (!Config.curr_language = Config.C_CPP) then Mleak_buckets.init_buckets !ml_buckets_arg; if (!Config.curr_language = Config.C_CPP) then Mleak_buckets.init_buckets !ml_buckets_arg;
let finish_logging () =
teardown_logging analyzer_out_of analyzer_err_of in
process_cluster_cmdline_exit (); match !cluster_cmdline with
| Some fname ->
let source_dirs = DB.find_source_dirs () in process_cluster_cmdline fname;
L.err "Found %d source files in %s@." (IList.length source_dirs) !Config.results_dir; finish_logging ()
| None ->
let clusters = DB.find_source_dirs () in
L.err "Found %d source files in %s@."
(IList.length clusters) !Config.results_dir;
let clusters = compute_clusters source_dirs in if !makefile_cmdline <> ""
then
ClusterMakefile.create_cluster_makefile clusters !makefile_cmdline
else
begin
IList.iteri
(fun i cluster -> analyze_cluster i cluster)
clusters;
L.stdout "Analysis finished in %as@." pp_elapsed_time ()
end;
output_json_makefile_stats clusters;
finish_logging ()
let tot_clusters = IList.length clusters in
IList.iter (analyze_cluster 0 tot_clusters) clusters;
teardown_logging analyzer_out_of analyzer_err_of;
if !cluster_cmdline = None then L.stdout "Analysis finished in %as@." pp_elapsed_time ()

@ -0,0 +1,11 @@
(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** Main module for the analysis after the capture phase *)

@ -172,13 +172,12 @@ let d_increase_indent (indent: int) =
let d_decrease_indent (indent: int) = let d_decrease_indent (indent: int) =
add_print_action (PTdecrease_indent, Obj.repr indent) add_print_action (PTdecrease_indent, Obj.repr indent)
let log_progress text counter total =
if !Config.show_progress_bar then
(counter := !counter + 1;
let percentage = (100 * !counter) / total in
F.fprintf Format.err_formatter "%s %d%s" text percentage "%\r";
F.fprintf Format.err_formatter "@?")
let log_progress_simple text = let log_progress_simple text =
if !Config.show_progress_bar then if !Config.show_progress_bar then
F.fprintf Format.err_formatter "%s@?" text F.fprintf Format.err_formatter "%s@?" text
let log_progress_file () =
log_progress_simple "F"
let log_progress_procedure () =
log_progress_simple "."

@ -130,6 +130,8 @@ val d_increase_indent : int -> unit
(** dump command to decrease the indentation level *) (** dump command to decrease the indentation level *)
val d_decrease_indent : int -> unit val d_decrease_indent : int -> unit
val log_progress : string -> int ref -> int -> unit (** Progress bar: start of the analysis of a file. *)
val log_progress_file : unit -> unit
val log_progress_simple : string -> unit (** Progress bar: start of the analysis of a procedure. *)
val log_progress_procedure : unit -> unit

@ -20,8 +20,10 @@ let ondemand_file () = Config.get_env_variable "INFER_ONDEMAND_FILE"
(** Read the directories to analyze from the ondemand file. *) (** Read the directories to analyze from the ondemand file. *)
let read_dirs_to_analyze () = let read_dirs_to_analyze () =
let lines_opt = match ondemand_file () with let lines_opt = match ondemand_file () with
| None -> None | None ->
| Some fname ->read_file fname in None
| Some fname ->
read_file fname in
match lines_opt with match lines_opt with
| None -> | None ->
None None
@ -33,6 +35,10 @@ let read_dirs_to_analyze () =
IList.iter do_line lines; IList.iter do_line lines;
Some !res Some !res
(** Directories to analyze from the ondemand file. *)
let dirs_to_analyze =
lazy (read_dirs_to_analyze ())
type analyze_ondemand = Procname.t -> unit type analyze_ondemand = Procname.t -> unit
type get_cfg = Procname.t -> Cfg.cfg option type get_cfg = Procname.t -> Cfg.cfg option
@ -118,6 +124,8 @@ let do_analysis ~propagate_exceptions curr_pdesc callee_pname =
let curr_pname = Cfg.Procdesc.get_proc_name curr_pdesc in let curr_pname = Cfg.Procdesc.get_proc_name curr_pdesc in
let really_do_analysis callee_pdesc analyze_proc = let really_do_analysis callee_pdesc analyze_proc =
(** Dot means start of a procedure *)
L.log_progress_procedure ();
if trace () then L.stderr "[%d] really_do_analysis %a -> %a@." if trace () then L.stderr "[%d] really_do_analysis %a -> %a@."
!nesting !nesting
Procname.pp curr_pname Procname.pp curr_pname

@ -10,7 +10,7 @@
(** Module for on-demand analysis. *) (** Module for on-demand analysis. *)
(** Optional set of source dirs to analyze in on-demand mode. *) (** Optional set of source dirs to analyze in on-demand mode. *)
val read_dirs_to_analyze : unit -> StringSet.t option val dirs_to_analyze : StringSet.t option Lazy.t
type analyze_ondemand = Procname.t -> unit type analyze_ondemand = Procname.t -> unit

Loading…
Cancel
Save