(* * 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 (** find transitive procedure calls for each procedure *) module ProcnameSet = PrettyPrintable.MakePPSet(struct type t = Procname.t let compare = Procname.compare let pp_element = Procname.pp end) module Domain = AbstractDomain.FiniteSet(ProcnameSet) module TransferFunctions (CFG : ProcCfg.S) = struct module CFG = CFG module Domain = Domain type extras = Stacktrace.t let json_of_summary caller astate loc loc_type = let procs = Domain.elements astate in let json = `Assoc [ ("caller", `String (Procname.to_string caller)); ("location", `Assoc [ ("type", `String loc_type); ("file", `String (DB.source_file_to_string loc.Location.file)); ("line", `Int loc.Location.line); ]); ("callees", `List (IList.map (fun pn -> `String (Procname.to_string pn)) procs)) ] in json let output_summary caller astate loc loc_type = let json = json_of_summary caller astate loc loc_type in let dir = Filename.concat Config.results_dir "crashcontext" in let suffix = F.sprintf "%s_%d" loc_type loc.Location.line in let fname = F.sprintf "%s.%s.json" (Procname.to_filename caller) suffix in let fpath = Filename.concat dir fname in DB.create_dir dir; Utils.write_json_to_file fpath json let exec_instr astate proc_data _ = function | Sil.Call (_, Const (Const.Cfun pn), _, loc, _) -> (** TODO: Match class. *) let caller = Cfg.Procdesc.get_proc_name proc_data.ProcData.pdesc in let matches_proc frame = frame.Stacktrace.method_str = (Procname.get_method caller) in let proc_in_trace = IList.exists matches_proc proc_data.ProcData.extras.Stacktrace.frames in if proc_in_trace then begin let frame = IList.find matches_proc proc_data.ProcData.extras.Stacktrace.frames in let new_astate = Domain.add pn astate in if Stacktrace.frame_matches_location frame loc then begin output_summary caller new_astate loc "call_site" end; new_astate end else 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.Letderef _ | Set _ | Prune _ | Declare_locals _ | Stackop _ | Remove_temps _ | Abstract _ | Nullify _ -> astate end module Analyzer = AbstractInterpreter.Make (ProcCfg.Exceptional) (Scheduler.ReversePostorder) (TransferFunctions) (** Stacktrace lookup: * 1) Check if trace_ref is already set and use that. * 2) If not, load trace from the file specified in Config.stacktrace. *) let trace_ref = ref None let load_trace () = (** Check Config.stacktrace is set and points to a file, * call Stacktrace.of_json_file *) let filename = match Config.stacktrace with | None -> failwith "Missing command line option: '--stacktrace stack.json' \ must be used when running '-a crashcontext'. This \ option expects a JSON formated stack trace. See \ tests/codetoanalyze/java/crashcontext/*.json for \ examples of the expected format." | Some fname -> fname in let new_trace = Stacktrace.of_json_file filename in trace_ref := Some new_trace; new_trace let checker { Callbacks.proc_desc; tenv; } = let trace = match !trace_ref with | None -> load_trace () | Some t -> t in ignore(Analyzer.exec_pdesc (ProcData.make proc_desc tenv trace))