diff --git a/infer/bin/BuckAnalyze b/infer/bin/BuckAnalyze index 828bac0a9..65731f996 100755 --- a/infer/bin/BuckAnalyze +++ b/infer/bin/BuckAnalyze @@ -269,6 +269,7 @@ def get_basic_stats(stats): to_skip = { 'files', + 'procedures', 'lines', 'cores', 'time', diff --git a/infer/bin/inferlib.py b/infer/bin/inferlib.py index bf91a320b..cb3fd7e1a 100644 --- a/infer/bin/inferlib.py +++ b/infer/bin/inferlib.py @@ -663,9 +663,12 @@ class Infer: elapsed = utils.elapsed_time(start_time) self.timing['total'] = elapsed self.save_stats() + + procs_total = self.stats['int']['procedures'] files_total = self.stats['int']['files'] - files_plural = '' if files_total <= 1 else 's' - print('\n%d file%s analyzed' % (files_total, files_plural)) + procs_str = utils.get_plural('procedure', procs_total) + files_str = utils.get_plural('file', files_total) + print('\nAnalyzed %s in %s' % (procs_str, files_str)) return self.stats else: return dict({}) diff --git a/infer/bin/utils.py b/infer/bin/utils.py index 6416d26a9..d285647d9 100644 --- a/infer/bin/utils.py +++ b/infer/bin/utils.py @@ -355,6 +355,11 @@ def create_json_report(out_dir): json.dump(issues, file_out, indent=2) +def get_plural(_str, count): + plural_str = _str if count == 1 else _str + 's' + return '%d %s' % (count, plural_str) + + class AbsolutePathAction(argparse.Action): """Convert a path from relative to absolute in the arg parser""" def __call__(self, parser, namespace, values, option_string=None): diff --git a/infer/src/backend/inferanalyze.ml b/infer/src/backend/inferanalyze.ml index 375d56d60..b94a7ff05 100644 --- a/infer/src/backend/inferanalyze.ml +++ b/infer/src/backend/inferanalyze.ml @@ -356,8 +356,11 @@ let file_pname_to_cg file_pname = let cg_fname = DB.source_dir_get_internal_file source_dir ".cg" in Cg.load_from_file cg_fname -let output_json_file_stats num_files num_lines = - let file_stats = `Assoc [ ("files", `Int num_files); ("lines", `Int num_lines) ] in +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.stats_filename) in Yojson.Basic.pretty_to_channel f file_stats @@ -369,6 +372,7 @@ let create_minimal_clusters file_cg exe_env to_analyze_map : cluster list = let seen = ref Procname.Set.empty in let clusters = ref [] in let total_files = ref 0 in + let total_procs = ref 0 in let total_LOC = ref 0 in let create_cluster_elem (file_pname, changed_procs) = (* create a cluster_elem for the file *) let source_file = source_file_from_pname file_pname in @@ -377,8 +381,6 @@ let create_minimal_clusters file_cg exe_env to_analyze_map : cluster list = match file_pname_to_cg file_pname with | None -> { ce_file = source_file; ce_naprocs = 0; ce_active_procs = []; ce_source_map = Procname.Map.empty } | Some cg -> - total_files := !total_files + 1; - total_LOC := !total_LOC + (Cg.get_nLOC cg); (* decide whether a proc is active using pname_to_fname, i.e. whether this is the file associated to it *) let proc_is_selected pname = match !select_proc with | None -> true @@ -399,6 +401,9 @@ let create_minimal_clusters file_cg exe_env to_analyze_map : cluster list = with Not_found -> () in list_iter do_proc all_procs; !mapr in + total_files := !total_files + 1; + total_procs := !total_procs + naprocs; + total_LOC := !total_LOC + (Cg.get_nLOC cg); { ce_file = source_file; ce_naprocs = naprocs; ce_active_procs = active_procs; ce_source_map = source_map } in let choose_next_file list = (* choose next file from the weakly ordered list *) let file_has_no_unseen_dependents fname = @@ -438,7 +443,7 @@ let create_minimal_clusters file_cg exe_env to_analyze_map : cluster list = end; build_clusters list'' in build_clusters sorted_files; - output_json_file_stats !total_files !total_LOC; + output_json_file_stats !total_files !total_procs !total_LOC; list_rev !clusters let cluster_nfiles cluster = list_length cluster diff --git a/infer/tests/utils/InferStats.java b/infer/tests/utils/InferStats.java index 9a84821e5..7583d6304 100644 --- a/infer/tests/utils/InferStats.java +++ b/infer/tests/utils/InferStats.java @@ -26,6 +26,9 @@ public class InferStats { @JsonProperty(value = "files") int numFiles; + @JsonProperty(value = "procedures") + int numProcedures; + @JsonProperty(value = "lines") int numLines; } @@ -65,6 +68,10 @@ public class InferStats { return intFields.numFiles; } + public int getNumProcedures() { + return intFields.numProcedures; + } + public int getNumLines() { return intFields.numLines; } diff --git a/infer/tests/utils/matchers/NumberOfProceduresAnalyzed.java b/infer/tests/utils/matchers/NumberOfProceduresAnalyzed.java new file mode 100644 index 000000000..823eb6055 --- /dev/null +++ b/infer/tests/utils/matchers/NumberOfProceduresAnalyzed.java @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2015 - 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. +*/ + +package utils.matchers; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import utils.InferError; +import utils.InferStats; + +public class NumberOfProceduresAnalyzed extends BaseMatcher { + + private int expectedNumProcedures; + private int actualNumProcedures; + + public NumberOfProceduresAnalyzed(int expectedNumProcedures) { + this.expectedNumProcedures = expectedNumProcedures; + this.actualNumProcedures = -1; + } + + @Override + public boolean matches(Object o) { + InferStats stats = (InferStats) o; + actualNumProcedures = stats.getNumProcedures(); + return actualNumProcedures == expectedNumProcedures; + } + + @Override + public void describeTo(Description description) { + description.appendText(expectedNumProcedures + " procedures analyzed by Infer."); + } + + @Override + public void describeMismatch(Object item, Description description) { + InferStats stats = (InferStats) item; + description.appendText("found " + actualNumProcedures + " procedures analyzed by Infer."); + } + + public static Matcher numberOfProceduresAnalyzed( + int expectedNumProcedures) { + return new NumberOfProceduresAnalyzed(expectedNumProcedures); + } + +}