123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288(**************************************************************************)(* *)(* 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). *)(* *)(**************************************************************************)openCil_types(* Kernel functions with a custom function [compare] independent of vids. So the
callgraph and its iterations are independent from the vids generator and is
only dependent of the analyzed program itself. *)moduleKf_sorted=structtypet=Kernel_function.tletequal=Kernel_function.equallethashkf=Hashtbl.hash(Kernel_function.get_namekf)letcomparekf1kf2=ifkf1==kf2then0elseletres=String.compare(Kernel_function.get_namekf1)(Kernel_function.get_namekf2)inifres<>0thenreselse(* Backup solution, will compare underlying varinfos ids *)Kernel_function.comparekf1kf2endmoduleG=Graph.Imperative.Digraph.ConcreteBidirectionalLabeled(Kf_sorted)(structincludeCil_datatype.Stmtletdefault=Cil.dummyStmtend)moduleD=Datatype.Make(structtypet=G.tletname="Callgraph.Cg"letreprs=[G.create()]includeDatatype.Serializable_undefinedletmem_project=Datatype.never_any_projectend)(* State for the callgraph *)moduleState=State_builder.Option_ref(D)(structletname="Callgraph.Cg"letdependencies=[Eva.Analysis.self;Globals.Functions.self]end)moduleStateHook=Hook.Build(D)letself=State.selfletis_computed()=State.is_computed()letadd_hook=StateHook.extend(** @return the list of functions which address is taken.*)letget_pointed_kfs=(* memoized result *)letres=refNoneinfun()->letcompute()=ifOptions.Function_pointers.get()thenletl=ref[]inleto=objectinheritVisitor.frama_c_inplacemethod!vexpre=matche.enodewith|AddrOf(Varvi,NoOffset)whenCil.isFunctionTypevi.vtype->(* function pointer *)letkf=tryGlobals.Functions.getviwithNot_found->assertfalseinl:=kf::!l;Cil.SkipChildren|_->Cil.DoChildrenendinVisitor.visitFramacFileSameGlobalso(Ast.get());!lelse(* ignore function pointers when the option is off *)[]inmatch!reswith|None->letl=compute()inres:=Somel;l|Somel->lletis_entry_pointkf=tryletmain,_=Globals.entry_point()inKernel_function.equalkfmainwithGlobals.No_such_entry_point_->false(* complexity = O(number of statements);
approximate function pointers to the set of functions which address is
taken *)letsyntactic_computeg=leto=object(self)inheritVisitor.frama_c_inplace(* add only-declared functions into the graph *)method!vvdecvi=tryletkf=Globals.Functions.getviinifKernel_function.is_definitionkfthenCil.DoChildrenelsebeginG.add_vertexgkf;Cil.SkipChildrenendwithNot_found->Cil.SkipChildren(* add defined functions into the graph *)method!vfunc_f=G.add_vertexg(Option.getself#current_kf);Cil.DoChildren(* add edges from callers to callees into the graph *)method!vinst=function|Call(_,{enode=Lval(Varvi,NoOffset)},_,_)->(* direct function call *)letcallee=tryGlobals.Functions.getviwithNot_found->assertfalseinletcaller=Option.getself#current_kfinG.add_edge_eg(caller,Option.getself#current_stmt,callee);Cil.SkipChildren|Call_->(* call via a function pointer: add an edge from each function which
the address is taken to this callee. *)letpointed=get_pointed_kfs()inletcaller=Option.getself#current_kfinList.iter(funcallee->G.add_edge_eg(caller,Option.getself#current_stmt,callee))pointed;Cil.SkipChildren|Local_init(_,ConsInit(v,_,_),_)->letcallee=tryGlobals.Functions.getvwithNot_found->assertfalseinletcaller=Option.getself#current_kfinG.add_edge_eg(caller,Option.getself#current_stmt,callee);Cil.SkipChildren|Local_init(_,AssignInit_,_)|Set_|Skip_|Asm_|Code_annot_->(* skip children for efficiency *)Cil.SkipChildren(* for efficiency purpose, skip many items *)method!vexpr_=Cil.SkipChildrenmethod!vtype_=Cil.SkipChildrenmethod!vannotation_=Cil.SkipChildrenmethod!vcode_annot_=Cil.SkipChildrenmethod!vbehavior_=Cil.SkipChildrenendinVisitor.visitFramacFileSameGlobalso(Ast.get());(* now remove the potential irrelevant nodes wrt selected options *)ifnot(Options.Uncalled.get()&&Options.Uncalled_leaf.get())thenG.iter_vertex(funkf->lethas_pred=tryG.iter_pred(fun_->raiseExit)gkf;falsewithExit->trueinifnot(has_pred(* no caller *)||is_entry_pointkf)thenletmust_kept=Options.Uncalled.get()(* uncalled functions must be kept *)&&(Options.Uncalled_leaf.get()(* uncalled leaf must be kept *)||Kernel_function.is_definitionkf(* [kf] is a leaf *))inifnotmust_keptthenG.remove_vertexgkf)g(* complexity = O(number of function calls);
approximate function pointers as computed by [Value]. *)letsemantic_computeg=Globals.Functions.iter(funkf->letcallers=Eva.Results.callsiteskfinletmust_add=callers<>[](* the function is called *)||is_entry_pointkf||(Options.Uncalled.get()(* uncalled functions must be added *)&&(Options.Uncalled_leaf.get()(* uncalled leaf must be added *)||Kernel_function.is_definitionkf)(* [kf] is not a leaf *))inifmust_addthenbeginG.add_vertexgkf;List.iter(fun(caller,callsites)->List.iter(funstmt->G.add_edge_eg(caller,stmt,kf))callsites)callersend)letcompute()=letg=G.create()in(* optimize with [Value] when either it is already computed or someone
requires it anyway *)ifDynamic.Parameter.Bool.get"-eva"()thenbeginEva.Analysis.compute();semantic_computegendelse(ifEva.Analysis.is_computed()thensemantic_computeelsesyntactic_compute)g;State.mark_as_computed();StateHook.applyg;gletget()=State.memocomputeletcompute()=ignore(compute())moduleGraphviz_attributes=structincludeG(* We rewrite [iter_edges_e] so that multiple calls to the same function
from the same caller do not give rise to multi-edges. *)letiter_edges_eiterg=letaux_ev=(* This comparison function ignores the statement (as we want to
coalesce all call sites together). The first element of the triple
is always [v], so it can also be ignored. *)letcomp_e(_,_,kf1)(_,_,kf2)=Kf_sorted.comparekf1kf2inletuniq_e=List.sort_uniqcomp_e(G.succ_egv)inList.iteriteruniq_einG.iter_vertexaux_egletgraph_attributes_=[`Ratio(`Float0.5)]letvertex_name=Kernel_function.get_nameletvertex_attributeskf=[`Style(ifKernel_function.is_definitionkfthen`Boldelse`Dotted)]letedge_attributes_=[]letdefault_vertex_attributes_=[]letdefault_edge_attributes_=[]letget_subgraph_=NoneendmoduleSubgraph=Subgraph.Make(G)(D)(structletself=State.selfletname=State.nameletget=getletvertexkf=kfend)letdump()=letmoduleGV=Graph.Graphviz.Dot(Graphviz_attributes)inletg=Subgraph.get()inOptions.dumpGV.output_graphg(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)