123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702(* Time-stamp: <modified the 03/05/2019 (at 11:22) by Erwan Jahier> *)typelabel=stringtypeuniq_id=stringtypenode_call=label*uniq_idtypewire=labeltyperank=int(* used to display clocks *)typeport=Inofint|Outofint|Clktypelink=node_call*port*wiretypecall_graph=linklisttypeclock=string*Data.v(* name , value *)openEventtypecall_tbl=(node_call,linklist*varlist*varlist*varlist*src_info)Hashtbl.tletcall_tbl:call_tbl=Hashtbl.create0letverbose=reffalseletlog_file="callgraph.log"letlog=open_outlog_file(*********************************************************************************)typecaller_tbl=(node_call,node_call)Hashtbl.tletcaller_tbl:caller_tbl=Hashtbl.create0lettop="top","top"(* the father of all fathers *)let(caller:node_call->node_call)=funnc->tryHashtbl.findcaller_tblncwithNot_found->toplet(caller_update:node_call->node_call->unit)=funcallercalled->Hashtbl.replacecaller_tblcalledcaller(*********************************************************************************)typetag=intmoduleTags=Set.Make(structtypet=tagletcompare=compareend)typetags=Tags.ttypetags_ref=TagofTags.t|Refofnode_call*wire(* the give (unique) names to tags *)typetag_tbl=(node_call*wire,tag)Hashtbl.t(* associer les valeurs et le tags a un wire+le nodecall appelant ? *)(* type tags_tbl = (link, rtags) Hashtbl.t *)typetags_tbl=(node_call*wire,Data.v*tags_ref)Hashtbl.tlettag_cpt=ref0lettag_tbl:tag_tbl=Hashtbl.create0lettags_tbl:tags_tbl=Hashtbl.create0(* The only spot where tags are created are:
- top-level inputs
- local vars
- constants
*)let(make_tag:node_call->wire->unit)=funncwire->tryignore(Hashtbl.findtag_tbl(nc,wire))withNot_found->lettag=!tag_cptinincrtag_cpt;if!verbosethenPrintf.printf"Add (%s,%s) -> %d\n"(sndnc)wiretag;Hashtbl.addtag_tbl(nc,wire)(tag);Hashtbl.addtags_tbl(nc,wire)(Data.U,Tag(Tags.singletontag));()(* also returns the entry in the tags_ref tbl that contains the real tags *)letrec(get_tags:node_call->wire->node_call*wire*Data.v*tags)=funncwire->(matchHashtbl.findtags_tbl(nc,wire)with|v,Tagtags->nc,wire,v,tags|_,Ref(nc2,w2)->assert((nc2,w2)<>(nc,wire));get_tagsnc2w2)(* create a tag if necessary (this first time a tag site is encountered) *)let(get_tags_nf:node_call->wire->node_call*wire*Data.v*tags)=funncwire->tryget_tagsncwirewith_->make_tagncwire;tryget_tagsncwirewithNot_found->assertfalse(* used to make node params reference towards node args *)let(make_tag_ref:node_call->node_call->wire->wire->unit)=funncncfwire_argwire_par->tryignore(Hashtbl.findtag_tbl(nc,wire_par))(* the ref already exist; we are cool *)withNot_found->letncr,wr,_,_=get_tags_nfncfwire_arginlettag_ref=Ref(ncr,wr)inHashtbl.addtag_tbl(nc,wire_par)(-2);Hashtbl.addtags_tbl(nc,wire_par)(Data.U,tag_ref);()let(get_tag:node_call->wire->tag)=funncwire->(* nc is the caller *)try(Hashtbl.findtag_tbl(nc,wire))withNot_found->(* Printf.printf "Cannot find the tag for (%s,%s)\n" (snd nc) wire; *)-1(*********************************************************************************)(* pack list of elt into list of list of size n (at most) *)let(pack:'alist->int->'alistlist)=funln->letrecauxacccptl=matchl,accwith|_,[]->assertfalse|[],_->acc|elt::tail,acc0::acc_tail->ifcpt=0thenaux([elt]::acc)ntailelseaux((elt::acc0)::acc_tail)(cpt-1)tailinaux[[]]nllet(int_list_to_str:intlist->stringlist)=funil->assert(il<>[]);letinter2strlh=ifl=hthenstring_of_intlelsePrintf.sprintf"%d-%d"lhinletrecaux(l,h)accil=matchilwith|[]->(inter2strlh)::acc|x::t->ifx=h+1thenaux(l,x)acctelseaux(x,x)((inter2strlh)::acc)tinletf=List.hdilinaux(f,f)[](List.tlil)let_=assert(int_list_to_str[1;2;3;4;5;7;8;9;11]=(List.rev["1-5";"7-9";"11"]))lettags2strtags=lettagl=Tags.fold(funtagacc->tag::acc)tags[]inlettagl=List.revtaglinlettagstrl=int_list_to_strtaglinlettagstrll=packtagstrl10inletl=List.map(funtagl->String.concat","tagl)tagstrllin"{"^(String.concat",\n"l)^"}"lettags_ref2str=function|Tagtags->tags2strtags|Ref(nc,w)->""letval_to_stringv=matchData.val_to_stringstring_of_floatvwith|"Lustre::true"->"t"|"Lustre::false"->"f"|s->sletd()=Hashtbl.iter(fun((lbl,uid),w)(v,tags)->Printf.printf"(%s,%s),%s: %s (%s)\n"lbluidw(tags_ref2strtags)(val_to_stringv))tags_tbllet(get_link_val:link->Data.v)=fun(nc,_,wire)->let_,_,v,_=get_tags_nf(callernc)wireinv(* raises Not_found the first time it is called in get_link. *)let(tags_of_link:link->tags)=fun(nc,_,wire)->let_,_,_,tags=get_tags_nf(callernc)wireintags(* replace add (vith Tags.union) tags to the tags pointed by (nc, wire) *)let(update_tags:node_call->wire->tags->unit)=funncwiretags->tryletnc,wire,v,ptags=get_tags_nfncwireinlettags=Tags.unionptagstagsinif!verbosethenPrintf.printf"(%s,%s) -> Some tags [update_tags]\n"(sndnc)wire;Hashtbl.replacetags_tbl(nc,wire)(v,Tagtags)withNot_found->make_tagncwire;letv,_=Hashtbl.findtags_tbl(nc,wire)inif!verbosethenPrintf.printf"(%s,%s) -> Some tags [update_tags 1]\n"(sndnc)wire;Hashtbl.replacetags_tbl(nc,wire)(v,Tagtags)let(update_val:node_call->wire->Data.v->unit)=funncf(* meant to be the caller *)wirev->tryletnc,w,_v,tags=get_tags_nfncfwireinif!verbosethenPrintf.printf"(%s,%s) -> Some tags [update_val]\n"(sndnc)wire;Hashtbl.replacetags_tbl(nc,w)(v,Tagtags)with_->make_tagncfwire;letnc,w,_v,tags=get_tags_nfncfwireinif!verbosethenPrintf.printf"(%s,%s) -> Some tags [update_val 1]\n"(sndnc)wire;Hashtbl.replacetags_tbl(nc,w)(v,Tagtags)(*********************************************************************************)openEventexceptionNoSourceInfolet(get_src:Event.t->src_info)=fune->matche.sinfowith|None->raiseNoSourceInfo|Somesi->si()(*********************************************************************************)(* to merge pre.set and pre.get in the same node *)typesrc_info_select=(string*(int*int)*(int*int)*src_info_atomoption)list(* pre.set and pre.get shares this information *)letcpt=ref0letpre_tbl=Hashtbl.create0(* We don't want to create new node instances from one step to
another, hence we tabulate it using src_info as a key, which is
wrong for nodes that are called via meta-operators !!! Indeed, each
node call has the exact same src_info (same line numbers, same
stack, etc.). Arf. *)letnode_tbl=Hashtbl.create0let(get_nodecall:Event.t->node_call)=fune->letname=e.nameinletsi=get_srceinletlabel=try(List.hdsi.atoms).strwith_->nameinmatchnamewith|"Lustre::pre.set"->letkey=List.map(funa->a.file,a.line,a.char,a.stack)si.atomsinletlabel,uniq=(tryHashtbl.findpre_tblkeywithNot_found->(* should only occur when pre.set is the first event*)("pre","pre"))inlabel,uniq|"Lustre::pre.get"->(letkey=List.map(funa->a.file,a.line,a.char,a.stack)si.atomsintryHashtbl.findpre_tblkeywithNot_found->letlabel,uniq="pre",Printf.sprintf"%s_%d""pre"!cptinHashtbl.addpre_tblkey(label,uniq);incrcpt;label,uniq)|_->(letkey=name,List.map(funa->a.file,a.line,a.char,a.stack)si.atomsintryHashtbl.findnode_tblkeywithNot_found->letuniq=Printf.sprintf"%s_%d"name!cptinincrcpt;Hashtbl.addnode_tblkey(label,uniq);label,uniq)(*********************************************************************************)let(get_val:var->Data.substlist->Data.v)=fun(v,_)s->tryList.assocvswithNot_found->failwith("can't find the value of "^v)letlink2str((_,id),port,label)=Printf.sprintf"%s -> %s"idlabel(*********************************************************************************)let(get_links:Event.t->node_call->node_call->clocklist->linklist)=funencfnccclks->(* returns the link corresponding to ncc I/O, and the clock links *)assert(e.kind=Exit);letsi=get_srceinletmake_linkis_inputi(arg,par)=letwire_arg=fstarginlet_wire_par=fstparinletv=get_valpare.datainletlink=ncc,(ifis_inputthenInielseOuti),wire_arginupdate_valncfwire_argv;linkinletin_links=List.mapi(make_linktrue)si.in_substin(* *)letout_links=List.mapi(make_linkfalse)si.out_substin(* *)letclk_links=List.map(fun(c,cval)->ncc,Clk,c)clksinList.iter2(fun(nc,_,w)(_,cval)->update_valncfwcval)clk_linksclks;in_links@out_links@clk_links(*********************************************************************************)letpdf_viewer=ref"xpdf -remote "letget_urlstr=Printf.sprintf"%s.pdf"str(*********************************************************************************)let(gen_dot:varlist->varlist->varlist->Event.src_info->node_call->bool->bool->call_graph->unit)=funinputsoutputslocssincfullviewll->letlbl,uid=ncinletinterface=fst(List.split(inputs@outputs))inletdot=uid^".dot"inletps=uid^".ps"inletpdf=uid^".pdf"inletoc=open_outdotinletdllink=let(node_call,io,wire)=linkinletf,t=sndnode_call,wireinletf,t,shape=matchiowith|In_->t,f,""|Out_->f,t,""|Clk->t,f,"headport=n; arrowhead=dot"inletv=get_link_vallinkinPrintf.fprintfoc"\"%s\" -> \"%s\" [%s label=\"%s\"]\n"ftshape(val_to_stringv)inletlocals,newvars=List.fold_left(fun(loc,nv)(_,_,l)->ifl.[0]='_'thenif(List.memlnv)then(loc,nv)else(loc,l::nv)elseif(List.memlloc)then(loc,nv)else(l::loc,nv))([],[])llinletnodes=List.fold_left(funacc(nc,_,_)->ifList.memncaccthenaccelsenc::acc)[]llinletll1,ll2=List.partition(fun(_,_,label)->List.memlabelinterface)llinlettooltip=""inoutput_stringoc"digraph G {
rankdir=LR;
node [shape = rect];
{\n";letvar_list=ref[]in(* used to collect used vars (useful to know which
array or struct access should be shown) *)letpr_varoptvar=ifnot(List.memvar!var_list)thenvar_list:=var::!var_list;let_,_,v,tags=get_tags_nfncvarinlettag=get_tagncvarinlettagstr=iftag<0then""elsestring_of_inttaginletcolor=ifv=Data.Uthen";color=tomato1; fontcolor=tomato1"else""inPrintf.fprintfoc"\"%s\" [label=\"%s\n%s %s\" %s %s]\n"varvartagstr(tags2strtags)optcolorinList.iter(pr_var"shape=diamond")newvars;List.iter(pr_var"shape=ellipse")locals;List.iter(fun(label,id)->letlinks,_,_,_,_=Hashtbl.findcall_tbl(label,id)iniflinks=[]thenPrintf.fprintfoc"\"%s\" [label=\"%s\" tooltip=\"%s\"]\n"idlabeltooltipelsePrintf.fprintfoc"\"%s\" [label=\"%s\" URL=\"%s\" tooltip=\"%s\"]\n"idlabel(get_urlid)tooltip)nodes;ifll<>[]then(List.iter(pr_var"style=filled fillcolor=lightblue shape=ellipse")(fst(List.splitinputs));List.iter(pr_var"style=filled fillcolor=red shape=ellipse")(fst(List.splitoutputs)););Printf.fprintfoc"}\n subgraph cluster1 { \nlabel=\"%s\"; \n"(List.hdsi.atoms).str;ifll<>[]then(List.iterdlll2;output_stringoc"}\n";List.iterdlll1;)else((* List.iter *)(* (fun (v,_) -> Printf.fprintf oc "\"%s\" [style=filled fillcolor=lightblue]\n" v) *)(* e.inputs; *)(* List.iter *)(* (fun (v,_) -> Printf.fprintf oc "\"%s\" [style=filled fillcolor=red]\n" v) *)(* e.outputs; *)(* output_string oc "}\n"; *)(* List.iter dl_outter oll; *));output_stringoc"}\n";flushoc;close_outoc;letcmd=iffullthenifviewthenPrintf.sprintf"dot %s -Tps2 > %s && ps2pdf %s&& %s %s %s &\n"dotpsps!pdf_viewerpdfpdfelsePrintf.sprintf"dot %s -Tps2 > %s && ps2pdf %s &\n"dotpspselseifviewthenPrintf.sprintf"dot %s -Tpdf > %s&& %s %s %s & \n"dotpdf!pdf_viewerpdfpdfelsePrintf.sprintf"dot %s -Tpdf > %s \n"dotpdfinoutput_stringlogcmd;flushlog;ignore(Sys.commandcmd)(*********************************************************************************)openDataletclk_stack:clocklistref=ref[]letnc_stack:node_calllistref=ref[]letlk_stack:linklistlistref=ref[[]](* previous event number to prevent time travel during callgraph computation *)letpre_enb=ref0(* since pre is split into get and set, we need to store its inputs at set events
to be able to propagate the tags at get events
*)letpre_input_tbl=Hashtbl.create0(* we know at call events if an arrow is at its first step;
therefore we store this info at call(arrow) step and use it at
exit(arrow) step *)letlast_arrow_first=reftrue(* [add_tags l t] add the tags t *)let(add_tags:node_call->linklist->tags->unit)=funncolt->letotags=List.maptags_of_linkolinletotags=List.map(Tags.uniont)otagsinList.iter2(fun(_,_,w)ntag->update_tagsncwntag)olotagsletboolred_dominmaxncfilol=(* there are 3 cases:
(1) | I={i | xi }| < min
in this case we return the intersection of tag_i forall i *not* in I
(2) | I={i | xi }| > max
in this case we return the intersection of tag_i forall i in I
(3) | I={i | xi }| in [min,max]
in this case we return the union of tag_i forall i in I
*)letvals=List.mapget_link_valilinlettags=List.maptags_of_linkilinletn=List.fold_left(funaccv->ifv=Btruethenacc+1elseacc)0valsinlett=ifn<minthen(* case 1 *)List.fold_left2(funacctv->ifv=BfalsethenTags.interacctelseacc)Tags.emptytagsvalselseifn>maxthen(* case 2 *)List.fold_left2(funacctv->ifv=BtruethenTags.interacctelseacc)Tags.emptytagsvalselse(* n=1, case 3 *)List.fold_left2(funacctv->ifv=BtruethenTags.unionacctelseacc)Tags.emptytagsvalsinadd_tagsncfoltletpropagate_tags_predefnccncfeilclol=matche.namewith|"Lustre::if"->(letc,i1,i2=matchilwith[c;i1;i2]->c,i1,i2|_->assertfalseinlettc,t1,t2=tags_of_linkc,tags_of_linki1,tags_of_linki2inmatchget_link_valcwith|Btrue->add_tagsncfol(Tags.uniontct1)|Bfalse->add_tagsncfol(Tags.uniontct2)|U->()|_->assertfalse)|"Lustre::and"->(leti1,i2=matchilwith[i1;i2]->i1,i2|_->assertfalseinlett1,t2=tags_of_linki1,tags_of_linki2inmatchget_link_vali1,get_link_vali2with|Btrue,Btrue->add_tagsncfol(Tags.uniont1t2)|Btrue,Bfalse->add_tagsncfolt2|Bfalse,Btrue->add_tagsncfolt1|Bfalse,Bfalse->add_tagsncfol(Tags.intert1t2)|U,_|_,U->()|_->assertfalse)|"Lustre::or"->(leti1,i2=matchilwith[i1;i2]->i1,i2|_->assertfalseinlett1,t2=tags_of_linki1,tags_of_linki2inmatchget_link_vali1,get_link_vali2with|Btrue,Btrue->add_tagsncfol(Tags.intert1t2)|Btrue,Bfalse->add_tagsncfolt1|Bfalse,Btrue->add_tagsncfolt2|Bfalse,Bfalse->add_tagsncfol(Tags.uniont1t2)|U,_|_,U->()|_->assertfalse)|"Lustre::impl"->(leti1,i2=matchilwith[i1;i2]->i1,i2|_->assertfalseinlett1,t2=tags_of_linki1,tags_of_linki2inmatchget_link_vali1,get_link_vali2with|Btrue,Btrue->add_tagsncfolt1|Btrue,Bfalse->add_tagsncfol(Tags.uniont1t2)|Bfalse,Btrue->add_tagsncfol(Tags.intert1t2)|Bfalse,Bfalse->add_tagsncfolt2|U,_|_,U->()|_->assertfalse)|"Lustre::xor"->(* boolred_do 1 1 ncf il ol *)(* there are 3 cases:
(1) forall i, not(xi)
in this case we take the intersection of all tag_i
(2) | I={i | xi }| >= 2
in this case we take the intersection of all { tag_i | i in I}
(3) exist a unique i st xi (i.e.,forall j<>i, not xj)
in this case we return tag_i
*)letvals=List.mapget_link_valilinlettags=List.maptags_of_linkilinletn=List.fold_left(funaccv->ifv=Btruethenacc+1elseacc)0valsinlett=ifn=0then(* case 1 *)List.fold_left(funacct->Tags.interacct)Tags.emptytagselseifn>1then(* case 2 *)List.fold_left2(funacctv->ifv=BtruethenTags.interacctelseacc)Tags.emptytagsvalselse(* n=1, case 3 *)List.fold_left2(funacctv->ifv=Btruethentelseacc)Tags.emptytagsvalsinadd_tagsncfolt|"Lustre::nor"->boolred_do00ncfilol|"Lustre::current"->assertfalse(* sno *)|"Lustre::diese"->(boolred_do01ncfilol)|"Assign"->(leti=matchilwith[i]->i|_->assertfalseinlett=tags_of_linkiinadd_tagsncfolt)|"Lustre::arrow"->(leti1,i2=matchilwith[i1;i2]->i1,i2|_->assertfalseinlett=if!last_arrow_firstthentags_of_linki1elsetags_of_linki2inadd_tagsncfolt)|"Lustre::pre.set"->((* XXX useless??? *)leti=matchilwith[i1]->i1|_->assertfalseinPrintf.printf"Store the input of %s\n"(sndncc);Hashtbl.replacepre_input_tblncci)|"Lustre::pre.get"->((* XXX useless??? *)tryleti=Hashtbl.findpre_input_tblnccinlett=tags_of_linkiinPrintf.printf"Got the input of %s\n"(sndncc);add_tagsncfoltwithNot_found->())|n->(* by default, we propagate all tags *)if(il=[])thenPrintf.printf"Warning: %s has no input!\n"nelseletitags=List.maptags_of_linkilinletitag=List.fold_leftTags.union(List.hditags)(List.tlitags)inadd_tagsncfolitaglet(propagate_out_tags:node_call->node_call->Event.t->linklist->linklist->clocklist->unit)=funncncfesub_linkslinksclks->letil,ol=List.partition(fun(_,io,_)->matchiowithOut_->false|_->true)linksinletcl,il=List.partition(fun(_,io,_)->matchiowithClk->true|_->false)ilinletclock_tags=List.maptags_of_linkclinletclock_tags=List.fold_left(funacct->Tags.unionacct)Tags.emptyclock_tagsinadd_tagsncfolclock_tags;(* Printf.printf "il=%s\ncl=%s\nol=%s\nsub_links=%s\n" *)(* (String.concat ", \n\t" (List.map link2str il)) *)(* (String.concat ", \n\t" (List.map link2str cl)) *)(* (String.concat ", \n\t" (List.map link2str ol)) *)(* (String.concat ", \n\t" (List.map link2str sub_links)) *)(* ; *)ifsub_links=[]thenpropagate_tags_predefncncfeilclolelse()(* the core of the work *)letrec(update_tagcov:Event.t->unit)=fune->ifnot(e.nb<>!pre_enb||e.nb<>!pre_enb+1)thenfailwith"cannot skip event or move backwards when computing tag coverage";ifnot(e.lang="lustre")thenprint_string"Not a Lustre node\n"else(pre_enb:=e.nb;matche.kind,e.name,!lk_stackwith|Call,"when",_->(letsi=get_srceinletcarg,cpar=List.hdsi.in_substinletclk=(fstcarg),get_valcpare.datainclk_stack:=clk::!clk_stack)|Exit,"when",_->clk_stack:=List.tl!clk_stack|Call,_,lstk->(letnc=get_nodecalleinletcaller=if!nc_stack=[]thentopelseList.hd!nc_stackinletsi=get_srceincaller_updatecallernc;nc_stack:=nc::!nc_stack;lk_stack:=[]::lstk;ife.name="Lustre::arrow"then(letfirst=tryList.assoc"_memory"e.datawithNot_found->assertfalseinlast_arrow_first:=(first=Btrue))else((* lustre::arrow has a local var (_memory), but it is
(currently) not traced and is necessary covered anywayrd *)letvars=ife.depth=2then(e.inputs@e.outputs@e.locals)else(e.locals)in(* let vars = (e.inputs @ e.outputs @ e.locals) in *)letwires=fst(List.splitvars)inList.iter(funw->make_tagncw)wires);List.iter(fun((arg,_),(par,_))->make_tag_refnccallerargpar)si.in_subst;List.iter(fun((arg,_),(par,_))->make_tag_refnccallerargpar)si.out_subst;)|Exit,n,sub_links::links::lstk->(letnc,nc_father=match!nc_stackwith|nc1::nc2::_->nc1,nc2|[nc]->nc,top|[]->assertfalseinletnlinks=get_linksenc_fathernc!clk_stackinletsi=get_srceinpropagate_out_tagsncnc_fatheresub_linksnlinks!clk_stack;Hashtbl.replacecall_tblnc(sub_links,e.inputs,e.outputs,e.locals,si);lk_stack:=(links@nlinks)::lstk;nc_stack:=tryList.tl!nc_stackwith_->assertfalse;)|Ltop,_,_->lk_stack:=[[]];clk_stack:=[];nc_stack:=[]|MicroStep_,_,_->assertfalse|Exit,_,_::[]->assertfalse|Exit,_,[]->assertfalse)let(next:Event.t->Event.t)=fune->lete=RdbgStdLib.nexteinupdate_tagcove;eletrec(nexti:Event.t->int->Event.t)=funei->ifi=0theneelsenexti(nexte)(i-1)letdump_call_tbl()=Hashtbl.iter(fun(label,uniq)(links,_,_,_,_)->iflinks<>[]thenletstrl=List.map(fun((_,id),port,label)->Printf.sprintf"%s %s"idlabel)linksinletstr=String.concat"\n\t"strlinPrintf.printf"'%s-%s' \n\t%s\n"labeluniqstr)call_tbl;;letgen_all_dotevent=Hashtbl.iter(fun(label,uniq)(links,inputs,outputs,locals,si)->iflinks<>[]thenletview=(get_nodecallevent=(label,uniq))ingen_dotinputsoutputslocalssi(label,uniq)trueviewlinks)call_tblletgen_one_dotevent=let(label,uniq)=get_nodecalleventinletlinks,inputs,locals,outputs,si=Hashtbl.findcall_tbl(label,uniq)ingen_dotinputsoutputslocalssi(label,uniq)falsetruelinksletinit()=Hashtbl.clearcall_tbl;Hashtbl.cleartags_tbl;Hashtbl.cleartag_tbl;Hashtbl.clearpre_tbl;cpt:=0;tag_cpt:=0;pre_enb:=0;clk_stack:=[];nc_stack:=[];lk_stack:=[[]](*
add_hook "tagcov" Tagcov.update_tagcov;;
e:= RdbgStdLib.next !e;;
update_tagcov !e;;
let tc () =
update_tagcov !e;
e := next !e; while (!e.kind <> Ltop) do e := next !e done;;
e := next !e;;
let c () = gen_call_graph !e;;
let cf () = gen_call_graph_full !e;;
let d () = display_call_graph !e;;
*)