[trace] infer subcommand for inferTraceBugs

Summary:
Replace `inferTraceBugs` with `infer-explore` with a similar CLI. Some options changed:
- --max-level -> --max-nesting, and "max" is the default value instead of a possible value
- --no-source -> --no-source-preview

Reviewed By: mbouaziz

Differential Revision: D5526651

fbshipit-source-id: 8383f37
master
Jules Villard 7 years ago committed by Facebook Github Bot
parent b2ee1152fe
commit 679b125ac4

1
.gitignore vendored

@ -98,6 +98,7 @@ buck-out/
/infer/bin/infer-analyze
/infer/bin/infer-capture
/infer/bin/infer-compile
/infer/bin/infer-explore
/infer/bin/infer-report
/infer/bin/infer-reportdiff
/infer/bin/infer-run

@ -5,5 +5,3 @@
*infer* : Main command to run Infer. Check out the docs for instructions on how to use it.
*infer-<command>* : Infer subcommands. Running `infer-<command> [options]` is the same as running `infer <command> [options]`.
*inferTraceBugs* : Python script to explore the error traces in Infer reports

@ -469,9 +469,6 @@ endif
(cd $(DESTDIR)$(libdir)/infer/infer/bin && \
$(REMOVE) $$alias && \
$(LN_S) infer $$alias); done
(cd $(DESTDIR)$(bindir)/ && \
$(REMOVE) inferTraceBugs && \
$(LN_S) $(libdir_relative_to_bindir)/infer/infer/lib/python/inferTraceBugs inferTraceBugs)
$(QUIET)for i in $(MAN_DIR)/man1/*; do \
$(INSTALL_DATA) -C $$i $(DESTDIR)$(mandir)/man1/$$(basename $$i); \
done

@ -60,14 +60,13 @@ INFER_COMMANDS = \
infer-analyze \
infer-capture \
infer-compile \
infer-explore \
infer-report \
infer-reportdiff \
infer-run \
INFERTRACEBUGS_BIN = $(BIN_DIR)/inferTraceBugs
INFER_CREATE_TRACEVIEW_LINKS = InferCreateTraceViewLinks
INFER_CREATE_TRACEVIEW_LINKS_BIN = $(BIN_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS)
INFERTRACEBUGS_BIN_RELPATH = infer/bin/inferTraceBugs
INFER_COMMAND_MANUALS = $(INFER_COMMANDS:%=$(MAN_DIR)/man1/%.1)
INFER_MANUAL = $(MAN_DIR)/man1/infer.1

@ -45,15 +45,14 @@ base_parser.add_argument('--no-source',
help='Do not print code excerpts')
base_parser.add_argument('--select',
metavar='N',
nargs=1,
type=int,
help='Select bug number N. '
'If omitted, prompts you for input.')
base_parser.add_argument('--max-level',
metavar='N',
nargs=1,
type=int,
help='Level of nested procedure calls to show. '
'Can be "max", in which case all levels are shown. '
'If omitted, prompts you for input.')
'By default, all levels are shown.')
base_parser.add_argument('--html',
action='store_true',
help='Generate HTML report.')
@ -159,7 +158,7 @@ class Selector(object):
def prompt_report(self):
report_number = 0
if self.args.select is not None:
report_number = self.parse_report_number(self.args.select[0], True)
report_number = self.parse_report_number(self.args.select, True)
else:
self.show_choices()
@ -174,19 +173,7 @@ class Selector(object):
return self.reports[report_number]
def prompt_level(self):
if self.args.max_level is not None:
return self.parse_max_level(self.args.max_level[0], True)
max_level_str = raw_input(
'Choose maximum level of nested procedures calls (default=max): ')
if max_level_str == '':
max_level = sys.maxsize
else:
max_level = self.parse_max_level(max_level_str)
print('')
return max_level
return self.parse_max_level(self.args.max_level, True)
def parse_report_number(self, s, show_help=False):
try:
@ -202,14 +189,14 @@ class Selector(object):
return n
def parse_max_level(self, s, show_help=False):
if s == 'max':
if s is None:
return sys.maxsize
try:
n = int(s)
except ValueError:
show_error_and_exit(
'ERROR: integer max level or "max" expected',
'ERROR: integer max level expected',
show_help)
if n < 0:

@ -157,7 +157,7 @@ def _text_of_report_list(project_root, reports, bugs_txt_path, limit=None,
if limit >= 0 and n_issues > limit:
text_errors += colorize.color(
('\n\n...too many issues to display (limit=%d exceeded), please ' +
'see %s or run `inferTraceBugs` for the remaining issues.')
'see %s or run `infer-explore` for the remaining issues.')
% (limit, bugs_txt_path), colorize.HEADER, formatter)
issues_found = 'Found {n_issues}'.format(

@ -73,6 +73,8 @@ let setup_results_dir () =
|| Config.(buck || continue_capture || maven || reactive_mode) )
then remove_results_dir () ;
create_results_dir ()
| Explore
-> assert_results_dir "please run an infer analysis first"
let () =
if Config.print_builtins then Builtin.print_and_exit () ;
@ -119,3 +121,19 @@ let () =
-> run (Lazy.force Driver.mode_from_command_line)
| Diff
-> Diff.diff (Lazy.force Driver.mode_from_command_line)
| Explore
-> let if_some key opt args =
match opt with None -> args | Some arg -> key :: string_of_int arg :: args
in
let if_true key opt args = if not opt then args else key :: args in
let if_false key opt args = if opt then args else key :: args in
let args =
if_some "--max-level" Config.max_nesting @@ if_true "--only-show" Config.only_show
@@ if_false "--no-source" Config.source_preview @@ if_true "--html" Config.html
@@ if_some "--select" Config.select ["-o"; Config.results_dir]
in
let prog = Config.lib_dir ^/ "python" ^/ "inferTraceBugs" in
if is_error (Unix.waitpid (Unix.fork_exec ~prog ~argv:(prog :: args) ())) then
L.external_error
"** Error running the reporting script:@\n** %s %s@\n** See error above@." prog
(String.concat ~sep:" " args)

@ -24,6 +24,7 @@ let command_to_name =
; (Capture, "capture")
; (Compile, "compile")
; (Diff, "diff")
; (Explore, "explore")
; (Report, "report")
; (ReportDiff, "reportdiff")
; (Run, "run") ]
@ -38,8 +39,8 @@ let command_of_exe_name exe_name =
List.find_map command_to_name ~f:(fun (cmd, name) ->
if String.equal exe_name (exe_name_of_command_name name) then Some cmd else None )
let mk_command_doc ~see_also:see_also_commands ?and_also ?environment:environment_opt
?files:files_opt ~synopsis =
let mk_command_doc ~see_also:see_also_commands ?environment:environment_opt ?files:files_opt
~synopsis =
let section = 1 in
let see_also =
let exe_names =
@ -47,8 +48,7 @@ let mk_command_doc ~see_also:see_also_commands ?and_also ?environment:environmen
let exe = exe_name_of_command cmd in
Printf.sprintf "$(b,%s)(%d)" (Cmdliner.Manpage.escape exe) section )
in
let suffix = Option.value ~default:"" and_also in
[`P (String.concat ~sep:", " exe_names ^ suffix)]
[`P (String.concat ~sep:", " exe_names)]
in
let environment =
Option.value environment_opt
@ -133,6 +133,16 @@ let diff =
~description:[`P "EXPERIMENTAL AND IN NO WAY READY TO USE"]
~see_also:CLOpt.([ReportDiff; Run])
let explore =
mk_command_doc ~title:"Infer Explore"
~short_description:"explore the error traces in infer reports"
~synopsis:{|$(b,infer) $(b,explore) $(i,[options])|}
~description:
[ `P
"Show the list of bugs on the console and explore symbolic program traces emitted by infer to explain a report. Can also generate an HTML report from a JSON report."
]
~see_also:CLOpt.([Report; Run])
let infer =
mk_command_doc ~title:"Infer Static Analyzer"
~short_description:"static analysis for Java and C/C++/Objective-C/Objective-C++"
@ -211,7 +221,7 @@ $(b,infer) $(i,[options])|}
}|}
]
~see_also:(List.filter ~f:(function CLOpt.Clang -> false | _ -> true) CLOpt.all_commands)
~and_also:", $(b,inferTraceBugs)" "infer"
"infer"
let report =
mk_command_doc ~title:"Infer Reporting" ~short_description:"compute and manipulate infer results"
@ -267,6 +277,7 @@ let command_to_data =
; mk Capture capture
; mk Compile compile
; mk Diff diff
; mk Explore explore
; mk Report report
; mk ReportDiff reportdiff
; mk Run run ]

@ -90,6 +90,7 @@ type command =
| Clang
| Compile
| Diff
| Explore
| Report
| ReportDiff
| Run
@ -97,7 +98,7 @@ type command =
let equal_command = [%compare.equal : command]
let all_commands = [Analyze; Capture; Clang; Compile; Diff; Report; ReportDiff; Run]
let all_commands = [Analyze; Capture; Clang; Compile; Diff; Explore; Report; ReportDiff; Run]
type command_doc =
{ title: Cmdliner.Manpage.title

@ -34,6 +34,7 @@ type command =
(** set up the infer environment then run the compilation commands without capturing the
source files *)
| Diff (** orchestrate a diff analysis *)
| Explore (** explore infer reports *)
| Report (** post-process infer results and reports *)
| ReportDiff (** compute the difference of two infer reports *)
| Run (** orchestrate the capture, analysis, and reporting of a compilation command *)

@ -420,7 +420,7 @@ let startup_action =
match initial_command with
| Some Clang
-> NoParse
| None | Some (Analyze | Capture | Compile | Diff | Report | ReportDiff | Run)
| None | Some (Analyze | Capture | Compile | Diff | Explore | Report | ReportDiff | Run)
-> InferCommand
let exe_usage =
@ -474,7 +474,7 @@ let () =
-> assert false (* filtered out *)
| Report
-> `Add
| Analyze | Capture | Compile | Diff | ReportDiff | Run
| Analyze | Capture | Compile | Diff | Explore | ReportDiff | Run
-> `Reject
in
(* make sure we generate doc for all the commands we know about *)
@ -871,7 +871,8 @@ and ( bo_debug
, write_dotty ) =
let all_generic_manuals =
List.filter_map CLOpt.all_commands ~f:(fun cmd ->
if CLOpt.(equal_command cmd Clang) then None else Some (cmd, manual_generic) )
if List.mem ~equal:CLOpt.equal_command CLOpt.([Clang; Explore]) cmd then None
else Some (cmd, manual_generic) )
in
let bo_debug =
CLOpt.mk_int ~default:0 ~long:"bo-debug"
@ -1159,6 +1160,9 @@ and help_format =
~in_help:(List.map CLOpt.all_commands ~f:(fun command -> (command, manual_generic)))
"Show this help in the specified format. $(b,auto) sets the format to $(b,plain) if the environment variable $(b,TERM) is \"dumb\" or undefined, and to $(b,pager) otherwise."
and html =
CLOpt.mk_bool ~long:"html" ~in_help:CLOpt.([(Explore, manual_generic)]) "Generate html report."
and icfg_dotty_outfile =
CLOpt.mk_path_opt ~long:"icfg-dotty-outfile" ~meta:"path"
"If set, specifies path where .dot file should be written, it overrides the path for all other options that would generate icfg file otherwise"
@ -1260,6 +1264,11 @@ and margin =
CLOpt.mk_int ~deprecated:["set_pp_margin"] ~long:"margin" ~default:100 ~meta:"int"
"Set right margin for the pretty printing functions"
and max_nesting =
CLOpt.mk_int_opt ~long:"max-nesting"
~in_help:CLOpt.([(Explore, manual_generic)])
"Level of nested procedure calls to show. Trace elements beyond the maximum nesting level are skipped. If omitted, all levels are shown."
and merge =
CLOpt.mk_bool ~deprecated:["merge"] ~long:"merge"
~in_help:CLOpt.([(Analyze, manual_buck_flavors)])
@ -1294,6 +1303,11 @@ and objc_memory_model =
and only_footprint =
CLOpt.mk_bool ~deprecated:["only_footprint"] ~long:"only-footprint" "Skip the re-execution phase"
and only_show =
CLOpt.mk_bool ~long:"only-show"
~in_help:CLOpt.([(Explore, manual_generic)])
"Show the list of reports and exit"
and passthroughs =
CLOpt.mk_bool ~long:"passthroughs" ~default:false
"In error traces, show intermediate steps that propagate data. When false, error traces are shorter and show only direct flow via souces/sinks"
@ -1459,6 +1473,7 @@ and results_dir =
(CLOpt.(
[ (Analyze, manual_generic)
; (Capture, manual_generic)
; (Explore, manual_generic)
; (Run, manual_generic)
; (Report, manual_generic) ]))
~meta:"dir" "Write results and internal files in the specified directory"
@ -1472,6 +1487,11 @@ and seconds_per_iteration =
CLOpt.mk_float_opt ~deprecated:["seconds_per_iteration"] ~long:"seconds-per-iteration"
~meta:"float" "Set the number of seconds per iteration (see $(b,--iterations))"
and select =
CLOpt.mk_int_opt ~long:"select" ~meta:"N"
~in_help:CLOpt.([(Explore, manual_generic)])
"Select bug number $(i,N). If omitted, prompt for input."
and siof_safe_methods =
CLOpt.mk_string_list ~long:"siof-safe-methods"
~in_help:CLOpt.([(Analyze, manual_siof)])
@ -1498,6 +1518,11 @@ and skip_translation_headers =
~in_help:CLOpt.([(Capture, manual_clang)])
~meta:"path prefix" "Ignore headers whose path matches the given prefix"
and source_preview =
CLOpt.mk_bool ~long:"source-preview" ~default:true
~in_help:CLOpt.([(Explore, manual_generic)])
"print code excerpts around trace elements"
and sources = CLOpt.mk_string_list ~long:"sources" "Specify the list of source files"
and sourcepath = CLOpt.mk_string_opt ~long:"sourcepath" "Specify the sourcepath"
@ -1978,6 +2003,8 @@ and frontend_stats = !frontend_stats
and headers = !headers
and html = !html
and icfg_dotty_outfile = !icfg_dotty_outfile
and ignore_trivial_traces = !ignore_trivial_traces
@ -2027,6 +2054,8 @@ and log_file = !log_file
and makefile_cmdline = !makefile
and max_nesting = !max_nesting
and merge = !merge
and ml_buckets = !ml_buckets
@ -2049,6 +2078,8 @@ and only_cheap_debug = !only_cheap_debug
and only_footprint = !only_footprint
and only_show = !only_show
and passthroughs = !passthroughs
and patterns_never_returning_null =
@ -2131,6 +2162,8 @@ and save_analysis_results = !save_results
and seconds_per_iteration = !seconds_per_iteration
and select = !select
and show_buckets = !print_buckets
and show_progress_bar = !progress_bar
@ -2147,6 +2180,8 @@ and skip_duplicated_types = !skip_duplicated_types
and skip_translation_headers = !skip_translation_headers
and source_preview = !source_preview
and sources = !sources
and sourcepath = !sourcepath

@ -411,6 +411,8 @@ val generated_classes : string option
val headers : bool
val html : bool
val icfg_dotty_outfile : string option
val ignore_trivial_traces : bool
@ -480,6 +482,8 @@ val makefile_cmdline : string
val maven : bool
val max_nesting : int option
val merge : bool
val ml_buckets :
@ -501,6 +505,8 @@ val only_cheap_debug : bool
val only_footprint : bool
val only_show : bool
val pmd_xml : bool
val precondition_stats : bool
@ -565,6 +571,8 @@ val save_analysis_results : string option
val seconds_per_iteration : float option
val select : int option
val show_buckets : bool
val show_progress_bar : bool
@ -581,6 +589,8 @@ val skip_duplicated_types : bool
val skip_translation_headers : string list
val source_preview : bool
val spec_abs_level : int
val specs_library : string list

@ -20,26 +20,26 @@ infer-out/report.json: $(SOURCES) $(CLANG_DEPS)
.PHONY: test1
test1: infer-out/report.json
$(QUIET)$(call silent_on_success,Testing inferTraceBugs: --max-level=max,\
$(PYTHON_DIR)/inferTraceBugs -o infer-out \
--select 0 --max-level max)
$(QUIET)$(call silent_on_success,Testing infer-explore: --max-nesting=3,\
$(INFER_BIN) explore -o infer-out \
--select 0 --max-nesting 3)
.PHONY: test2
test2: infer-out/report.json
$(QUIET)$(call silent_on_success,Testing inferTraceBugs: --max-level=0,\
$(PYTHON_DIR)/inferTraceBugs -o infer-out \
--select 0 --max-level 0)
$(QUIET)$(call silent_on_success,Testing infer-explore: --max-nesting=0,\
$(INFER_BIN) explore -o infer-out \
--select 0 --max-nesting 0)
.PHONY: test3
test3: infer-out/report.json
$(QUIET)$(call silent_on_success,Testing inferTraceBugs: --max-level=max --no-source,\
$(PYTHON_DIR)/inferTraceBugs -o infer-out \
--select 0 --max-level max --no-source)
$(QUIET)$(call silent_on_success,Testing infer-explore: --no-source-preview,\
$(INFER_BIN) explore -o infer-out \
--select 0 --no-source-preview)
.PHONY: test4
test4: infer-out/report.json
$(QUIET)$(call silent_on_success,Testing inferTraceBugs: --only-show,\
$(PYTHON_DIR)/inferTraceBugs -o infer-out \
$(QUIET)$(call silent_on_success,Testing infer-explore: --only-show,\
$(INFER_BIN) explore -o infer-out \
--only-show)

@ -17,7 +17,7 @@ SOURCES = ../codetoanalyze/make/utf8_in_function_names.c
include $(TESTS_DIR)/clang.make
infer-out/report.json: $(CLANG_DEPS) $(INFERTRACEBUGS_BIN) $(SOURCES) $(HEADERS) $(MAKEFILE_LIST)
infer-out/report.json: $(CLANG_DEPS) $(SOURCES) $(HEADERS) $(MAKEFILE_LIST)
# set non-utf8-supporting locale
$(QUIET)LC_ALL=C; \
$(call silent_on_success,Testing Infer is immune to UTF-8 in procnames,\
@ -25,9 +25,9 @@ infer-out/report.json: $(CLANG_DEPS) $(INFERTRACEBUGS_BIN) $(SOURCES) $(HEADERS)
$(QUIET)$(call check_no_duplicates,infer-out/duplicates.txt)
# make sure inferTraceBugs is immune to UTF-8
$(QUIET)$(call silent_on_success,Testing inferTraceBugs is immune to UTF-8 in procnames,\
$(INFERTRACEBUGS_BIN) --max-level max --select 0)
$(INFER_BIN) explore --select 0)
$(QUIET)$(call silent_on_success,Testing inferTraceBugs --html is immune to UTF-8 in procnames,\
$(INFERTRACEBUGS_BIN) --html)
$(INFER_BIN) explore --html)
$(QUIET)[ -f infer-out/report.html/index.html ]
# run again to check that infer manages to delete the results directory
$(QUIET)LC_ALL=C; \

Loading…
Cancel
Save