Add documentation and better naming around checker callbacks

Summary:
1. Some invariants are tricky enough to be documented. This is especially
important for cases related with error reporting. Lets document it.
2. Cluster callback -> File callback rename.

Reviewed By: ngorogiannis

Differential Revision: D20093932

fbshipit-source-id: e716f1f5b
master
Mitya Lyubarskiy 5 years ago committed by Facebook Github Bot
parent d50039b495
commit d94b365b65

@ -7,7 +7,9 @@
open! IStd open! IStd
(** Module for maps from procnames to error logs. *) (** Module for storing issues detected outside of per-procedure analysis (and hence not serialized
as a part of procedure summary). Normally those are issues detected at the file-level analysis
step. Individual checkers are responsible for storing their own list of such issues. *)
type t type t
val empty : t val empty : t

@ -60,8 +60,8 @@ let analyze_target : (SchedulerTypes.target, Procname.t) Tasks.doer =
analyze_source_file exe_env source_file analyze_source_file exe_env source_file
let output_json_makefile_stats clusters = let output_json_makefile_stats files =
let num_files = List.length clusters in let num_files = List.length files in
let num_procs = 0 in let num_procs = 0 in
(* can't compute it at this stage *) (* can't compute it at this stage *)
let num_lines = 0 in let num_lines = 0 in
@ -144,7 +144,7 @@ let analyze source_files_to_analyze =
else ( else (
L.environment_info "Parallel jobs: %d@." Config.jobs ; L.environment_info "Parallel jobs: %d@." Config.jobs ;
let build_tasks_generator () = tasks_generator_builder_for source_files_to_analyze in let build_tasks_generator () = tasks_generator_builder_for source_files_to_analyze in
(* Prepare tasks one cluster at a time while executing in parallel *) (* Prepare tasks one file at a time while executing in parallel *)
RestartScheduler.setup () ; RestartScheduler.setup () ;
let runner = let runner =
Tasks.Runner.create ~jobs:Config.jobs ~f:analyze_target ~child_epilogue:BackendStats.get Tasks.Runner.create ~jobs:Config.jobs ~f:analyze_target ~child_epilogue:BackendStats.get

@ -47,6 +47,10 @@ type t =
; status: Status.t ; status: Status.t
; proc_desc: Procdesc.t ; proc_desc: Procdesc.t
; err_log: Errlog.t ; err_log: Errlog.t
(** Those are issues that are detected for this procedure after per-procedure analysis. In
addition to that there can be errors detected after file-level analysis (next stage
after per-procedure analysis). This latter category of errors should NOT be written
here, use [IssueLog] and its serialization capabilities instead. *)
; mutable callee_pnames: Procname.Set.t } ; mutable callee_pnames: Procname.Set.t }
val poly_fields : t PolyFields.t val poly_fields : t PolyFields.t

