123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462(* Copyright (C) 2014, Thomas Leonard *)(* Note: we expect some kind of logger to process the trace buffer to collect
events, but currently we don't have any barriers to ensure that the buffer
is in a consistent state (although it usually is). So for now, you should
pause tracing before trying to parse the buffer. In particular, GC events
complicate things because we may need to add a GC event while in the middle
of adding some other event. *)openBigarraymoduleBS=struct(* Replacement for endianBigstring that avoids pulling in a Unix dependency *)externalset_64:Cstruct.buffer->int->int64->unit="%caml_bigstring_set64"externalswap64:int64->int64="%bswap_int64"externalunsafe_chr:int->char="%identity"letset_int8soffv=Array1.setsoff(unsafe_chrv)[@@ocaml.inline]letset_int64_lesoffv=ifSys.big_endianthenset_64soff(swap64v)elseset_64soffv[@@ocaml.inline]endtypeid=intletlast_id=ref0letmint_id()=incrlast_id;!last_idtypehiatus_reason=|Wait_for_work|Suspend|Hibernatetypeevent=|Wait|Task|Bind|Try|Choose|Pick|Join|Map|Condition|On_success|On_failure|On_termination|On_any|Ignore_result|Async|Promise|Semaphore|Switch|Stream|Mutextypelog_buffer=(char,int8_unsigned_elt,c_layout)Array1.tletcurrent_thread=ref(-1)letint_of_thread_typet=matchtwith|Wait->0|Task->1|Bind->2|Try->3|Choose->4|Pick->5|Join->6|Map->7|Condition->8|On_success->9|On_failure->10|On_termination->11|On_any->12|Ignore_result->13|Async->14|Promise->15|Semaphore->16|Switch->17|Stream->18|Mutex->19modulePacket=structletmagic=0xc1fc1fc1lletuuid="\x05\x88\x3b\x8d\x52\x1a\x48\x7b\xb3\x97\x45\x6a\xb1\x50\x68\x0c"(*
[%%cstruct
type packet_header = {
(* Stream header, repeated for each packet *)
magic: uint32_t;
uuid: uint8_t [@len 16];
(* Packet header *)
size: uint32_t;
stream_packet_count: uint16_t;
content_size_low: uint16_t; (* 2x16 bit to avoid allocating an Int32 *)
content_size_high: uint16_t;
} [@@little_endian]
]
*)(* Auto-generated code from the above (to avoid a dependency on ppxlib) *)letsizeof_packet_header=30letset_packet_header_magicvx=Cstruct.LE.set_uint32v0xletset_packet_header_uuidsrcsrcoffdst=Cstruct.blit_from_stringsrcsrcoffdst416letset_packet_header_sizevx=Cstruct.LE.set_uint32v20xletset_packet_header_stream_packet_countvx=Cstruct.LE.set_uint16v24xletset_packet_header_content_size_lowvx=Cstruct.LE.set_uint16v26xletset_packet_header_content_size_highvx=Cstruct.LE.set_uint16v28x(* End auto-generated code *)typet={packet_start:int;header:Cstruct.t;packet_end:int;}letfirst_eventpacket=packet.packet_start+sizeof_packet_headerletpacket_endpacket=packet.packet_endletset_content_endpacketcontent_end=letheader=packet.headerinletbits=(content_end-packet.packet_start)*8inset_packet_header_content_size_lowheader(bitsland0xffff);set_packet_header_content_size_highheader(bitslsr16)letclear~countpacket=letbits=sizeof_packet_header*8inletheader=packet.headerinset_packet_header_stream_packet_countheader(countland0xffff);set_packet_header_content_size_lowheader(bitsland0xffff);set_packet_header_content_size_highheader(bitslsr16)letmake~count~off~lenbuffer=letheader=Cstruct.of_bigarray~off~len:sizeof_packet_headerbufferinset_packet_header_magicheadermagic;set_packet_header_uuiduuid0header;set_packet_header_sizeheader(Int32.of_int(len*8));letpacket={packet_start=off;header;packet_end=off+len;}inclear~countpacket;packetendmoduleControl=struct(* Following LTT, our trace buffer is divided into a small number of
* fixed-sized "packets", each of which contains many events. When there
* isn't room in the current packet for the next event, we move to the next
* packet. This wastes a few bytes at the end of each packet, but it allows
* us to discard whole packets at a time when we need to overwrite something.
*)typet={log:log_buffer;timestamper:log_buffer->int->unit;(* Write a timestamp at the given offset. *)mutablenext_event:int;(* Index to write next event (always < packet_end) *)mutablepacket_end:int;packets:Packet.tarray;mutableactive_packet:int;(* Each packet is numbered, making it easy to get the order when reading the
* ring buffer and allowing for detection of missed packets. *)mutablenext_stream_packet_count:int;}letevent_log=refNoneletstoplog=match!event_logwith|Someactivewhenlog==active->event_log:=None|_->failwith"Log is not currently tracing!"letop_creates=0(* let op_read = 1 *)letop_fulfills=2letop_fails=3(* let op_becomes = 4 *)letop_label=5letop_increase=6letop_switch=7(* let op_gc = 8 *)(* let op_old_signal = 9 *)letop_try_read=10letop_counter_value=11letop_read_later=12letop_signal=13letwrite64logvi=BS.set_int64_lelogiv;i+8letwrite8logvi=BS.set_int8logiv;i+1letwrite_stringlogvi=letl=String.lengthvinforidx=0tol-1doArray1.setlog(i+idx)v.[idx]done;Array1.setlog(i+l)'\x00';i+l+1(* The current packet is full. Move to the next one. *)letnext_packetlog=log.active_packet<-(log.active_packet+1)modArray.lengthlog.packets;letpacket=log.packets.(log.active_packet)inlog.packet_end<-Packet.packet_endpacket;log.next_event<-Packet.first_eventpacket;letcount=log.next_stream_packet_countinPacket.clearpacket~count;log.next_stream_packet_count<-count+1letrecadd_eventlogoplen=(* Note: be careful about allocation here, as doing GC will add another event... *)leti=log.next_eventinletnew_i=i+9+lenin(* >= rather than > is slightly wasteful, but avoids next_event overlapping the next packet *)ifnew_i>=log.packet_endthen((* Printf.printf "can't write %d at %d\n%!" (9 + len) i; *)letold_packet=log.packets.(log.active_packet)inassert(i>Packet.first_eventold_packet);next_packetlog;add_eventlogoplen)else((* Printf.printf "writing at %d\n%!" i; *)log.next_event<-new_i;Packet.set_content_endlog.packets.(log.active_packet)new_i;log.timestamperlog.logi;i+8|>write8log.logop)(* This is faster than [let end_event = ignore]! *)externalend_event:int->unit="%ignore"(*
let end_event i =
match !event_log with
| None -> assert false
| Some log -> assert (i = log.next_event || log.next_event = 0)
*)letwrite_tidlogtid=write64log(Int64.of_inttid)letnote_createdlogchildthread_type=add_eventlogop_creates17|>write_tidlog.log!current_thread|>write_tidlog.logchild|>write8log.log(int_of_thread_typethread_type)|>end_eventletnote_readlog~readerinput=add_eventlogop_read_later16|>write_tidlog.logreader|>write_tidlog.loginput|>end_eventletnote_try_readlogthreadinput=add_eventlogop_try_read16|>write_tidlog.logthread|>write_tidlog.loginput|>end_eventletnote_signal~srclogdst=add_eventlogop_signal16|>write_tidlog.logdst|>write_tidlog.logsrc|>end_eventletnote_resolvedlogp~ex=matchexwith|Someex->letmsg=Printexc.to_stringexinadd_eventlogop_fails(17+String.lengthmsg)|>write_tidlog.log!current_thread|>write_tidlog.logp|>write_stringlog.logmsg|>end_event|None->add_eventlogop_fulfills16|>write_tidlog.log!current_thread|>write_tidlog.logp|>end_event(*
let note_becomes log input main =
if main <> input then (
add_event log op_becomes 16
|> write64 log.log input
|> write64 log.log main
|> end_event
)
*)letnote_labellogthreadmsg=add_eventlogop_label(9+String.lengthmsg)|>write_tidlog.logthread|>write_stringlog.logmsg|>end_eventletnote_increaselogcounteramount=add_eventlogop_increase(17+String.lengthcounter)|>write_tidlog.log!current_thread|>write64log.log(Int64.of_intamount)|>write_stringlog.logcounter|>end_eventletnote_counter_valuelogcountervalue=add_eventlogop_counter_value(17+String.lengthcounter)|>write_tidlog.log!current_thread|>write64log.log(Int64.of_intvalue)|>write_stringlog.logcounter|>end_eventletnote_switchlognew_current=ifnew_current<>!current_threadthen(current_thread:=new_current;add_eventlogop_switch8|>write_tidlog.lognew_current|>end_event)letnote_suspendlog()=current_thread:=(-1);add_eventlogop_switch8|>write_tidlog.log(-1)|>end_event(*
let note_gc duration =
match !event_log with
| None -> ()
| Some log ->
add_event log op_gc 8
|> write64 log.log (duration *. 1000000000. |> Int64.of_float)
|> end_event
*)letmake~timestamperlog=letsize=Array1.dimloginletn_packets=4inletpacket_size=size/n_packetsinletpackets=Array.initn_packets(funi->letoff=i*packet_sizeinletlen=ifi=n_packets-1thensize-offelsepacket_sizeinPacket.make~count:i~off~lenlog)inletactive_packet=0in{log;timestamper;packets;active_packet;packet_end=Packet.packet_endpackets.(active_packet);next_event=Packet.first_eventpackets.(active_packet);next_stream_packet_count=1;}letstart(log:t)=event_log:=Somelog;current_thread:=-1endletlabelname=match!Control.event_logwith|None->()|Somelog->Control.note_labellog!current_threadnameletnote_fork()=letchild=mint_id()inbeginmatch!Control.event_logwith|None->()|Somelog->Control.note_createdlogchildTaskend;childletnote_created?labelidty=match!Control.event_logwith|None->()|Somelog->Control.note_createdlogidty;Option.iter(Control.note_labellogid)labelletnote_switchnew_current=match!Control.event_logwith|None->()|Somelog->Control.note_switchlognew_currentletnote_hiatus_reason=match!Control.event_logwith|None->()|Somelog->Control.note_suspendlog()letnote_resumenew_current=match!Control.event_logwith|None->()|Somelog->Control.note_switchlognew_currentletnote_try_readinput=match!Control.event_logwith|None->()|Somelog->Control.note_try_readlog!current_threadinputletnote_read?readerinput=match!Control.event_logwith|None->()|Somelog->letreader=matchreaderwith|None->!current_thread|Somer->rinControl.note_readlog~readerinputletnote_resolvedid~ex=match!Control.event_logwith|None->()|Somelog->Control.note_resolvedlogid~exletnote_signal?srcdst=match!Control.event_logwith|None->()|Somelog->letsrc=matchsrcwith|None->!current_thread|Somex->xinControl.note_signal~srclogdstletnote_increasecounteramount=match!Control.event_logwith|None->()|Somelog->Control.note_increaselogcounteramountletnote_counter_valuecountervalue=match!Control.event_logwith|None->()|Somelog->Control.note_counter_valuelogcountervalueletshould_resolvethread=match!Control.event_logwith|None->()|Somelog->Control.note_labellogthread"__should_resolve"(* Hack! *)