123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)openDatamoduleMd=MarkdownmodulePkg=PackagemoduleSenv=Server_parameters(* -------------------------------------------------------------------------- *)(* --- Frama-C Parameters --- *)(* -------------------------------------------------------------------------- *)letpackage=Package.package~name:"parameters"~title:"All Frama-C parameters"()(* Ignore any parameter with an invalid name. *)letis_valid_parameter_namename=letis_valid_char=function|'a'..'z'|'A'..'Z'|'0'..'9'|'-'->true|_->falseinname.[0]='-'&&String.for_allis_valid_charname(* Should a parameter be exported? *)letis_exported_parameter(p:Typed_parameter.parameter)=p.visible&&p.reconfigurable&&is_valid_parameter_namep.name(* Translates a parameter name into a valid camlCase request. *)letcamlCaseParameterNamename=matchString.split_on_char'-'namewith|""::head::tail->List.fold_left(^)head(List.mapString.capitalize_asciitail)|_->Senv.fatal"Invalid parameter name %s"namemoduleParameterType=structtypet=Typed_parameter.parameterletdescr=Markdown.plain"Type of a command-line parameter"letjtype=Data.declare~package~name:"parameterType"~descrJstringletto_json(parameter:t)=matchparameter.accessorwith|Bool_->`String"Bool"|Int_->`String"Int"|Float_->`String"Float"|String_->`String"String"letof_json_=Data.failure"ParameterType.of_json not implemented"endmoduleParameterRange=structtypet=Typed_parameter.parameterletjtype=letdescr=Md.plain"Range of values for an integer or float parameter, \
or list of possible values of a string parameter"inData.declare~package~name:"parameterRange"~descr(Junion[Jnull;Jtuple[Jnumber;Jnumber];JarrayJstring])letto_json(parameter:t)=matchparameter.accessorwith|Bool_->`Null|Int(_accessor,range)->letmin,max=range()in`List[`Intmin;`Intmax]|Float(_accessor,range)->letmin,max=range()in`List[`Floatmin;`Floatmax]|String(_accessor,values)->matchvalues()with|[]->`Null|list->`List(List.map(funs->`Strings)list)letof_json_=Data.failure"ParameterRange.of_json not implemented"endmoduleParameterData=structtypeparameterletjparameter:parameterRecord.signature=Record.signature()letfieldnamedescr=Record.fieldjparameter~name~descr:(Md.plaindescr)(moduleJstring)letname=field"name""parameter name"lethelp=field"help""parameter help message"letstate=field"state""name of the synchronized state for this parameter"lettyp=letdescr=Md.plain"Parameter type : bool, int, float or string"inRecord.fieldjparameter~name:"type"~descr(moduleParameterType)letrange=letdescr=Md.plain"Range of values for an integer or float parameter, \
or list of possible values for a string parameter"inRecord.fieldjparameter~name:"range"~descr(moduleParameterRange)letis_set=letdescr=Md.plain"Has the parameter been set by the user?"inRecord.fieldjparameter~name:"isSet"~descr(moduleJbool)letdata=Record.publish~package~name:"parameter"~descr:(Md.plain"Information about a Frama-C parameter")jparametermoduleR:Record.Swithtyper=parameter=(valdata)typet=Typed_parameter.tletjtype=R.jtypeletto_json(parameter:t)=R.default|>R.setnameparameter.name|>R.sethelpparameter.help|>R.settypparameter|>R.setstate(camlCaseParameterNameparameter.name)|>R.setrangeparameter|>R.setis_set(parameter.is_set())|>R.to_jsonletof_json_=Data.failure"Parameter.of_json not implemented"endmodulePluginData=structtypepluginletjplugin:pluginRecord.signature=Record.signature()letfieldname=letdescr=Md.plain("Plug-in "^name)inRecord.fieldjplugin~name~descr(moduleJstring)letname=field"name"letshortname=field"shortname"lethelp=field"help"letdata=Record.publish~package~name:"plugin"~descr:(Md.plain"Information about a Frama-C plug-in")jpluginmoduleR:Record.Swithtyper=plugin=(valdata)typet=Plugin.pluginletjtype=R.jtypeletto_json(plugin:t)=R.default|>R.setnameplugin.p_name|>R.setshortnameplugin.p_shortname|>R.sethelpplugin.p_help|>R.to_jsonletof_json_=Data.failure"Plugin.of_json not implemented"endlet()=Request.register~package~kind:`GET~name:"getPlugins"~descr:(Md.plain"Return the list of available Frama-C plug-ins")~input:(moduleJunit)~output:(moduleJlist(PluginData))(fun()->Plugin.fold_on_plugins(funpacc->p::acc)[])let()=Request.register~package~kind:`GET~name:"getPluginParameters"~descr:(Md.plain"Return the list of parameters of a Frama-C plug-in")~input:(moduleJstring)~output:(moduleJlist(Jpair(Jstring)(Jlist(ParameterData))))beginfunname->tryletplugin=Plugin.get_from_namenameinletaddgroupparamsacc=(group,List.filteris_exported_parameterparams)::accinHashtbl.foldaddplugin.p_parameters[]withNot_found->Data.failure"No plug-in of name %S"nameendlet()=Request.register~package~kind:`GET~name:"isSetParameter"~descr:(Md.plain"Has the given parameter been set?")~input:(moduleJstring)~output:(moduleJbool)beginfunname->try(Typed_parameter.getname).is_set()withNot_found->Data.failure"No parameter of name %S"nameend(* Registers a synchronized state for the given parameter. *)letregister_parameterparameter=letopenTyped_parameterinletparameter_name=parameter.nameinletdescr=Md.plain("State of parameter "^parameter_name)inletname=camlCaseParameterNameparameter_nameinletregisterdataaccessor=letadd_hookf=accessor.add_update_hook(fun_x->fx)inignore(States.register_state~package~name~descr~data~get:accessor.get~set:accessor.set~add_hook())inmatchparameter.accessorwith|Bool(accessor,_)->register(moduleData.Jbool)accessor|Int(accessor,_)->register(moduleData.Jint)accessor|Float(accessor,_)->register(moduleData.Jfloat)accessor|String(accessor,_)->register(moduleData.Jstring)accessor(* Registers requests for all parameters of the given plugin. *)letregister_plugin_parametersplugin=letregister_group_grouplist=List.iterregister_parameter(List.filteris_exported_parameterlist)inHashtbl.iterregister_groupplugin.Plugin.p_parameters(* Automatically registers requests for all Frama-C parameters. *)letregister_all()=Plugin.iter_on_pluginsregister_plugin_parametersletapply_once=letonce=reftrueinfunf()->if!oncethen(once:=false;f())let()=Cmdline.run_after_extended_stage(apply_onceregister_all)(* -------------------------------------------------------------------------- *)(* --- Frama-C Kernel Services --- *)(* -------------------------------------------------------------------------- *)letpackage=Pkg.package~name:"services"~title:"Kernel Services"~readme:"kernel.md"()(* -------------------------------------------------------------------------- *)(* --- Config --- *)(* -------------------------------------------------------------------------- *)let()=letsignature=Request.signature~input:(moduleJunit)()inletresultnamedescr=Request.resultsignature~name~descr:(Md.plaindescr)(moduleJstring)inletresult_listnamedescr=Request.resultsignature~name~descr:(Md.plaindescr)(moduleJlist(Jstring))inletset_version=result"version""Frama-C version"inletset_codename=result"codename""Frama-C codename"inletset_version_codename=result"version_codename""Frama-C version and codename"inletset_datadir=result_list"datadir""Shared directory (FRAMAC_SHARE)"inletset_pluginpath=result_list"pluginpath""Plugin directories (FRAMAC_PLUGIN)"inRequest.register_sig~package~kind:`GET~name:"getConfig"~descr:(Md.plain"Frama-C Kernel configuration")signaturebeginfunrq()->set_versionrqSystem_config.Version.id;set_codenamerqSystem_config.Version.codename;set_version_codenamerqSystem_config.Version.id_and_codename;set_datadirrq(Filepath.to_string_listSystem_config.Share.dirs);set_pluginpathrq(Filepath.to_string_listSystem_config.Plugins.dirs);end(* -------------------------------------------------------------------------- *)(* --- Load saves --- *)(* -------------------------------------------------------------------------- *)let()=Request.register~package~kind:`SET~name:"load"~descr:(Md.plain"Load a save file. Returns an error, if not successful.")~input:(moduleJfile)~output:(moduleJoption(Jstring))(funfile->tryProject.load_allfile;NonewithProject.IOErrorerr->Someerr)let()=Request.register~package~kind:`SET~name:"save"~descr:(Md.plain"Save the current session. Returns an error, if not successful.")~input:(moduleJfile)~output:(moduleJoption(Jstring))(funfile->tryProject.save_allfile;NonewithProject.IOErrorerr->Someerr)(* -------------------------------------------------------------------------- *)(* --- Log kind --- *)(* -------------------------------------------------------------------------- *)moduleLogKind=structletkinds=Enum.dictionary()lett_kindvaluenamedescr=Enum.tag~name~descr:(Md.plaindescr)~valuekindslett_error=t_kindLog.Error"ERROR""User Error"lett_warning=t_kindLog.Warning"WARNING""User Warning"lett_feedback=t_kindLog.Feedback"FEEDBACK""Plugin Feedback"lett_result=t_kindLog.Result"RESULT""Plugin Result"lett_failure=t_kindLog.Failure"FAILURE""Plugin Failure"lett_debug=t_kindLog.Debug"DEBUG""Analyser Debug"let()=Enum.set_lookupkindsbeginfunction|Log.Error->t_error|Log.Warning->t_warning|Log.Feedback->t_feedback|Log.Result->t_result|Log.Failure->t_failure|Log.Debug->t_debugendletdata=Request.dictionary~package~name:"logkind"~descr:(Md.plain"Log messages categories.")kindsinclude(valdata:Swithtypet=Log.kind)end(* -------------------------------------------------------------------------- *)(* --- Synchronized array of log events --- *)(* -------------------------------------------------------------------------- *)letmodel=States.model()let()=States.columnmodel~name:"kind"~descr:(Md.plain"Message kind")~data:(moduleLogKind)~get:(fun(evt,_)->evt.Log.evt_kind)let()=States.columnmodel~name:"plugin"~descr:(Md.plain"Emitter plugin")~data:(moduleJalpha)~get:(fun(evt,_)->evt.Log.evt_plugin)let()=States.columnmodel~name:"message"~descr:(Md.plain"Message text")~data:(moduleJstring)~get:(fun(evt,_)->Log.Event.messageevt)let()=States.optionmodel~name:"category"~descr:(Md.plain"Message category (only for debug or warning messages)")~data:(moduleJstring)~get:(fun(evt,_)->evt.Log.evt_category)let()=States.optionmodel~name:"source"~descr:(Md.plain"Source file position")~data:(moduleKernel_ast.Position)~get:(fun(evt,_)->evt.Log.evt_source)letgetMarker(evt,_id)=Option.bindPrinter_tag.loc_to_localizableevt.Log.evt_sourceletgetDeclt=Option.bindPrinter_tag.declaration_of_localizable(getMarkert)let()=States.optionmodel~name:"marker"~descr:(Md.plain"Marker at the message position (if any)")~data:(moduleKernel_ast.Marker)~get:getMarkerlet()=States.optionmodel~name:"decl"~descr:(Md.plain"Declaration containing the message position (if any)")~data:(moduleKernel_ast.Decl)~get:getDeclletiterf=ignore(Messages.fold(funievt->f(evt,i);succi)0)letadd_reload_hookf=Project.register_after_set_current_hook~user_only:false(fun_->f())let_array=States.register_array~package~name:"message"~descr:(Md.plain"Log messages")~key:(fun(_evt,i)->string_of_inti)~iter~add_update_hook:Messages.add_hook~add_reload_hookmodel(* -------------------------------------------------------------------------- *)(* --- Log Events --- *)(* -------------------------------------------------------------------------- *)moduleLogEvent=structtyperlogletjlog:rlogRecord.signature=Record.signature()letkind=Record.fieldjlog~name:"kind"~descr:(Md.plain"Message kind")(moduleLogKind)letplugin=Record.fieldjlog~name:"plugin"~descr:(Md.plain"Emitter plugin")(moduleJalpha)letmessage=Record.fieldjlog~name:"message"~descr:(Md.plain"Message text")(moduleJstring)letcategory=Record.optionjlog~name:"category"~descr:(Md.plain"Message category (DEBUG or WARNING)")(moduleJstring)letsource=Record.optionjlog~name:"source"~descr:(Md.plain"Source file position")(moduleKernel_ast.Position)letdata=Record.publish~package~name:"log"~descr:(Md.plain"Message event record.")jlogmoduleR:Record.Swithtyper=rlog=(valdata)typet=Log.eventletjtype=R.jtypeletto_jsonevt=R.default|>R.setpluginevt.Log.evt_plugin|>R.setkindevt.Log.evt_kind|>R.setcategoryevt.Log.evt_category|>R.setsourceevt.Log.evt_source|>R.setmessage(Log.Event.messageevt)|>R.to_jsonletof_jsonjs=letr=R.of_jsonjsin{Log.evt_plugin=R.getpluginr;Log.evt_kind=R.getkindr;Log.evt_category=R.getcategoryr;Log.evt_source=R.getsourcer;Log.evt_message=Rich_text.of_string(R.getmessager);}end(* -------------------------------------------------------------------------- *)(* --- Log Monitoring --- *)(* -------------------------------------------------------------------------- *)letmonitoring=reffalseletmonitored=reffalseletevents:Log.eventQueue.t=Queue.create()letset_monitoringflag=ifflag!=!monitoringthenmonitoring:=flag;if!monitoring&¬!monitoredthenbeginmonitored:=true;Log.add_listener(funevt->if!monitoringthenQueue.addevtevents)endletmonitor_serveractivity=ifnot(Senv.AutoLog.get())thenset_monitoringactivityletmonitor_autologs()=ifSenv.AutoLog.get()thenbeginSenv.feedback"Auto-log started.";set_monitoringtrue;endlet()=Main.onmonitor_server;Cmdline.run_after_configuring_stagemonitor_autologs(* -------------------------------------------------------------------------- *)(* --- Log Requests --- *)(* -------------------------------------------------------------------------- *)(* TODO:LC: shall have an array here. *)let()=Request.register~package~kind:`SET~name:"setLogs"~descr:(Md.plain"Turn logs monitoring on/off")~input:(moduleJbool)~output:(moduleJunit)set_monitoringlet()=Request.register~package~kind:`GET~name:"getLogs"~descr:(Md.plain"Flush the last emitted logs since last call (max 100)")~input:(moduleJunit)~output:(moduleJlist(LogEvent))beginfun()->letpool=ref[]inletcount=ref100inwhilenot(Queue.is_emptyevents)&&!count>0dodecrcount;pool:=Queue.popevents::!pooldone;List.rev!poolend(* -------------------------------------------------------------------------- *)