@ -7,37 +7,35 @@
open! IStd open! IStd
(** Module to register and invoke callbacks *)
type proc_callback_args = type proc_callback_args =
{get_procs_in_file: Procname.t -> Procname.t list; summary: Summary.t; exe_env: Exe_env.t} {get_procs_in_file: Procname.t -> Procname.t list; summary: Summary.t; exe_env: Exe_env.t}
type proc_callback_t = proc_callback_args -> Summary.t type proc_callback_t = proc_callback_args -> Summary.t
type cluster_callback_args = type file_callback_args =
{procedures: Procname.t list; source_file: SourceFile.t; exe_env: Exe_env.t} {procedures: Procname.t list; source_file: SourceFile.t; exe_env: Exe_env.t}
type cluster_callback_t = cluster_callback_args -> unit type file_callback_t = file_callback_args -> unit
type procedure_callback = type procedure_callback =
{name: string; dynamic_dispatch: bool; language: Language.t; callback: proc_callback_t} {checker_name: string; dynamic_dispatch: bool; language: Language.t; callback: proc_callback_t}
type cluster_callback = {name: string; language: Language.t; callback: cluster_callback_t} type file_callback = {checker_name: string; language: Language.t; callback: file_callback_t}
let procedure_callbacks = ref [] let procedure_callbacks = ref []
let cluster_callbacks = ref [] let file_callbacks = ref []
let register_procedure_callback ~name ?(dynamic_dispatch = false) language let register_procedure_callback ~checker_name ?(dynamic_dispatch = false) language
(callback : proc_callback_t) = (callback : proc_callback_t) =
procedure_callbacks := {name; dynamic_dispatch; language; callback} :: !procedure_callbacks procedure_callbacks :=
{checker_name; dynamic_dispatch; language; callback} :: !procedure_callbacks
let register_cluster_callback ~name language (callback : cluster_callback_t) = let register_file_callback ~checker_name language (callback : file_callback_t) =
cluster_callbacks := {name; language; callback} :: !cluster_callbacks file_callbacks := {checker_name; language; callback} :: !file_callbacks
(** Invoke all registered procedure callbacks on the given procedure. *)
let iterate_procedure_callbacks exe_env summary = let iterate_procedure_callbacks exe_env summary =
let proc_desc = Summary.get_proc_desc summary in let proc_desc = Summary.get_proc_desc summary in
let proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
@ -55,11 +53,11 @@ let iterate_procedure_callbacks exe_env summary =
in in
let is_specialized = Procdesc.is_specialized proc_desc in let is_specialized = Procdesc.is_specialized proc_desc in
List.fold ~init:summary List.fold ~init:summary
~f:(fun summary {name; dynamic_dispatch; language; callback} -> ~f:(fun summary {checker_name; dynamic_dispatch; language; callback} ->
if Language.equal language procedure_language && (dynamic_dispatch || not is_specialized) then ( if Language.equal language procedure_language && (dynamic_dispatch || not is_specialized) then (
PerfEvent.( PerfEvent.(
log (fun logger -> log (fun logger ->
log_begin_event logger ~name ~categories:["backend"] log_begin_event logger ~name:checker_name ~categories:["backend"]
~arguments:[("proc", `String (Procname.to_string proc_name))] ~arguments:[("proc", `String (Procname.to_string proc_name))]
() )) ; () )) ;
let summary = callback {get_procs_in_file; summary; exe_env} in let summary = callback {get_procs_in_file; summary; exe_env} in
@ -69,9 +67,8 @@ let iterate_procedure_callbacks exe_env summary =
!procedure_callbacks !procedure_callbacks
(** Invoke all registered cluster callbacks on a cluster of procedures. *) let iterate_file_callbacks procedures exe_env source_file =
let iterate_cluster_callbacks procedures exe_env source_file = if not (List.is_empty !file_callbacks) then
if not (List.is_empty !cluster_callbacks) then
let environment = {procedures; source_file; exe_env} in let environment = {procedures; source_file; exe_env} in
let language_matches language = let language_matches language =
match procedures with match procedures with
@ -85,4 +82,4 @@ let iterate_cluster_callbacks procedures exe_env source_file =
if language_matches language then ( if language_matches language then (
Language.curr_language := language ; Language.curr_language := language ;
callback environment ) ) callback environment ) )
!cluster_callbacks !file_callbacks

@ -7,7 +7,18 @@
open! IStd open! IStd
(** Module to register and invoke callbacks *) (** Module to register and invoke checkers' callbacks. *)
(* There are two types of callbacks:
1) Procedure level callback.
The checker is responsible for updating checker-specific summary artifacts for that procedure, including
writing errors that were detected during per-procedure analysis.
2) File level callback. Guaranteed to be invoked after all procedure-level callbacks for this files are invoked,
and generated summaries are serialized.
The checker is responsible for doing any additional work, but SHALL NOT modify summaries at this point, including updating
summaries' error log.
Additional errors can be issued at this stage using capabilities of [IssueLog].
*)
type proc_callback_args = type proc_callback_args =
{get_procs_in_file: Procname.t -> Procname.t list; summary: Summary.t; exe_env: Exe_env.t} {get_procs_in_file: Procname.t -> Procname.t list; summary: Summary.t; exe_env: Exe_env.t}
@ -20,20 +31,21 @@ type proc_callback_args =
- Procedure for the callback to act on. *) - Procedure for the callback to act on. *)
type proc_callback_t = proc_callback_args -> Summary.t type proc_callback_t = proc_callback_args -> Summary.t
type cluster_callback_args = type file_callback_args =
{procedures: Procname.t list; source_file: SourceFile.t; exe_env: Exe_env.t} {procedures: Procname.t list; source_file: SourceFile.t; exe_env: Exe_env.t}
type cluster_callback_t = cluster_callback_args -> unit type file_callback_t = file_callback_args -> unit
val register_procedure_callback : val register_procedure_callback :
name:string -> ?dynamic_dispatch:bool -> Language.t -> proc_callback_t -> unit checker_name:string -> ?dynamic_dispatch:bool -> Language.t -> proc_callback_t -> unit
(** register a procedure callback *) (** Register a procedure callback (see details above) *)
val register_cluster_callback : name:string -> Language.t -> cluster_callback_t -> unit val register_file_callback : checker_name:string -> Language.t -> file_callback_t -> unit
(** register a cluster callback *) (** Register a file callback (see details above) *)
val iterate_procedure_callbacks : Exe_env.t -> Summary.t -> Summary.t val iterate_procedure_callbacks : Exe_env.t -> Summary.t -> Summary.t
(** Invoke all registered procedure callbacks on the given procedure. *) (** Invoke all registered procedure callbacks on the given procedure. *)
val iterate_cluster_callbacks : Procname.t list -> Exe_env.t -> SourceFile.t -> unit val iterate_file_callbacks : Procname.t list -> Exe_env.t -> SourceFile.t -> unit
(** Invoke all registered cluster callbacks on a cluster of procedures. *) (** Invoke all registered file callbacks on a file. Guaranteed to be called after all
procedure-level callbacks are invoked *)

