123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242(**************************************************************************)(* *)(* 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_typestypecoverage_stats={syntactic_calls:int;indirect_calls:int;total_stmts:int;covered_stmts:int;}letadd_syntactic_callstats={statswithsyntactic_calls=stats.syntactic_calls+1}letadd_indirect_callstats={statswithindirect_calls=stats.indirect_calls+1}letempty_stats={syntactic_calls=0;indirect_calls=0;total_stmts=0;covered_stmts=0}typecall_kind=No_call|Only_indirect|Directtypecallee_info={call:call_kind;is_analyzed:bool;visited:bool;}letno_call={call=No_call;is_analyzed=false;visited=false;}letindirect_call={no_callwithcall=Only_indirect}letdirect_call={indirect_callwithcall=Direct}letvisitinfo={infowithvisited=true;}letis_analyzed_functionvi=not(Cil.is_in_libcvi.vattr)&&Kernel_function.is_definition(Globals.Functions.getvi)&¬(List.exists(funs->List.exists(funkf->Cil_datatype.Varinfo.equal(Kernel_function.get_vikf)vi)(Globals.FileIndex.get_functions(Filepath.Normalized.of_strings)))(Markdown_report.Mdr_params.Stubs.get()))&¬(List.memvi.vname(String.split_on_char','(Dynamic.Parameter.String.get"-eva-use-spec"())))&¬(List.memvi.vname(List.map(funs->List.hd(String.split_on_char':'s))(String.split_on_char','(Dynamic.Parameter.String.get"-eva-builtin"()))))letis_analyzed_infoviinfo={infowithis_analyzed=is_analyzed_functionvi;}classeva_coverage_vis~from_entry_point=object(self)inheritVisitor.frama_c_inplacevalmutablestats=empty_statsvalcalls=Cil_datatype.Varinfo.Hashtbl.create17methodprivateincr_total_stmts=stats<-{statswithtotal_stmts=stats.total_stmts+1}methodprivateincr_covered_stmts=stats<-{statswithcovered_stmts=stats.covered_stmts+1}method!vstmt_auxs=(* We only consider real statements: Blocks do not count. *)matchs.skindwith|Block_|UnspecifiedSequence_->Cil.DoChildren|_->self#incr_total_stmts;ifEva.Results.is_reachablesthenself#incr_covered_stmts;Cil.DoChildrenmethod!vinsti=matchiwith|Call(_,{enode=Lval(Varvi,NoOffset)},_,_)|Local_init(_,ConsInit(vi,_,_),_)->ifCil_datatype.Varinfo.Hashtbl.memcallsvithenbeginletinfo=Cil_datatype.Varinfo.Hashtbl.findcallsviinCil_datatype.Varinfo.Hashtbl.replacecallsvi{infowithcall=Direct}endelsebeginCil_datatype.Varinfo.Hashtbl.addcallsvi(is_analyzed_infovidirect_call)end;Cil.SkipChildren|Call(_,{enode=Lval(Mem_,NoOffset)},_,_)->lets=Option.getself#current_stmtinletkfs=Eva.Results.calleesinlethandle_onekf=letvi=Kernel_function.get_vikfinifnot(Cil_datatype.Varinfo.Hashtbl.memcallsvi)thenbeginCil_datatype.Varinfo.Hashtbl.addcallsvi(is_analyzed_infoviindirect_call)endelsebeginletinfo=Cil_datatype.Varinfo.Hashtbl.findcallsviinifinfo.call=No_callthenbeginCil_datatype.Varinfo.Hashtbl.replacecallsvi{infowithcall=Only_indirect}endendinList.iterhandle_onekfs;Cil.SkipChildren|_->Cil.SkipChildren(* No need to go further. *)methodcompute()=lettreat_callviinforeached=letmust_visit=notinfo.visited&&info.is_analyzedinCil_datatype.Varinfo.Hashtbl.replacecallsvi(visitinfo);ifmust_visitthenbeginletkf=Globals.Functions.getviinignore(Visitor.visitFramacKf(self:>Visitor.frama_c_inplace)kf);end;reached&¬must_visitinletcheck_fixpoint()=Cil_datatype.Varinfo.Hashtbl.foldtreat_callcallstrueinifnotfrom_entry_pointthenbeginGlobals.Functions.iter_on_fundecs(fun{svar}->Cil_datatype.Varinfo.Hashtbl.addcallssvar(is_analyzed_infosvarno_call))end;letvi=Globals.Functions.get_vi(Globals.Functions.find_by_name(Kernel.MainFunction.get()))in(* main entry point might be a stub, but we still would like
to collect non-stubs calls from it.
*)letinfo=is_analyzed_infovidirect_callinCil_datatype.Varinfo.Hashtbl.replacecallsvi{infowithis_analyzed=true};whilenot(check_fixpoint())do()done;Cil_datatype.Varinfo.Hashtbl.fold(fun_infostats->ifinfo.is_analyzedthenbeginmatchinfo.callwith|Direct->add_syntactic_callstats|Only_indirect->add_indirect_callstats|No_call->statsendelsestats)callsstatsendletnb_fundefs()=Globals.Functions.fold(funkfnb->ifKernel_function.is_definitionkf&&is_analyzed_function(Kernel_function.get_vikf)thennb+1elsenb)0openMarkdownletcoverage_md_gen()=letmain=Kernel.MainFunction.get()inEva.Analysis.compute();letvis=neweva_coverage_vis~from_entry_point:falseinletstats=vis#compute()inletsummary_whole=Markdown.format"There are %d function definitions that are not stubbed. They represent \
%d statements, of which %d are potentially reachable through Eva, \
resulting in a **statement coverage of %.1f%%** with respect to the \
entire application."(nb_fundefs())stats.total_stmtsstats.covered_stmts(float_of_intstats.covered_stmts*.100./.float_of_intstats.total_stmts)inletvis=neweva_coverage_vis~from_entry_point:trueinletstats=vis#compute()inletsummary=Markdown.format"There were potentially %d functions syntactically reachable from %s."stats.syntactic_callsmaininletsummary=ifstats.indirect_calls=0thensummaryelsesummary@Markdown.format"In addition, %d were found potentially reachable through \
indirect calls."stats.indirect_callsinletsummary=summary@Markdown.format"These functions contain %d statements, \
of which %d are potentially reachable according to Eva, resulting in \
a **statement coverage of %.1f%%** with respect to the perimeter set \
by this entry point."stats.total_stmtsstats.covered_stmts(float_of_intstats.covered_stmts*.100./.float_of_intstats.total_stmts)in[Block[Textsummary_whole];Block[Textsummary]]letdomains_md_gen()=leteva_domains=Eva.Parameters.enabled_domains()inletdomains=List.filter(fun(name,_)->name<>"cvalue")eva_domainsinletaux(name,descr)=(plain"domain"@boldname),plaindescrinList.mapauxdomainslet()=Markdown_report.Md_gen.Eva_info.coverage_md_gen:=coverage_md_gen;Markdown_report.Md_gen.Eva_info.domains_md_gen:=domains_md_gen;Markdown_report.Md_gen.Eva_info.loaded:=true