123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443(**************************************************************************)(* *)(* 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_typesopenCil_datatypeletcompare_vi_namesv1v2=Extlib.compare_ignore_casev1.vnamev2.vnameclasscoverageAuxVisitor~libc=object(self)inheritVisitor.frama_c_inplace(* Visit the body and the spec of a function *)methodprivatevisit_functionvi=letkf=Globals.Functions.getviinletself=(self:>Visitor.frama_c_visitor)inifMetrics_base.consider_function~libcvithenbegin(* Visit the spec. There might be references to function pointers in
the assigns *)letspec=Annotations.funspeckfinignore(Visitor.visitFramacFunspecselfspec);end;(try(* Visit the body if we have one *)letfundec=Kernel_function.get_definitionkfinignore(Visitor.visitFramacFunctionselffundec);withKernel_function.No_Definition->())(* Visit the initializer of the given var, if it exists, and returns it *)methodprivatevisit_non_function_varvi=ifMetrics_base.consider_variable~libcvithentry(* Visit the initializer if there is one *)letinit=Globals.Vars.findviinmatchinitwith|{init=None}->None|{init=Someinit}->ignore(Visitor.visitFramacInit(self:>Visitor.frama_c_visitor)viNoOffsetinit);SomeinitwithNot_found->(* not a global *)NoneelseNoneendletdkey_syn=Metrics_parameters.register_category"syntactic-visitor"(* Reachability metrics: from a given entry point
compute a conservative estimation
of the functions that can be transitively called *)classcallableFunctionsVisitor~libc=object(self)inheritcoverageAuxVisitor~libcassuper(* Functions reachable syntactically *)valmutablecallable=Varinfo.Set.empty(* All globals initializers visited *)valmutableinitializers=[]methodinitializers=initializers(* All varinfos visited so far. Used to avoid looping *)valvisited=Varinfo.Hashtbl.create17(* Varinfos remaining to visit *)valtodo=Stack.create()methodalready_seenvi=Varinfo.Hashtbl.memvisitedvi(* Each time we see a variable, mark it as to be visited. If it is a function,
consider it is called *)method!vvrblvi=ifnot(self#already_seenvi)thenbeginifCil.isFunctionTypevi.vtype&&Metrics_base.consider_function~libcvithenbeginMetrics_parameters.feedback~dkey:dkey_syn"marking %a as callable"Printer.pp_varinfovi;callable<-Varinfo.Set.addvicallable;end;Stack.pushvitodo;end;Cil.SkipChildren(* no children anyway *)method!visit_non_function_varvi=letr=super#visit_non_function_varviin(matchrwith|None->()|Someinit->initializers<-(vi,init)::initializers);rmethodcomputevi=(* Initialization *)Stack.cleartodo;Stack.pushvitodo;Varinfo.Hashtbl.clearvisited;Metrics_parameters.feedback~dkey:dkey_syn"marking %a as callable"Printer.pp_varinfovi;ifMetrics_base.consider_function~libcvithencallable<-Varinfo.Set.singletonvi;(* Reach fixpoint *)whilenot(Stack.is_emptytodo)doletvi=Stack.poptodoinifnot(self#already_seenvi)thenbeginMetrics_parameters.debug"Coverage: visiting %s"vi.vname;Varinfo.Hashtbl.addvisitedvi();ifCil.isFunctionTypevi.vtypethenself#visit_functionvielseignore(self#visit_non_function_varvi)end;done;callableendtypecoverage_metrics={syntactic:Cil_datatype.Varinfo.Set.t;semantic:Cil_datatype.Varinfo.Set.t;initializers:(Cil_types.varinfo*Cil_types.init)list;}classdeadCallsVisitorfmt~libccov_metrics=letunseen=Varinfo.Set.diffcov_metrics.syntacticcov_metrics.semanticinobject(self)inheritcoverageAuxVisitor~libcvalmutablecurrent_initializer=None(* When an unseen function is reachable by the body of a function reached,
or inside an initializer, display the information *)methodprivatereached_funvi=ifMetrics_base.consider_function~libcvi&&Varinfo.Set.memviunseenthenmatchself#current_kfwith|None->(matchcurrent_initializerwith|None->(* This can happen in the rare cases a function is referenced by
a specification *)Format.fprintffmt"@[<h>%s referenced by an ACSL specification (at %a)@]@ "vi.vnameLocation.pretty(Cil.CurrentLoc.get())|Somevinit->Format.fprintffmt"@[<h>Initializer of %s references %s (at %t)@]@ "vinit.vnamevi.vnameCil.pp_thisloc)|Somef->ifVarinfo.Set.mem(Kernel_function.get_vif)cov_metrics.semanticthenletmess=matchself#current_stmtwith|Some{skind=Instr(Call(_,{enode=Lval(Varv,NoOffset)},_,_)|Local_init(_,ConsInit(v,_,_),_))}whenVarinfo.equalvvi->"calls"|_->"references"inFormat.fprintffmt"@[<h>Function %a %s %s (at %a)@]@ "Kernel_function.prettyfmessvi.vnameLocation.pretty(Cil.CurrentLoc.get())method!vvrblvi=ifCil.isFunctionTypevi.vtypethenself#reached_funvi;Cil.SkipChildren(* no children anyway *)(* uses initializers *)methodcompute_and_print=ifnot(Varinfo.Set.is_emptyunseen)||cov_metrics.initializers<>[]thenbeginFormat.fprintffmt"@[<v>%a@ "(Metrics_base.mk_hdr2)"References to non-analyzed functions";letsorted_semantic=List.sortcompare_vi_names(Varinfo.Set.elementscov_metrics.semantic)inList.iterself#visit_functionsorted_semantic;letsorted_initializers=List.sort(fun(v1,_)(v2,_)->compare_vi_namesv1v2)cov_metrics.initializersinList.iter(fun(vinit,init)->current_initializer<-Somevinit;ignore(Visitor.visitFramacInit(self:>Visitor.frama_c_visitor)vinitNoOffsetinit);current_initializer<-None;)sorted_initializers;Format.fprintffmt"@]"endendclasscoverageByFun=objectinheritVisitor.frama_c_inplacevalmutabletotal=0valmutablevalue=0method!vstmts=total<-total+1;ifEva.Results.is_reachablesthenvalue<-value+1;Cil.DoChildrenmethodresult=(total,value)endmoduleKf_Coverage=Kernel_function.Make_Table(Datatype.Triple(Datatype.Int)(Datatype.Int)(Datatype.Float))(structletname="Metrics_coverage.Kf_coverage"letsize=7letdependencies=[Eva.Analysis.self;Metrics_parameters.Libc.self]end)letis_computed_by_fun()=Kf_Coverage.length()>0letget_coverage=Kf_Coverage.findletcompute_coverage_forkf=tryletdec=Kernel_function.get_definitionkfinletvis=newcoverageByFuninignore(Visitor.visitFramacFunction(vis:>Visitor.frama_c_visitor)dec);let(total,value)=vis#resultinletpercent=(float_of_intvalue)/.(float_of_inttotal)*.100.inKf_Coverage.replacekf(total,value,percent)withKernel_function.No_Definition->()letcompute_coverage_by_fun()=ifEva.Analysis.is_computed()&¬(is_computed_by_fun())thenletlibc=Metrics_parameters.Libc.get()inGlobals.Functions.iter(funkf->ifEva.Results.is_calledkf&&Metrics_base.consider_function~libc(Kernel_function.get_vikf)thencompute_coverage_forkf)letclear_coverage_by_fun=Kf_Coverage.clearletcompute_syntactic~libckf=letvis=newcallableFunctionsVisitor~libcinletres=vis#compute(Kernel_function.get_vikf)inres,vis#initializers;;letdkey_sem=Metrics_parameters.register_category"semantic-visitor"letcompute_semantic~libc=assert(Eva.Analysis.is_computed());letres=refVarinfo.Set.emptyin(* Just iter on all the functions and consult the appropriate table *)Globals.Functions.iter(funkf->ifEva.Results.is_calledkf&&Metrics_base.consider_function~libc(Kernel_function.get_vikf)thenbeginMetrics_parameters.feedback~dkey:dkey_sem"marking %a as called"Kernel_function.prettykf;res:=Varinfo.Set.add(Kernel_function.get_vikf)!resend);!res;;letcardinality~libcs=Varinfo.Set.cardinal(Varinfo.Set.filter(funvi->Metrics_base.consider_function~libcvi)s)classsyntactic_printer~libcreachable=object(self)methodprivateall_funs=Globals.Functions.fold(funkfacc->letvi=Kernel_function.get_vikfinifMetrics_base.consider_function~libcvithenVarinfo.Set.addviaccelseacc)Varinfo.Set.emptymethodprivatepp_fun_set_by_filefmtset=letadd_bindingmapfilenamefvinfo=letset=tryletx=Datatype.Filepath.Map.findfilenamemapinVarinfo.Set.addfvinfoxwithNot_found->Varinfo.Set.addfvinfoVarinfo.Set.emptyinDatatype.Filepath.Map.addfilenamesetmapinletmap=Varinfo.Set.fold(funfvinfoacc->ifMetrics_base.consider_function~libcfvinfothenletpath=Metrics_base.file_of_vinfodeffvinfoinadd_bindingaccpathfvinfoelseacc)setDatatype.Filepath.Map.emptyinFormat.fprintffmt"@[<v 0>";Datatype.Filepath.Map.iter(funpathfvinfoset->Format.fprintffmt"@[<hov 2><%a>:@ %a@]@ "Datatype.Filepath.prettypath(funfmtvinfoset->letvars=Varinfo.Set.elementsvinfosetinletsorted_vars=List.sortcompare_vi_namesvarsinList.iter(funvinfo->Format.fprintffmt"%a;@ "Printer.pp_varinfovinfo)sorted_vars)fvinfoset)map;Format.fprintffmt"@]"methodpp_reached_from_functionfmtkf=letcard_syn=cardinality~libcreachableinlettitle_reach=Format.asprintf"%a: %d"Kernel_function.prettykfcard_syninletall=self#all_funsinletcard_all=cardinality~libcallinlettitle_unreach=Format.asprintf"%a: %d"Kernel_function.prettykf(card_all-card_syn)inFormat.fprintffmt"@[<v 0>%a@ %a@ %a@ %a@]"(Metrics_base.mk_hdr2)(Format.sprintf"Functions syntactically reachable from %s"title_reach)self#pp_fun_set_by_filereachable(Metrics_base.mk_hdr2)(Format.sprintf"Functions syntactically unreachable from %s"title_unreach)self#pp_fun_set_by_file(Varinfo.Set.diffallreachable)endclasssemantic_printer~libc(cov_metrics:coverage_metrics)=object(self)inheritsyntactic_printer~libccov_metrics.syntactic(* uses semantic and initializers *)methodpp_unreached_callsfmt=letv=newdeadCallsVisitor~libcfmtcov_metricsinv#compute_and_print(* uses semantic *)methodpp_value_coveragefmt=assert(Eva.Analysis.is_computed());letall=self#all_funsinletsyntactic=cov_metrics.syntacticandsemantic=cov_metrics.semanticinletunseen=Varinfo.Set.diffsyntacticsemanticinletunseen_num=cardinality~libcunseeninletnall=cardinality~libcallinletnsyn=cardinality~libcsyntacticandnsem=cardinality~libcsemanticinletpercent=(float_of_intnsem)*.100.0/.(float_of_intnsyn)inFormat.fprintffmt"@[<v 0>\
%a@ \
Syntactically reachable functions = %d (out of %d)@ \
Semantically reached functions = %d@ \
Coverage estimation = %.1f%% @ "(Metrics_base.mk_hdr1)"Eva coverage statistics"nsynnallnsempercent;ifunseen_num>0thenFormat.fprintffmt"@ @[<v 2>Unreached functions (%d) =@ %a@]"unseen_numself#pp_fun_set_by_fileunseen;Format.fprintffmt"@]"(* uses semantic *)methodpp_stmts_reached_by_functionfmt=compute_coverage_by_fun();letl=Kf_Coverage.fold(funkf(total,value,percent)l->(kf,total,value,percent)::l)[]in(* Sort by percentage (higher first),
then sort by name (for same percentage) *)letl=List.sort(fun(kf1,_,_,p1)(kf2,_,_,p2)->letc=comparep2p1inifc=0thencomparekf1kf2elsec)linletsum_total,sum_value=List.fold_left(fun(at,av)(_,t,v,_)->at+t,av+v)(0,0)linletpercent=ifsum_total=0then0.else100.*.(float_of_intsum_value)/.(float_of_intsum_total)inFormat.fprintffmt"@[<v 0>%a@ \
%d stmts in analyzed functions, %d stmts analyzed (%.1f%%)@ "(Metrics_base.mk_hdr2)"Statements analyzed by Eva"sum_totalsum_valuepercent;List.iter(fun(kf,total,visited,percent)->Format.fprintffmt"%a: %d stmts out of %d (%.1f%%)@ "Kernel_function.prettykfvisitedtotalpercent)l;Format.fprintffmt"@]"endletpercent_coverage~libccov_metrics=letnsyn=cardinality~libccov_metrics.syntacticandnsem=cardinality~libccov_metrics.semanticinletpercent=(float_of_intnsem)/.(float_of_intnsyn)*.100.0inpercent;;letcompute~libc=assert(Eva.Analysis.is_computed());letsemantic=compute_semantic~libcinletmain=fst(Globals.entry_point())inletsyntactic,initializers=compute_syntactic~libcmainin{syntactic;semantic;initializers};;(* Reexport a simpler function *)letcompute_syntactic~libckf=fst(compute_syntactic~libckf)(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)