123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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 *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Hook for displaying analysis logs as a tree *)openMopsaopenFormatopenCore.All(** Logs options *)moduletypeOPTIONS=sigvalname:stringvalshort:boolendmoduleHook(Options:OPTIONS)=struct(** {2 Hook header} *)(** *************** *)letname=Options.name(** {2 Initialization} *)(** ****************** *)(* We use a stack for keeping the duration of exec and eval *)letstack=Stack.create()letinitctx=Stack.clearstack(** {2 Indentation} *)(** *************** *)letcolorlevels=letcode=(levelmod16)*16+10inif!Debug.print_colorthenPrintf.sprintf"\027[1;38;5;%dm%s\027[0m"codeselsestypesymbol=|BEGIN|END|MSG(** Symbol of a new entry *)letsymbol_to_stringsymbollevel=matchsymbolwith|BEGIN->colorlevel"+"|END->colorlevel"o"|MSG->colorlevel"*"letis_end_symbol=function|END->true|_->false(** Tabulation *)lettablevel=colorlevel"|"letcur_level()=max(Stack.lengthstack)0(** Indent a message by adding tabs at the beginning of each line *)letindent~symbolfmt=(* Get the formatted message as a string *)Format.kasprintf(funstr->(* Split the message into lines *)letlines=String.split_on_char'\n'strinletlevel=cur_level()inmatchlineswith|[]->()|first::others->(* The first line is prefixed with the entry symbol *)letfirst'=(symbol_to_stringsymbollevel)^" "^firstin(* The other lines are prefixed with the indent symbol *)letothers'=ifnot(is_end_symbolsymbol)thenList.map(funline->(tablevel)^" "^line)otherselseList.map(funline->" "^line)othersin(* Add the margin *)letmargin=List.initlevel(funi->(tabi)^" ")|>String.concat""inletlines'=List.map(funline->margin^line)(first'::others')inprintf"%a@."(pp_print_list~pp_sep:(funfmt()->fprintffmt"@\n")pp_print_string)lines')fmtletreachloc=indent"reaching %a"pp_rangeloc~symbol:MSGletpp_Sfmtstmt=fprintffmt"@[<v 3>S [| %a@] |]"pp_stmtstmtletpp_Esemanticfmtexp=fprintffmt"@[<v 3>E [| %a : %a@] |]%a"pp_exprexppp_typexp.etyp(funfmts->ifis_any_semanticsthen()elsefprintffmt"<%a>"pp_semantics)semanticletpp_route_if_anyfmtroute=ifcompare_routeroutetoplevel=0then()elsefprintffmt" in %a"pp_routerouteletget_timing()=trySys.time()-.Stack.popstackwithStack.Empty->Float.nan(** {2 Events handlers} *)(** ******************* *)leton_before_execroutestmtmanflow=reachstmt.srange;ifOptions.shortthenindent"%a%a"pp_Sstmtpp_route_if_anyroute~symbol:BEGINelseindent"%a%a@,input @[%a@]"pp_Sstmtpp_route_if_anyroute(format(Flow.printman.lattice.print))flow~symbol:BEGIN;Stack.push(Sys.time())stackleton_after_execroutestmtmanflowpost=lettime=get_timing()inletnb=Cases.cardinalpostinifOptions.shortthenindent"%a%a done [%.4fs, %d case%a]"pp_Sstmtpp_route_if_anyroutetimenbDebug.plurial_intnb~symbol:ENDelseindent"%a%a done [%.4fs, %d case%a]@ output: @[%a@]"pp_Sstmtpp_route_if_anyroutetimenbDebug.plurial_intnb(Cases.print(funfmt_flow->format(Flow.printman.lattice.print)fmtflow))post~symbol:ENDleton_before_evalroutesemanticexpmanflow=ifOptions.shortthenindent"%a%a"(pp_Esemantic)exppp_route_if_anyroute~symbol:BEGINelseindent"%a%a@,input: @[%a@]"(pp_Esemantic)exppp_route_if_anyroute(format(Flow.printman.lattice.print))flow~symbol:BEGIN;Stack.push(Sys.time())stackleton_after_evalroutesemanticexpmanflowevl=lettime=get_timing()inletpp_evlfmtevl=Cases.print_result(funfmteflow->Format.fprintffmt"%a : %a%a"pp_exprepp_type.etyp(funfmttrans->ifSemanticMap.is_emptytransthen()elsefprintffmt" ‖ %a"(pp_print_list~pp_sep:(funfmt()->fprintffmt" ‖ ")(funfmt(s,e)->fprintffmt"%a ⇝ %a : %a"pp_semanticspp_exprepp_type.etyp))(SemanticMap.bindingstrans))e.etrans)fmtevlinletnb=Cases.cardinalevlinifOptions.shortthenindent"%a = %a%a done [%.4fs, %d case%a]"(pp_Esemantic)exppp_evlevlpp_route_if_anyroutetimenbDebug.plurial_intnb~symbol:ENDelseindent"%a%a done [%.4fs, %d case%a]@ output: @[%a]"(pp_Esemantic)exppp_route_if_anyroutetimenbDebug.plurial_intnbpp_evlevl~symbol:ENDleton_finishmanflow=Stack.clearstackendlet()=Core.Hook.register_stateless_hook(moduleHook(structletname="logs"letshort=falseend));Core.Hook.register_stateless_hook(moduleHook(structletname="short-logs"letshort=trueend))