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.

101 lines
3.7 KiB

(*
* Copyright (c) 2009-2013, Monoidics ltd.
* Copyright (c) 2013-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.
*)
(** Main module for the analysis after the capture phase *)
open! IStd
module L = Logging
let clear_caches () =
Ondemand.clear_cache () ;
Summary.clear_cache () ;
Typ.Procname.SQLite.clear_cache ()
(** Create tasks to analyze an execution environment *)
let analyze_source_file : SourceFile.t Tasks.doer =
fun source_file ->
DB.Results_dir.init source_file ;
let exe_env = Exe_env.mk () in
L.(debug Analysis Medium) "@\nProcessing '%a'@." SourceFile.pp source_file ;
(* clear cache for each source file to avoid it growing unboundedly *)
clear_caches () ;
Callbacks.analyze_file exe_env source_file ;
if Config.write_html then Printer.write_all_html_files source_file
let output_json_makefile_stats clusters =
let num_files = List.length clusters in
let num_procs = 0 in
(* can't compute it at this stage *)
let num_lines = 0 in
let file_stats =
`Assoc [("files", `Int num_files); ("procedures", `Int num_procs); ("lines", `Int num_lines)]
in
(* write stats file to disk, intentionally overwriting old file if it already exists *)
let f = Out_channel.create (Filename.concat Config.results_dir Config.proc_stats_filename) in
Yojson.Basic.pretty_to_channel f file_stats
let source_file_should_be_analyzed ~changed_files source_file =
(* whether [fname] is one of the [changed_files] *)
let is_changed_file = Option.map changed_files ~f:(SourceFile.Set.mem source_file) in
let check_modified () =
let modified = SourceFiles.is_freshly_captured source_file in
if modified then L.debug Analysis Medium "Modified: %a@\n" SourceFile.pp source_file ;
modified
in
match is_changed_file with
| Some b ->
b
| None when Config.reactive_mode ->
check_modified ()
| None ->
true
let register_active_checkers () =
match Config.analyzer with
| Checkers | Crashcontext ->
RegisterCheckers.get_active_checkers () |> RegisterCheckers.register
| CaptureOnly | CompileOnly | Linters ->
()
let main ~changed_files =
( match Config.modified_targets with
| Some file ->
MergeCapture.record_modified_targets_from_file file
| None ->
() ) ;
register_active_checkers () ;
(* delete all specs when doing a full analysis so that we do not report on procedures that do
not exist anymore *)
if not Config.reactive_mode then DB.Results_dir.clean_specs_dir () ;
let all_source_files = SourceFiles.get_all () in
let source_files_to_analyze =
List.filter ~f:(source_file_should_be_analyzed ~changed_files) all_source_files
in
let n_source_files = List.length source_files_to_analyze in
L.progress "Found %d%s source file%s to analyze in %s@." n_source_files
( if Config.reactive_mode || Option.is_some changed_files then
" (out of " ^ string_of_int (List.length all_source_files) ^ ")"
else "" )
(if Int.equal n_source_files 1 then "" else "s")
Config.results_dir ;
(* empty all caches to minimize the process heap to have less work to do when forking *)
clear_caches () ;
if Int.equal Config.jobs 1 then (
Tasks.run_sequentially ~f:analyze_source_file source_files_to_analyze ;
L.progress "@\nAnalysis finished in %as@." Pp.elapsed_time () )
else (
L.environment_info "Parallel jobs: %d@." Config.jobs ;
(* Prepare tasks one cluster at a time while executing in parallel *)
let runner = Tasks.Runner.create ~jobs:Config.jobs ~f:analyze_source_file in
Tasks.Runner.run runner ~tasks:source_files_to_analyze ) ;
output_json_makefile_stats source_files_to_analyze