Skip to content

Commit eb4e40f

Browse files
jvillardfacebook-github-bot
authored andcommitted
[ondemand][refactor] more precise return type for detecting cycles
Summary: This is needed up the stack. Introduces slight code duplication around registering caller->callee relationship but that's because we won't be doing it in all cases further up the stack. Reviewed By: skcho Differential Revision: D64537731 Privacy Context Container: L1208441 fbshipit-source-id: 3041d3f76e8efb169b603aa61435888a500151b1
1 parent 430ddbe commit eb4e40f

File tree

1 file changed

+78
-63
lines changed

1 file changed

+78
-63
lines changed

infer/src/backend/ondemand.ml

Lines changed: 78 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,17 @@ let edges_to_ignore = ref None
7676

7777
(** use either [is_active] or [edges_to_ignore] to determine if we should return an empty summary to
7878
avoid mutual recursion cycles *)
79-
let in_mutual_recursion_cycle ~caller_summary ~callee specialization =
79+
let detect_mutual_recursion_cycle ~caller_summary ~callee specialization =
8080
match (!edges_to_ignore, caller_summary) with
8181
| Some edges_to_ignore, Some {Summary.proc_name} ->
82-
Procname.Map.find_opt proc_name edges_to_ignore
83-
|> Option.exists ~f:(fun recursive_callees -> Procname.Set.mem callee recursive_callees)
82+
let is_replay_recursive_callee =
83+
Procname.Map.find_opt proc_name edges_to_ignore
84+
|> Option.exists ~f:(fun recursive_callees -> Procname.Set.mem callee recursive_callees)
85+
in
86+
if is_replay_recursive_callee then `ReplayCycleCut else `NotInMutualRecursionCycle
8487
| None, _ | _, None ->
85-
ActiveProcedures.mem {proc_name= callee; specialization}
88+
if ActiveProcedures.mem {proc_name= callee; specialization} then `InMutualRecursionCycle
89+
else `NotInMutualRecursionCycle
8690

8791

8892
let procedure_is_defined proc_name =
@@ -403,66 +407,77 @@ let double_lock_for_restart ~lazy_payloads analysis_req callee_pname specializat
403407

