123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* 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, version 2.1. *)(* *)(* It 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. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)openServeropenDataopenDive_typesletpackage=Package.package~plugin:"dive"~title:"Dive Services"()letdeclarename?(descr=name)=Data.declare~package~name~descr:(Markdown.plaindescr)moduleEnum(X:sigtypetend)=structincludeEnumletdictionary:X.tdictionary=Enum.dictionary()lettagnamedescr=Enum.tag~name~descr:(Markdown.plaindescr)dictionaryletpublishlookupnamedescr=set_lookupdictionarylookup;Request.dictionary~package~name~descr:(Markdown.plaindescr)dictionaryendmoduleRecord()=structincludeRecordtyperecordletrecord:recordRecord.signature=Record.signature()letfieldname?(descr=name)=Record.fieldrecord~name~descr:(Markdown.plaindescr)letoptionname?(descr=name)=Record.optionrecord~name~descr:(Markdown.plaindescr)letpublish?descrname=letdescr=Option.mapMarkdown.plaindescrinRecord.publishrecord~package~name?descrend(* -------------------------------------------------------------------------- *)(* --- Data types --- *)(* -------------------------------------------------------------------------- *)letstmt_to_locationstmt=letkf=Kernel_function.find_englobing_kfstmtin(kf,Printer_tag.PStmtStart(kf,stmt))letorigin_to_locations=function|Studia.Writes.Assigns|CallDirects->[stmt_to_locations]|CallIndirect_s->[]|GlobalInit(_vi,_init)->[]|FormalInit(_vi,callsites)->List.concat_map(fun(_,l)->List.mapstmt_to_locationl)callsitesmoduleRange:Data.Swithtypet=intoptionrange=structincludeRecord()letbackward=option"backward"~descr:"range for the write dependencies"jintletforward=option"forward"~descr:"range for the read dependencies"jintletdescr="Parametrization of the exploration range."include(valpublish"range"~descr)typet=intoptionrangeletto_jsonr=default|>setbackwardr.backward|>setforwardr.forward|>to_jsonletof_jsonjs=letr=of_jsonjsin{backward=getbackwardr;forward=getforwardr;}endmoduleWindow:Data.Swithtypet=window=structincludeRecord()letperception=letdescr="how far dive will explore from root nodes ; \
must be a finite range"infield"perception"~descr(moduleRange)lethorizon=letdescr="range beyond which the nodes must be hidden"infield"horizon"~descr(moduleRange)letdescr="Global parametrization of the exploration."include(valpublish"explorationWindow"~descr)typet=windowletto_jsonw=default|>setperceptionw.perception|>sethorizonw.horizon|>to_jsonletof_jsonjs=letr=of_jsonjsin{perception=getperceptionr;horizon=gethorizonr;}endmoduleNodeId=structincludeData.Jintletjtype=declare"nodeId"~descr:"A node identifier in the graph"jtypeendmoduleCallsite=structtypet=Cil_types.kernel_function*Cil_types.kinstrletjtype=declare"callsite"(Jrecord["fun",Jstring;"instr",Junion[Jnumber;Jtag"global"];])letoutput_kinstr=function|Cil_types.Kglobal->`String"global"|Cil_types.Kstmtstmt->`Intstmt.sidletto_json(kf,kinstr)=`Assoc[("fun",`String(Kernel_function.get_namekf));("instr",output_kinstrkinstr);]letof_json_=Data.failure"Callsite.of_json not implemented"endmoduleCallstack=structincludeData.Jlist(Callsite)letdescr="The callstack context for a node"letjtype=declare"callstack"~descrjtypeendmoduleNodeLocality=structincludeRecord()letfile=field"file"jstringletcallstack=option"callstack"(moduleCallstack)letdescr="The description of a node locality"include(valpublish"nodeLocality"~descr)typet=node_localityletto_jsont=letto_option=function[]->None|l->Somelindefault|>setfilet.loc_file|>setcallstack(to_optiont.loc_callstack)|>to_jsonletof_jsonjs=letr=of_jsonjsin{loc_file=getfiler;loc_callstack=Option.value~default:[](getcallstackr)}endmoduleNodeKind=structincludeEnum(structtypet=node_kindend)letscalar=tag"scalar""a single memory cell"letcomposite=tag"composite""a memory bloc containing cells"letscattered=tag"scattered""a set of memory locations designated by \
an lvalue"letunknown=tag"unknown""an unresolved memory location"letalarm=tag"alarm""an alarm emitted by Frama-C"letabsolute=tag"absolute""a memory location designated by a range \
of adresses"letstring=tag"string""a string literal"leterror=tag"error""a placeholder node when an error prevented \
the generation process"letconst=tag"const""a numeric constant literal"letlookup=function|Scalar_->scalar|Composite_->composite|Scattered_->scattered|Unknown_->unknown|Alarm_->alarm|AbsoluteMemory->absolute|String_->string|Const_->const|Error_->errorinclude(valpublishlookup"nodeKind""The nature of a node")endmoduleTaint=structincludeEnum(structtypet=Eva.Results.taintend)letuntainted=tag"untainted""not tainted by anything"letindirect=tag"indirect""tainted by control"letdirect=tag"direct""tainted by data"letlookup=function|Eva.Results.Direct->direct|Indirect->indirect|Untainted->untaintedinclude(valpublishlookup"taint""Taint of a memory location")endmoduleComputation=structincludeEnum(structtypet=computationend)letyes=tag"yes""all dependencies have been computed"letpartial=tag"partial""some dependencies have been explored"letno=tag"no""dependencies have not been computed"letlookup=function|Done->yes|Partial_->partial|NotDone->noletdescr="The computation state of a node read or write dependencies"include(valpublishlookup"exploration"descr)endmoduleNodeSpecialRange=structincludeEnum(structtypet=node_rangeend)letempty=tag"empty""no value ever computed for this node"letsingleton=tag"singleton""this node can only have one value"letwide=tag"wide""this node can take almost all values of its type"letlookup=function|Empty->empty|Singleton->singleton|Wide->wide|Normal_->raiseNot_foundletdescr="A qualitative description of the range of values \
that this node can take."include(valpublishlookup"nodeSpecialRange"descr)endmoduleNodeRange=structtypet=node_rangeletdescr="A qualitative or quantitative description of the range of \
values that this node can take."letjtype=declare"nodeRange"~descr(Junion[Jnumber;NodeSpecialRange.jtype])letto_json=function|Normalrange_grade->`Intrange_grade|range->NodeSpecialRange.to_jsonrangeletof_json_=Data.failure"NodeRange.of_json not implemented"endmoduleNode=structincludeRecord()letid=field"id"(moduleNodeId)letlabel=field"label"jstringletnkind=field"nkind"(moduleNodeKind)letlocality=field"locality"(moduleNodeLocality)letis_root=field"is_root"jboolletbackward_explored=field"backward_explored"(moduleComputation)letforward_explored=field"forward_explored"(moduleComputation)letwrites=field"writes"(moduleJlist(Kernel_ast.Location))letvalues=field"values"(joptionjstring)letrange=field"range"(moduleNodeRange)lettyp=option"type"jstringlettaint=option"taint"(moduleTaint)include(valpublish"node")letnode_typenode=matchNode_kind.to_lvalnode.node_kindwith|None->None|Somelval->lettyp=Cil.typeOfLvallvalinSome(Pretty_utils.to_stringCil_printer.pp_typtyp)letto_jsonnode=default|>setidnode.node_key|>setlabel(Pretty_utils.to_stringNode_kind.prettynode.node_kind)|>setnkindnode.node_kind|>setlocalitynode.node_locality|>setis_rootnode.node_is_root|>setbackward_explorednode.node_writes_computation|>setforward_explorednode.node_reads_computation|>setwrites(List.concat_maporigin_to_locationsnode.node_writes)|>setvalues(Option.map(Pretty_utils.to_stringCvalue.V.pretty)node.node_values)|>setrangenode.node_range|>settyp(node_typenode)|>settaintnode.node_taint|>to_jsonendmoduleDependency=structincludeRecord()letid=field"id"jintletsrc=field"src"(moduleNodeId)letdst=field"dst"(moduleNodeId)letdkind=field"dkind"jstringletorigins=field"origins"(moduleJlist(Kernel_ast.Location))include(valpublish"dependency"~descr:"The dependency between two nodes")letdep_kind=function|Callee->"callee"|Data->"data"|Address->"addr"|Control->"ctrl"|Composition->"comp"letto_json(n1,dep,n2)=default|>setiddep.dependency_key|>setsrcn1.node_key|>setdstn2.node_key|>setdkind(dep_kinddep.dependency_kind)|>setorigins(List.concat_maporigin_to_locationsdep.dependency_origins)|>to_jsonendmoduleElement=structtypet=Context.element=Nodeofnode|Edgeof(node*dependency*node)letdescr="A graph element, either a node or a dependency"letjtype=declare"element"~descr(Junion[Node.jtype;Dependency.jtype])letto_json=function|Nodev->Node.to_jsonv|Edgeedge->Dependency.to_jsonedgeend(* -------------------------------------------------------------------------- *)(* --- State handling --- *)(* -------------------------------------------------------------------------- *)letglobal_window=ref{perception={backward=Some2;forward=Some1};horizon={backward=None;forward=None};}letget_context=(* TODO: projectify ? *)letcontext=Context.create()infun()->ifEva.Analysis.is_computed()thencontextelseServer.Data.failure"Eva analysis not computed"letverticesg=Dive_graph.fold_vertex(funnacc->n::acc)g[]letedgesg=Dive_graph.fold_edges_e(fundacc->d::acc)g[]letoutput_graphg=`Assoc[("nodes",`List(List.mapNode.to_json(verticesg)));("deps",`List(List.mapDependency.to_json(edgesg)))]letoutput_to_jsonout_channelg=letjson=output_graphginYojson.Basic.to_channelout_channeljsonmoduleGraph=structletname="graph"letmodel=States.model()letdescr=Markdown.plain"The graph being built as a set of vertices and \
edges"letkey=function|Element.Nodev->Format.sprintf"n%d"v.node_key|Edge(_,dep,_)->Format.sprintf"d%d"dep.dependency_keylet()=States.columnmodel~name:"element"~descr:(Markdown.plain"a graph element")~data:(moduleElement)~get:(funel->el)letiterf=letcontext=get_context()inletgraph=Context.get_graphcontextinDive_graph.iter_vertex(funv->f(Element.Nodev))graph;Dive_graph.iter_edges_e(fune->f(Element.Edgee))graphlet_array=lethookf=fung->f(get_context())ginStates.register_array~package~name~descr~key~itermodel~add_update_hook:(hookContext.set_update_hook)~add_remove_hook:(hookContext.set_remove_hook)~add_reload_hook:(hookContext.set_clear_hook)endmoduleNodeId'=structtypet=nodeletjtype=NodeId.jtypeletto_jsonnode=NodeId.to_jsonnode.node_keyletof_jsonjson=letnode_key=Data.Jint.of_jsonjsonintryContext.find_node(get_context())node_keywithNot_found->Data.failure"no node '%d' in the current graph"node_keyend(* -------------------------------------------------------------------------- *)(* --- Actions --- *)(* -------------------------------------------------------------------------- *)letfinalize'context=function|None->()|Somenode->letmay_exploref=Option.iter(fundepth->f~depthcontextnode)inmay_exploreBuild.explore_backward!global_window.perception.backward;may_exploreBuild.explore_forward!global_window.perception.forward;lethorizon=!global_window.horizoninifOption.is_somehorizon.forward||Option.is_somehorizon.backwardthenBuild.reduce_to_horizoncontexthorizonnodeletfinalizecontextnode=finalize'context(Somenode)let()=Request.register~package~kind:`SET~name:"window"~descr:(Markdown.plain"Set the exploration window")~input:(moduleWindow)~output:(moduleData.Junit)(funwindow->global_window:=window)let()=Request.register~package~kind:`EXEC~name:"clear"~descr:(Markdown.plain"Erase the graph and start over with an empty one")~input:(moduleData.Junit)~output:(moduleData.Junit)(fun()->Context.clear(get_context()))let()=Request.register~package~kind:`EXEC~name:"add"~descr:(Markdown.plain"Add a node to the graph")~input:(moduleKernel_ast.Marker)~output:(moduleJoption(NodeId'))beginfunloc->letcontext=get_context()inletnode=Build.add_localizablecontextlocinfinalize'contextnode;nodeendlet()=Request.register~package~kind:`EXEC~name:"explore"~descr:(Markdown.plain"Explore the graph starting from an existing vertex")~input:(moduleNodeId')~output:(moduleData.Junit)beginfunnode->letcontext=get_context()inBuild.showcontextnode;finalizecontextnodeendlet()=Request.register~package~kind:`EXEC~name:"show"~descr:(Markdown.plain"Show the dependencies of an existing vertex")~input:(moduleNodeId')~output:(moduleData.Junit)beginfunnode->letcontext=get_context()inBuild.showcontextnode;Build.explore_backward~depth:1contextnode;finalize'contextNoneendlet()=Request.register~package~kind:`EXEC~name:"hide"~descr:(Markdown.plain"Hide the dependencies of an existing vertex")~input:(moduleNodeId')~output:(moduleData.Junit)beginfunnode->letcontext=get_context()inBuild.hidecontextnode;finalize'contextNoneend