123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2023 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/>. *)(* *)(****************************************************************************)openCore.AllopenMopsa_utilsopenLocationopenCallstackopenInterfaceopenFormatopenBreakpointopenToplevelopenActionopenEnvdbopenTracemoduleMake(Toplevel:TOPLEVEL)=structletopt_show_var_scope=reftrue(** Commands *)typeterminal_command_kind=|Breakofstring(** Add a breakpoint *)|Continue(** Stop at next breakpoint *)|MopsaBackTrace(** Returns the current backtrace of Mopsa *)|Next(** Stop at next statement and skip function calls *)|Step(** Step into function calls *)|Finish(** Finish current function *)|NextI(** Stop at next statement and skip nodes in the interpretation sub-tree *)|StepI(** Step into interpretation sub-tree *)|Printof(string*stringoption)list*(string*int)option(** Print the abstract state or the value of variables *)(* string * string option: variable name, potential function name *)|Envofstringlist(** Print the current abstract environment, associated to token T_cur,
eventually projected on a list of domains *)|State(** Print the current abstract state *)|Where(** Show current program point *)|Infoofinfo_command(** Print extra information *)|Enableofenable_command(** Enable an option *)|Disableofenable_command(** Disable an option *)|Setofset_command*string(** Set an option *)|Unsetofset_command(** Unset an option *)|BackTrace(** Print the callstack *)|LoadScriptofstring|Trace|Backward(** Information sub-commands *)andinfo_command=|Alarms|Breakpoints|Tokens|Variables|Context(** Enable/Disable sub-commands *)andenable_command=|Hookofstring(** Set/Unset sub-commands *)andset_command=|Debug|Script|ShowVarScopetypeterminal_command_redirection=|Pipeofstring|Fileofstring(** Commands *)typeterminal_command={(** Kind of the command *)kind:terminal_command_kind;(** Optional flag *)redirection:terminal_command_redirectionoption;}(** Print a command *)letpp_terminal_command_kindfmt=function|Breakloc->Format.fprintffmt"break %s"loc|MopsaBackTrace->Format.fprintffmt"mopsa_bt"|Continue->Format.pp_print_stringfmt"continue"|Next->Format.pp_print_stringfmt"next"|Finish->Format.pp_print_stringfmt"finish"|NextI->Format.pp_print_stringfmt"nexti"|Step->Format.pp_print_stringfmt"step"|StepI->Format.pp_print_stringfmt"stepi"|Print([],None)->Format.pp_print_stringfmt"print"|Print(vars,None)->Format.fprintffmt"print %a"(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt",")(funfmt(v,o_f)->matcho_fwith|None->fprintffmt"%s"v|Somef->fprintffmt"%s:%s"vf))vars|Print(vars,Some(file,line))->Format.fprintffmt"print@%s:%d %a"fileline(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt",")(funfmt(v,o_f)->matcho_fwith|None->fprintffmt"%s"v|Somef->fprintffmt"%s:%s"vf))vars|Env[]->Format.pp_print_stringfmt"env"|Envdomains->Format.fprintffmt"env %a"(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt",")pp_print_string)domains|State->Format.pp_print_stringfmt"state"|Where->Format.pp_print_stringfmt"where"|InfoAlarms->Format.pp_print_stringfmt"info alarms"|InfoBreakpoints->Format.pp_print_stringfmt"info breakpoints"|InfoTokens->Format.pp_print_stringfmt"info tokens"|InfoVariables->Format.pp_print_stringfmt"info variables"|InfoContext->Format.pp_print_stringfmt"info context"|Enable(Hookh)->Format.fprintffmt"enable hook %s"h|Disable(Hookh)->Format.fprintffmt"disable hook %s"h|Set(Debug,d)->Format.fprintffmt"set debug %s"d|Set(Script,d)->Format.fprintffmt"set script %s"d|Set(ShowVarScope,d)->Format.fprintffmt"set showvarscope %s"d|LoadScripts->Format.fprintffmt"load script %s"s|UnsetDebug->Format.pp_print_stringfmt"unset debug"|UnsetScript->Format.pp_print_stringfmt"unset script"|UnsetShowVarScope->Format.pp_print_stringfmt"unset showvarscope"|BackTrace->Format.pp_print_stringfmt"backtrace"|Trace->Format.pp_print_stringfmt"trace"|Backward->Format.pp_print_stringfmt"backward"letpp_terminal_command_redirectionfmt=function|Pipeshell->Format.fprintffmt" | %s"shell|Filefile->Format.fprintffmt" > %s"fileletpp_terminal_commandfmtc=Format.fprintffmt"%a%a"pp_terminal_command_kindc.kind(Format.pp_print_optionpp_terminal_command_redirection)c.redirection(** Print help message *)letprint_usage()=printf"Available commands:@.";printf" b[reak] <[file:]line> add a breakpoint at a line@.";printf" b[reak] <function> add a breakpoint at a function@.";printf" b[reak] @name add a named breakpoint (will break when the analysis executes an S_break name)@.";printf" b[reak] #a[larm] break at the next alarm (and go back at the statement generating the alarm)@.";printf" c[ontinue] run until next breakpoint@.";printf" n[ext] stop at next statement and skip function calls.@.";printf" n[ext]i stop at next statement and skip nodes in the interpretation sub-tree@.";printf" s[tep] step into function calls@.";printf" s[tep]i step into interpretation sub-tree@.";printf" f[inish] finish current function@.";printf" b[ack]w[ard] go backward to the calling site@.";printf" e[nable] h[hook] <h> enable a hook@.";printf" d[isable] h[hook] <h> disable a hook@.";printf" s[et] d[ebug] <d> set debug channels@.";printf" u[nset] d[ebug] unset debug channels@.";printf" s[et] script <file> store commands into a file@.";printf" To be used in combination with load script <file>@.";printf" u[nset] script do not store commands in file anymore@.";printf" load script <file> reads script command from <file>@.";printf" help print this message@.";printf"The commands below support shell commands (`env | grep foo`, `mopsa_bt | tac`, ...):@.";printf" p[rint] print the abstract state@.";printf" p[rint] <vars> print the value of selected variables@.";printf" For example, `p x,y:f,z:*` prints x in the current scope, y in the scope of f, and z in all scopes@.";printf" p[rint] <vars> #<f>:<l> print the value of selected variables at the given program location@.";printf" e[nv] print the current abstract environment@.";printf" e[nv] <domain>,... print the current abstract environment of selected domains@.";printf" state print the full abstract state (map from flow token to environment)@.";printf" b[ack]t[race] print the current call stack@.";printf" t[race] print the analysis trace@.";printf" w[here] show current program point@.";printf" i[info] a[larms] print the list of detected alarms@.";printf" i[info] c[hecks] print the list of performed checks@.";printf" i[info] b[reakpoints] print the list of registered breakpoints@.";printf" i[info] t[okens] print the list of flow tokens@.";printf" i[info] v[ariables] print the list of variables@.";printf" i[info] c[on]t[e]x[t] print the flow-insensitive context@.";printf" mopsa_bt shows the current backtrace of the analyzer@.";printf"You can chain multiple commands with ';'@.";()(** Print input prompt *)letprint_prompt()=printf"%a %a @?"Debug.(color_strteal)"mopsa"Debug.(color_strgreen)">>"(** Context of LineEdit library *)letlinedit_ctx=LineEdit.create_ctx()(** Reference to the last commands read from the prompt *)letlast_prompt_commands=ref[](** Buffer of upcoming commands to execute *)letcommands_buffer=Queue.create()letscript:out_channeloptionref=refNone(* Read the next command as a string *)letrecread_terminal_command_string()=(* Check the commands buffer *)ifnot(Queue.is_emptycommands_buffer)thenQueue.popcommands_bufferelse(* Buffer is empty, so ask the user *)let()=print_prompt()intryletinput=LineEdit.read_linelinedit_ctx|>String.triminifString.lengthinput>0&&input.[0]='#'thenread_terminal_command_string()else(* Split the input into multiple commands with delimeter ';' *)letparts=String.split_on_char';'input|>List.mapString.trim|>List.filter(function""->false|_->true)inmatchparts,!last_prompt_commandswith|[],[]->(* No command entered and no command in history, so ask user again *)read_terminal_command_string()|[],_->(* No command entered, but we know the last command, so replay it *)let()=List.iter(func->Queue.addccommands_buffer)!last_prompt_commandsinread_terminal_command_string()|_->(* Some commands entered. Save them in [last_prompt_commands],
initialize [commands_buffer] and iterate again *)last_prompt_commands:=parts;List.iter(func->Queue.addccommands_buffer)parts;read_terminal_command_string()withEnd_of_file->raiseExitexceptionReadNewCommandletparse_command_kindsflow=letparts=String.split_on_char' 's|>List.mapString.trim|>List.filter(function""->false|_->true)inmatchpartswith|["exit"]->raiseExit|["continue"|"c"]->Continue|["mopsa_bt"]->MopsaBackTrace|["next"|"n"]->Next|["step"|"s"]->Step|["finish"|"f"]->Finish|["nexti"|"ni"]->NextI|["stepi"|"si"]->StepI|["where"|"w"]->Where|["backtrace"|"bt"]->BackTrace|["break"|"b";l]->Breakl|["backward"|"back"|"bw"]->Backward|("env"|"e")::domains->letdomains=List.fold_left(funaccs->letparts=String.split_on_char','s|>List.filter(function""->false|_->true)inSetExt.StringSet.unionacc(SetExt.StringSet.of_listparts))SetExt.StringSet.emptydomainsinEnv(SetExt.StringSet.elementsdomains)|["state"]->State|["help"|"h"]->print_usage();raiseReadNewCommand|["info"|"i";"tokens"|"t"]|["it"]->InfoTokens|["info"|"i";"breakpoints"|"b"]|["ib"]->InfoBreakpoints|["info"|"i";"alarms"|"a"]|["ia"]->InfoAlarms|["info"|"i";"variables"|"vars"|"var"|"v"]|["iv"]->InfoVariables|["info"|"i";"context"|"ctx"]|["ictx"]->InfoContext|["enable"|"en";"hook"|"h";h]|["eh";h]->Enable(Hookh)|["disable"|"d";"hook"|"h";h]|["dh";h]->Disable(Hookh)|["set"|"s";"debug"|"d";d]|["sd";d]->Set(Debug,d)|["unset"|"u";"debug"|"d"]|["ud"]->UnsetDebug|["set"|"s";"script"|"s";d]|["sc";d]->Set(Script,d)|["unset"|"u";"script"|"s"]|["uc"]->UnsetScript|["set";"showvarscope"]->Set(ShowVarScope,"")|["unset";"showvarscope"]->UnsetShowVarScope|["load";"script";s]|["ls";s]->LoadScripts|("print"|"p")::vars->letcs=Flow.get_callstackflowinletvars=List.fold_left(funaccs->letparts=String.split_on_char','s|>List.filter(function""->false|_->true)inletparts=List.map(funs->letl=String.split_on_char':'sinifList.lengthl=1thenifcs=[]thenList.hdl,NoneelseList.hdl,Some(List.hdcs).call_fun_orig_nameelseletf=List.hd@@List.tllinifString.comparef"*"=0thenList.hdl,Noneelse(List.hdl,Somef))partsinparts@acc)[]varsinPrint(vars,None)|print::varswhenStr.string_match(Str.regexp{|\(p\|print\)@\(.+\):\([0-9]+\)|})print0->letfile=Str.matched_group2printinletline=Str.matched_group3print|>int_of_stringinletvars=List.fold_left(funaccs->letparts=String.split_on_char','s|>List.filter(function""->false|_->true)inletparts=List.map(funs->letl=String.split_on_char':'sinifList.lengthl=1thenList.hdl,Noneelse(List.hdl,Some(List.hd@@List.tll)))partsinparts@acc)[]varsinPrint(vars,Some(file,line))|["trace"|"t"]->Trace|_->printf"Unknown command %s@."s;print_usage();raiseReadNewCommandletparse_command_redirections=ifStr.(string_match(regexp{|\([^|]+\)|\(.*\)|})s0)thenletcmd=Str.matched_group1s|>String.triminletredirect=Pipe(Str.matched_group2s|>String.trim)incmd,SomeredirectelseifStr.(string_match(regexp{|\([^>]+\)>\([^>]+\)|})s0)thenletcmd=Str.matched_group1s|>String.triminletredirect=File(Str.matched_group2s|>String.trim)incmd,Someredirectelses,None(** Read the next command *)letrecread_terminal_commandloggerflow=lets=read_terminal_command_string()inloggers;lets,redirection=parse_command_redirectionsintryletkind=parse_command_kindsflowin{kind;redirection}withReadNewCommand->read_terminal_commandloggerflow(** {2 Pretty printers} *)(** ******************* *)moduleAddr=structtypet=addrletcompare=compare_addrletprint=unformatpp_addrletfrom_expre=matchekindewith|E_addr(addr,_)->addr|_->assertfalseendmoduleAddrSet=structincludeSetExt.Make(Addr)letprintprinters=pp_listAddr.printprinter(elementss)~lopen:"{"~lsep:","~lclose:"}"endletdummy_range=mk_fresh_range()(** Print value of variables *)letpp_varsfmtactionnamesmanflow=ifman.lattice.is_bottom(Flow.getT_curman.latticeflow)thenfprintffmt"⊥@."elseAst.Var.force_print_uniq_with_uidfalse(fun()->letnames=matchnameswith|[]->List.map(funv->asprintf"%a"pp_varv,None)(action_line_varsaction)|_->namesinletnames_global,names_by_func=List.fold_left(fun(global_acc,func_acc)(name,o_f)->matcho_fwith|None->(name::global_acc,func_acc)|Somef->(global_acc,ifMapExt.StringMap.memffunc_accthenMapExt.StringMap.addf(name::MapExt.StringMap.findffunc_acc)func_accelseMapExt.StringMap.addf[name]func_acc))([],MapExt.StringMap.empty)namesinletvars=ask_and_reduceman.ask(Q_defined_variablesNone)flowinletvmap=List.fold_left(funaccv->letvname=asprintf"%a"pp_varvinletold=OptionExt.defaultVarSet.empty(MapExt.StringMap.find_optvnameacc)inMapExt.StringMap.addvname(VarSet.addvold)acc)MapExt.StringMap.emptyvarsinletaddrs=ask_and_reduceman.askQ_allocated_addressesflowinletamap=List.fold_left(funacca->letaname=asprintf"%a"pp_addrainletold=OptionExt.defaultAddrSet.empty(MapExt.StringMap.find_optanameacc)inMapExt.StringMap.addaname(AddrSet.addaold)acc)MapExt.StringMap.emptyaddrsinletvfound,afound,not_found=List.fold_left(fun(vfound,afound,not_found)name->matchMapExt.StringMap.find_optnamevmapwith|Somevars->(VarSet.elementsvars)@vfound,afound,not_found|None->matchMapExt.StringMap.find_optnameamapwith|Someaddrs->vfound,(AddrSet.elementsaddrs)@afound,not_found|None->vfound,afound,name::not_found)([],[],[])names_globalinletvfound,afound,not_found=MapExt.StringMap.fold(funfuncvs(vfound,afound,not_found)->letvars=ask_and_reduceman.ask(Q_defined_variables(Somefunc))flowinletvmap=List.fold_left(funaccv->letvname=asprintf"%a"pp_varvinletold=OptionExt.defaultVarSet.empty(MapExt.StringMap.find_optvnameacc)inMapExt.StringMap.addvname(VarSet.addvold)acc)MapExt.StringMap.emptyvarsinList.fold_left(fun(vfound,afound,not_found)name->matchMapExt.StringMap.find_optnamevmapwith|Somevars->(VarSet.elementsvars)@vfound,afound,not_found|None->matchMapExt.StringMap.find_optnameamapwith|Someaddrs->vfound,(AddrSet.elementsaddrs)@afound,not_found|None->vfound,afound,name::not_found)(vfound,afound,not_found)vs)names_by_func(vfound,afound,not_found)inletprotect_printprint_thunk=ifnot@@!opt_show_var_scopethenCore.Ast.Var.force_print_uniq_with_uidfalse(fun()->print_thunk())elseprint_thunk()in(* let () = Ast.Var.print_uniq_with_uid := true in *)letnot_found'=letprinter=empty_printer()inletfound,not_found=List.fold_left(fun(found,not_found)v->trylet()=ifVarSet.cardinal@@OptionExt.defaultVarSet.empty(MapExt.StringMap.find_opt(asprintf"%a"pp_varv)vmap)>1thenprotect_print(fun()->man.print_exprflowprinter(mk_varvdummy_range))elseman.print_exprflowprinter(mk_varvdummy_range)intrue,not_foundwithNot_found->letvname=asprintf"%a"pp_varvinfound,vname::not_found)(false,[])vfoundinletfound,not_found=List.fold_left(fun(found,not_found)a->trylet()=man.print_exprflowprinter(mk_addradummy_range)intrue,not_foundwithNot_found->letaname=asprintf"%a"pp_addrainfound,aname::not_found)(found,not_found)afoundiniffoundthenfprintffmt"%a@."pflushprinter;not_foundin(matchnot_found@not_found'with|[]->()|l->fprintffmt"Variable%a %a not found@."Debug.plurial_listnot_found(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt", ")(funfmtvname->fprintffmt"'%s'"vname))l))letinit()=printf"@.%a@.Type '%a' to get the list of commands.@.@."(Debug.boldpp_print_string)("Welcome to Mopsa "^Version.version^"!")(Debug.boldpp_print_string)"help"letreachactionmanflow=(* Print the range of the next action *)printf"%a@."Debug.(colorfushiapp_relative_range)(action_rangeaction);(* Print location in the source code *)pp_action_source_codestd_formatteraction;(* Print interpreter action *)printf"%a@."(pp_action~truncate:true~indent:0)actionletpp_alarmsfmtalarms=fprintffmt"%d new alarm%a detected: @[<v>@."(List.lengthalarms)Debug.plurial_listalarms;ignore(Output.Text.construct_checks_summary~print:true(Alarm.alarms_to_reportalarms)None);fprintffmt"@]@."letalarmalarmsactionmanflow=pp_alarmsFormat.std_formatteralarmsletloggercmd_str=ifList.memcmd_str["us";"unset script";"unset s"]then()else(* todo: currently logs unset script, whoops *)match!scriptwith|None->()|Somech->letfile_fmt=formatter_of_out_channelchinFormat.fprintffile_fmt"%s@."cmd_str(* todo: remove last @. ... *)letpp_output_commandactionenvdbmanflowfmt=function|BackTrace->letcs=Flow.get_callstackflowinfprintffmt"%a@."pp_callstackcs|MopsaBackTrace->let(in_file_descr,out_file_descr)=Unix.pipe()inletin_channel=Unix.in_channel_of_descrin_file_descrinletout_channel=Unix.out_channel_of_descrout_file_descrinPrintexc.print_raw_backtraceout_channel(Printexc.get_callstackInt.max_int);letbuffer=Buffer.create100inclose_outout_channel;letrecloop()=matchinput_linein_channelwith|l->Buffer.add_stringbuffer(l^"\n");loop()|exceptionEnd_of_file->Buffer.contentsbufferinletr=loop()inclose_inin_channel;fprintffmt"%s@."r|Print(names,loc)->letctx=Flow.get_ctxflowinletenv=matchlocwith|None->Some(action,flow)|Some(file,line)->matchfind_envdb_optfilelineenvdbwith|None->None|Some(action',envs)->fprintffmt"%a@."(pp_action~truncate:false~indent:0)action';letenv=CallstackMap.fold(fun_->man.lattice.joinctx)envsman.lattice.bottominSome(action',Flow.singletonctxT_curenv)in(matchenvwith|None->()|Some(action',flow')->pp_varsfmtaction'namesmanflow')|Env[]->letenv=Flow.getT_curman.latticeflowinfprintffmt"%a@."(Print.formatman.lattice.print)env|Envdomains->letenv=Flow.getT_curman.latticeflowinletre=List.map(fund->(* Replace wildcard shortcut '_' *)letd'=Str.global_replace(Str.regexp_string"_")".*"din(* Accept any domain name containing the given string *)".*"^d'^".*")domains|>(* Add alternative operator between domains names *)String.concat"\\|"|>Str.regexpinletpobj=pboxman.lattice.printenvinletpobj'=match_print_object_keysrepobjinfprintffmt"%a@."pp_print_objectpobj'|State->fprintffmt"%a@."(Print.format(Flow.printman.lattice.print))flow|Where->pp_action_source_codestd_formatteraction;fprintffmt"%a@."Debug.(colorfushiapp_relative_range)(action_rangeaction);fprintffmt"%a@."(pp_action~truncate:false~indent:0)action|InfoTokens->lettokens=Flow.fold(funacctk_->tk::acc)[]flowinfprintffmt"%a@."(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt"@\n")pp_token)tokens|InfoAlarms->letreport=Flow.get_reportflowin(ifis_safe_reportreportthenprintf"%a No alarm@."Debug.(color_strgreen)"✔";let_=Output.Text.construct_checks_summary~print:truereportNonein())|InfoBreakpoints->fprintffmt"%a@."Breakpoint.pp_breakpoint_set!breakpoints|InfoVariables->letvars=ask_and_reduceman.ask(Q_defined_variablesNone)flowinfprintffmt"@[<v>%a@]@."(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt"@,")(funfmtv->Query.pp_var_with_typefmt(v,v.vtyp)))vars|InfoContext->letctx=Flow.get_ctxflowinfprintffmt"%a@."(pp_ctxman.lattice.print)ctx|Trace->fprintffmt"%a@."pp_tracestate.trace|_->assertfalseletprocess_output_commandcmdactionenvdbmanflow=matchcmd.redirectionwith|None->pp_output_commandactionenvdbmanflowFormat.std_formattercmd.kind|Some(Filefile)->letold_print_color=!Debug.print_colorinDebug.print_color:=false;letoutput=asprintf"%a"(pp_output_commandactionenvdbmanflow)cmd.kindinDebug.print_color:=old_print_color;letch=open_outfileinoutput_stringchoutput;flushch;close_outch|Some(Pipeshell)->letold_print_color=!Debug.print_colorinletoutput=asprintf"%a"(pp_output_commandactionenvdbmanflow)cmd.kindinDebug.print_color:=old_print_color;letin_ch,out_ch=Unix.open_processshellinoutput_stringout_choutput;flushout_ch;close_outout_ch;letreciter()=tryprintf"%s@."(input_linein_ch);iter()withEnd_of_file->()initer();close_inin_chletrecread_commandactionenvdbmanflow=letcmd=tryread_terminal_commandloggerflowwithExit->exit0inmatchcmd.kindwith|Breakloc->let()=tryletdefault_file=tryget_range_file(action_rangeaction)with_->""(* FIXME: use first file in list of analyzed files *)inletbp=parse_breakpointdefault_filelocinbreakpoints:=BreakpointSet.addbp!breakpoints;withInvalid_breakpoint_syntax->printf"Invalid breakpoint syntax@."inread_commandactionenvdbmanflow|Continue->Interface.Continue|Next->Next|NextI->NextI|Step->Step|StepI->StepI|Finish->Finish|Backward->Backward|Enable(Hookhook)->ifnot(Hook.mem_hookhook)then(printf"Hook '%s' not found@."hook;read_commandactionenvdbmanflow)else(Hook.activate_hookhook;letctx=Hook.init_hookhook(Flow.get_ctxflow)inletflow=Flow.set_ctxctxflowinread_commandactionenvdbmanflow)|Disable(Hookhook)->ifnot(Hook.mem_hookhook)then(printf"Hook '%s' not found@."hook)else(Hook.deactivate_hookhookmanflow);read_commandactionenvdbmanflow|Set(Debug,channel)->Debug.set_channelschannel;read_commandactionenvdbmanflow|UnsetDebug->Debug.set_channels"";read_commandactionenvdbmanflow|Set(Script,filename)->letch=open_outfilenameinscript:=Somech;read_commandactionenvdbmanflow|UnsetScript->let()=match!scriptwith|None->()|Somech->close_outch;script:=Noneinread_commandactionenvdbmanflow|LoadScripts->letch=open_insinletlines=letrecprocessres=tryprocess((input_linech)::res)withEnd_of_file->List.revresinprocess[]inList.iter(funl->Queue.addlcommands_buffer)lines;close_inch;read_commandactionenvdbmanflow|Set(ShowVarScope,_)->opt_show_var_scope:=true;read_commandactionenvdbmanflow|UnsetShowVarScope->opt_show_var_scope:=false;read_commandactionenvdbmanflow|BackTrace|MopsaBackTrace|Print_|Env_|State|Where|Info_|Trace->process_output_commandcmdactionenvdbmanflow;read_commandactionenvdbmanflowletfinishmanflow=()leterrorex=printf"%a@\n%a@."(Debug.color_strDebug.red)"Analysis aborted"(funfmt->function|Exceptions.Panic(msg,"")->fprintffmt"panic: %s@."msg|Exceptions.Panic(msg,loc)->fprintffmt"panic raised in %s: %s@."locmsg|Exceptions.PanicAtLocation(range,msg,"")->fprintffmt"panic in %a: %s@."Location.pp_rangerangemsg|Exceptions.PanicAtLocation(range,msg,loc)->fprintffmt"%a: panic raised in %s: %s@."Location.pp_rangerangelocmsg|Exceptions.PanicAtFrame(range,cs,msg,"")->fprintffmt"panic in %a: %s@\nTrace:@\n%a@."Location.pp_rangerangemsgpp_callstackcs|Exceptions.PanicAtFrame(range,cs,msg,loc)->fprintffmt"%a: panic raised in %s: %s@\nTrace:@\n%a@."Location.pp_rangerangelocmsgpp_callstackcs|Exceptions.SyntaxError(range,msg)->fprintffmt"%a: syntax error: %s@."Location.pp_rangerangemsg|Exceptions.UnnamedSyntaxErrorrange->fprintffmt"%a: syntax error@."Location.pp_rangerange|Exceptions.SyntaxErrorListl->fprintffmt"Syntax errors:@\n @[%a@]@."(pp_print_list~pp_sep:(funfmt()->fprintffmt"@\n")(funfmt(range,msg)->fprintffmt"%a: %s"Location.pp_rangerangemsg))l|Exceptions.UnnamedSyntaxErrorListl->fprintffmt"Syntax errors:@\n @[%a@]@."(pp_print_list~pp_sep:(funfmt()->fprintffmt"@\n")Location.pp_range)l|ex->fprintffmt"Uncaught exception: %s@."(Printexc.to_stringex))exend