404408
let rec analyze_callee exe_env ~lazy_payloads (analysis_req : AnalysisRequest.t) ~specialization
405409
?caller_summary ?(from_file_analysis = false) callee_pname : _ AnalysisResult.t =
406-
let cycle_detected =
407-
in_mutual_recursion_cycle ~caller_summary ~callee:callee_pname specialization
408-
in
409-
let analysis_result_of_option opt = Result.of_option opt ~error:AnalysisResult.AnalysisFailed in
410-
register_callee ~cycle_detected ?caller_summary callee_pname ;
411-
if cycle_detected then Error MutualRecursionCycle
412-
else if is_in_block_list callee_pname then Error InBlockList
413-
else
414-
let analyze_callee_aux specialization_context =
415-
error_if_ondemand_analysis_during_replay ~from_file_analysis caller_summary callee_pname ;
416-
RestartScheduler.with_lock ~get_actives:ActiveProcedures.get_all callee_pname ~f:(fun () ->
417-
(* the restart scheduler wants to avoid duplicated work, but between checking for a
418-
summary and taking the lock on computing it someone else might have finished computing
419-
the summary we want *)
420-
match double_lock_for_restart ~lazy_payloads analysis_req callee_pname specialization with
421-
| `YesSummary summary ->
422-
Stats.incr_ondemand_double_analysis_prevented () ;
423-
Some summary
424-
| `NoSummary ->
425-
Procdesc.load callee_pname
426-
>>= fun callee_pdesc ->
427-
let previous_global_state = AnalysisGlobalState.save () in
428-
protect
429-
~f:(fun () ->
430-
(* preload tenv to avoid tainting preanalysis timing with IO *)
431-
let tenv = Exe_env.get_proc_tenv exe_env callee_pname in
432-
AnalysisGlobalState.initialize callee_pdesc tenv ;
433-
Timer.time Preanalysis
410+
match detect_mutual_recursion_cycle ~caller_summary ~callee:callee_pname specialization with
411+
| `InMutualRecursionCycle | `ReplayCycleCut ->
412+
register_callee ~cycle_detected:true ?caller_summary callee_pname ;
413+
Error MutualRecursionCycle
414+
| `NotInMutualRecursionCycle -> (
415+
register_callee ~cycle_detected:false ?caller_summary callee_pname ;
416+
let analysis_result_of_option opt =
417+
Result.of_option opt ~error:AnalysisResult.AnalysisFailed
418+
in
419+
if is_in_block_list callee_pname then Error InBlockList
420+
else
421+
let analyze_callee_aux specialization_context =
422+
error_if_ondemand_analysis_during_replay ~from_file_analysis caller_summary callee_pname ;
423+
RestartScheduler.with_lock ~get_actives:ActiveProcedures.get_all callee_pname
424+
~f:(fun () ->
425+
(* the restart scheduler wants to avoid duplicated work, but between checking for a
426+
summary and taking the lock on computing it someone else might have finished computing
427+
the summary we want *)
428+
match
429+
double_lock_for_restart ~lazy_payloads analysis_req callee_pname specialization
430+
with
431+
| `YesSummary summary ->
432+
Stats.incr_ondemand_double_analysis_prevented () ;
433+
Some summary
434+
| `NoSummary ->
435+
Procdesc.load callee_pname
436+
>>= fun callee_pdesc ->
437+
let previous_global_state = AnalysisGlobalState.save () in
438+
protect
434439
~f:(fun () ->
435-
let caller_pname = caller_summary >>| fun summ -> summ.Summary.proc_name in
436-
let summary =
437-
run_proc_analysis exe_env tenv analysis_req specialization_context
438-
?caller_pname callee_pdesc
439-
in
440-
set_complete_result analysis_req summary ;
441-
Some summary )
442-
~on_timeout:(fun span ->
443-
L.debug Analysis Quiet
444-
"TIMEOUT after %fs of CPU time analyzing %a:%a, outside of any checkers \
445-
(pre-analysis timeout?)@\n"
446-
span SourceFile.pp (Procdesc.get_attributes callee_pdesc).translation_unit
447-
Procname.pp callee_pname ;
448-
None ) )
449-
~finally:(fun () -> AnalysisGlobalState.restore previous_global_state) )
450-
in
451-
match is_summary_already_computed ~lazy_payloads analysis_req callee_pname specialization with
452-
| `SummaryReady summary ->
453-
Ok summary
454-
| `ComputeDefaultSummary ->
455-
analyze_callee_aux None |> analysis_result_of_option
456-
| `ComputeDefaultSummaryThenSpecialize specialization ->
457-
(* recursive call so that we detect mutual recursion on the unspecialized summary *)
458-
analyze_callee exe_env ~lazy_payloads analysis_req ~specialization:None ?caller_summary
459-
~from_file_analysis callee_pname
460-
|> Result.bind ~f:(fun summary ->
461-
analyze_callee_aux (Some (summary, specialization)) |> analysis_result_of_option )
462-
| `AddNewSpecialization (summary, specialization) ->
463-
analyze_callee_aux (Some (summary, specialization)) |> analysis_result_of_option
464-
| `UnknownProcedure ->
465-
Error UnknownProcedure
440+
(* preload tenv to avoid tainting preanalysis timing with IO *)
441+
let tenv = Exe_env.get_proc_tenv exe_env callee_pname in
442+
AnalysisGlobalState.initialize callee_pdesc tenv ;
443+
Timer.time Preanalysis
444+
~f:(fun () ->
445+
let caller_pname =
446+
caller_summary >>| fun summ -> summ.Summary.proc_name
447+
in
448+
let summary =
449+
run_proc_analysis exe_env tenv analysis_req specialization_context
450+
?caller_pname callee_pdesc
451+
in
452+
set_complete_result analysis_req summary ;
453+
Some summary )
454+
~on_timeout:(fun span ->
455+
L.debug Analysis Quiet
456+
"TIMEOUT after %fs of CPU time analyzing %a:%a, outside of any \
457+
checkers (pre-analysis timeout?)@\n"
458+
span SourceFile.pp
459+
(Procdesc.get_attributes callee_pdesc).translation_unit Procname.pp
460+
callee_pname ;
461+
None ) )
462+
~finally:(fun () -> AnalysisGlobalState.restore previous_global_state) )
463+
in
464+
match
465+
is_summary_already_computed ~lazy_payloads analysis_req callee_pname specialization
466+
with
467+
| `SummaryReady summary ->
468+
Ok summary
469+
| `ComputeDefaultSummary ->
470+
analyze_callee_aux None |> analysis_result_of_option
471+
| `ComputeDefaultSummaryThenSpecialize specialization ->
472+
(* recursive call so that we detect mutual recursion on the unspecialized summary *)
473+
analyze_callee exe_env ~lazy_payloads analysis_req ~specialization:None ?caller_summary
474+
~from_file_analysis callee_pname
475+
|> Result.bind ~f:(fun summary ->
476+
analyze_callee_aux (Some (summary, specialization)) |> analysis_result_of_option )
477+
| `AddNewSpecialization (summary, specialization) ->
478+
analyze_callee_aux (Some (summary, specialization)) |> analysis_result_of_option
479+
| `UnknownProcedure ->
480+
Error UnknownProcedure )
466481

467482

468483
let analyze_proc_name exe_env analysis_req ?specialization ~caller_summary callee_pname =

0 commit comments

Comments
 (0)