@ -381,13 +381,13 @@ let analyze_procedures exe_env procs_to_analyze source_file_opt =
Option.iter source_file_opt ~f:(fun source_file -> Option.iter source_file_opt ~f:(fun source_file ->
if Config.dump_duplicate_symbols then dump_duplicate_procs source_file procs_to_analyze ) ; if Config.dump_duplicate_symbols then dump_duplicate_procs source_file procs_to_analyze ) ;
Option.iter source_file_opt ~f:(fun source_file -> Option.iter source_file_opt ~f:(fun source_file ->
Callbacks.iterate_cluster_callbacks procs_to_analyze exe_env source_file ; Callbacks.iterate_file_callbacks procs_to_analyze exe_env source_file ;
create_perf_stats_report source_file ) ; create_perf_stats_report source_file ) ;
unset_exe_env () ; unset_exe_env () ;
Language.curr_language := saved_language Language.curr_language := saved_language
(** Invoke all procedure and cluster callbacks on a given environment. *) (** Invoke all procedure-level and file-level callbacks on a given environment. *)
let analyze_file (exe_env : Exe_env.t) source_file = let analyze_file (exe_env : Exe_env.t) source_file =
let procs_to_analyze = SourceFiles.proc_names_of_source source_file in let procs_to_analyze = SourceFiles.proc_names_of_source source_file in
analyze_procedures exe_env procs_to_analyze (Some source_file) analyze_procedures exe_env procs_to_analyze (Some source_file)

@ -236,7 +236,6 @@ end = struct
Errlog.update global_err_log (Summary.get_err_log summary) Errlog.update global_err_log (Summary.get_err_log summary)
(** Create filename.ext.html. *)
let write_html_file filename procs = let write_html_file filename procs =
let fname_encoding = DB.source_file_encoding filename in let fname_encoding = DB.source_file_encoding filename in
let fd, fmt = Io_infer.Html.create filename [".."; fname_encoding] in let fd, fmt = Io_infer.Html.create filename [".."; fname_encoding] in

@ -38,4 +38,5 @@ val write_proc_html : Procdesc.t -> unit
(** Write html file for the procedure. *) (** Write html file for the procedure. *)
val write_all_html_files : SourceFile.t -> unit val write_all_html_files : SourceFile.t -> unit
(** Create filename.ext.html for each file in the cluster. *) (** Group procedures used in the file by their corresponding source files, and create
filename.ext.html for each such a file. *)

