123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273(************************************************************************)(* * The Rocq Prover / The Rocq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)type'eprofile_state_gen={events:'e;sums:NewProfile.sums;counters:NewProfile.Counters.t;}typeprofile_state=NewProfile.MiniJson.tlistlistprofile_state_genletempty_profstate={events=[];sums=NewProfile.empty_sums;counters=NewProfile.Counters.zero;}(** Partially interpreted control flags.
[ControlTime {duration}] means "Time" where the
partial interpretation has already take [duration].
In [ControlRedirect], [truncate] tells whether we should truncate
the file before printing (ie [false] if not the first phase).
Alternatively we could do [ControlRedirect of out_channel],
but that risks leaking open channels.
[ControlFail {st}] means "Fail" where the partial interpretation
did not fail and produced state [st].
[ControlSucceed {st}] means "Succeed" where the partial
interpretation succeeded and produced state [st].
*)type'statecontrol_entry=|ControlTimeof{duration:System.duration}|ControlInstructionsof{instructions:System.instruction_count}|ControlProfileof{to_file:stringoption;profstate:profile_state}|ControlRedirectof{fname:string;truncate:bool}|ControlTimeoutof{remaining:float}|ControlFailof{st:'state}|ControlSucceedof{st:'state}type'statecontrol_entries='statecontrol_entrylistletcheck_timeout_fn=ifn<=0.thenCErrors.user_errPp.(str"Timeout must be > 0.")letwith_measuremeasureaddfmtflaginitf=letresult=measuref()inletresult=matchresultwith|Ok(v,d)->Ok(v,addinitd)|Error(e,d)->Error(e,addinitd)inbeginmatchresultwith|Ok(v,result)->Some(flagresult,v)|Error(e,_)->Feedback.msg_notice@@fmtresult;Exninfo.iraiseeendletmeasure_profilef()=letstart_cnt=NewProfile.Counters.get()inletevents,sums,v=NewProfile.with_profiling(fun()->tryOk(f())withe->Error(Exninfo.capturee))inletcounters=NewProfile.Counters.(get()-start_cnt)inletprof={events=events;sums;counters}inmatchvwith|Okv->Ok(v,prof)|Errore->Error(e,prof)letadd_profileab={events=ifCList.is_emptyb.eventsthena.eventselseb.events::a.events;sums=NewProfile.sums_uniona.sumsb.sums;counters=NewProfile.Counters.(a.counters+b.counters);}letfmt_profilingcounters(sums:NewProfile.sums)=letopenPpinletsums=CString.Map.bindingssumsinletsums=List.sort(fun(_,(t1,_))(_,(t2,_))->Float.comparet2t1)sumsinletlongest=List.fold_left(funlongest(name,_)->maxlongest(String.lengthname))0sumsinletpr_one(name,(time,cnt))=hov1(strname++str":"++brk(1+longest-String.lengthname,0)++str(Format.asprintf"%a"NewProfile.pptimetime)++pr_comma()++intcnt++str" calls")inv0(NewProfile.Counters.printcounters++spc()++spc()++prlist_with_sepspcpr_onesums)(* Output comma and newline separated events given as a list of nonempty lists.
The last event is not followed by a comma. *)letrecoutput_eventsfmt=function|[]->()|[[last]]->Format.fprintffmt"%a\n"NewProfile.MiniJson.prlast|[]::rest->assertfalse|(current::next)::rest->Format.fprintffmt"%a,\n"NewProfile.MiniJson.prcurrent;matchnextwith|[]->output_eventsfmtrest|_::_->output_eventsfmt(next::rest)letfmt_profileto_filev=let{events;sums;counters}=matchvwith|Ok(_,x)->x|Error(_,x)->xinto_file|>Option.iter(funto_file->letto_file=System.get_output_path(to_file^".json")inletf=open_outto_fileinletfmt=Format.formatter_of_out_channelfinNewProfile.format_headerfmt;output_eventsfmtevents;NewProfile.format_footerfmt;close_outf);fmt_profilingcounterssumsletwith_timeout~timeout:nf=check_timeout_fn;letstart=Unix.gettimeofday()inbeginmatchControl.timeoutnf()with|None->Exninfo.iraise(Exninfo.captureCErrors.Timeout)|Somev->letstop=Unix.gettimeofday()inletremaining=n-.(stop-.start)inifremaining<=0.thenExninfo.iraise(Exninfo.captureCErrors.Timeout)elseSome(ControlTimeout{remaining},v)endletreal_error_loc~cmdloc~eloc=ifLoc.finereloccmdlocthenelocelsecmdloc(* Restoring the state is the caller's responsibility *)letwith_failf:(Loc.toption*Pp.t,'a)result=tryletx=f()inErrorxwith(* Fail Timeout is a common pattern so we need to support it. *)|e->(* The error has to be printed in the failing state *)let_,infoasexn=Exninfo.captureeinifCErrors.is_anomalye&&e!=CErrors.TimeoutthenExninfo.iraiseexn;Ok(Loc.get_locinfo,CErrors.iprintexn)type('st0,'st)with_local_state={with_local_state:'a.'st0->(unit->'a)->'st*'a}lettrivial_state={with_local_state=fun()f->(),f()}letwith_fail~loc~with_local_statest0f=lettransient_st,res=with_local_state.with_local_statest0(fun()->with_failf)inmatchreswith|Errorv->Some(ControlFail{st=transient_st},v)|Ok(eloc,msg)->letloc=if!Flags.test_modethenreal_error_loc~cmdloc:loc~elocelseNoneinifnot!Flags.quiet||!Flags.test_modethenFeedback.msg_notice?locPp.(str"The command has indeed failed with message:"++fnl()++msg);Noneletwith_succeed~with_local_statest0f=lettransient_st,v=with_local_state.with_local_statest0finSome(ControlSucceed{st=transient_st},v)letunder_one_control~loc~with_local_statecontrolf=matchcontrolwith|ControlTime{duration}->with_measureSystem.measure_durationSystem.duration_addSystem.fmt_transaction_result(funduration->ControlTime{duration})durationf|ControlInstructions{instructions}->with_measureSystem.count_instructionsSystem.instruction_count_addSystem.fmt_instructions_result(funinstructions->ControlInstructions{instructions})instructionsf|ControlProfile{to_file;profstate}->with_measuremeasure_profileadd_profile(funv->fmt_profileto_filev)(funprofstate->ControlProfile{to_file;profstate})profstatef|ControlRedirect{fname;truncate}->letv=Topfmt.with_output_to_file~truncatefnamef()inSome(ControlRedirect{fname;truncate=false},v)|ControlTimeout{remaining}->with_timeout~timeout:remainingf|ControlFail{st}->with_fail~loc~with_local_statestf|ControlSucceed{st}->with_succeed~with_local_statestfletrecunder_control~loc~with_local_statecontrols~noopf=matchcontrolswith|[]->[],f()|control::rest->letf()=under_control~loc~with_local_staterest~noopfinmatchunder_one_control~loc~with_local_statecontrolfwith|Some(control,(rest,v))->control::rest,v|None->[],noopletignore_state={with_local_state=fun_f->(),f()}letrecafter_last_phase~loc=function|[]->false|control::rest->(* don't match on [control] before processing [rest]: correctly handle eg [Fail Fail]. *)letrest()=after_last_phase~locrestinmatchunder_one_control~loc~with_local_state:ignore_statecontrolrestwith|None->true|Some(control,noop)->matchcontrolwith|ControlTime{duration}->Feedback.msg_notice@@System.fmt_transaction_result(Ok((),duration));noop|ControlInstructions{instructions}->Feedback.msg_notice@@System.fmt_instructions_result(Ok((),instructions));noop|ControlProfile{to_file;profstate}->Feedback.msg_notice@@fmt_profileto_file(Ok((),profstate));noop|ControlRedirect_->noop|ControlTimeout_->noop|ControlFail_->CErrors.user_errPp.(str"The command has not failed!")|ControlSucceed_->true(** A global default timeout, controlled by option "Set Default Timeout n".
Use "Unset Default Timeout" to deactivate it. *)letdefault_timeout=refNoneletcheck_timeoutn=ifn<=0thenCErrors.user_errPp.(str"Timeout must be > 0.")let()=letopenGoptionsindeclare_int_option{optstage=Summary.Stage.Synterp;optdepr=None;optkey=["Default";"Timeout"];optread=(fun()->!default_timeout);optwrite=(funn->Option.itercheck_timeoutn;default_timeout:=n)}lethas_timeoutctrl=ctrl|>List.exists(function|Vernacexpr.ControlTimeout_->true|_->false)letadd_default_timeoutcontrol=match!default_timeoutwith|None->control|Somen->ifhas_timeoutcontrolthencontrolelseVernacexpr.ControlTimeoutn::controlletfrom_syntax_one:Vernacexpr.control_flag->unitcontrol_entry=function|ControlTime->ControlTime{duration=System.empty_duration}|ControlInstructions->ControlInstructions{instructions=Ok0L}|ControlProfileto_file->ControlProfile{to_file;profstate=empty_profstate}|ControlRedirects->ControlRedirect{fname=s;truncate=true}|ControlTimeouttimeout->(* don't check_timeout here as the error won't be caught by surrounding Fail *)ControlTimeout{remaining=float_of_inttimeout}|ControlFail->ControlFail{st=()}|ControlSucceed->ControlSucceed{st=()}letfrom_syntaxcontrol=List.mapfrom_syntax_one(add_default_timeoutcontrol)