123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179(**************************************************************************)(* *)(* 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). *)(* *)(**************************************************************************)openServermoduleG=Services.G(* --- Package declaration --- *)letpackage=Package.package~plugin:"callgraph"~title:"Callgraph"()(* --- Helper modules --- *)moduleRecord()=structmoduleRecord=Data.Recordtyperecordletrecord:recordRecord.signature=Record.signature()letfieldname?(descr=name)=Record.fieldrecord~name~descr:(Markdown.plaindescr)letpublish?descrname=letdescr=Option.mapMarkdown.plaindescrinRecord.publishrecord~package~name?descrendmoduleEnum(X:sigtypetend)=structmoduleEnum=Data.Enumletdictionary:X.tEnum.dictionary=Enum.dictionary()lettagnamedescr=Enum.tag~name~descr:(Markdown.plaindescr)dictionaryletpublishlookupnamedescr=Enum.set_lookupdictionarylookup;Request.dictionary~package~name~descr:(Markdown.plaindescr)dictionaryend(* --- Types --- *)moduleVertex=structincludeRecord()letkf=field"kf"(moduleKernel_ast.Function)~descr:"The function represented by the node"letis_root=field"is_root"Data.jbool~descr:"whether this node is the root of a service"letroot=field"root"(moduleKernel_ast.Function)~descr:"the root of this node's service"include(valpublish"vertex")typet=Cil_types.kernel_functionService_graph.vertexletto_json(v:Cil_types.kernel_functionService_graph.vertex)=default|>setkfv.node|>setis_rootv.is_root|>setrootv.root.node|>to_jsonletof_json_js=Data.failure"Vertex.of_json not implemented"endmoduleEdgeKind=structincludeEnum(structtypet=Service_graph.edgeend)letinter_services=tag"inter_services""a call between two services"letinter_functions=tag"inter_functions""a call inside a service"letboth=tag"both""both cases above"letlookup=function|Service_graph.Inter_services->inter_services|Inter_functions->inter_functions|Both->bothinclude(valpublishlookup"edgeKind""Whether the call goes through services or not")endmoduleEdge=structincludeRecord()letsrc=field"src"(moduleKernel_ast.Function)letdst=field"dst"(moduleKernel_ast.Function)letkind=field"kind"(moduleEdgeKind)include(valpublish"edge")typet=G.E.tletto_json(e:t)=default|>setsrc(G.E.srce).node|>setdst(G.E.dste).node|>setkind(G.E.labele)|>to_jsonletof_json_js=Data.failure"Edge.of_json not implemented"endmoduleGraph=structincludeRecord()letvertices=field"vertices"(moduleData.Jlist(Vertex))letedges=field"edges"(moduleData.Jlist(Edge))include(valpublish"graph"~descr:"The callgraph of the current project")typet=G.tletget_vertices(g:t)=G.fold_vertex(funvacc->v::acc)g[]letget_edges(g:t)=G.fold_edges_e(funvacc->v::acc)g[]letto_json(g:t)=default|>setvertices(get_verticesg)|>setedges(get_edgesg)|>to_jsonletof_json_js=Data.failure"Graph.of_json not implemented"end(* --- Requests --- *)let_signal=States.register_value~package~name:"callgraph"~descr:(Markdown.plain"The current callgraph or an empty graph if it has not been computed yet")~output:(moduleData.Joption(Graph))~add_hook:Services.add_hook~get:beginfun()->ifServices.is_computed()thenSome(Services.get())elseNoneend()let_signal=States.register_value~package~name:"isComputed"~descr:(Markdown.plain"This boolean is true if the graph has been computed")~output:(moduleData.Jbool)~add_hook:Services.add_hook~get:Services.is_computed()let()=Request.register~package~kind:`EXEC~name:"compute"~descr:(Markdown.plain"Compute the callgraph for the current project")~input:(moduleData.Junit)~output:(moduleData.Junit)(fun()->ignore(Services.get()))