@ -21,7 +21,7 @@ let () =
type callback_fun = type callback_fun =
| Procedure of Callbacks.proc_callback_t | Procedure of Callbacks.proc_callback_t
| DynamicDispatch of Callbacks.proc_callback_t | DynamicDispatch of Callbacks.proc_callback_t
| Cluster of Callbacks.cluster_callback_t | File of Callbacks.file_callback_t
type callback = callback_fun * Language.t type callback = callback_fun * Language.t
@ -101,8 +101,8 @@ let all_checkers =
; callbacks= ; callbacks=
[ (Procedure RacerD.analyze_procedure, Language.Clang) [ (Procedure RacerD.analyze_procedure, Language.Clang)
; (Procedure RacerD.analyze_procedure, Language.Java) ; (Procedure RacerD.analyze_procedure, Language.Java)
; (Cluster RacerD.file_analysis, Language.Clang) ; (File RacerD.file_analysis, Language.Clang)
; (Cluster RacerD.file_analysis, Language.Java) ] } ; (File RacerD.file_analysis, Language.Java) ] }
(* toy resource analysis to use in the infer lab, see the lab/ directory *) (* toy resource analysis to use in the infer lab, see the lab/ directory *)
; { name= "resource leak" ; { name= "resource leak"
; active= Config.resource_leak ; active= Config.resource_leak
@ -131,9 +131,9 @@ let all_checkers =
; active= Config.starvation ; active= Config.starvation
; callbacks= ; callbacks=
[ (Procedure Starvation.analyze_procedure, Language.Java) [ (Procedure Starvation.analyze_procedure, Language.Java)
; (Cluster Starvation.reporting, Language.Java) ; (File Starvation.reporting, Language.Java)
; (Procedure Starvation.analyze_procedure, Language.Clang) ; (Procedure Starvation.analyze_procedure, Language.Clang)
; (Cluster Starvation.reporting, Language.Clang) ] } ; (File Starvation.reporting, Language.Clang) ] }
; { name= "purity" ; { name= "purity"
; active= Config.purity || Config.loop_hoisting ; active= Config.purity || Config.loop_hoisting
; callbacks= ; callbacks=
@ -156,11 +156,12 @@ let register checkers =
let register_callback (callback, language) = let register_callback (callback, language) =
match callback with match callback with
| Procedure procedure_cb -> | Procedure procedure_cb ->
Callbacks.register_procedure_callback ~name language procedure_cb Callbacks.register_procedure_callback ~checker_name:name language procedure_cb
| DynamicDispatch procedure_cb -> | DynamicDispatch procedure_cb ->
Callbacks.register_procedure_callback ~name ~dynamic_dispatch:true language procedure_cb Callbacks.register_procedure_callback ~checker_name:name ~dynamic_dispatch:true language
| Cluster cluster_cb -> procedure_cb
Callbacks.register_cluster_callback ~name language cluster_cb | File file_cb ->
Callbacks.register_file_callback ~checker_name:name language file_cb
in in
List.iter ~f:register_callback callbacks List.iter ~f:register_callback callbacks
in in

@ -1151,7 +1151,7 @@ let aggregate_by_class exe_env procedures =
(* Gathers results by analyzing all the methods in a file, then (* Gathers results by analyzing all the methods in a file, then
post-processes the results to check an (approximation of) thread post-processes the results to check an (approximation of) thread
safety *) safety *)
let file_analysis ({procedures; source_file; exe_env} : Callbacks.cluster_callback_args) = let file_analysis ({procedures; source_file; exe_env} : Callbacks.file_callback_args) =
let class_map = aggregate_by_class exe_env procedures in let class_map = aggregate_by_class exe_env procedures in
Typ.Name.Map.fold Typ.Name.Map.fold
(fun classname methods issue_log -> (fun classname methods issue_log ->

@ -7,6 +7,6 @@
open! IStd open! IStd
val file_analysis : Callbacks.cluster_callback_t val file_analysis : Callbacks.file_callback_t
val analyze_procedure : Callbacks.proc_callback_t val analyze_procedure : Callbacks.proc_callback_t

@ -9,6 +9,6 @@ open! IStd
val analyze_procedure : Callbacks.proc_callback_t val analyze_procedure : Callbacks.proc_callback_t
val reporting : Callbacks.cluster_callback_t val reporting : Callbacks.file_callback_t
val whole_program_analysis : unit -> unit val whole_program_analysis : unit -> unit

Loading…
Cancel
Save