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.

135 lines
5.3 KiB

(*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! Utils
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 = IList.hd
(Str.split (Str.regexp "(") stacktree.Stacktree_j.method_name) in
match stacktree.Stacktree_j.location with
| None ->
failwith "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 = IList.map
(Ag_util.Json.from_file Stacktree_j.read_stacktree)
summary_files in
let summary_map = IList.fold_left
(fun acc stacktree ->
StringMap.add (frame_id_of_summary stacktree) stacktree acc)
StringMap.empty
summaries in
let expand_stack_frame frame =
(* TODO: Implement k > 1 case *)
let frame_id = frame_id_of_stackframe frame in
if StringMap.exists (fun key _ -> key = frame_id) summary_map then
StringMap.find frame_id summary_map
else
stracktree_of_frame frame in
let expanded_frames = IList.map expand_stack_frame stacktrace.frames in
let crashcontext = { Stacktree_j.stack = expanded_frames} in
Ag_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 not (Sys.is_directory path) && Filename.check_suffix path "json" &&
string_is_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
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 -> begin
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. *)
| Not_found -> assert false
end 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
IList.iter 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 begin
let buck_out = match Config.buck_out with
| Some dir -> dir
| None -> "buck-out" in
Config.project_root // buck_out
end
else Config.results_dir in
collect_all_summaries
root_summaries_dir Config.stacktrace Config.stacktraces_dir
let pp_stacktree fmt st =
Format.fprintf fmt "%s" (Stacktree_j.string_of_stacktree st)