@@ -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
8892let procedure_is_defined proc_name =
@@ -403,66 +407,77 @@ let double_lock_for_restart ~lazy_payloads analysis_req callee_pname specializat
403407
404408let 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
468483let analyze_proc_name exe_env analysis_req ?specialization ~caller_summary callee_pname =
0 commit comments