From 7106de35a35b3e3b8ca9ed916f3ab522c8a42e66 Mon Sep 17 00:00:00 2001 From: Nikos Gorogiannis Date: Thu, 9 May 2019 13:38:58 -0700 Subject: [PATCH] [issuelogs] less imperative Reviewed By: jvillard Differential Revision: D15278599 fbshipit-source-id: 54b190d94 --- infer/src/IR/IssueLog.ml | 71 ++++++++-------- infer/src/IR/IssueLog.mli | 22 ++--- infer/src/backend/InferPrint.ml | 4 +- infer/src/backend/reporting.ml | 7 +- infer/src/backend/reporting.mli | 3 +- infer/src/clang/cFrontend_checkers_main.ml | 3 +- infer/src/clang/cFrontend_errors.ml | 5 +- infer/src/clang/cFrontend_errors.mli | 2 + infer/src/concurrency/RacerD.ml | 99 ++++++++++++---------- infer/src/concurrency/starvation.ml | 43 +++++----- infer/tests/build_systems/ant/issues.exp | 20 ----- 11 files changed, 145 insertions(+), 134 deletions(-) diff --git a/infer/src/IR/IssueLog.ml b/infer/src/IR/IssueLog.ml index 7134b40c3..e9ababf79 100644 --- a/infer/src/IR/IssueLog.ml +++ b/infer/src/IR/IssueLog.ml @@ -9,31 +9,35 @@ open! IStd -let errLogMap = ref Typ.Procname.Map.empty +type t = Errlog.t Typ.Procname.Map.t -let get_errlog procname = - try Typ.Procname.Map.find procname !errLogMap - with Caml.Not_found -> - let errlog = Errlog.empty () in - errLogMap := Typ.Procname.Map.add procname errlog !errLogMap ; - errlog +let empty = Typ.Procname.Map.empty + +let get_or_add ~proc m = + match Typ.Procname.Map.find_opt proc m with + | Some errlog -> + (m, errlog) + | None -> + let errlog = Errlog.empty () in + let m = Typ.Procname.Map.add proc errlog m in + (m, errlog) let issues_serializer : Errlog.t Typ.Procname.Map.t Serialization.serializer = Serialization.create_serializer Serialization.Key.issues -let iter f = Typ.Procname.Map.iter f !errLogMap +let iter ~f m = Typ.Procname.Map.iter f m -let store directory source_file = - if not (Typ.Procname.Map.is_empty !errLogMap) then ( - let abbrev_source_file = DB.source_file_encoding source_file in - let issues_dir = Config.results_dir ^/ directory in +let store ~dir ~file m = + if not (Typ.Procname.Map.is_empty m) then ( + let abbrev_source_file = DB.source_file_encoding file in + let issues_dir = Config.results_dir ^/ dir in Utils.create_dir issues_dir ; let filename = DB.filename_from_string (Filename.concat issues_dir (abbrev_source_file ^ ".issue")) in - Serialization.write_to_file issues_serializer filename ~data:!errLogMap ) + Serialization.write_to_file issues_serializer filename ~data:m ) else () @@ -42,27 +46,26 @@ let load_issues issues_file = Serialization.read_from_file issues_serializer iss (** Load all the issues in the given dir and update the issues map *) let load dir = - let () = errLogMap := Typ.Procname.Map.empty in let issues_dir = Filename.concat Config.results_dir dir in - let children_opt = try Some (Sys.readdir issues_dir) with Sys_error _ -> None in - let load_issues_to_map issues_file = + let load_issues_to_map init issues_file = let file = DB.filename_from_string (Filename.concat issues_dir issues_file) in - match load_issues file with - | Some map -> - errLogMap := - Typ.Procname.Map.merge - (fun _ issues1 issues2 -> - match (issues1, issues2) with - | Some issues1, Some issues2 -> - Errlog.update issues1 issues2 ; Some issues1 - | Some issues1, None -> - Some issues1 - | None, Some issues2 -> - Some issues2 - | None, None -> - None ) - !errLogMap map - | None -> - () + load_issues file + |> Option.fold ~init ~f:(fun acc map -> + Typ.Procname.Map.merge + (fun _ issues1 issues2 -> + match (issues1, issues2) with + | Some issues1, Some issues2 -> + Errlog.update issues1 issues2 ; Some issues1 + | Some issues1, None -> + Some issues1 + | None, Some issues2 -> + Some issues2 + | None, None -> + None ) + acc map ) in - match children_opt with Some children -> Array.iter ~f:load_issues_to_map children | None -> () + match Sys.readdir issues_dir with + | children -> + Array.fold children ~init:empty ~f:load_issues_to_map + | exception Sys_error _ -> + empty diff --git a/infer/src/IR/IssueLog.mli b/infer/src/IR/IssueLog.mli index f8c5719bd..3b48bfc1a 100644 --- a/infer/src/IR/IssueLog.mli +++ b/infer/src/IR/IssueLog.mli @@ -7,19 +7,21 @@ open! IStd -(** Module to store a map from procnames to error logs. Starts with an empty map. *) +(** Module for maps from procnames to error logs. *) +type t -val iter : (Typ.Procname.t -> Errlog.t -> unit) -> unit +val empty : t + +val iter : f:(Typ.Procname.t -> Errlog.t -> unit) -> t -> unit (** iterate a function on map contents *) -val get_errlog : Typ.Procname.t -> Errlog.t -(** Get the error log for a given procname. If not present, then add the association from - procname to an empty error log and return the latter. *) +val get_or_add : proc:Typ.Procname.t -> t -> t * Errlog.t +(** Get the error log for a given procname. If there is none, add an empty one to the map. + Return the resulting map together with the errlog. *) -val store : string -> SourceFile.t -> unit -(** If there are any issues in the log, [store dirname filename] stores map to [infer-out/dirname/filename]. +val store : dir:string -> file:SourceFile.t -> t -> unit +(** If there are any issues in the log, [store ~dir ~file] stores map to [infer-out/dir/file]. Otherwise, no file is written. *) -val load : string -> unit -(** [load directory] resets the issue map first, then walks [infer-out/directory], merging all - maps stored in the found files into the current map. *) +val load : string -> t +(** [load directory] walks [infer-out/directory], merging maps stored in files into one map. *) diff --git a/infer/src/backend/InferPrint.ml b/infer/src/backend/InferPrint.ml index 165c8cda6..457cb5236 100644 --- a/infer/src/backend/InferPrint.ml +++ b/infer/src/backend/InferPrint.ml @@ -1223,8 +1223,8 @@ let pp_summary_and_issues formats_by_report_kind issue_formats = List.iter [Config.lint_issues_dir_name; Config.starvation_issues_dir_name; Config.racerd_issues_dir_name] ~f:(fun dir_name -> - IssueLog.load dir_name ; - IssueLog.iter (pp_lint_issues filters formats_by_report_kind linereader) ) ; + IssueLog.load dir_name + |> IssueLog.iter ~f:(pp_lint_issues filters formats_by_report_kind linereader) ) ; finalize_and_close_files formats_by_report_kind stats diff --git a/infer/src/backend/reporting.ml b/infer/src/backend/reporting.ml index b9e7553d0..b87f057e3 100644 --- a/infer/src/backend/reporting.ml +++ b/infer/src/backend/reporting.ml @@ -82,12 +82,13 @@ let log_error = log_issue_from_summary_simplified Exceptions.Error let log_warning = log_issue_from_summary_simplified Exceptions.Warning -let log_issue_external procname severity ~loc ~ltr ?access issue_type error_message = +let log_issue_external procname ~issue_log severity ~loc ~ltr ?access issue_type error_message = let exn = checker_exception issue_type error_message in - let errlog = IssueLog.get_errlog procname in + let issue_log, errlog = IssueLog.get_or_add issue_log ~proc:procname in let node = Errlog.UnknownNode in log_issue_from_errlog procname ~clang_method_kind:None severity errlog ~loc ~node ~session:0 ~ltr - ~access ~extras:None exn + ~access ~extras:None exn ; + issue_log let log_error_using_state summary exn = diff --git a/infer/src/backend/reporting.mli b/infer/src/backend/reporting.mli index 1c77c3a4f..4febc03f8 100644 --- a/infer/src/backend/reporting.mli +++ b/infer/src/backend/reporting.mli @@ -45,13 +45,14 @@ val log_error_using_state : Summary.t -> exn -> unit val log_issue_external : Typ.Procname.t + -> issue_log:IssueLog.t -> Exceptions.severity -> loc:Location.t -> ltr:Errlog.loc_trace -> ?access:string -> IssueType.t -> string - -> unit + -> IssueLog.t (** Log an issue to the error log in [IssueLog] associated with the given procname. *) val is_suppressed : diff --git a/infer/src/clang/cFrontend_checkers_main.ml b/infer/src/clang/cFrontend_checkers_main.ml index fb908feb7..6f6ef320d 100644 --- a/infer/src/clang/cFrontend_checkers_main.ml +++ b/infer/src/clang/cFrontend_checkers_main.ml @@ -374,6 +374,7 @@ let linters_files = let do_frontend_checks (trans_unit_ctx : CFrontend_config.translation_unit_context) ast = + CFrontend_errors.issue_log := IssueLog.empty ; L.(debug Capture Quiet) "Loading the following linters files: %a@\n" (Pp.comma_seq Format.pp_print_string) @@ -400,7 +401,7 @@ let do_frontend_checks (trans_unit_ctx : CFrontend_config.translation_unit_conte CFrontend_errors.invoke_set_of_checkers_on_node parsed_linters context (Ctl_parser_types.Decl ast) ; List.iter ~f:(do_frontend_checks_decl parsed_linters context active_map) allowed_decls ; - IssueLog.store Config.lint_issues_dir_name source_file ; + IssueLog.store !CFrontend_errors.issue_log ~dir:Config.lint_issues_dir_name ~file:source_file ; L.(debug Linters Medium) "End linting file %a@\n" SourceFile.pp source_file ; CTL.save_dotty_when_in_debug_mode trans_unit_ctx.CFrontend_config.source_file (*if CFrontend_config.tableaux_evaluation then ( diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index 9e4d83a35..84e16ecb3 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -459,6 +459,8 @@ let expand_checkers macro_map path_map checkers = List.map ~f:expand_one_checker checkers +let issue_log = ref IssueLog.empty + (** Add a frontend warning with a description desc at location loc to the errlog of a proc desc *) let log_frontend_issue method_decl_opt (node : Ctl_parser_types.ast_node) (issue_desc : CIssue.issue_desc) = @@ -469,7 +471,8 @@ let log_frontend_issue method_decl_opt (node : Ctl_parser_types.ast_node) | None -> Typ.Procname.Linters_dummy_method in - let errlog = IssueLog.get_errlog procname in + let issue_log', errlog = IssueLog.get_or_add ~proc:procname !issue_log in + issue_log := issue_log' ; let err_desc = Errdesc.explain_frontend_warning issue_desc.description issue_desc.suggestion issue_desc.loc in diff --git a/infer/src/clang/cFrontend_errors.mli b/infer/src/clang/cFrontend_errors.mli index b5f490533..747c4afef 100644 --- a/infer/src/clang/cFrontend_errors.mli +++ b/infer/src/clang/cFrontend_errors.mli @@ -7,6 +7,8 @@ open! IStd +val issue_log : IssueLog.t ref + type linter = { condition: CTL.t ; issue_desc: CIssue.issue_desc diff --git a/infer/src/concurrency/RacerD.ml b/infer/src/concurrency/RacerD.ml index eaa3c216f..71066943a 100644 --- a/infer/src/concurrency/RacerD.ml +++ b/infer/src/concurrency/RacerD.ml @@ -722,9 +722,9 @@ let make_trace ~report_kind original_path = (original_trace, original_end, None) -let log_issue current_pname ~loc ~ltr ~access issue_type error_message = - Reporting.log_issue_external current_pname Exceptions.Warning ~loc ~ltr ~access issue_type - error_message +let log_issue current_pname ~issue_log ~loc ~ltr ~access issue_type error_message = + Reporting.log_issue_external current_pname Exceptions.Warning ~issue_log ~loc ~ltr ~access + issue_type error_message type reported_access = @@ -733,7 +733,7 @@ type reported_access = ; tenv: Tenv.t ; procdesc: Procdesc.t } -let report_thread_safety_violation ~make_description ~report_kind +let report_thread_safety_violation ~issue_log ~make_description ~report_kind ({threads; snapshot; tenv; procdesc} : reported_access) = let open RacerDDomain in let pname = Procdesc.get_proc_name procdesc in @@ -750,10 +750,10 @@ let report_thread_safety_violation ~make_description ~report_kind let error_message = F.sprintf "%s%s" description explanation in let end_locs = Option.to_list original_end @ Option.to_list conflict_end in let access = IssueAuxData.encode end_locs in - log_issue pname ~loc ~ltr ~access issue_type error_message + log_issue pname ~issue_log ~loc ~ltr ~access issue_type error_message -let report_unannotated_interface_violation reported_pname (reported_access : reported_access) = +let report_unannotated_interface_violation ~issue_log reported_pname reported_access = match reported_pname with | Typ.Procname.Java java_pname -> let class_name = Typ.Procname.Java.get_class_name java_pname in @@ -764,11 +764,11 @@ let report_unannotated_interface_violation reported_pname (reported_access : rep (MF.wrap_monospaced Typ.Procname.pp) reported_pname MF.pp_monospaced class_name MF.pp_monospaced "@ThreadSafe" in - report_thread_safety_violation ~make_description ~report_kind:UnannotatedInterface + report_thread_safety_violation ~issue_log ~make_description ~report_kind:UnannotatedInterface reported_access | _ -> (* skip reporting on C++ *) - () + issue_log let make_unprotected_write_description pname final_sink_site initial_sink_site final_sink = @@ -994,7 +994,7 @@ let should_report_guardedby_violation classname_str ({snapshot; tenv; procdesc} currently not distinguishing different locks, and are treating "known to be confined to a thread" as if "known to be confined to UI thread". *) -let report_unsafe_accesses classname (aggregated_access_map : ReportMap.t) = +let report_unsafe_accesses ~issue_log classname (aggregated_access_map : ReportMap.t) = let open RacerDDomain in let open RacerDModels in let is_duplicate_report ({snapshot; procdesc} : reported_access) @@ -1032,10 +1032,10 @@ let report_unsafe_accesses classname (aggregated_access_map : ReportMap.t) = {reported with reported_unannotated_calls; reported_sites} else reported in - let report_unsafe_access accesses reported_acc + let report_unsafe_access accesses (reported_acc, issue_log) ({snapshot; threads; tenv; procdesc} as reported_access) = let pname = Procdesc.get_proc_name procdesc in - if is_duplicate_report reported_access reported_acc then reported_acc + if is_duplicate_report reported_access reported_acc then (reported_acc, issue_log) else match snapshot.access.elem with | Access.InterfaceCall reported_pname @@ -1043,10 +1043,12 @@ let report_unsafe_accesses classname (aggregated_access_map : ReportMap.t) = && ThreadsDomain.is_any threads && is_marked_thread_safe procdesc tenv -> (* un-annotated interface call + no lock in method marked thread-safe. warn *) - report_unannotated_interface_violation reported_pname reported_access ; - update_reported reported_access reported_acc + let issue_log = + report_unannotated_interface_violation ~issue_log reported_pname reported_access + in + (update_reported reported_access reported_acc, issue_log) | Access.InterfaceCall _ -> - reported_acc + (reported_acc, issue_log) | (Access.Write _ | ContainerWrite _) when Typ.Procname.is_java pname -> let conflict = if ThreadsDomain.is_any threads then @@ -1064,14 +1066,17 @@ let report_unsafe_accesses classname (aggregated_access_map : ReportMap.t) = if AccessSnapshot.is_unprotected snapshot && (Option.is_some conflict || ThreadsDomain.is_any threads) - then ( - report_thread_safety_violation ~make_description:make_unprotected_write_description - ~report_kind:(WriteWriteRace conflict) reported_access ; - update_reported reported_access reported_acc ) - else reported_acc + then + let issue_log = + report_thread_safety_violation ~issue_log + ~make_description:make_unprotected_write_description + ~report_kind:(WriteWriteRace conflict) reported_access + in + (update_reported reported_access reported_acc, issue_log) + else (reported_acc, issue_log) | Access.Write _ | ContainerWrite _ -> (* Do not report unprotected writes for ObjC_Cpp *) - reported_acc + (reported_acc, issue_log) | (Access.Read _ | ContainerRead _) when AccessSnapshot.is_unprotected snapshot -> (* unprotected read. report all writes as conflicts for java. for c++ filter out unprotected writes *) @@ -1083,13 +1088,16 @@ let report_unsafe_accesses classname (aggregated_access_map : ReportMap.t) = else not (AccessSnapshot.is_unprotected snapshot) in List.find ~f:is_conflict accesses - |> Option.value_map ~default:reported_acc ~f:(fun conflict -> + |> Option.value_map ~default:(reported_acc, issue_log) ~f:(fun conflict -> let make_description = make_read_write_race_description ~read_is_sync:false conflict in let report_kind = ReadWriteRace conflict.snapshot.access in - report_thread_safety_violation ~make_description ~report_kind reported_access ; - update_reported reported_access reported_acc ) + let issue_log = + report_thread_safety_violation ~issue_log ~make_description ~report_kind + reported_access + in + (update_reported reported_access reported_acc, issue_log) ) | Access.Read _ | ContainerRead _ -> (* protected read. report unprotected writes and opposite protected writes as conflicts *) let can_conflict (snapshot1 : AccessSnapshot.t) (snapshot2 : AccessSnapshot.t) = @@ -1102,45 +1110,49 @@ let report_unsafe_accesses classname (aggregated_access_map : ReportMap.t) = else TraceElem.is_write other_snapshot.access && can_conflict snapshot other_snapshot in List.find accesses ~f:is_conflict - |> Option.value_map ~default:reported_acc ~f:(fun conflict -> + |> Option.value_map ~default:(reported_acc, issue_log) ~f:(fun conflict -> (* protected read with conflicting unprotected write(s). warn. *) let make_description = make_read_write_race_description ~read_is_sync:true conflict in let report_kind = ReadWriteRace conflict.snapshot.access in - report_thread_safety_violation ~make_description ~report_kind reported_access ; - update_reported reported_access reported_acc ) + let issue_log = + report_thread_safety_violation ~issue_log ~make_description ~report_kind + reported_access + in + (update_reported reported_access reported_acc, issue_log) ) in - let report_accesses_on_location (reportable_accesses : reported_access list) init = + let report_accesses_on_location reportable_accesses init = (* Don't report on location if all accesses are on non-concurrent contexts *) if List.for_all reportable_accesses ~f:(fun ({threads} : reported_access) -> - (* FIXME this should be any thread or no thread *) ThreadsDomain.is_any threads |> not ) then init else List.fold reportable_accesses ~init ~f:(report_unsafe_access reportable_accesses) in let report_guardedby_violations_on_location grouped_accesses init = if Config.racerd_guardedby then - List.fold grouped_accesses ~init ~f:(fun acc r -> - if should_report_guardedby_violation classname r && not (is_duplicate_report r acc) then ( - report_thread_safety_violation ~make_description:make_guardedby_violation_description - ~report_kind:GuardedByViolation r ; - update_reported r acc ) - else acc ) + List.fold grouped_accesses ~init ~f:(fun (acc, issue_log) r -> + if should_report_guardedby_violation classname r && not (is_duplicate_report r acc) then + let issue_log = + report_thread_safety_violation ~issue_log ~report_kind:GuardedByViolation + ~make_description:make_guardedby_violation_description r + in + (update_reported r acc, issue_log) + else (acc, issue_log) ) else init in - let report grouped_accesses reported_acc = + let report grouped_accesses (reported, issue_log) = (* reset the reported reads and writes for each memory location *) - let reported_acc = - { reported_acc with + let reported = + { reported with reported_writes= Typ.Procname.Set.empty ; reported_reads= Typ.Procname.Set.empty } in - report_guardedby_violations_on_location grouped_accesses reported_acc + report_guardedby_violations_on_location grouped_accesses (reported, issue_log) |> report_accesses_on_location grouped_accesses in - ReportMap.fold report aggregated_access_map empty_reported |> ignore + ReportMap.fold report aggregated_access_map (empty_reported, issue_log) |> snd (* create a map from [abstraction of a memory loc] -> accesses that @@ -1173,8 +1185,9 @@ let aggregate_by_class file_env = (* Gathers results by analyzing all the methods in a file, then post-processes the results to check an (approximation of) thread safety *) -let file_analysis {Callbacks.procedures; source_file} = +let file_analysis ({procedures; source_file} : Callbacks.cluster_callback_args) = + let init = IssueLog.empty in aggregate_by_class procedures - |> String.Map.iteri ~f:(fun ~key:classname ~data:class_env -> - report_unsafe_accesses classname (make_results_table class_env) ) ; - IssueLog.store Config.racerd_issues_dir_name source_file + |> String.Map.fold ~init ~f:(fun ~key:classname ~data:class_env issue_log -> + make_results_table class_env |> report_unsafe_accesses ~issue_log classname ) + |> IssueLog.store ~dir:Config.racerd_issues_dir_name ~file:source_file diff --git a/infer/src/concurrency/starvation.ml b/infer/src/concurrency/starvation.ml index 9dc7f63e5..de920f17e 100644 --- a/infer/src/concurrency/starvation.ml +++ b/infer/src/concurrency/starvation.ml @@ -191,7 +191,7 @@ module ReportMap : sig val add_strict_mode_volation : report_add_t - val log : Tenv.t -> Procdesc.t -> t -> unit + val log : IssueLog.t -> Tenv.t -> Procdesc.t -> t -> IssueLog.t end = struct type problem = | Starvation of StarvationDomain.Event.severity_t @@ -229,8 +229,8 @@ end = struct add loc report map - let log tenv pdesc map = - let log_report loc {problem; pname; ltr; message} = + let log start_issue_log tenv pdesc map = + let log_report ~issue_log loc {problem; pname; ltr; message} = let issue_type = match problem with | Deadlock _ -> @@ -240,22 +240,25 @@ end = struct | StrictModeViolation _ -> IssueType.strict_mode_violation in - if Reporting.is_suppressed tenv pdesc issue_type then () - else Reporting.log_issue_external pname Exceptions.Error ~loc ~ltr issue_type message + if Reporting.is_suppressed tenv pdesc issue_type then issue_log + else + Reporting.log_issue_external ~issue_log pname Exceptions.Error ~loc ~ltr issue_type message in let mk_deduped_report ({message} as report) = { report with message= Printf.sprintf "%s Additional report(s) on the same line were suppressed." message } in - let log_reports compare loc = function + let log_reports compare loc reports issue_log = + match reports with | [] -> - () + issue_log | [(_, report)] -> - log_report loc report + log_report ~issue_log loc report | reports -> List.max_elt ~compare reports - |> Option.iter ~f:(fun (_, rep) -> mk_deduped_report rep |> log_report loc) + |> Option.fold ~init:issue_log ~f:(fun acc (_, rep) -> + mk_deduped_report rep |> log_report ~issue_log:acc loc ) in let filter_map_deadlock = function {problem= Deadlock l} as r -> Some (l, r) | _ -> None in let filter_map_starvation = function @@ -273,15 +276,15 @@ end = struct let compare_reports weight_compare (w, r) (w', r') = match weight_compare w w' with 0 -> String.compare r.message r'.message | result -> result in - let log_location loc problems = + let log_location loc problems issue_log = let deadlocks = List.filter_map problems ~f:filter_map_deadlock in - log_reports (compare_reports Int.compare) loc deadlocks ; let starvations = List.filter_map problems ~f:filter_map_starvation in - log_reports (compare_reports StarvationDomain.Event.compare_severity_t) loc starvations ; let strict_mode_violations = List.filter_map problems ~f:filter_map_strict_mode_violation in - log_reports (compare_reports Int.compare) loc strict_mode_violations + log_reports (compare_reports Int.compare) loc deadlocks issue_log + |> log_reports (compare_reports StarvationDomain.Event.compare_severity_t) loc starvations + |> log_reports (compare_reports Int.compare) loc strict_mode_violations in - LocMap.iter log_location map + LocMap.fold log_location map start_issue_log end let should_report_deadlock_on_current_proc current_elem endpoint_elem = @@ -486,12 +489,14 @@ let report_starvation env {StarvationDomain.events; ui} report_map' = let reporting {Callbacks.procedures; source_file} = - let report_procedure ((tenv, proc_desc) as env) = + let report_procedure issue_log ((tenv, proc_desc) as env) = if should_report proc_desc then Payload.read proc_desc (Procdesc.get_proc_name proc_desc) - |> Option.iter ~f:(fun summary -> + |> Option.value_map ~default:issue_log ~f:(fun summary -> report_deadlocks env summary ReportMap.empty - |> report_starvation env summary |> ReportMap.log tenv proc_desc ) + |> report_starvation env summary + |> ReportMap.log issue_log tenv proc_desc ) + else issue_log in - List.iter procedures ~f:report_procedure ; - IssueLog.store Config.starvation_issues_dir_name source_file + List.fold procedures ~init:IssueLog.empty ~f:report_procedure + |> IssueLog.store ~dir:Config.starvation_issues_dir_name ~file:source_file diff --git a/infer/tests/build_systems/ant/issues.exp b/infer/tests/build_systems/ant/issues.exp index 714b748f0..30e431028 100644 --- a/infer/tests/build_systems/ant/issues.exp +++ b/infer/tests/build_systems/ant/issues.exp @@ -54,61 +54,41 @@ codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.Guarded codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample$4.readFromInnerClassBad2():java.lang.String, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure readFromInnerClassBad2()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample$Sub.badSub():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure badSub()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample$Sub.badSub():void, 491, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.xForSub`,,access to `this.xForSub`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample$Sub.badSub():void, 491, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.xForSub`,,access to `this.xForSub`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.badGuardedByNormalLock():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure badGuardedByNormalLock()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.badGuardedByNormalLock():void, 526, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedbynl`,,access to `this.guardedbynl`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.badGuardedByNormalLock():void, 526, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedbynl`,,access to `this.guardedbynl`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.badGuardedByReentrantLock():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure badGuardedByReentrantLock()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.badGuardedByReentrantLock():void, 530, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedbyrel`,,access to `this.guardedbyrel`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.badGuardedByReentrantLock():void, 530, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedbyrel`,,access to `this.guardedbyrel`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.byRefTrickyBad():java.lang.Object, 5, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure byRefTrickyBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.byRefTrickyBad():java.lang.Object, 281, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.g`,,access to `this.g`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.byRefTrickyBad():java.lang.Object, 281, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.g`,,access to `this.g`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.guardedByTypeSyntaxBad():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure guardedByTypeSyntaxBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.guardedByTypeSyntaxBad():void, 2, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure guardedByTypeSyntaxBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.guardedByTypeSyntaxBad():void, 573, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedByLock1`,,access to `this.guardedByLock1`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.guardedByTypeSyntaxBad():void, 573, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedByLock1`,,access to `this.guardedByLock1`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.guardedByTypeSyntaxBad():void, 574, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedByLock2`,,access to `this.guardedByLock2`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.guardedByTypeSyntaxBad():void, 574, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.guardedByLock2`,,access to `this.guardedByLock2`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFAfterBlockBad():void, 3, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure readFAfterBlockBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFAfterBlockBad():void, 98, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFAfterBlockBad():void, 98, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBad():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure readFBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBad():void, 66, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBad():void, 66, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadButSuppressed():void, 71, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadButSuppressed():void, 71, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadButSuppressedOther():void, 76, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadButSuppressedOther():void, 76, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadWrongAnnotation():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure readFBadWrongAnnotation()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadWrongAnnotation():void, 109, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadWrongAnnotation():void, 109, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadWrongLock():void, 2, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure readFBadWrongLock()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadWrongLock():void, 85, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFBadWrongLock():void, 85, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFOkMethodAnnotated():void, 114, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFOkMethodAnnotated():void, 114, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFOkSynchronized():void, 127, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readFOkSynchronized():void, 127, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readGFromCopyOk():void, 267, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.mCopyOfG`,,access to `this.mCopyOfG`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readGFromCopyOk():void, 267, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.mCopyOfG`,,access to `this.mCopyOfG`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readHBad():void, 2, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure readHBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.readHBadSynchronizedMethodShouldntHelp():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure readHBadSynchronizedMethodShouldntHelp()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.reassignCopyOk():void, 149, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [access to `this.mCopyOfG`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.reassignCopyOk():void, 149, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [access to `this.mCopyOfG`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.synchronizedMethodReadBad():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure synchronizedMethodReadBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.synchronizedMethodReadBad():void, 138, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.synchronizedMethodReadBad():void, 138, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.synchronizedMethodWriteBad():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure synchronizedMethodWriteBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.synchronizedOnThisBad():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure synchronizedOnThisBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.synchronizedOnThisBad():void, 205, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `infer.GuardedByExample.sGuardedByClass`,,access to `infer.GuardedByExample.sGuardedByClass`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.synchronizedOnThisBad():void, 205, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `infer.GuardedByExample.sGuardedByClass`,,access to `infer.GuardedByExample.sGuardedByClass`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.writeFAfterBlockBad():void, 3, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure writeFAfterBlockBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.writeFAfterBlockBad():void, 104, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.writeFAfterBlockBad():void, 104, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.writeFBad():void, 1, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure writeFBad()] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.writeFBad():void, 80, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] -codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.writeFBad():void, 80, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `this.f`,,access to `this.f`] codetoanalyze/java/infer/GuardedByExample.java, codetoanalyze.java.infer.GuardedByExample.writeFBadWrongLock():void, 2, UNSAFE_GUARDED_BY_ACCESS, no_bucket, ERROR, [start of procedure writeFBadWrongLock()] codetoanalyze/java/infer/HashMapExample.java, codetoanalyze.java.infer.HashMapExample.getAfterClearBad():void, 5, NULL_DEREFERENCE, no_bucket, ERROR, [start of procedure getAfterClearBad()] codetoanalyze/java/infer/HashMapExample.java, codetoanalyze.java.infer.HashMapExample.getAfterRemovingTheKeyBad():void, 5, NULL_DEREFERENCE, no_bucket, ERROR, [start of procedure getAfterRemovingTheKeyBad()]