Reviewed By: jeremydubreil, mbouaziz, jvillard Differential Revision: D13861427 fbshipit-source-id: 85e340bb5master
parent
e6d2872a4e
commit
374538a02f
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 ]
|
|
@ -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…
Reference in new issue