123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164letunique_id=letr=ref0infun()->incrr;!r;;(* Used to track the current libname in such a way that for functor applications, it is
the calling libraries name that gets registered. *)moduleCurrent_libname=structletnull="<unknown>"letlibname_ref=refnullletsetstr=libname_ref:=strletunset()=libname_ref:=nullletget()=!libname_refendmoduleCurrent_bench_module_stack=structlett=ref[]letpushs=t:=s::!tletpop_exn()=t:=List.tl!tletto_name()=match!twith|[]->None|ms->Some(String.concat"."(List.revms));;end(* This is the main data structure of this module. An [Entry.t] represents a benchmark
along with some metadata about is position, arguments etc. *)moduleEntry=structtype('param,'a)parameterised_spec={arg_name:string;params:(string*'param)list;thunk:'param->unit->'a}typetest_spec=|Regular_thunk:([`init]->unit->'a)->test_spec|Parameterised_thunk:('param,'a)parameterised_spec->test_spectypet={unique_id:int;code:string;type_conv_path:string;name:string;filename:string;line:int;startpos:int;endpos:int;test_spec:test_spec;bench_module_name:stringoption}letcomparet1t2=comparet1.unique_idt2.unique_id(* Extracts module name from ["filename.ml.Module"], which is the format of [ext_name]
as set by [typeconv]. *)letget_module_name_optt=letstr=t.type_conv_pathinletlen=String.lengthstrinletrecloopi=ifi+4<=lenthenifString.substri4=".ml."thenSome(String.substr(i+4)(len-i-4))elseloop(i+1)elseNoneinloop0;;letwith_test_specttest_spec={twithtest_spec}end(* Inspect system environment variables to decide if benchmarks are being run. This is
called by the code generated by the [pa_bench] syntax to decide if the global hashtable
should be populated. *)letadd_environment_var=letv=trySys.getenv"BENCHMARKS_RUNNER"with|Not_found->""inv="TRUE";;(* This hashtable contains all the benchmarks from all the of libraries that have been
loaded. At the time the benchmarks are registering themselves with [ppx_bench_lib] we
don't yet know which libraries will need to be run. *)letlibs_to_entries:(string,Entry.tlist)Hashtbl.t=Hashtbl.create10letlookup_rev_lib~libname=tryHashtbl.findlibs_to_entrieslibnamewith|Not_found->[];;letlookup_lib~libname=List.rev(lookup_rev_lib~libname)letforce_drop=(* Useful for js_of_ocaml to perform deadcode elimination.
see ppx/ppx_inline_test/runtime-lib/runtime.ml [Action.get] for more details *)tryignore(Sys.getenv"FORCE_DROP_BENCH":string);truewith|Not_found->false;;letget_mode()=ifforce_dropthen`Ignoreelse`Collectlet[@inlinenever]add_bench~name~code~filename~type_conv_path~line~startpos~endpostest_spec=matchget_mode()with|`Ignore->()|`Collect->letlibname=Current_libname.get()inletentry={Entry.code;unique_id=unique_id();type_conv_path;bench_module_name=Current_bench_module_stack.to_name();name;filename;line;startpos;endpos;test_spec}inHashtbl.addlibs_to_entrieslibname(entry::lookup_rev_lib~libname);;let[@inlinenever]add_bench_module~name~code:_~type_conv_path:_~filename:_~line:_~startpos:_~endpos:_f=matchget_mode()with|`Ignore->()|`Collect->(* Running f registers the benchmarks using BENCH *)Current_bench_module_stack.pushname;(tryf();Current_bench_module_stack.pop_exn()with|ex->Current_bench_module_stack.pop_exn();raiseex);;