123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258(*
* Copyright (c) 2018-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.
*)(** Trace file construction.
This file is meant to be used from Tezos. OCaml version 4.09 and the 32bit
architecture should be supported.
A module [Make_replayable] has yet to be implemented. *)openLwt.Syntax(** Make state trace collector. *)moduleMake_stat(Store:Irmin.Generic_key.KV)=structmoduleDef=Trace_definitions.Stat_tracetypet={writer:Def.writer;store_path:string;mutablet0:Mtime_clock.counter;mutableprev_merge_durations:floatlist;mutablecommit_before:Def.bag_of_stats*Def.store_before;}(** Imperative stat trace collector. It is optimised to minimise the CPU
footprint. *)moduleBag_of_stats=structletpack()=letmodulePack_stats=Irmin_pack.StatsinletmoduleUnix_pack_stats=Irmin_pack_unix.Statsinletpack_s=Pack_stats.get()inletunix_s=Unix_pack_stats.get()inletinode=Pack_stats.(Inode.exportpack_s.inode)inletpack_store=Unix_pack_stats.(Pack_store.exportunix_s.pack_store)inletfinds=Def.{total=pack_store.total;from_staging=pack_store.from_staging;from_lru=pack_store.from_lru;from_pack_direct=pack_store.from_pack_direct;from_pack_indexed=pack_store.from_pack_indexed;}inDef.{finds;appended_hashes=pack_store.appended_hashes;appended_offsets=pack_store.appended_offsets;inode_add=inode.inode_add;inode_remove=inode.inode_remove;inode_of_seq=inode.inode_of_seq;inode_of_raw=inode.inode_of_raw;inode_rec_add=inode.inode_rec_add;inode_rec_remove=inode.inode_rec_remove;inode_to_binv=inode.inode_to_binv;inode_decode_bin=inode.inode_decode_bin;inode_encode_bin=inode.inode_encode_bin;}lettree()=letopenStore.Treeinletv=counters()inDef.{contents_hash=v.contents_hash;contents_find=v.contents_find;contents_add=v.contents_add;node_hash=v.node_hash;node_mem=v.node_mem;node_add=v.node_add;node_find=v.node_find;node_val_v=v.node_val_v;node_val_find=v.node_val_find;node_val_list=v.node_val_list;}letindexprev_merge_durations=letopenIndex.Statsinletv=get()inletnew_merge_durations=ifv.merge_durations==prev_merge_durationsthen[]else(* This is anoying to compute. We can't rely on nb_merge.
Assume that all merge durations are unique.
Assume that we never have >10 merges at once.
*)letrecauxacc=function|[]->acc|hd::tl->ifList.memhdprev_merge_durationsthen(assert(acc=[])(* No oldie after a newies *);auxacctl)elseaux((hd/.1e6)::acc)tlinletl=aux[]v.merge_durationsinassert(l<>[])(* At least one newie *);linDef.{bytes_read=v.bytes_read;nb_reads=v.nb_reads;bytes_written=v.bytes_written;nb_writes=v.nb_writes;nb_merge=v.nb_merge;new_merge_durations;}letgc()=letopenGcinletv=quick_stat()inDef.{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;}letsize_of_filepath=letopenUnix.LargeFileintry(statpath).st_sizewithUnix.Unix_error_->0Lletdiskstore_path=let(/)leftright=Filename.concatleftrightinDef.{index_data=store_path/"index"/"data"|>size_of_file;index_log=store_path/"index"/"log"|>size_of_file;index_log_async=store_path/"index"/"log_async"|>size_of_file;store_dict=store_path/"store.dict"|>size_of_file;store_pack=store_path/"store.pack"|>size_of_file;}letnow()=Mtime_clock.now()|>Mtime.to_uint64_ns|>Int64.to_float|>(*.)1e-9letcreatestore_pathprev_merge_durations=Def.{pack=pack();tree=tree();index=indexprev_merge_durations;gc=gc();disk=diskstore_path;timestamp_wall=now();timestamp_cpu=Sys.time();}endletcreate_file:string->Def.config->string->t=funpathconfigstore_path->letheader=Def.{config;hostname=Unix.gethostname();word_size=Sys.word_size;timeofday=Unix.gettimeofday();initial_stats=Bag_of_stats.createstore_pathIndex.Stats.((get()).merge_durations);}inletdummy_commit_before=(header.initial_stats,Def.{nodes=0;leafs=0;skips=0;depth=0;width=0})in{writer=Def.create_filepathheader;store_path;t0=Mtime_clock.counter();prev_merge_durations=Index.Stats.((get()).merge_durations);commit_before=dummy_commit_before;}letflush{writer;_}=Def.flushwriterletclose{writer;_}=Def.closewriterletremove{writer;_}=Def.removewriterletshort_op_begint=t.t0<-Mtime_clock.counter()letshort_op_end{t0;writer;_}short_op=letduration=Mtime_clock.countt0|>Mtime.Span.to_s|>Int32.bits_of_floatinletop=matchshort_opwith|`Add->`Addduration|`Remove->`Removeduration|`Find->`Findduration|`Mem->`Memduration|`Mem_tree->`Mem_treeduration|`Checkout->`Checkoutduration|`Copy->`CopydurationinDef.append_rowwriteropletcreate_store_beforetree=let+Store.Tree.{nodes;leafs;skips;depth;width}=Store.Tree.stats~force:falsetreeinDef.{nodes;leafs;skips;depth;width}letcreate_store_aftertree=let*watched_nodes_length=Lwt_list.map_s(fun(_,steps)->Store.Tree.lengthtreesteps)Def.step_list_per_watched_nodeinLwt.returnDef.{watched_nodes_length}letcommit_beginttree=short_op_begint;letstats_before=Bag_of_stats.createt.store_patht.prev_merge_durationsint.prev_merge_durations<-Index.Stats.((get()).merge_durations);let+store_before=create_store_beforetreeint.commit_before<-(stats_before,store_before)letcommit_endttree=letduration=Mtime_clock.countt.t0|>Mtime.Span.to_sinletduration=duration|>Int32.bits_of_floatinletstats_after=Bag_of_stats.createt.store_patht.prev_merge_durationsint.prev_merge_durations<-Index.Stats.((get()).merge_durations);let+store_after=create_store_aftertreeinletop=`CommitDef.{duration;before=fstt.commit_before;after=stats_after;store_before=sndt.commit_before;store_after;}inDef.append_rowt.writeropend