123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182(* Yoann Padioleau
*
* Copyright (C) 1998-2023 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
* license.txt for more details.
*)openCommon(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* Pad's poor's man profiler. See pfff's Main.ml for example of use
* and the -profile command-line flag.
*
* You should probably rely on ocamlprof, perf, memprof, and the
* many other OCaml profiling tools.
*)(*****************************************************************************)(* Types *)(*****************************************************************************)typeprof=ProfAll|ProfNone|ProfSomeofstringlist(*****************************************************************************)(* Helpers *)(*****************************************************************************)let(with_open_stringbuf:((string->unit)*Buffer.t->unit)->string)=funf->letbuf=Buffer.create1000inletprs=Buffer.add_stringbuf(s^"\n")inf(pr,buf);Buffer.contentsbuf(*****************************************************************************)(* Globals *)(*****************************************************************************)letprofile=refProfNoneletshow_trace_profile=reffalseletcheck_profilecategory=match!profilewith|ProfAll->true|ProfNone->false|ProfSomel->List.memcategoryllet_profile_table=ref(Hashtbl.create100)letadjust_profile_entrycategorydifftime=letxtime,xcount=tryHashtbl.find!_profile_tablecategorywith|Not_found->letxtime=ref0.0inletxcount=ref0inHashtbl.add!_profile_tablecategory(xtime,xcount);(xtime,xcount)inxtime:=!xtime+.difftime;incrxcount;()(*****************************************************************************)(* Entry points *)(*****************************************************************************)(* subtle: don't forget to give all argumens to f, otherwise partial app
* and will profile nothing.
*
* todo: try also detect when complexity augment each time, so can
* detect the situation for a function gets worse and worse ?
*)letprofile_codecategoryf=ifnot(check_profilecategory)thenf()else(if!show_trace_profilethenpr2(spf"> %s"category);lett=Unix.gettimeofday()inletres,prefix=try(Ok(f()),"")with(*TODO: Timeout _ as*)|exn->lete=Exception.catchexnin(Errore,"*")inletcategory=prefix^categoryin(* add a '*' to indicate timeout func *)lett'=Unix.gettimeofday()inif!show_trace_profilethenpr2(spf"< %s"category);adjust_profile_entrycategory(t'-.t);matchreswith|Okres->res|Errore->Exception.reraisee)let_is_in_exclusif=ref(None:stringoption)letprofile_code_exclusifcategoryf=ifnot(check_profilecategory)thenf()elsematch!_is_in_exclusifwith|Somes->failwith(spf"profile_code_exclusif: %s but already in %s "categorys)|None->_is_in_exclusif:=Somecategory;Fun.protect(fun()->profile_codecategoryf)~finally:(fun()->_is_in_exclusif:=None)letprofile_code_inside_exclusif_ok_category_f=failwith"Todo"(*****************************************************************************)(* Diagnostic *)(*****************************************************************************)(* todo: also put % ? also add % to see if coherent numbers *)letprofile_diagnostic()=if!profile=*=ProfNonethen""elseletxs=Hashtbl.fold(funkvacc->(k,v)::acc)!_profile_table[]|>List.sort(fun(_k1,(t1,_n1))(_k2,(t2,_n2))->comparet2t1)inwith_open_stringbuf(fun(pr,_)->pr"---------------------";pr"profiling result";pr"---------------------";xs|>List.iter(fun(k,(t,n))->pr(Printf.sprintf"%-40s : %10.3f sec %10d count"k!t!n)))letreport_if_take_timetimethresholdsf=lett=Unix.gettimeofday()inletres=f()inlett'=Unix.gettimeofday()inift'-.t>float_of_inttimethresholdthenpr2(Printf.sprintf"Note: processing took %7.1fs: %s"(t'-.t)s);res(*****************************************************************************)(* Entry points *)(*****************************************************************************)letprofile_code2categoryf=profile_codecategory(fun()->if!profile=*=ProfAllthenpr2("starting: "^category);lett=Unix.gettimeofday()inletres=f()inlett'=Unix.gettimeofday()inif!profile=*=ProfAllthenpr2(spf"ending: %s, %fs"category(t'-.t));res)(*****************************************************************************)(* Init *)(*****************************************************************************)letflags()=[("-profile",Arg.Unit(fun()->profile:=ProfAll)," output profiling information");("-show_trace_profile",Arg.Setshow_trace_profile," show trace");]letprint_diagnostics_and_gc_stats()=pr2(profile_diagnostic());Gc.print_statstderr(* ugly *)let_=Common.before_exit:=(fun()->if!profile<>ProfNonethenprint_diagnostics_and_gc_stats())::!Common.before_exit