You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.8 KiB
182 lines
6.8 KiB
(*
|
|
* 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
|