123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141openTrace_coreletspf=Printf.sprintfletfpf=Printf.fprintftypeoutput=[`Fileofstring](** Env variable used to communicate to subprocesses, which trace ID to use *)letenv_var_trace_id="TRACE_TEF_TLDR_TRACE_ID"(** Env variable used to communicate to subprocesses, which trace ID to use *)letenv_var_unix_socket="TRACE_TEF_TLDR_SOCKET"letget_unix_socket()=matchSys.getenv_optenv_var_unix_socketwith|Somes->s|None->lets="/tmp/tldrs.socket"in(* children must agree on the socket file *)Unix.putenvenv_var_unix_sockets;stypeas_client={trace_id:string;socket:string;(** Unix socket address *)emit_tef_at_exit:stringoption;(** For parent, ask daemon to emit traces here *)}typerole=as_clientoptionletto_hex(s:string):string=letopenStringinleti_to_hex(i:int)=ifi<10thenChar.chr(i+Char.code'0')elseChar.chr(i-10+Char.code'a')inletres=Bytes.create(2*lengths)infori=0tolengths-1doletn=Char.code(getsi)inBytes.setres(2*i)(i_to_hex((nland0xf0)lsr4));Bytes.setres((2*i)+1)(i_to_hex(nland0x0f))done;Bytes.unsafe_to_stringresletcreate_trace_id():string=letnow=Unix.gettimeofday()inletrand=Random.State.make_self_init()inletrand_bytes=Bytes.create16infori=0toBytes.lengthrand_bytes-1doBytes.setrand_bytesi(Random.State.intrand256|>Char.chr)done;(* convert to hex *)spf"tr-%d-%s"(int_of_floatnow)(to_hex@@Bytes.unsafe_to_stringrand_bytes)(** Find what this particular process has to do wrt tracing *)letfind_role~out():role=matchSys.getenv_optenv_var_trace_idwith|Sometrace_id->Some{trace_id;emit_tef_at_exit=None;socket=get_unix_socket()}|None->letwrite_to_filepath=(* normalize path so the daemon knows what we're talking about *)letpath=ifFilename.is_relativepaththenFilename.concat(Unix.getcwd())pathelsepathinlettrace_id=create_trace_id()inUnix.putenvenv_var_trace_idtrace_id;{trace_id;emit_tef_at_exit=Somepath;socket=get_unix_socket()}in(matchoutwith|`Filepath->Some(write_to_filepath)|`Env->(matchSys.getenv_opt"TRACE"with|Some("1"|"true")->Some(write_to_file"trace.json")|Somepath->Some(write_to_filepath)|None->None))letcollector_(client:as_client):Collector.t=(* connect to unix socket *)letsock=Unix.socketUnix.PF_UNIXUnix.SOCK_STREAM0in(tryUnix.connectsock(Unix.ADDR_UNIXclient.socket)withexn->failwith@@spf"Could not open socket to `tldrs` demon at %S: %s"client.socket(Printexc.to_stringexn));letout=Unix.out_channel_of_descrsockin(* what to do when the collector shuts down *)letfinally()=(tryflushoutwith_->());tryUnix.closesockwith_->()infpfout"OPEN %s\n%!"client.trace_id;(* ask the collector to emit the trace in a user-chosen file, perhaps *)Option.iter(funfile->fpfout"EMIT_TEF_AT_EXIT %s\n"file)client.emit_tef_at_exit;Trace_tef.Private_.collector_jsonl~finally~out:(`Outputout)()letcollector~out():collector=letrole=find_role~out()inmatchrolewith|None->assertfalse|Somec->collector_copenstructletregister_atexit=lethas_registered=reffalseinfun()->ifnot!has_registeredthen(has_registered:=true;at_exitTrace_core.shutdown)endletsetup?(out=`Env)()=letrole=find_role~out()inmatchrolewith|None->()|Somec->register_atexit();Trace_core.setup_collector@@collector_cletwith_setup?out()f=setup?out();Fun.protect~finally:Trace_core.shutdownfmodulePrivate_=structincludeTrace_tef.Private_end