123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292(*
* Copyright (c) 2022-2022 Tarides <contact@tarides.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)open!ImportmoduleSteps_timer=structtypeduration=Stats.Latest_gc.duration={wall:float;sys:float;user:float;}typet={timer:duration;prev_stepname:string}letget_wtime()=(Mtime_clock.now()|>Mtime.to_uint64_ns|>Int64.to_float)/.1e9letget_stime()=Rusage.((getSelf).stime)letget_utime()=Rusage.((getSelf).utime)letcreatefirst_stepname=letwall=get_wtime()inletsys=get_stime()inletuser=get_utime()inlettimer={wall;sys;user}in{timer;prev_stepname=first_stepname}letprogressprevnext_stepname=letwall=get_wtime()inletsys=get_stime()inletuser=get_utime()inletnext={wall;sys;user}inletwall=next.wall-.prev.timer.wallinletsys=next.sys-.prev.timer.sysinletuser=next.user-.prev.timer.userinletdelta=(prev.prev_stepname,{wall;sys;user})inletnext={timer=next;prev_stepname=next_stepname}in(next,delta)endmoduleMain=structmoduleS=Stats.Latest_gctypet={stats:S.stats;timer:Steps_timer.t}(** [t] is the running state while computing the stats *)letcreatefirst_stepname~generation~commit_offset~before_suffix_start_offset~before_suffix_end_offset~after_suffix_start_offset=letstats=Irmin.Type.(randomS.stats_t|>unstage)()in(* [repr] provides doesn't provide a generator that fills a type with
zeroes but it provides a random generator. Let's use it for our initial
value. *)letstats=S.{statswithgeneration;steps=[];commit_offset;before_suffix_start_offset;before_suffix_end_offset;after_suffix_start_offset;}inlettimer=Steps_timer.createfirst_stepnamein{stats;timer}letfinish_current_steptnext_stepname=lettimer,prev_step=Steps_timer.progresst.timernext_stepnameinletstats={t.statswithsteps=prev_step::t.stats.steps}in{stats;timer}letfinalisetworker~after_suffix_end_offset=lett=finish_current_stept"will not appear in the stats"in{t.statswithS.worker;after_suffix_end_offset;steps=List.revt.stats.steps;}endmoduleWorker=structmoduleS=Stats.Latest_gctypet={stats:S.worker;current_stepname:string;prev_wtime:float;prev_stime:float;prev_utime:float;prev_rusage:S.rusage;prev_ocaml_gc:S.ocaml_gc;}(** [t] is the running state while computing the stats *)letis_darwin=lazy(trymatchUnix.open_process_in"uname"|>input_linewith|"Darwin"->true|_->falsewithUnix.Unix_error_->false)letget_wtime()=(Mtime_clock.now()|>Mtime.to_uint64_ns|>Int64.to_float)/.1e9letget_stime()=Rusage.((getSelf).stime)letget_utime()=Rusage.((getSelf).utime)letget_rusage:unit->S.rusage=fun()->letRusage.{maxrss;minflt;majflt;inblock;oublock;nvcsw;nivcsw;_}=Rusage.(getSelf)inletmaxrss=ifLazy.forceis_darwinthenInt64.divmaxrss1000LelsemaxrssinS.{maxrss;minflt;majflt;inblock;oublock;nvcsw;nivcsw}letget_ocaml_gc:unit->S.ocaml_gc=fun()->letopenStdlib.Gcinletv=quick_stat()inS.{minor_words=v.minor_words;promoted_words=v.promoted_words;major_words=v.major_words;minor_collections=v.minor_collections;major_collections=v.major_collections;heap_words=v.heap_words;compactions=v.compactions;top_heap_words=v.top_heap_words;stack_size=v.stack_size;}letcreate:string->t=funfirst_stepname->(* Reseting all irmin-pack stats. We'll reset again at every step.
Since the GC worker lives alone in a fork, these global variable mutations
will not interfere with the rest of the world. *)Stats.reset_stats();Irmin_pack.Stats.reset_stats();letwtime=get_wtime()inletstime=get_stime()inletutime=get_utime()inletrusage=get_rusage()inletocaml_gc=get_ocaml_gc()inletstats=S.{initial_maxrss=rusage.maxrss;initial_heap_words=ocaml_gc.heap_words;initial_top_heap_words=ocaml_gc.top_heap_words;initial_stack_size=ocaml_gc.stack_size;steps=[];files=[];objects_traversed=Int63.zero;suffix_transfers=[];}in{stats;current_stepname=first_stepname;prev_utime=utime;prev_wtime=wtime;prev_stime=stime;prev_rusage=rusage;prev_ocaml_gc=ocaml_gc;}letset_objects_traversedtv=letstats={t.statswithobjects_traversed=Int63.of_intv}in{twithstats}letadd_suffix_transfertcount=letstats={t.statswithsuffix_transfers=count::t.stats.suffix_transfers}in{twithstats}letfinish_current_steptnext_stepname=letwtime=get_wtime()inletstime=get_stime()inletutime=get_utime()inletduration=letwall=wtime-.t.prev_wtimeinletsys=stime-.t.prev_stimeinletuser=utime-.t.prev_utimeinS.{wall;sys;user}inletprev_rusage,rusage=letx=t.prev_rusageinlety=get_rusage()inlet(-)=Int64.subin(y,S.{ywithminflt=y.minflt-x.minflt;majflt=y.majflt-x.majflt;inblock=y.inblock-x.inblock;oublock=y.oublock-x.oublock;nvcsw=y.nvcsw-x.nvcsw;nivcsw=y.nivcsw-x.nivcsw;})inletprev_ocaml_gc,ocaml_gc=letx=t.prev_ocaml_gcinlety=get_ocaml_gc()in(y,S.{ywithminor_words=y.minor_words-.x.minor_words;promoted_words=y.promoted_words-.x.promoted_words;major_words=y.major_words-.x.major_words;minor_collections=y.minor_collections-x.minor_collections;major_collections=y.major_collections-x.major_collections;compactions=y.compactions-x.compactions;})in(* [clone] duplicates a value. Used below to snapshot mutable values. *)letclonetypereprv=matchIrmin.Type.to_stringtypereprv|>Irmin.Type.of_stringtypereprwith|Error_->assertfalse|Okv->vinletpack_store=Stats.((get()).pack_store|>Pack_store.export|>clonePack_store.t)inStats.report_index();letindex=Stats.((get()).index|>Index.export|>cloneIndex.t)inletinode=Irmin_pack.Stats.((get()).inode|>Inode.export|>cloneInode.t)inStats.reset_stats();Irmin_pack.Stats.reset_stats();letstep=S.{duration;rusage;ocaml_gc;index;pack_store;inode}in(* The [steps] list is built in reverse order and reversed in [finalise] *)letsteps=(t.current_stepname,step)::t.stats.stepsinletstats={t.statswithsteps}in{current_stepname=next_stepname;stats;prev_wtime=wtime;prev_stime=stime;prev_utime=utime;prev_rusage;prev_ocaml_gc;}letadd_file_sizetfile_namesize=letstats={t.statswithfiles=(file_name,size)::t.stats.files}in{twithstats}letfinalise:t->S.worker=funt->lett=finish_current_stept"will not appear in the stats"in{t.statswithsteps=List.revt.stats.steps;suffix_transfers=List.revt.stats.suffix_transfers;}end