123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498(* elpi: embedded lambda prolog interpreter *)(* copyright: 2014 - 2017 Enrico Tassi <enrico.tassi@inria.fr> *)(* license: GNU Lesser General Public License Version 2.1 or later *)(* ------------------------------------------------------------------------- *)moduleF=FormatmoduleIntMap=Map.Make(structtypet=intletcomparexy=x-yend)moduleStrMap=Map.Make(String)moduleStr=Re.Strletdebug=reffalseletwhere_loc=ref("",0,max_int)letcur_step=refIntMap.emptyletfilter=ref[]letfonly=ref[]letponly=ref[]lethot=reffalseletcollect_perf=reffalselettrace_noprint=reffalseletcur_pred=refNonetypemessage_kind=Start|Stopof{cause:string;time:float}|Infotypej=J:(F.formatter->'a->unit)*'a->jtypemessage={runtime_id:int;goal_id:int;kind:message_kind;name:string;step:int;payload:jlist;}letprinter:(message->unit)ref=ref(fun_->assertfalse)modulePerf=structtypeperf_frame={name:string;self:float;progeny:perf_frameStrMap.t;}letperf_stack=ref[{name="main";self=0.0;progeny=StrMap.empty}]letcollect_perf_entern=if!collect_perfthenmatch!perf_stackwith|{progeny;_}::_whenStrMap.memnprogeny->perf_stack:=StrMap.findnprogeny::!perf_stack|_->perf_stack:={name=n;self=0.0;progeny=StrMap.empty}::!perf_stackletrecmergem1m2=StrMap.fold(fun_({name;self;progeny}asv)m->trylet{self=t;progeny=p;_}=StrMap.findnameminStrMap.addname{name;self=self+.t;progeny=mergeprogenyp}mwithNot_found->StrMap.addnamevm)m1m2letcollect_perf_exittime=if!collect_perfthenmatch!perf_stackwith|{name=n1;_}astop::({name=n2;_}asprev)::restwhenn1=n2->perf_stack:={name=n2;self=prev.self;progeny=mergetop.progenyprev.progeny}::rest|top::({progeny;_}asprev)::rest->lettop={topwithself=top.self+.time}inperf_stack:={prevwithprogeny=StrMap.addtop.nametopprogeny}::rest|_->assertfalseletrecprint_treefmthot{name;self;progeny}indent=lettprogeny,(phot,thot)=StrMap.fold(funn{self;_}(x,(_,mastop))->x+.self,(ifself>mthen(n,self)elsetop))progeny(0.0,("",0.0))inletphot=ifthot*.2.0>tprogeny&&StrMap.cardinalprogeny>1&&indent<6thenphotelse""inF.fprintffmt"%s- %-20s %s %6.3f %6.3f %s\n"String.(makeindent' ')nameString.(make(max0(20-indent))' ')self(self-.tprogeny)(ifname=hotthen"!"else"");StrMap.iter(fun_t->print_treefmtphott(indent+2))progenyletprint_perf()=whileList.length!perf_stack>1docollect_perf_exit0.0;done;letstack=match!perf_stackwith|[{progeny;_}]->progeny|_->assertfalseinletpayloadfmt=F.fprintffmt" %-20s %s %6s %6s\n""name"String.(make20' ')"total""self";F.fprintffmt"%s\n"(String.make80'-');StrMap.iter(fun_t->print_treefmt"run"t0)stack;F.pp_print_flushfmt()in!printer{runtime_id=0;kind=Info;goal_id=0;name="perf";step=0;payload=[J((funfmt()->payloadfmt),())]}let()=at_exit(fun()->if!collect_perfthenprint_perf())endmoduleTrace=structletget_cur_step~runtime_idk=tryletm=IntMap.findruntime_id!cur_stepintryStrMap.findkmwithNot_found->tryStrMap.find"run"mwithNot_found->0withNot_found->0letcondition~runtime_idk=(* -trace-on *)!debug&&(* -trace-at *)letloc,first_step,last_step=!where_locin((!hot&&k<>loc)||(k=loc&&letcur_step=get_cur_step~runtime_idkinhot:=cur_step>=first_step&&cur_step<=last_step;!hot)||(get_cur_step~runtime_id:0"run"=0&&first_step=0&&k="user:newgoal"))(* -trace-only *)&&(!fonly=[]||List.exists(funp->Str.string_matchpk0)!fonly)(* -trace-skip *)&¬(List.exists(funp->Str.string_matchpk0)!filter)(* -trace-only-pred *)&&(match!cur_predwith|None->true|Somepred->!ponly=[]||List.exists(funp->Str.string_matchppred0)!ponly)letinit?(where="",0,max_int)?(skip=[])?(only=[])?(only_pred=[])b=cur_step:=IntMap.empty;debug:=b;filter:=List.mapStr.regexpskip;fonly:=List.mapStr.regexponly;ponly:=List.mapStr.regexponly_pred;where_loc:=where;hot:=false;;;letincr_cur_step~runtime_idk=letn=get_cur_step~runtime_idkinletn=n+1intryletm=IntMap.findruntime_id!cur_stepinletm=StrMap.addknmincur_step:=IntMap.addruntime_idm!cur_stepwithNot_found->letm=StrMap.emptyinletm=StrMap.addknmincur_step:=IntMap.addruntime_idm!cur_stependletincr_cur_step=Trace.incr_cur_stepletenter~runtime_idkpayload=Trace.incr_cur_step~runtime_idk;ifTrace.condition~runtime_idkthenbeginPerf.collect_perf_enterk;ifnot!trace_noprintthen!printer{runtime_id;goal_id=0;name=k;step=Trace.get_cur_step~runtime_idk;kind=Start;payload=[J((funfmt()->payloadfmt),())]}endletinfo~runtime_id?(goal_id=0)kpayload=ifnot!trace_noprint&&Trace.condition~runtime_idkthen!printer{runtime_id;goal_id;name=k;step=Trace.get_cur_step~runtime_idk;kind=Info;payload}exceptionTREC_CALLofObj.t*Obj.t(* ('a -> 'b) * 'a *)exceptionOKletpr_exc=function|OK->"ok"|e->"error: "^Printexc.to_stringeletexit~runtime_idktailcalletime=lete=matchewithNone->OK|Somex->xinifTrace.condition~runtime_idkthenbeginPerf.collect_perf_exittime;ifnot!trace_noprintthen!printer{runtime_id;goal_id=0;name=k;step=Trace.get_cur_step~runtime_idk;kind=Stop{cause=(iftailcallthen"->"elsepr_exce);time};payload=[J((fun__->()),())]}end(* Json *)letpp_sfmts=Format.fprintffmt"%S"sletpp_ifmti=Format.fprintffmt"%d"iletpp_ffmtf=Format.fprintffmt"%f"fletpp_kvfmt=function|k,J(pp_v,v)->F.fprintffmt"%a : %a"pp_skpp_vvletpp_jfmt=function|J(pp,x)->ppfmtxletrecpp_comma_lfmtpp=function|[]->()|x::xs->F.fprintffmt",";ppfmtx;pp_comma_lfmtppxsletpp_afmt(l:jlist)=F.fprintffmt"[";beginmatchlwith|[]->()|x::l->pp_jfmtx;pp_comma_lfmtpp_jlend;F.fprintffmt"]"moduleJSON_STRING_ENCODING=struct(* This code is from Yojson *)lethexn=Char.chr(ifn<10thenn+48elsen+87)letwrite_specialsrcstartstopobstr=Buffer.add_substringobsrc!start(stop-!start);Buffer.add_stringobstr;start:=stop+1letwrite_control_charsrcstartstopobc=Buffer.add_substringobsrc!start(stop-!start);Buffer.add_stringob"\\u00";Buffer.add_charob(hex(Char.codeclsr4));Buffer.add_charob(hex(Char.codecland0xf));start:=stop+1letfinish_stringsrcstartob=tryBuffer.add_substringobsrc!start(String.lengthsrc-!start)withexc->Printf.eprintf"src=%S start=%i len=%i\n%!"src!start(String.lengthsrc-!start);raiseexcletwrite_string_bodyobs=letstart=ref0infori=0toString.lengths-1domatchs.[i]with'"'->write_specialsstartiob"\\\""|'\\'->write_specialsstartiob"\\\\"|'\b'->write_specialsstartiob"\\b"|'\012'->write_specialsstartiob"\\f"|'\n'->write_specialsstartiob"\\n"|'\r'->write_specialsstartiob"\\r"|'\t'->write_specialsstartiob"\\t"|'\x00'..'\x1F'|'\x7F'asc->write_control_charsstartiobc|_->()done;finish_stringsstartobendletpp_asfmt(l:jlist)=letpp_jfmtx=lets=F.asprintf"%a"pp_jxinletb=Buffer.create64inJSON_STRING_ENCODING.write_string_bodybs;F.fprintffmt"\"%s\""(Buffer.contentsb)inF.fprintffmt"[";beginmatchlwith|[]->()|x::l->pp_jfmtx;pp_comma_lfmtpp_jlend;F.fprintffmt"]"letpp_dfmt(l:(string*j)list)=F.fprintffmt"{";beginmatchlwith|[]->()|x::l->pp_kvfmtx;pp_comma_lfmtpp_kvlend;F.fprintffmt"}"letpp_kindfmt=function|Start->pp_afmt[J(pp_s,"Start")]|Info->pp_afmt[J(pp_s,"Info")]|Stop{cause;time}->pp_afmt[J(pp_s,"Stop");J(pp_s,cause);J(pp_f,time)]letprint_jsonfmt=();fun{runtime_id;goal_id;kind;name;step;payload}->pp_dfmt["step",J(pp_i,step);"kind",J(pp_kind,kind);"goal_id",J(pp_i,goal_id);"runtime_id",J(pp_i,runtime_id);"name",J(pp_s,name);"payload",J(pp_as,payload)];F.pp_print_newlinefmt();F.pp_print_flushfmt()(* TTY *)lettty_formatter_maxcols=ref80lettty_formatter_maxbox=refmax_intletset_tty_formatter_maxcolsi=tty_formatter_maxcols:=iletset_tty_formatter_maxboxi=tty_formatter_maxbox:=iletpplistppelemfl=F.fprintff"@[<v>";List.iter(funx->F.fprintff"%a%s@,"ppelemx" ")l;F.fprintff"@]";;letprint_ttyfmt=();fun{runtime_id;goal_id;kind;name;step;payload}->matchkindwith|Start->F.fprintffmt"%s %d {{{@[<hov1> %a@]\n%!"namestep(pplistpp_j)payload|Stop{cause;time}->F.fprintffmt"}}} %s (%.3fs)\n%!"causetime|Info->F.fprintffmt" rid:%d step:%d gid:%d %s =@[<hov1> %a@]\n%!"runtime_idstepgoal_idname(pplistpp_j)payloadlet()=printer:=print_ttyF.err_formattertypetrace_format=TTY|JSONletset_trace_outputformatformatter=matchformatwith|TTY->F.pp_set_max_boxesformatter!tty_formatter_maxbox;F.pp_set_marginformatter!tty_formatter_maxcols;printer:=print_ttyformatter|JSON->printer:=print_jsonformatterletoutput_file=refNoneletend_trace~runtime_id=ifruntime_id=0thenmatch!output_filewith|None->()|Some(`Socketi)->Unix.closei|Some(`File(tmp,final))->trySys.renametmpfinalwith_->tryletic=open_intmpinletoc=open_outfinalintry(* fallback on copy *)whiletruedooutput_byteoc(input_byteic);donewith|End_of_file->close_outoc;close_inicwithe->Printf.eprintf"Cannot move nor copy %s to %s: %s\n"tmpfinal(Printexc.to_stringe);Stdlib.exit1letfmt_of_files=letof_socket~host~port=letopenUnixinmatchgetaddrinfohostport[AI_FAMILYPF_INET;AI_SOCKTYPESOCK_STREAM]with|[]->raiseNot_found|{ai_family;ai_socktype;ai_protocol;ai_addr;_}::_->lets=socketai_familyai_socktypeai_protocolinUnix.connectsai_addr;output_file:=Some(`Sockets);F.formatter_of_out_channel(Unix.out_channel_of_descrs)inletof_file~path:s=letfile=sinlettmp_file=s^".tmp"inoutput_file:=Some(`File(tmp_file,file));F.formatter_of_out_channel(open_outtmp_file)intryifs="stdout"thenF.std_formatterelseifs="stderr"thenF.err_formatterelseifs.[0]='/'||s.[0]='.'thenbeginof_file~path:sendelseletn=String.indexs':'inletprotocol,rest=String.subs0n,String.subs(n+1)(String.lengths-n-1)inifprotocol="file"thenletrest=String.subrest2(String.lengthrest-2)in(* kill // *)of_file~path:restelseifprotocol="tcp"thenletrest=String.subrest2(String.lengthrest-2)in(* kill // *)letn=String.indexrest':'inlethost,port=String.subrest0n,String.subrest(n+1)(String.lengthrest-n-1)inof_socket~host~portelseof_socket~host:protocol~port:restwithe->Printf.eprintf"error: %s\n"(Printexc.to_stringe);F.err_formatterletset_trace_output_fileformatfile=letformatter=fmt_of_filefileinset_trace_outputformatformatter(* we should make another file... *)letcollecting_stats=reffalseletlogs=ref[]letlog~runtime_idnamekeyvalue=if!collecting_statsthenlogs:=(name,key,Trace.get_cur_step~runtime_id"run",value)::!logslet()=at_exit(fun()->if!logs!=[]thenbeginList.iter(fun(name,key,step,value)->!printer{kind=Info;name=name;step=step;goal_id=0;runtime_id=0;payload=[J((funfmt()->F.fprintffmt"%s = %d"keyvalue),())]})!logsend)letusage={|
Tracing options:
-trace-at FNAME START STOP print trace between call START
and STOP of function FNAME (FNAME can be omitted, default is run)
-trace-on KIND FILE enable trace printing.
KIND is tty or json (default is tty).
FILE is stdout or stderr (default) or host:port or /path or ./path
or file://path or tcp://host:port
-trace-skip REX ignore trace items matching REX
-trace-only REX trace only items matching REX
-trace-only-pred REX trace only when the current predicate matches REX
-trace-tty-maxbox NUM Format max_boxes set to NUM
-trace-tty-maxcols NUM Format margin set to NUM
-stats-on Collect statistics
-perf-on Disable trace output, but keep perf
Tracing options can be used to debug your programs and the Elpi interpreter.
Tracing points for the user are prefixed with 'user:' while the ones
for the Elpi developer with 'dev:'. A sensible set of options to debug your
programs is: -trace-on -trace-at 1 9999 -trace-only '\(run\|select\|user:\)'
|};;letparse_argvargv=leton=reffalseinletwhere=ref("run",0,0)inletverbose=reffalseinletskip=ref[]inletonly=ref[]inletonly_pred=ref[]inletrecaux=function|[]->[]|"-trace-v"::rest->verbose:=true;auxrest|"-trace-at"::fname::start::stop::rest->ifStr.(string_match(regexp"[0-9]+")fname0)thenbeginwhere:=("run",int_of_stringfname,int_of_stringstart);aux(stop::rest)endelsebeginwhere:=(fname,int_of_stringstart,int_of_stringstop);auxrestend|"-trace-on"::"tty"::file::rest->set_trace_output_fileTTYfile;trace_noprint:=false;on:=true;auxrest|"-trace-on"::"json"::file::rest->set_trace_output_fileJSONfile;trace_noprint:=false;on:=true;auxrest|"-trace-on"::rest->trace_noprint:=false;on:=true;auxrest|"-stats-on"::rest->collecting_stats:=true;auxrest|"-trace-skip"::expr::rest->skip:=expr::!skip;auxrest|"-trace-only"::expr::rest->only:=expr::!only;auxrest|"-trace-only-pred"::pname::rest->only_pred:=pname::!only_pred;auxrest;|"-trace-tty-maxbox"::num::rest->set_tty_formatter_maxbox(int_of_stringnum);auxrest|"-trace-tty-maxcols"::num::rest->set_tty_formatter_maxcols(int_of_stringnum);auxrest|"-perf-on"::rest->collect_perf:=true;on:=true;trace_noprint:=true;auxrest|x::rest->x::auxrestinletrest=auxargvinTrace.init~where:!where~only:!only~only_pred:!only_pred~skip:!skip!on;rest;;letset_cur_predx=cur_pred:=xletget_cur_step~runtime_idx=Trace.get_cur_step~runtime_idx