[crashcontext] die

Reviewed By: jeremydubreil, mbouaziz, jvillard

Differential Revision: D13861427

fbshipit-source-id: 85e340bb5
master
Nikos Gorogiannis 6 years ago committed by Facebook Github Bot
parent e6d2872a4e
commit 374538a02f

@ -112,7 +112,6 @@ DIRECT_TESTS += \
java_bufferoverrun \ java_bufferoverrun \
java_checkers \ java_checkers \
java_classloads \ java_classloads \
java_crashcontext \
java_eradicate \ java_eradicate \
java_hoisting \ java_hoisting \
java_hoistingExpensive \ java_hoistingExpensive \

@ -68,14 +68,6 @@ OPTIONS
Activates: Enable --cost and disable all other checkers Activates: Enable --cost and disable all other checkers
(Conversely: --no-cost-only) (Conversely: --no-cost-only)
--crashcontext
Activates: the crashcontext checker for Java stack trace context
reconstruction (Conversely: --no-crashcontext)
--crashcontext-only
Activates: Enable --crashcontext and disable all other checkers
(Conversely: --no-crashcontext-only)
--debug,-g --debug,-g
Activates: Debug mode (also sets --debug-level 2, Activates: Debug mode (also sets --debug-level 2,
--developer-mode, --no-filtering, --print-buckets, --print-types, --developer-mode, --no-filtering, --print-buckets, --print-types,
@ -346,18 +338,6 @@ CLANG OPTIONS
--unsafe-malloc --unsafe-malloc
Activates: Assume that malloc(3) never returns null. (Conversely: Activates: Assume that malloc(3) never returns null. (Conversely:
--no-unsafe-malloc) --no-unsafe-malloc)
CRASHCONTEXT OPTIONS
--stacktrace file
File path containing a json-encoded Java crash stacktrace. Used to
guide the analysis (only with '-a crashcontext'). See
tests/codetoanalyze/java/crashcontext/*.json for examples of the
expected format.
--stacktraces-dir dir
Directory path containing multiple json-encoded Java crash
stacktraces. Used to guide the analysis (only with '-a
crashcontext'). See tests/codetoanalyze/java/crashcontext/*.json
for examples of the expected format.
JAVA OPTIONS JAVA OPTIONS
--annotation-reachability-custom-pairs json --annotation-reachability-custom-pairs json
Specify custom sources/sink for the annotation reachability Specify custom sources/sink for the annotation reachability

@ -181,14 +181,6 @@ OPTIONS
Costs report of the base revision to use for comparison Costs report of the base revision to use for comparison
See also infer-reportdiff(1). See also infer-reportdiff(1).
--crashcontext
Activates: the crashcontext checker for Java stack trace context
reconstruction (Conversely: --no-crashcontext) See also infer-analyze(1).
--crashcontext-only
Activates: Enable --crashcontext and disable all other checkers
(Conversely: --no-crashcontext-only) See also infer-analyze(1).
--current-to-previous-script shell --current-to-previous-script shell
Specify a script to checkout a previous version of the project to Specify a script to checkout a previous version of the project to
compare against, assuming we are on the current version already. compare against, assuming we are on the current version already.
@ -849,18 +841,6 @@ OPTIONS
Timeout for SQLite results database operations, in milliseconds. Timeout for SQLite results database operations, in milliseconds.
(default: five seconds times number of cores) See also infer-analyze(1), infer-capture(1), and infer-run(1). (default: five seconds times number of cores) See also infer-analyze(1), infer-capture(1), and infer-run(1).
--stacktrace file
File path containing a json-encoded Java crash stacktrace. Used to
guide the analysis (only with '-a crashcontext'). See
tests/codetoanalyze/java/crashcontext/*.json for examples of the
expected format. See also infer-analyze(1).
--stacktraces-dir dir
Directory path containing multiple json-encoded Java crash
stacktraces. Used to guide the analysis (only with '-a
crashcontext'). See tests/codetoanalyze/java/crashcontext/*.json
for examples of the expected format. See also infer-analyze(1).
--starvation --starvation
Activates: starvation analysis (Conversely: --no-starvation) Activates: starvation analysis (Conversely: --no-starvation)
See also infer-analyze(1). See also infer-analyze(1).
@ -1447,12 +1427,6 @@ INTERNAL OPTIONS
--sqlite-vfs-reset --sqlite-vfs-reset
Cancel the effect of --sqlite-vfs. Cancel the effect of --sqlite-vfs.
--stacktrace-reset
Cancel the effect of --stacktrace.
--stacktraces-dir-reset
Cancel the effect of --stacktraces-dir.
--starvation-skip-analysis json --starvation-skip-analysis json
Specify combinations of class/method list that should be skipped Specify combinations of class/method list that should be skipped
during starvation analysis (default: []) during starvation analysis (default: [])

@ -181,14 +181,6 @@ OPTIONS
Costs report of the base revision to use for comparison Costs report of the base revision to use for comparison
See also infer-reportdiff(1). See also infer-reportdiff(1).
--crashcontext
Activates: the crashcontext checker for Java stack trace context
reconstruction (Conversely: --no-crashcontext) See also infer-analyze(1).
--crashcontext-only
Activates: Enable --crashcontext and disable all other checkers
(Conversely: --no-crashcontext-only) See also infer-analyze(1).
--current-to-previous-script shell --current-to-previous-script shell
Specify a script to checkout a previous version of the project to Specify a script to checkout a previous version of the project to
compare against, assuming we are on the current version already. compare against, assuming we are on the current version already.
@ -849,18 +841,6 @@ OPTIONS
Timeout for SQLite results database operations, in milliseconds. Timeout for SQLite results database operations, in milliseconds.
(default: five seconds times number of cores) See also infer-analyze(1), infer-capture(1), and infer-run(1). (default: five seconds times number of cores) See also infer-analyze(1), infer-capture(1), and infer-run(1).
--stacktrace file
File path containing a json-encoded Java crash stacktrace. Used to
guide the analysis (only with '-a crashcontext'). See
tests/codetoanalyze/java/crashcontext/*.json for examples of the
expected format. See also infer-analyze(1).
--stacktraces-dir dir
Directory path containing multiple json-encoded Java crash
stacktraces. Used to guide the analysis (only with '-a
crashcontext'). See tests/codetoanalyze/java/crashcontext/*.json
for examples of the expected format. See also infer-analyze(1).
--starvation --starvation
Activates: starvation analysis (Conversely: --no-starvation) Activates: starvation analysis (Conversely: --no-starvation)
See also infer-analyze(1). See also infer-analyze(1).

@ -19,7 +19,7 @@ INFER_MAIN = infer
#### Checkers declarations #### #### Checkers declarations ####
INFER_ATDGEN_STUB_BASES = atd/jsonbug atd/runstate atd/stacktree atd/java_method_decl atd/perf_profiler atd/java_profiler_samples INFER_ATDGEN_STUB_BASES = atd/jsonbug atd/runstate atd/java_method_decl atd/perf_profiler atd/java_profiler_samples
INFER_ATDGEN_TYPES = j t INFER_ATDGEN_TYPES = j t
INFER_ATDGEN_STUB_ATDS = $(INFER_ATDGEN_STUB_BASES:.atd) INFER_ATDGEN_STUB_ATDS = $(INFER_ATDGEN_STUB_BASES:.atd)
INFER_ATDGEN_SUFFIXES = $(foreach atd_t,$(INFER_ATDGEN_TYPES),_$(atd_t).ml _$(atd_t).mli) INFER_ATDGEN_SUFFIXES = $(foreach atd_t,$(INFER_ATDGEN_TYPES),_$(atd_t).ml _$(atd_t).mli)

@ -1,28 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
type line_range_t = {
start_line : int;
end_line : int;
}
type location_t = {
location_type : string;
file : string;
?line : int option;
blame_range : line_range_t list;
}
type stacktree = {
method_name : string;
?location : location_t option;
callees : stacktree list;
}
type crashcontext_t = {
stack : stacktree list;
}

@ -15,7 +15,6 @@ type t =
; buffer_overrun_checker: BufferOverrunCheckerSummary.t option ; buffer_overrun_checker: BufferOverrunCheckerSummary.t option
; class_loads: ClassLoadsDomain.summary option ; class_loads: ClassLoadsDomain.summary option
; cost: CostDomain.summary option ; cost: CostDomain.summary option
; crashcontext_frame: Stacktree_t.stacktree option
; lab_resource_leaks: ResourceLeakDomain.summary option ; lab_resource_leaks: ResourceLeakDomain.summary option
; litho: LithoDomain.t option ; litho: LithoDomain.t option
; purity: PurityDomain.summary option ; purity: PurityDomain.summary option
@ -33,7 +32,6 @@ let pp pe fmt
; buffer_overrun_checker ; buffer_overrun_checker
; class_loads ; class_loads
; cost ; cost
; crashcontext_frame
; lab_resource_leaks ; lab_resource_leaks
; litho ; litho
; purity ; purity
@ -49,13 +47,11 @@ let pp pe fmt
| None -> | None ->
() ()
in in
F.fprintf fmt "%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a@\n" F.fprintf fmt "%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a@\n"
(pp_opt "Biabduction" (BiabductionSummary.pp pe)) (pp_opt "Biabduction" (BiabductionSummary.pp pe))
biabduction (pp_opt "TypeState" TypeState.pp) typestate biabduction (pp_opt "TypeState" TypeState.pp) typestate
(pp_opt "ClassLoads" ClassLoadsDomain.pp_summary) (pp_opt "ClassLoads" ClassLoadsDomain.pp_summary)
class_loads class_loads
(pp_opt "CrashContext" Crashcontext.pp_stacktree)
crashcontext_frame
(pp_opt "Quandary" QuandarySummary.pp) (pp_opt "Quandary" QuandarySummary.pp)
quandary quandary
(pp_opt "Siof" SiofDomain.Summary.pp) (pp_opt "Siof" SiofDomain.Summary.pp)
@ -83,16 +79,15 @@ let pp pe fmt
let empty = let empty =
{ annot_map= None { annot_map= None
; biabduction= None ; biabduction= None
; class_loads= None
; buffer_overrun_analysis= None ; buffer_overrun_analysis= None
; buffer_overrun_checker= None ; buffer_overrun_checker= None
; crashcontext_frame= None ; class_loads= None
; cost= None ; cost= None
; lab_resource_leaks= None
; litho= None ; litho= None
; purity= None ; purity= None
; quandary= None ; quandary= None
; racerd= None ; racerd= None
; lab_resource_leaks= None
; siof= None ; siof= None
; starvation= None ; starvation= None
; typestate= None ; typestate= None

@ -15,7 +15,6 @@ type t =
; buffer_overrun_checker: BufferOverrunCheckerSummary.t option ; buffer_overrun_checker: BufferOverrunCheckerSummary.t option
; class_loads: ClassLoadsDomain.summary option ; class_loads: ClassLoadsDomain.summary option
; cost: CostDomain.summary option ; cost: CostDomain.summary option
; crashcontext_frame: Stacktree_t.stacktree option
; lab_resource_leaks: ResourceLeakDomain.summary option ; lab_resource_leaks: ResourceLeakDomain.summary option
; litho: LithoDomain.t option ; litho: LithoDomain.t option
; purity: PurityDomain.summary option ; purity: PurityDomain.summary option

@ -1,146 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
module L = Logging
let frame_id_of_stackframe frame =
let loc_str =
match frame.Stacktrace.line_num with
| None ->
frame.Stacktrace.file_str
| Some line ->
F.sprintf "%s:%d" frame.Stacktrace.file_str line
in
F.sprintf "%s.%s(%s)" frame.Stacktrace.class_str frame.Stacktrace.method_str loc_str
let frame_id_of_summary stacktree =
let short_name = List.hd_exn (Str.split (Str.regexp "(") stacktree.Stacktree_j.method_name) in
match stacktree.Stacktree_j.location with
| None ->
L.(die InternalError)
"Attempted to take signature of a frame without location information. This is undefined."
| Some {line= Some line_num; file} ->
F.sprintf "%s(%s:%d)" short_name (Filename.basename file) line_num
| Some {file} ->
F.sprintf "%s(%s)" short_name (Filename.basename file)
let stracktree_of_frame frame =
{ Stacktree_j.method_name=
F.sprintf "%s.%s" frame.Stacktrace.class_str frame.Stacktrace.method_str
; location=
Some
{ Stacktree_j.location_type= "call_site"
; file= frame.Stacktrace.file_str
; line= frame.Stacktrace.line_num
; blame_range= [] }
; callees= [] }
(** k = 1 implementation, where k is the number of levels of calls inlined *)
let stitch_summaries stacktrace_file summary_files out_file =
let stacktrace = Stacktrace.of_json_file stacktrace_file in
let summaries =
List.map ~f:(Atdgen_runtime.Util.Json.from_file Stacktree_j.read_stacktree) summary_files
in
let summary_map =
List.fold
~f:(fun acc stacktree ->
String.Map.set ~key:(frame_id_of_summary stacktree) ~data:stacktree acc )
~init:String.Map.empty summaries
in
let expand_stack_frame frame =
(* TODO: Implement k > 1 case *)
let frame_id = frame_id_of_stackframe frame in
if String.Map.existsi ~f:(fun ~key ~data:_ -> String.equal key frame_id) summary_map then
String.Map.find_exn summary_map frame_id
else stracktree_of_frame frame
in
let expanded_frames = List.map ~f:expand_stack_frame stacktrace.frames in
let crashcontext = {Stacktree_j.stack= expanded_frames} in
Atdgen_runtime.Util.Json.to_file Stacktree_j.write_crashcontext_t out_file crashcontext
let collect_all_summaries root_summaries_dir stacktrace_file stacktraces_dir =
let method_summaries =
Utils.directory_fold
(fun summaries path ->
(* check if the file is a JSON file under the crashcontext dir *)
if
Sys.is_directory path <> `Yes
&& Filename.check_suffix path "json"
&& String.is_suffix ~suffix:"crashcontext" (Filename.dirname path)
then path :: summaries
else summaries )
[] root_summaries_dir
in
let pair_for_stacktrace_file =
match stacktrace_file with
| None ->
None
| Some file ->
let crashcontext_dir = Config.results_dir ^/ "crashcontext" in
Utils.create_dir crashcontext_dir ;
Some (file, crashcontext_dir ^/ "crashcontext.json")
in
let trace_file_regexp = Str.regexp "\\(.*\\)\\.json" in
let pairs_for_stactrace_dir =
match stacktraces_dir with
| None ->
[]
| Some s -> (
let dir = DB.filename_from_string s in
let trace_file_matcher path =
let path_str = DB.filename_to_string path in
Str.string_match trace_file_regexp path_str 0
in
let trace_fold stacktrace_file acc =
let stacktrace_file_str = DB.filename_to_string stacktrace_file in
let out_file = Str.matched_group 1 stacktrace_file_str ^ ".crashcontext.json" in
(stacktrace_file_str, out_file) :: acc
in
try DB.fold_paths_matching ~dir ~p:trace_file_matcher ~init:[] ~f:trace_fold
with
(* trace_fold runs immediately after trace_file_matcher in the
DB.fold_paths_matching statement below, so we don't need to
call Str.string_match again. *)
| Caml.Not_found
-> assert false )
in
let input_output_file_pairs =
match pair_for_stacktrace_file with
| None ->
pairs_for_stactrace_dir
| Some pair ->
pair :: pairs_for_stactrace_dir
in
let process_stacktrace (stacktrace_file, out_file) =
stitch_summaries stacktrace_file method_summaries out_file
in
List.iter ~f:process_stacktrace input_output_file_pairs
let crashcontext_epilogue ~in_buck_mode =
(* if we are the top-level process, then find the output directory and
collect all crashcontext summaries under it in a single
crashcontext.json file.
Important: Note that when running under buck, this is not the final
infer-out/ directory, but instead it is buck-out/, which contains the
infer output directories for every buck target. *)
let root_summaries_dir =
if in_buck_mode then
let buck_out = match Config.buck_out with Some dir -> dir | None -> "buck-out" in
Config.project_root ^/ buck_out
else Config.results_dir
in
collect_all_summaries root_summaries_dir Config.stacktrace Config.stacktraces_dir
let pp_stacktree fmt st = Format.pp_print_string fmt (Stacktree_j.string_of_stacktree st)

@ -1,44 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
(**
CRASHCONTEXT Intro [experimental]:
Crashcontext is an experimental analysis (in the future: a family of
analyses). It takes one or more stacktraces representing crashes
corresponding to the codebase being analyzed and expands them into
crashcontext.json files. These files incorporate further information about
the code that might have executed before the crash.
This analysis is run with '-a crashcontext' and must take either of the
following extra arguments:
--stacktrace stacktrace.json (a single stacktrace, output defaults to
crashcontext/crashcontext.json)
--stacktraces-dir dir (will expand every json stacktace in dir, output
crashcontext files will be saved to dir as well)
For further information, take a look at tests under:
infer/tests/codetoanalyze/java/crashcontext/ and
infer/tests/endtoend/java/crashcontext/
*)
val crashcontext_epilogue : in_buck_mode:bool -> unit
(**
Runs crashcontext epilogue code, which takes the per-method summaries
produced by crashcontext related analysis (future: analyses) and stitches
them together into a final crashcontext.json output file.
This code should run after all checkers when running with '--crashcontext'.
When running with buck, summaries are stitched across multiple buck targets,
so this runs at the end of the parent buck infer process only.
TODO: Similar integration with build systems other than buck.
*)
val pp_stacktree : Format.formatter -> Stacktree_t.stacktree -> unit

@ -16,13 +16,11 @@ module F = Format
module CLOpt = CommandLineOption module CLOpt = CommandLineOption
module L = Die module L = Die
type analyzer = Checkers | Crashcontext | Linters [@@deriving compare] type analyzer = Checkers | Linters [@@deriving compare]
let equal_analyzer = [%compare.equal: analyzer] let equal_analyzer = [%compare.equal: analyzer]
let string_to_analyzer = let string_to_analyzer = [("checkers", Checkers); ("linters", Linters)]
[("checkers", Checkers); ("crashcontext", Crashcontext); ("linters", Linters)]
let clang_frontend_action_symbols = let clang_frontend_action_symbols =
[("lint", `Lint); ("capture", `Capture); ("lint_and_capture", `Lint_and_capture)] [("lint", `Lint); ("capture", `Capture); ("lint_and_capture", `Lint_and_capture)]
@ -203,8 +201,6 @@ let manual_clang = "CLANG OPTIONS"
let manual_clang_linters = "CLANG LINTERS OPTIONS" let manual_clang_linters = "CLANG LINTERS OPTIONS"
let manual_crashcontext = "CRASHCONTEXT OPTIONS"
let manual_generic = Cmdliner.Manpage.s_options let manual_generic = Cmdliner.Manpage.s_options
let manual_hoisting = "HOISTING OPTIONS" let manual_hoisting = "HOISTING OPTIONS"
@ -589,7 +585,6 @@ and ( annotation_reachability
, bufferoverrun , bufferoverrun
, class_loads , class_loads
, cost , cost
, crashcontext
, eradicate , eradicate
, fragment_retains_view , fragment_retains_view
, immutable_cast , immutable_cast
@ -630,9 +625,6 @@ and ( annotation_reachability
and bufferoverrun = mk_checker ~long:"bufferoverrun" "the buffer overrun analysis" and bufferoverrun = mk_checker ~long:"bufferoverrun" "the buffer overrun analysis"
and class_loads = mk_checker ~long:"class-loads" ~default:false "Java class loading analysis" and class_loads = mk_checker ~long:"class-loads" ~default:false "Java class loading analysis"
and cost = mk_checker ~long:"cost" ~default:false "checker for performance cost analysis" and cost = mk_checker ~long:"cost" ~default:false "checker for performance cost analysis"
and crashcontext =
mk_checker ~long:"crashcontext"
"the crashcontext checker for Java stack trace context reconstruction"
and eradicate = and eradicate =
mk_checker ~long:"eradicate" "the eradicate @Nullable checker for Java annotations" mk_checker ~long:"eradicate" "the eradicate @Nullable checker for Java annotations"
and fragment_retains_view = and fragment_retains_view =
@ -714,7 +706,6 @@ and ( annotation_reachability
, bufferoverrun , bufferoverrun
, class_loads , class_loads
, cost , cost
, crashcontext
, eradicate , eradicate
, fragment_retains_view , fragment_retains_view
, immutable_cast , immutable_cast
@ -2078,24 +2069,6 @@ and sqlite_vfs =
CLOpt.mk_string_opt ?default ~long:"sqlite-vfs" "VFS for SQLite" CLOpt.mk_string_opt ?default ~long:"sqlite-vfs" "VFS for SQLite"
and stacktrace =
CLOpt.mk_path_opt ~deprecated:["st"] ~long:"stacktrace"
~in_help:InferCommand.[(Analyze, manual_crashcontext)]
~meta:"file"
"File path containing a json-encoded Java crash stacktrace. Used to guide the analysis (only \
with '-a crashcontext'). See tests/codetoanalyze/java/crashcontext/*.json for examples of \
the expected format."
and stacktraces_dir =
CLOpt.mk_path_opt ~long:"stacktraces-dir"
~in_help:InferCommand.[(Analyze, manual_crashcontext)]
~meta:"dir"
"Directory path containing multiple json-encoded Java crash stacktraces. Used to guide the \
analysis (only with '-a crashcontext'). See tests/codetoanalyze/java/crashcontext/*.json \
for examples of the expected format."
and stats_report = and stats_report =
CLOpt.mk_path_opt ~long:"stats-report" ~meta:"file" CLOpt.mk_path_opt ~long:"stats-report" ~meta:"file"
"Write a report of the analysis results to a file" "Write a report of the analysis results to a file"
@ -2413,9 +2386,6 @@ let post_parsing_initialization command_opt =
if !linters_developer_mode then linters := true ; if !linters_developer_mode then linters := true ;
if !default_linters then linters_def_file := linters_def_default_file :: !linters_def_file ; if !default_linters then linters_def_file := linters_def_default_file :: !linters_def_file ;
( match !analyzer with ( match !analyzer with
| Crashcontext ->
disable_all_checkers () ;
crashcontext := true
| Linters -> | Linters ->
disable_all_checkers () ; disable_all_checkers () ;
capture := false ; capture := false ;
@ -2575,8 +2545,6 @@ and costs_previous = !costs_previous
and current_to_previous_script = !current_to_previous_script and current_to_previous_script = !current_to_previous_script
and crashcontext = !crashcontext
and cxx = !cxx and cxx = !cxx
and cxx_infer_headers = !cxx_infer_headers and cxx_infer_headers = !cxx_infer_headers
@ -2939,10 +2907,6 @@ and sqlite_lock_timeout = !sqlite_lock_timeout
and sqlite_vfs = !sqlite_vfs and sqlite_vfs = !sqlite_vfs
and stacktrace = !stacktrace
and stacktraces_dir = !stacktraces_dir
and starvation = !starvation and starvation = !starvation
and starvation_skip_analysis = !starvation_skip_analysis and starvation_skip_analysis = !starvation_skip_analysis

@ -298,8 +298,6 @@ val costs_current : string option
val costs_previous : string option val costs_previous : string option
val crashcontext : bool
val current_to_previous_script : string option val current_to_previous_script : string option
val cxx : bool val cxx : bool
@ -630,10 +628,6 @@ val sqlite_lock_timeout : int
val sqlite_vfs : string option val sqlite_vfs : string option
val stacktrace : string option
val stacktraces_dir : string option
val starvation : bool val starvation : bool
val starvation_skip_analysis : Yojson.Basic.json val starvation_skip_analysis : Yojson.Basic.json

@ -166,21 +166,3 @@ end
let is_source_file path = let is_source_file path =
List.exists ~f:(fun ext -> Filename.check_suffix path ext) Config.source_file_extentions List.exists ~f:(fun ext -> Filename.check_suffix path ext) Config.source_file_extentions
(** Fold over all file paths recursively under [dir] which match [p]. *)
let fold_paths_matching ~dir ~p ~init ~f =
let rec paths path_list dir =
Array.fold
~f:(fun acc file ->
let path = dir ^/ file in
if Sys.is_directory path = `Yes then paths acc path else if p path then f path acc else acc
)
~init:path_list (Sys.readdir dir)
in
paths init dir
(** Return all absolute paths recursively under root_dir, matching the given
matcher function p *)
let paths_matching dir p = fold_paths_matching ~dir ~p ~init:[] ~f:(fun x xs -> x :: xs)

@ -77,10 +77,3 @@ val source_dir_from_source_file : SourceFile.t -> source_dir
val is_source_file : string -> bool val is_source_file : string -> bool
(** Check if a path is a Java, C, C++ or Objectve C source file according to the file extention *) (** Check if a path is a Java, C, C++ or Objectve C source file according to the file extention *)
val fold_paths_matching :
dir:filename -> p:(filename -> bool) -> init:'a -> f:(filename -> 'a -> 'a) -> 'a
(** Fold over all file paths recursively under [dir] which match [p]. *)
val paths_matching : string -> (string -> bool) -> string list
(** Return all file paths recursively under the given directory which match the given predicate *)

@ -1,181 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
module L = Logging
(** find transitive procedure calls for each procedure *)
module Domain = AbstractDomain.FiniteSet (Typ.Procname)
(* Store a single stacktree frame per method. That is, callees is
always []. Instead, the expanded per-method summaries are directly stored
in the output directory as JSON files and *only* for those methods that
will be part of the final crashcontext.json. *)
module SpecPayload = SummaryPayload.Make (struct
type t = Stacktree_j.stacktree
let update_payloads frame (payloads : Payloads.t) = {payloads with crashcontext_frame= Some frame}
let of_payloads (payloads : Payloads.t) = payloads.crashcontext_frame
end)
type extras_t = {stacktraces: Stacktrace.t list}
let line_range_of_pdesc pdesc =
let ploc = Procdesc.get_loc pdesc in
let start_line = ploc.Location.line in
let end_line =
Procdesc.fold_instrs pdesc ~init:start_line ~f:(fun acc _ instr ->
let new_loc = Sil.instr_get_loc instr in
max acc new_loc.Location.line )
in
{Stacktree_j.start_line; end_line}
let stacktree_of_pdesc pdesc ?(loc = Procdesc.get_loc pdesc) ?(callees = []) location_type =
let procname = Procdesc.get_proc_name pdesc in
let frame_loc =
Some
{ Stacktree_j.location_type
; file= SourceFile.to_string loc.Location.file
; line= Some loc.Location.line
; blame_range= [line_range_of_pdesc pdesc] }
in
{Stacktree_j.method_name= Typ.Procname.to_unique_id procname; location= frame_loc; callees}
let stacktree_stub_of_procname procname =
{Stacktree_j.method_name= Typ.Procname.to_unique_id procname; location= None; callees= []}
module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG
module Domain = Domain
type extras = extras_t
let stacktree_of_astate pdesc astate loc location_type =
let procs = Domain.elements astate in
let callees =
List.map
~f:(fun pn ->
match SpecPayload.read pdesc pn with
| None -> (
match Ondemand.get_proc_desc pn with
| None ->
stacktree_stub_of_procname pn
(* This can happen when the callee is in the same cluster/ buck
target, but it hasn't been checked yet. So we need both the
inter-target lookup (SpecPayload) and the intra-target
lookup (using get_proc_desc). *)
| Some callee_pdesc ->
stacktree_of_pdesc callee_pdesc "proc_start" )
| Some stracktree ->
stracktree )
procs
in
stacktree_of_pdesc pdesc ~loc ~callees location_type
let output_json_summary pdesc astate loc location_type =
let caller = Procdesc.get_proc_name pdesc in
let stacktree = stacktree_of_astate pdesc astate loc location_type in
let dir = Filename.concat Config.results_dir "crashcontext" in
let suffix = F.sprintf "%s_%d" location_type loc.Location.line in
let fname = F.sprintf "%s.%s.json" (Typ.Procname.to_filename caller) suffix in
let fpath = Filename.concat dir fname in
Utils.create_dir dir ;
Atdgen_runtime.Util.Json.to_file Stacktree_j.write_stacktree fpath stacktree
let exec_instr astate proc_data _ = function
| Sil.Call (_, Const (Const.Cfun pn), _, loc, _) -> (
let traces = proc_data.ProcData.extras.stacktraces in
let caller = Procdesc.get_proc_name proc_data.ProcData.pdesc in
let matches_proc frame =
let matches_class pname =
match pname with
| Typ.Procname.Java java_proc ->
String.equal frame.Stacktrace.class_str
(Typ.Procname.Java.get_class_name java_proc)
| Typ.Procname.ObjC_Cpp objc_cpp_prod ->
String.equal frame.Stacktrace.class_str
(Typ.Procname.ObjC_Cpp.get_class_name objc_cpp_prod)
| Typ.Procname.C _ ->
true (* Needed for test code. *)
| Typ.Procname.Block _
| Typ.Procname.Linters_dummy_method
| Typ.Procname.WithBlockParameters _ ->
L.(die InternalError) "Proc type not supported by crashcontext: block"
in
String.equal frame.Stacktrace.method_str (Typ.Procname.get_method caller)
&& matches_class caller
in
let all_frames = List.concat (List.map ~f:(fun trace -> trace.Stacktrace.frames) traces) in
match List.find ~f:matches_proc all_frames with
| Some frame ->
let new_astate = Domain.add pn astate in
( if Stacktrace.frame_matches_location frame loc then
let pdesc = proc_data.ProcData.pdesc in
output_json_summary pdesc new_astate loc "call_site" ) ;
new_astate
| None ->
astate )
| Sil.Call _ ->
(* We currently ignore calls through function pointers in C and
other potential special kinds of procedure calls to be added later,
e.g. Java reflection. *)
astate
| Sil.Load _ | Store _ | Prune _ | ExitScope _ | Abstract _ | Nullify _ ->
astate
let pp_session_name _node fmt = F.pp_print_string fmt "crashcontext"
end
module Analyzer = AbstractInterpreter.MakeRPO (TransferFunctions (ProcCfg.Exceptional))
let loaded_stacktraces =
(* Load all stacktraces defined in either Config.stacktrace or
Config.stacktraces_dir. *)
let json_files_in_dir dir =
let stacktrace_path_regexp = Str.regexp ".*\\.json" in
let path_matcher path = Str.string_match stacktrace_path_regexp path 0 in
DB.paths_matching dir path_matcher
in
let filenames =
match (Config.stacktrace, Config.stacktraces_dir) with
| None, None ->
None
| Some fname, None ->
Some [fname]
| None, Some dir ->
Some (json_files_in_dir dir)
| Some fname, Some dir ->
Some (fname :: json_files_in_dir dir)
in
match filenames with
| None ->
None
| Some files ->
Some (List.map ~f:Stacktrace.of_json_file files)
let checker {Callbacks.proc_desc; tenv; summary} : Summary.t =
( match loaded_stacktraces with
| None ->
L.(die UserError)
"Missing command line option. Either '--stacktrace stack.json' or '--stacktrace-dir \
./dir' must be used when running '-a crashcontext'. This options expects a JSON formated \
stack trace or a directory containing multiple such traces, respectively. See \
tests/codetoanalyze/java/crashcontext/*.json for examples of the expected format."
| Some stacktraces ->
let extras = {stacktraces} in
ignore (Analyzer.exec_pdesc (ProcData.make proc_desc tenv extras) ~initial:Domain.empty) ) ;
summary

@ -1,118 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
(** Module for parsing stack traces and using them to guide Infer analysis *)
open! IStd
module L = Logging
type frame = {class_str: string; method_str: string; file_str: string; line_num: int option}
type t = {exception_name: string; frames: frame list}
let new_line_regexp = Str.regexp "\n"
(* Pre-compute the regular expression matchers used to parse: *)
(* Stack frames into (procedure, location) tuples. *)
let frame_regexp = Str.regexp "\t*at \\(.*\\)(\\(.*\\))"
(* procedures into class and method name. *)
let procname_regexp = Str.regexp "\\(.*\\)\\.\\(.*\\)"
(* locations into file and line number information. *)
let file_and_line_regexp = Str.regexp "\\(.*\\):\\([0-9]+\\)"
(* exception information lines into thread id and exception type *)
let exception_regexp = Str.regexp "Exception in thread \"\\(.*\\)\" \\(.*\\)"
let make exception_name frames = {exception_name; frames}
let make_frame class_str method_str file_str line_num = {class_str; method_str; file_str; line_num}
let frame_matches_location frame_obj loc =
let lfname =
if SourceFile.is_invalid loc.Location.file then None
else Some (SourceFile.to_string loc.Location.file)
in
let matches_file =
Option.value_map lfname ~default:false ~f:(String.is_suffix ~suffix:frame_obj.file_str)
in
let matches_line =
match frame_obj.line_num with None -> false | Some line -> Int.equal line loc.Location.line
in
matches_file && matches_line
let parse_stack_frame frame_str =
(* separate the qualified method name and the parenthesized text/line number*)
ignore (Str.string_match frame_regexp frame_str 0) ;
let qualified_procname = Str.matched_group 1 frame_str in
let file_and_line = Str.matched_group 2 frame_str in
(* separate the class name from the method name *)
ignore (Str.string_match procname_regexp qualified_procname 0) ;
let class_str = Str.matched_group 1 qualified_procname in
let method_str = Str.matched_group 2 qualified_procname in
(* Native methods don't have debugging info *)
if String.equal file_and_line "Native Method" then
make_frame class_str method_str "Native Method" None
else
(* Separate the filename and line number.
note that a few methods might not have line number information,
for those, file_and_line includes only the filename. *)
let is_file_line = Str.string_match file_and_line_regexp file_and_line 0 in
let file_str, line_num =
if is_file_line then
( Str.matched_group 1 file_and_line
, Some (int_of_string (Str.matched_group 2 file_and_line)) )
else (file_and_line, None)
in
make_frame class_str method_str file_str line_num
let parse_exception_line exception_line =
ignore (Str.string_match exception_regexp exception_line 0) ;
let exception_name = Str.matched_group 2 exception_line in
exception_name
let of_string s =
let lines = Str.split new_line_regexp s in
match lines with
| exception_line :: trace ->
let exception_name = parse_exception_line exception_line in
let parsed = List.map ~f:parse_stack_frame trace in
make exception_name parsed
| [] ->
L.(die UserError) "Empty stack trace"
let of_json filename json =
let exception_name_key = "exception_type" in
let frames_key = "stack_trace" in
let extract_json_member key =
match Yojson.Basic.Util.member key json with
| `Null ->
L.(die UserError) "Missing key in supplied JSON data: %s (in file %s)" key filename
| item ->
item
in
let exception_name = Yojson.Basic.Util.to_string (extract_json_member exception_name_key) in
let frames =
Yojson.Basic.Util.to_list (extract_json_member frames_key)
|> List.map ~f:Yojson.Basic.Util.to_string
|> List.map ~f:String.strip
|> List.filter ~f:(fun s -> s <> "")
|> List.map ~f:parse_stack_frame
in
make exception_name frames
let of_json_file filename =
try of_json filename (Yojson.Basic.from_file filename) with
| Sys_error msg | Yojson.Json_error msg ->
L.(die UserError)
"Could not read or parse the supplied JSON stacktrace file %s :@\n %s" filename msg

@ -1,24 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
(** Module for parsing stack traces and using them to guide Infer analysis *)
type frame = {class_str: string; method_str: string; file_str: string; line_num: int option}
type t = {exception_name: string; frames: frame list}
val make : string -> frame list -> t
val make_frame : string -> string -> string -> int option -> frame
val frame_matches_location : frame -> Location.t -> bool
val of_string : string -> t
val of_json_file : string -> t

@ -55,9 +55,6 @@ let all_checkers =
; callbacks= ; callbacks=
[ (Procedure BufferOverrunChecker.checker, Language.Clang) [ (Procedure BufferOverrunChecker.checker, Language.Clang)
; (Procedure BufferOverrunChecker.checker, Language.Java) ] } ; (Procedure BufferOverrunChecker.checker, Language.Java) ] }
; { name= "crashcontext"
; active= Config.crashcontext
; callbacks= [(Procedure BoundedCallTree.checker, Language.Java)] }
; { name= "eradicate" ; { name= "eradicate"
; active= Config.eradicate ; active= Config.eradicate
; callbacks= [(Procedure Eradicate.callback_eradicate, Language.Java)] } ; callbacks= [(Procedure Eradicate.callback_eradicate, Language.Java)] }

@ -19,7 +19,7 @@ let run driver_mode =
let changed_files = read_config_changed_files () in let changed_files = read_config_changed_files () in
capture driver_mode ~changed_files ; capture driver_mode ~changed_files ;
analyze_and_report driver_mode ~changed_files ; analyze_and_report driver_mode ~changed_files ;
run_epilogue driver_mode run_epilogue ()
let setup () = let setup () =

@ -89,5 +89,5 @@ let diff driver_mode =
ReportDiff.reportdiff ~current_report:(Some current_report) ReportDiff.reportdiff ~current_report:(Some current_report)
~previous_report:(Some previous_report) ~current_costs:(Some current_costs) ~previous_report:(Some previous_report) ~current_costs:(Some current_costs)
~previous_costs:(Some previous_costs) ; ~previous_costs:(Some previous_costs) ;
Driver.run_epilogue driver_mode ; Driver.run_epilogue () ;
() ()

@ -550,11 +550,9 @@ let run_prologue mode =
() ()
let run_epilogue mode = let run_epilogue () =
if CLOpt.is_originator then ( if CLOpt.is_originator then (
let in_buck_mode = match mode with PythonCapture (BBuck, _) -> true | _ -> false in
if Config.developer_mode then StatsAggregator.generate_files () ; if Config.developer_mode then StatsAggregator.generate_files () ;
if Config.crashcontext then Crashcontext.crashcontext_epilogue ~in_buck_mode ;
if Config.fail_on_bug then fail_on_issue_epilogue () ; if Config.fail_on_bug then fail_on_issue_epilogue () ;
() ) ; () ) ;
if Config.buck_cache_mode then clean_results_dir () ; if Config.buck_cache_mode then clean_results_dir () ;

@ -41,7 +41,7 @@ val analyze_and_report :
?suppress_console_report:bool -> changed_files:SourceFile.Set.t option -> mode -> unit ?suppress_console_report:bool -> changed_files:SourceFile.Set.t option -> mode -> unit
(** run the analysis for the given mode *) (** run the analysis for the given mode *)
val run_epilogue : mode -> unit val run_epilogue : unit -> unit
(** cleanup infer-out/ for Buck, generate stats, and generally post-process the results of a run *) (** cleanup infer-out/ for Buck, generate stats, and generally post-process the results of a run *)
val read_config_changed_files : unit -> SourceFile.Set.t option val read_config_changed_files : unit -> SourceFile.Set.t option

@ -1,84 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module TestInterpreter =
AnalyzerTester.Make (BoundedCallTree.TransferFunctions (ProcCfg.Exceptional))
let tests =
let open OUnit2 in
let open AnalyzerTester.StructuredSil in
let initial = BoundedCallTree.Domain.empty in
let f_proc_name = Typ.Procname.from_string_c_fun "f" in
let g_proc_name = Typ.Procname.from_string_c_fun "g" in
let g_args = [(Exp.Const (Const.Cint IntLit.one), Typ.mk (Tint IInt))] in
let g_return = (ident_of_str "r", Typ.mk (Tint IInt)) in
let class_name = "com.example.SomeClass" in
let file_name = "SomeClass.java" in
let trace =
Stacktrace.make "java.lang.NullPointerException"
[ Stacktrace.make_frame class_name "foo" file_name (Some 16)
; Stacktrace.make_frame class_name "bar" file_name (Some 20) ]
in
let extras = {BoundedCallTree.stacktraces= [trace]} in
let multi_trace_1 =
Stacktrace.make "java.lang.NullPointerException"
[Stacktrace.make_frame class_name "foo" file_name (Some 16)]
in
let multi_trace_2 =
Stacktrace.make "java.lang.NullPointerException"
[Stacktrace.make_frame class_name "bar" file_name (Some 20)]
in
let multi_trace_extras = {BoundedCallTree.stacktraces= [multi_trace_1; multi_trace_2]} in
let caller_foo_name = Typ.Procname.from_string_c_fun "foo" in
let caller_bar_name = Typ.Procname.from_string_c_fun "bar" in
let caller_baz_name = Typ.Procname.from_string_c_fun "baz" in
let test_list_from_foo =
[ ( "on_call_add_proc_name"
, [make_call ~procname:f_proc_name []; (* means f() *) invariant "{ f }"] )
; ( "on_call_add_proc_name_w_args"
, [ make_call ~procname:g_proc_name ~return:g_return g_args
; (* means r = a.g(1) *)
invariant "{ g }" ] )
; ( "handle_two_proc_calls"
, [ make_call ~procname:f_proc_name []
; invariant "{ f }"
; make_call ~procname:g_proc_name ~return:g_return g_args
; invariant "{ f, g }" ] )
; ( "dont_record_procs_twice"
, [ make_call ~procname:f_proc_name []
; invariant "{ f }"
; make_call ~procname:f_proc_name []
; invariant "{ f }" ] ) ]
|> TestInterpreter.create_tests ~test_pname:caller_foo_name
~initial:BoundedCallTree.Domain.empty extras
in
let test_list_from_bar =
[ ( "on_call_anywhere_on_stack_add_proc_name"
, [make_call ~procname:f_proc_name []; (* means f() *) invariant "{ f }"] ) ]
|> TestInterpreter.create_tests ~test_pname:caller_bar_name extras ~initial
in
let test_list_from_baz =
[ ( "ignore_procs_unrelated_to_trace"
, [make_call ~procname:f_proc_name []; (* means f() *) invariant "{ }"] ) ]
|> TestInterpreter.create_tests ~test_pname:caller_baz_name extras ~initial
in
let test_list_multiple_traces_from_foo =
[ ( "on_call_add_proc_name_in_any_stack_1"
, [make_call ~procname:f_proc_name []; (* means f() *) invariant "{ f }"] ) ]
|> TestInterpreter.create_tests ~test_pname:caller_foo_name multi_trace_extras ~initial
in
let test_list_multiple_traces_from_bar =
[ ( "on_call_add_proc_name_in_any_stack_2"
, [make_call ~procname:f_proc_name []; (* means f() *) invariant "{ f }"] ) ]
|> TestInterpreter.create_tests ~test_pname:caller_bar_name multi_trace_extras ~initial
in
let test_list =
test_list_from_foo @ test_list_from_bar @ test_list_from_baz
@ test_list_multiple_traces_from_foo @ test_list_multiple_traces_from_bar
in
"bounded_calltree_test_suite" >::: test_list

@ -30,7 +30,6 @@ let () =
; AccessPathTests.tests ; AccessPathTests.tests
; AccessTreeTests.tests ; AccessTreeTests.tests
; AddressTakenTests.tests ; AddressTakenTests.tests
; BoundedCallTreeTests.tests
; DifferentialFiltersTests.tests ; DifferentialFiltersTests.tests
; DifferentialTests.tests ; DifferentialTests.tests
; FileDiffTests.tests ; FileDiffTests.tests
@ -41,7 +40,6 @@ let () =
; ProcCfgTests.tests ; ProcCfgTests.tests
; SchedulerTests.tests ; SchedulerTests.tests
; SeverityTests.tests ; SeverityTests.tests
; StacktraceTests.tests
; TaintTests.tests ; TaintTests.tests
; TraceTests.tests ; TraceTests.tests
; WeakTopologicalOrderTests.tests ] ; WeakTopologicalOrderTests.tests ]

@ -1,81 +0,0 @@
(*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
let tests =
let open OUnit2 in
let empty_string_test =
let empty_string_test_ _ =
assert_raises (Logging.InferUserError "Empty stack trace") (fun () -> Stacktrace.of_string "")
in
"empty_string" >:: empty_string_test_
in
let empty_trace_test =
let empty_stack_trace_s = "Exception in thread \"main\" java.lang.NullPointerException" in
let trace = Stacktrace.of_string empty_stack_trace_s in
let empty_trace_test_ _ = assert_equal trace.frames [] in
"empty_trace" >:: empty_trace_test_
in
let one_frame_trace_test =
let one_frame_trace_test_s =
"Exception in thread \"main\" java.lang.NullPointerException\n"
^ "\tat endtoend.java.checkers.crashcontext.MinimalCrashTest.main"
^ "(MinimalCrashTest.java:16)"
in
let trace = Stacktrace.of_string one_frame_trace_test_s in
let expected =
Stacktrace.make "java.lang.NullPointerException"
[ Stacktrace.make_frame "endtoend.java.checkers.crashcontext.MinimalCrashTest" "main"
"MinimalCrashTest.java" (Some 16) ]
in
let one_frame_trace_test_ _ = assert_equal trace expected in
"one_frame_trace" >:: one_frame_trace_test_
in
let multi_frame_trace_test =
let multi_frame_trace_test_s =
"Exception in thread \"main\" java.lang.NullPointerException\n\t"
^ "at endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest.bar"
^ "(MultiStackFrameCrashTest.java:16)\n"
^ "\tat endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest.foo"
^ "(MultiStackFrameCrashTest.java:20)\n"
^ "\tat endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest.main"
^ "(MultiStackFrameCrashTest.java:24)"
in
let trace = Stacktrace.of_string multi_frame_trace_test_s in
let class_name = "endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest" in
let file_name = "MultiStackFrameCrashTest.java" in
let expected =
Stacktrace.make "java.lang.NullPointerException"
[ Stacktrace.make_frame class_name "bar" file_name (Some 16)
; Stacktrace.make_frame class_name "foo" file_name (Some 20)
; Stacktrace.make_frame class_name "main" file_name (Some 24) ]
in
let multi_frame_trace_test_ _ = assert_equal trace expected in
"multi_frame_trace_test" >:: multi_frame_trace_test_
in
let missing_line_info_test =
let missing_line_info_test_s =
"Exception in thread \"main\" java.lang.NullPointerException\n"
^ "\tat endtoend.java.checkers.crashcontext.MinimalCrashTest.main"
^ "(MinimalCrashTest.java)"
in
let trace = Stacktrace.of_string missing_line_info_test_s in
let expected =
Stacktrace.make "java.lang.NullPointerException"
[ Stacktrace.make_frame "endtoend.java.checkers.crashcontext.MinimalCrashTest" "main"
"MinimalCrashTest.java" None ]
in
let missing_line_info_test_ _ = assert_equal trace expected in
"missing_line_info_test" >:: missing_line_info_test_
in
"all_tests_suite"
>::: [ empty_string_test
; empty_trace_test
; one_frame_trace_test
; multi_frame_trace_test
; missing_line_info_test ]

@ -7,5 +7,5 @@ Findlib has been successfully loaded. Additional directives:
Topfind.reset();; to force that packages will be reloaded Topfind.reset();; to force that packages will be reloaded
#thread;; to enable threads #thread;; to enable threads
n$13 n$5
false false

@ -1,34 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.crashcontext;
public class BranchingCallsExample {
public static void pre_bar() {
System.out.println("This runs before the crash.");
}
public static void post_bar() {
System.out.println("This doesn't.");
}
public static void bar() {
String s = null;
s.toString();
}
public static void foo() {
pre_bar();
bar();
post_bar();
}
public static void main(String[] args) {
foo();
}
}

@ -1 +0,0 @@
{"exception_type": "java.lang.NullPointerException", "stack_trace": ["at codetoanalyze.java.crashcontext.BranchingCallsExample.bar(BranchingCallsExample.java:24)","at codetoanalyze.java.crashcontext.BranchingCallsExample.foo(BranchingCallsExample.java:29)","at codetoanalyze.java.crashcontext.BranchingCallsExample.main(BranchingCallsExample.java:34)",""], "exception_message": "", "normvector_stack": ["codetoanalyze.java.crashcontext.BranchingCallsExample.bar","codetoanalyze.java.crashcontext.BranchingCallsExample.foo","endtoend.java.crashcontext.BranchingCallsExample.main"]}

@ -1,52 +0,0 @@
# Copyright (c) 2016-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
include $(TESTS_DIR)/java.make
include $(TESTS_DIR)/base.make
SOURCES = $(wildcard *.java)
OBJECTS = $(patsubst %.java,%.class,$(SOURCES))
EXP_TESTs = $(sort $(patsubst %.java,issues-%.exp.test,$(SOURCES)))
INFER_OUTs = $(patsubst %.java,infer-out-%,$(SOURCES))
$(OBJECTS): $(SOURCES)
$(JAVAC) -cp $(CLASSPATH) $(SOURCES)
# analyze a single source file and generate the test results for it
issues-%.exp.test: $(JAVA_DEPS) %.stacktrace.json %.java
$(QUIET)$(call silent_on_success,Testing crashcontext: $*,\
$(INFER_BIN) --crashcontext-only -o infer-out-$* --stacktrace $*.stacktrace.json \
-- $(JAVAC) -cp $(CLASSPATH) $*.java)
# add a newline at the end of the json when creating the exp.test
$(QUIET)$(COPY) infer-out-$*/crashcontext/crashcontext.json $@ && echo >> $@
# combine the test results for all the source files
issues.exp.test: $(EXP_TESTs)
$(QUIET)cat $^ > $@
default: compile
.PHONY: compile
compile: $(OBJECTS)
.PHONY: analyze
analyze: $(EXP_TESTs)
.PHONY: print
print: issues.exp.test
.PHONY: test
test: issues.exp.test
$(QUIET)cd $(TESTS_DIR) && \
$(call check_no_diff,$(TEST_REL_DIR)/issues.exp,$(TEST_REL_DIR)/issues.exp.test)
.PHONY: replace
replace: issues.exp.test
cp $< issues.exp
.PHONY: clean
clean:
$(REMOVE_DIR) $(INFER_OUTs) $(OBJECTS) $(EXP_TESTs)

@ -1,30 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.crashcontext;
public class MethodNameClashExample {
public static class A {
public static void foo() {
String s = null;
s.toString();
}
}
public static class B {
public static void foo() {
A.foo();
}
}
public static void main(String[] args) {
B.foo();
}
}

@ -1 +0,0 @@
{"exception_type": "java.lang.NullPointerException", "stack_trace": ["at codetoanalyze.java.crashcontext.MethodNameClashExample$A.foo(MethodNameClashExample.java:18)","at codetoanalyze.java.crashcontext.MethodNameClashExample$B.foo(MethodNameClashExample.java:26)","at codetoanalyze.java.crashcontext.MethodNameClashExample.main(MethodNameClashExample.java:32)",""], "exception_message": "", "normvector_stack": ["codetoanalyze.java.crashcontext.MethodNameClashExample$A.foo","codetoanalyze.java.crashcontext.MethodNameClashExample$B.foo","codetoanalyze.java.crashcontext.MethodNameClashExample.main"]}

@ -1,16 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.crashcontext;
public class MinimalCrashExample {
public static void main(String[] args) {
String s = null;
s.toString();
}
}

@ -1 +0,0 @@
{"exception_type": "java.lang.NullPointerException", "stack_trace": ["at codetoanalyze.java.crashcontext.MinimalCrashExample.main(MinimalCrashExample.java:16)",""], "exception_message": "", "normvector_stack": ["codetoanalyze.java.crashcontext.MinimalCrashExample.main"]}

@ -1,24 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.crashcontext;
public class MultiStackFrameCrashExample {
public static void bar() {
String s = null;
s.toString();
}
public static void foo() {
bar();
}
public static void main(String[] args) {
foo();
}
}

@ -1 +0,0 @@
{"exception_type": "java.lang.NullPointerException", "stack_trace": ["at codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.bar(MultiStackFrameCrashExample.java:16)","at codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.foo(MultiStackFrameCrashExample.java:20)","at codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.main(MultiStackFrameCrashExample.java:24)",""], "exception_message": "", "normvector_stack": ["codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.bar","codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.foo","endtoend.java.crashcontext.MultiStackFrameCrashExample.main"]}

@ -1,30 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.crashcontext;
import java.lang.reflect.Method;
public class NativeMethodExample {
public static void foo() {
String s = null;
s.toString();
}
public static void main(String[] args) {
try {
// Calling method.invoke is a reliable way of getting a native method
// in the stack (from the implementation of reflection) between this
// method and the target of the reflective invocation.
Method method = NativeMethodExample.class.getDeclaredMethod("foo");
Object o = method.invoke(new Object[] {});
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
}

@ -1 +0,0 @@
{"exception_type": "java.lang.NullPointerException", "stack_trace": ["at codetoanalyze.java.crashcontext.NativeMethodExample.foo(NativeMethodExample.java:18)","at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)","at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)","at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)","at java.lang.reflect.Method.invoke(Method.java:497)","at codetoanalyze.java.crashcontext.NativeMethodExample.main(NativeMethodExample.java:27)",""], "exception_message": "", "normvector_stack": ["codetoanalyze.java.crashcontext.NativeMethodExample.foo","sun.reflect.NativeMethodAccessorImpl.invoke0","sun.reflect.NativeMethodAccessorImpl.invoke","at sun.reflect.DelegatingMethodAccessorImpl.invoke","java.lang.reflect.Method.invoke","codetoanalyze.java.crashcontext.NativeMethodExample.main",""]}

@ -1,5 +0,0 @@
{"stack":[{"method_name":"codetoanalyze.java.crashcontext.BranchingCallsExample.bar","location":{"location_type":"call_site","file":"BranchingCallsExample.java","line":24,"blame_range":[]},"callees":[]},{"method_name":"codetoanalyze.java.crashcontext.BranchingCallsExample.foo","location":{"location_type":"call_site","file":"BranchingCallsExample.java","line":29,"blame_range":[]},"callees":[]},{"method_name":"codetoanalyze.java.crashcontext.BranchingCallsExample.main","location":{"location_type":"call_site","file":"BranchingCallsExample.java","line":34,"blame_range":[]},"callees":[]}]}
{"stack":[{"method_name":"codetoanalyze.java.crashcontext.MethodNameClashExample$A.foo","location":{"location_type":"call_site","file":"MethodNameClashExample.java","line":18,"blame_range":[]},"callees":[]},{"method_name":"codetoanalyze.java.crashcontext.MethodNameClashExample$B.foo","location":{"location_type":"call_site","file":"MethodNameClashExample.java","line":26,"blame_range":[]},"callees":[]},{"method_name":"codetoanalyze.java.crashcontext.MethodNameClashExample.main","location":{"location_type":"call_site","file":"MethodNameClashExample.java","line":32,"blame_range":[]},"callees":[]}]}
{"stack":[{"method_name":"codetoanalyze.java.crashcontext.MinimalCrashExample.main","location":{"location_type":"call_site","file":"MinimalCrashExample.java","line":16,"blame_range":[]},"callees":[]}]}
{"stack":[{"method_name":"codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.bar","location":{"location_type":"call_site","file":"MultiStackFrameCrashExample.java","line":16,"blame_range":[]},"callees":[]},{"method_name":"codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.foo","location":{"location_type":"call_site","file":"MultiStackFrameCrashExample.java","line":20,"blame_range":[]},"callees":[]},{"method_name":"codetoanalyze.java.crashcontext.MultiStackFrameCrashExample.main","location":{"location_type":"call_site","file":"MultiStackFrameCrashExample.java","line":24,"blame_range":[]},"callees":[]}]}
{"stack":[{"method_name":"codetoanalyze.java.crashcontext.NativeMethodExample.foo","location":{"location_type":"call_site","file":"NativeMethodExample.java","line":18,"blame_range":[]},"callees":[]},{"method_name":"sun.reflect.NativeMethodAccessorImpl.invoke0","location":{"location_type":"call_site","file":"Native Method","blame_range":[]},"callees":[]},{"method_name":"sun.reflect.NativeMethodAccessorImpl.invoke","location":{"location_type":"call_site","file":"NativeMethodAccessorImpl.java","line":62,"blame_range":[]},"callees":[]},{"method_name":"sun.reflect.DelegatingMethodAccessorImpl.invoke","location":{"location_type":"call_site","file":"DelegatingMethodAccessorImpl.java","line":43,"blame_range":[]},"callees":[]},{"method_name":"java.lang.reflect.Method.invoke","location":{"location_type":"call_site","file":"Method.java","line":497,"blame_range":[]},"callees":[]},{"method_name":"codetoanalyze.java.crashcontext.NativeMethodExample.main(java.lang.String[]):void","location":{"location_type":"call_site","file":"NativeMethodExample.java","line":27,"blame_range":[{"start_line":19,"end_line":27}]},"callees":[{"method_name":"java.lang.Error.<init>(java.lang.Throwable)","callees":[]},{"method_name":"java.lang.Class.getDeclaredMethod(java.lang.String,java.lang.Class[]):java.lang.reflect.Method","callees":[]},{"method_name":"java.lang.reflect.Method.invoke(java.lang.Object,java.lang.Object[]):java.lang.Object","callees":[]},{"method_name":"__instanceof","callees":[]},{"method_name":"__new","callees":[]},{"method_name":"__new_array","callees":[]},{"method_name":"__unwrap_exception","callees":[]}]}]}
Loading…
Cancel
Save