123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)moduleKernel_file=FileopenCil_datatypeopenCil_typesopenMetrics_base;;typecilast_metrics={fundecl_calls:intMetrics_base.VInfoMap.t;(* undefined and unspecified *)funspec_calls:intMetrics_base.VInfoMap.t;(* undefined, but with spec *)fundef_calls:intMetrics_base.VInfoMap.t;(* defined *)extern_global_vars:Metrics_base.VInfoSet.t;basic_global_metrics:BasicMetrics.t}(** Syntactic metrics
=================
The goal is to collect various (syntactic) information about the source code
(slocs, assignments, loops, ...).
From those one can compute McCabe's cyclomatic complexity.
*)classtypesloc_visitor=objectinheritVisitor.generic_frama_c_visitor(* Get the number of times a function has been called if it has been
defined (fundef), specified (funspec), or just declared (fundecl).
*)methodfundecl_calls:intMetrics_base.VInfoMap.tmethodfunspec_calls:intMetrics_base.VInfoMap.tmethodfundef_calls:intMetrics_base.VInfoMap.t(* Global variables with 'Extern' storage *)methodextern_global_vars:Metrics_base.VInfoSet.t(* Get the computed metrics *)methodget_global_metrics:BasicMetrics.t(* Print the metrics of a file [Filepath.t] to a formatter
Yields a fatal error if the file does not exist (or has no metrics).
*)methodpp_file_metrics:Format.formatter->Filepath.t->unitmethodpp_detailed_text_metrics:Format.formatter->unit(** Print results of all file and functions to the given formatter as text *)methodprint_stats:Format.formatter->unit(** Print computed metrics to a formatter *)methodget_metrics_map:(BasicMetrics.tOptionKf.Map.t)Filepath.Map.t(** Compute and return per-function metrics *)end(* Various metrics computing visitor on Cil AST.
These metrics are a necessary step to compute cyclomatic complexity.
*)openBasicMetrics;;classslocVisitor~libc:sloc_visitor=object(self)inheritVisitor.frama_c_inplace(* Global metrics store for this Cil AST *)valglobal_metrics=refBasicMetrics.empty_metrics(* Local metrics in computation *)vallocal_metrics=refBasicMetrics.empty_metrics(* Local metrics are kept stored after computation in this map of maps.
Its storing hierarchy is as follows: filename -> function_name -> metrics
*)valmutablemetrics_map:(BasicMetrics.tOptionKf.Map.t)Filepath.Map.t=Filepath.Map.emptyvalmutableseen_vars=Varinfo.Set.empty;valfundecl_calls:intVInfoMap.tref=refVInfoMap.empty;valfunspec_calls:intVInfoMap.tref=refVInfoMap.empty;valfundef_calls:intVInfoMap.tref=refVInfoMap.empty;valextern_global_vars=refVInfoSet.empty(* Getters/setters *)methodfundecl_calls=!fundecl_callsmethodfunspec_calls=!funspec_callsmethodfundef_calls=!fundef_callsmethodextern_global_vars=!extern_global_varsmethodget_global_metrics=!global_metricsmethodget_metrics_map=metrics_mapmethodprivateupdate_metrics_mapfilenamekfmap=metrics_map<-Filepath.Map.addfilenamekfmapmetrics_map(* Utility method to increase metrics counts *)methodprivateincr_both_metricsf=apply_then_setfglobal_metrics;apply_then_setflocal_metricsmethodprivateadd_mapmapvinfovalue=map:=VInfoMap.addvinfovalue!mapmethodprivatestats_of_filenamefilename=tryFilepath.Map.findfilenamemetrics_mapwith|Not_found->Metrics_parameters.fatal"Metrics for file %a not_found@."Filepath.prettyfilenamemethodpp_file_metricsfmtfilename=Format.fprintffmt"@[<v 0>%a@]"(funfmtfilename->letfun_tbl=self#stats_of_filenamefilenameinOptionKf.Map.iter(fun_fun_namefmetrics->Format.fprintffmt"@ %a"pp_base_metricsfmetrics)fun_tbl;)filenamemethodpp_detailed_text_metricsfmt=Filepath.Map.iter(funfilename_func_tbl->Format.fprintffmt"%a"self#pp_file_metricsfilename)metrics_mapmethodprint_statsfmt=Format.pp_set_formatter_stag_functionsfmtMetrics_base.html_stag_functions;Format.pp_set_tagsfmttrue;letpr_hdrfmthdr_name=Format.fprintffmt"@{<th>%s@}"hdr_nameinFilepath.Map.iter(funfilenamefunc_tbl->Metrics_parameters.result~level:2"%a"self#pp_file_metricsfilename;iffunc_tbl<>OptionKf.Map.emptythenbeginFormat.fprintffmt"@[<v 0>@{<h3>%a@}<br/>@ \
@{<table>\
@[<v 2>@ \
@[<v 2>@{<tbody>@ \
@{<tr>@[<v 2>@ \
%a@ %a@ %a@ %a@ %a@ %a@ %a@ %a@ %a@ @]@}@ \
%a@ \
@}@]@]@ @} \
@]@ "Filepath.prettyfilenamepr_hdr"Function"pr_hdr"#If stmts"pr_hdr"#Assignments"pr_hdr"#Loops"pr_hdr"#Calls"pr_hdr"#Gotos"pr_hdr"#Pointer dereferencing"pr_hdr"#Exits"pr_hdr"Cyclomatic value"(funfmtfun_tbl->OptionKf.Map.iter(fun_fnamefmetrics->Format.fprintffmt"%a"pp_base_metrics_as_html_rowfmetrics;)fun_tbl)func_tbl;endelseMetrics_parameters.warning"Filename <%a> has no functions@."Filepath.prettyfilename)metrics_map(* Save the local metrics currently computed.
Clears it before starting a new metrics computation (e.g. when entering a new
function definition.
Global metrics are never reset as they define metrics on the whole Cil.file.
*)methodprivaterecord_and_clear_function_metricsmetrics=letfilename=metrics.cfile_nameinletfuncname=metrics.cfuncinlocal_metrics:=BasicMetrics.set_cyclo!local_metrics(BasicMetrics.compute_cyclo!local_metrics);global_metrics:=BasicMetrics.set_cyclo!global_metrics(!global_metrics.ccyclo+!local_metrics.ccyclo);(tryletfun_tbl=Filepath.Map.findfilenamemetrics_mapinself#update_metrics_mapfilename(OptionKf.Map.addfuncname!local_metricsfun_tbl);with|Not_found->letnew_kfmap=OptionKf.Map.addfuncname!local_metricsOptionKf.Map.emptyinself#update_metrics_mapfilenamenew_kfmap;);local_metrics:=empty_metrics;method!vvdecvi=ifnot(Varinfo.Set.memviseen_vars)then(ifAst_types.is_funvi.vtypethen(ifconsider_function~libcvithenbeginglobal_metrics:=incr_funcs!global_metrics;(* Mark the function as seen, adding 0 to the number of calls *)self#update_call_mapsvi0;end)else(ifvi.vglob&¬vi.vtemp&&Metrics_base.consider_variable~libcvithen(global_metrics:=incr_glob_vars!global_metrics;ifvi.vstorage=Externthenextern_global_vars:=VInfoSet.addvi!extern_global_vars));seen_vars<-Varinfo.Set.addviseen_vars;);Cil.SkipChildrenmethod!vfuncfdec=ifconsider_function~libcfdec.svarthenbegin(* Here, we get to a fundec definition.this function has a body,
let's put it to the "function with source" table. *)local_metrics:={!local_metricswithcfile_name=file_of_fundeffdec;cfunc=Some(Globals.Functions.getfdec.svar);cfuncs=1;(* Only one function is indeed being defined here *)};letfvinfo=fdec.svarin(ifnot(VInfoMap.memfvinfo!fundef_calls)then(* Never seen before, including never been called *)self#add_mapfundef_callsfvinfo0);(* On return record the analysis of the function. *)Cil.ChangeDoChildrenPost(fdec,fun_->beginif!local_metrics<>empty_metricsthenself#record_and_clear_function_metrics!local_metrics;fdec;end);endelseCil.SkipChildrenmethod!vlhosthost=beginmatchhostwith|Mem_->self#incr_both_metricsincr_ptrs;|_->()end;Cil.DoChildrenmethod!vstmts=self#incr_both_metricsincr_slocs;letdo_children=matchs.skindwith|If_->self#incr_both_metricsincr_ifs;self#incr_both_metricsincr_dpoints;true|Loop_->self#incr_both_metricsincr_loops;true|Goto_->self#incr_both_metricsincr_gotos;true|Return_->self#incr_both_metricsincr_exits;true|Switch(_,_,_slist,_)->true(* The catching block is one more possible flow alternative *)|TryFinally_|TryExcept_->self#incr_both_metricsincr_dpoints;true|UnspecifiedSequencel->List.iter(fun(s,_,_,_,_)->ignore(Visitor.visitFramacStmt(self:>Visitor.frama_c_visitor)s))l;false|_->truein(* Default cases are not path choice points, as normal labels.
Non-default cases are ... just like if statements.
*)letrechas_case_labellabels=matchlabelswith|(Case_)::_->self#incr_both_metricsincr_dpoints;|_::labels->has_case_labellabels|[]->()inhas_case_labels.labels;ifdo_childrenthenCil.DoChildrenelseCil.SkipChildrenmethod!vexpre=begin(* Logical ANDs and ORs are lazy and generate two different paths *)matche.enodewith|BinOp((LAnd|LOr),_,_,_)->self#incr_both_metricsincr_dpoints;|_->()end;Cil.DoChildrenmethodprivateimage(glob:global)=(* extract just the name of the global , for printing purposes *)matchglobwith|GVar(v,_,_)->v.vname^" (GVar) "|GVarDecl(v,_)->v.vname^" (GVarDecl) "|GFunDecl(_,v,_)->v.vname^" (GFunDecl) "|GFun(fdec,_)->fdec.svar.vname^" (GFun) "|GType(ty,_)->ty.tname|GCompTag(ci,_)|GCompTagDecl(ci,_)->ci.cname|GEnumTagDecl(ei,_)|GEnumTag(ei,_)->ei.ename|GAsm(_,_)|GPragma_|GText_->""|GAnnot(an,_)->beginmatchanwith|Dfun_or_pred(li,_)->li.l_var_info.lv_name|Dvolatile(_,_,_,_,_)->" (Volatile) "|Daxiomatic(s,_,_,_)|Dmodule(s,_,_,_,_)->s|Dtype(lti,_)->lti.lt_name|Dlemma(ln,_,_,_,_,_)->ln|Dinvariant(toto,_)->toto.l_var_info.lv_name|Dtype_annot(ta,_)->ta.l_var_info.lv_name|Dmodel_annot(mi,_)->mi.mi_name|Dextended({ext_name},_,_)->" (Extension "^ext_name^")"endmethodprivateimages(globs:globallist)=(* extract just the names of the globals, for printing purposes *)letles_images=List.mapself#imageglobsinString.concat","les_imagesmethodprivateupdate_call_mapsvinfoincrement=ifconsider_function~libcvinfothenletupdate_call_mapfuncmap=self#add_mapfuncmapvinfo(increment+tryVInfoMap.findvinfo!funcmapwithNot_found->0)inifvinfo.vdefinedthenupdate_call_mapfundef_callselseifAnnotations.has_funspec(Globals.Functions.getvinfo)thenupdate_call_mapfunspec_callselseupdate_call_mapfundecl_callsmethod!vinsti=beginmatchiwith|Call(v,lv,_,_)->self#incr_both_metricsincr_calls;(matchlvwith|Varvinfo->self#update_call_mapsvinfo1|_->());(matchvwith|Some_->self#incr_both_metricsincr_assigns|None->());|Set_->self#incr_both_metricsincr_assigns;|Local_init(_,AssignInit_,_)->self#incr_both_metricsincr_assigns|Local_init(_,ConsInit(f,_,k),_)->(* if f takes the address of the initialized variable as first
argument, there's no explicit assignment in the current function. *)(matchkwith|Plain_func->self#incr_both_metricsincr_assigns|Constructor->());self#incr_both_metricsincr_calls;self#update_call_mapsf1|Asm_|Skip_|Code_annot_->()end;Cil.DoChildrenendletdkey=Metrics_parameters.register_category"used-files"classreachable_from_mainvisited_vardefs=objectinheritVisitor.frama_c_inplacevalvisited_vardefs=refvisited_vardefsmethodget_visited_vardefs=!visited_vardefsmethod!vvrblvi=ifvi.vglob&¬(Varinfo.Set.memvi!visited_vardefs)thenbeginMetrics_parameters.feedback~dkey"visiting %a"Printer.pp_varinfovi;visited_vardefs:=Varinfo.Set.addvi!visited_vardefs;tryletkf=Globals.Functions.getviintryletfd=Kernel_function.get_definitionkfinletvis=newreachable_from_main!visited_vardefsinignore(Visitor.visitFramacFunction(vis:>Visitor.frama_c_visitor)fd);visited_vardefs:=Varinfo.Set.union!visited_vardefsvis#get_visited_vardefswithKernel_function.No_Definition->()withNot_found->(* global var, not function *)letdef=Ast.def_or_last_declviinletvis=newreachable_from_main!visited_vardefsinignore(Visitor.visitFramacGlobal(vis:>Visitor.frama_c_visitor)def);visited_vardefs:=Varinfo.Set.union!visited_vardefsvis#get_visited_vardefsend;Cil.SkipChildrenendletreachable_from_main()=trylet(kf,_)=Globals.entry_point()inMetrics_parameters.feedback~dkey"compute_reachable_from_main: %a"Kernel_function.prettykf;tryletmain_fd=Kernel_function.get_definitionkfinletvis=newreachable_from_main(Varinfo.Set.singletonmain_fd.svar)inignore(Visitor.visitFramacFunction(vis:>Visitor.frama_c_visitor)main_fd);Some(Varinfo.Set.elementsvis#get_visited_vardefs)withKernel_function.No_Definition->NonewithGlobals.No_such_entry_point_->None(* Requires a main function *)letcompute_files_defining_globalsgvars=List.fold_left(funaccvi->Metrics_parameters.feedback~dkey"looking for global: %a"Printer.pp_varinfovi;letdef=Ast.def_or_last_declviinletis_def=matchdefwith|GVar_|GFun_->true|_->falseinifis_defthenletloc=Cil_datatype.Global.locdefinifFileloc.equallocFileloc.unknownthenaccelsebeginMetrics_parameters.feedback~dkey"found %s at: %a"(ifis_defthen"definition"else"declaration")Fileloc.prettyloc;Filepath.Set.add(Fileloc.pathloc)accendelseacc)(Filepath.Set.empty)gvarsclasslogic_loc_visitor=objectinheritVisitor.frama_c_inplacevallocs=refFileloc.Set.emptymethodget_locs=!locsmethod!vtermt=locs:=Fileloc.Set.addt.term_loc!locs;Cil.DoChildrenmethod!vpredicatep=locs:=Fileloc.Set.addp.pred_loc!locs;Cil.DoChildrenendletget_filenames_in_funspeckf=tryletspec=Annotations.funspeckfinMetrics_parameters.feedback~dkey"looking for files in the spec of: %a"Kernel_function.prettykf;List.fold_left(funaccb->letvisitor=newlogic_loc_visitorinignore(Visitor.visitFramacBehavior(visitor:>Visitor.frama_c_visitor)b);letlocs=visitor#get_locsinFileloc.Set.fold(funlocacc'->letpath=Fileloc.pathlocinMetrics_parameters.feedback~dkey~once:true"found annotation in: %a"Filepath.prettypath;Filepath.Set.addpathacc')locsacc)Filepath.Set.emptyspec.spec_behaviorwithAnnotations.No_funspec_->Filepath.Set.emptyletcompute_files_defining_funspecsgvars=List.fold_left(funaccvi->tryletkf=Globals.Functions.getviinletfs=get_filenames_in_funspeckfinFilepath.Set.unionaccfswithNot_found->acc)Filepath.Set.emptygvarsletused_files()=matchreachable_from_main()with|None->Metrics_parameters.abort"'%s' requires an entry point (-main) with a body"Metrics_parameters.UsedFiles.option_name|Somereachable_gvars->letused_for_defs=compute_files_defining_globalsreachable_gvarsinletused_for_specs=compute_files_defining_funspecsreachable_gvarsinFilepath.Set.unionused_for_defsused_for_specsletpretty_used_filesused_files=(* Note: used_files may also contain #include'd files,
but we only want those given on the command line *)letcmdline_files=List.fold_left(funaccfile->Filepath.Set.add(Filepath.of_string(Kernel_file.get_namefile))acc)Filepath.Set.empty(Kernel_file.get_all())inletused_cmdline_files,used_included_files=Filepath.Set.partition(funpath->Filepath.Set.mempathcmdline_files)used_filesinletis_c_files=String.ends_with~suffix:".c"s&&s<>".c"inletused_included_c_files=Filepath.Set.filter(funf->is_c_file(Filepath.to_string_absf))used_included_filesinletused_implicitly_included_c_files=Filepath.Set.diffused_included_c_filescmdline_filesinletunused_cmdline_files=Filepath.Set.diffcmdline_filesused_cmdline_filesinletnbs=Filepath.Set.cardinalsinletpp_filepathstitlefmtpaths=letn=nbpathsinifn=0thenFormat.ifprintffmt""elselettitle_len=String.lengthtitleinFormat.fprintffmt"@\n%s (%d)\
@\n%s\
@\n%a@\n"titlen(String.make(title_len+4)'=')(Pretty_utils.pp_list~sep:" \\@\n"~suf:" \\"Filepath.pretty)(Filepath.Set.elementspaths)inMetrics_parameters.result"Used files starting at function '%a':@\n\
- command line has %d out of %d file(s) being used@\n\
- %d used file(s) inside #include directives, of which %d '.c' file(s).@\n\
%a%a%a"Kernel_function.pretty(fst(Globals.entry_point()))(nbused_cmdline_files)(nbcmdline_files)(nbused_included_files)(nbused_included_c_files)(pp_filepaths"Used command-line files")used_cmdline_files(pp_filepaths"Unused command-line files")unused_cmdline_files(pp_filepaths"Used, but implicitly included C files")used_implicitly_included_c_filesletdump_htmlfmtcil_visitor=(* Activate tagging for html *)Format.pp_set_formatter_stag_functionsfmthtml_stag_functions;Format.pp_set_tagsfmttrue;letpr_rowsfmtn=Format.fprintffmt"@{<tr>@[<v 1>@ \
@{<td class=\"entry\">%s@}@ \
@{<td class=\"stat\">%d@}@]@ @} "sninletpr_statsfmtvisitor=letmetrics=visitor#get_global_metricsinFormat.fprintffmt"@[<v 0>@{<table>%a@}@]"(funfmtmetrics->List.iter2(funtextvalue->pr_rowtextfmtvalue)["SLOC";"Number of if statements";"Number of assignments";"Number of loops";"Number of calls";"Number of gotos";"Number of pointer accesses";][metrics.cslocs;metrics.cifs;metrics.cassigns;metrics.cloops;metrics.ccalls;metrics.cgotos;metrics.cptrs;])metricsinletpr_preludefmtcil_visitor=Format.fprintffmt"@[<v 0>\
@{<div>@ \
@{<h1>@{<span>Metrics@}@}@ \
@{<h2>Synthetic results@}@ <br/>@ \
@{<span>Defined function(s)@} (%d): <br/>@ \
@[ %a@]@ <br/>@ <br/>@ \
@{<span>Specified-only function(s)@} (%d):@ <br/>@ \
@[ %a@]@ <br>@ <br/>@ \
@{<span>Undefined and unspecified function(s)@} (%d):@ <br/>@ \
@[ %a@]@ <br>@ <br/>@ \
@{<span>'Extern' global variable(s)@} (%d):@ <br/>@ \
@[ %a@]@ <br>@ <br/>@ \
@{<span>Potential entry point(s)@} (%d):@ <br/>@ \
@[ %a@]@ <br/>@ <br/>@ \
@}@]"(VInfoMap.cardinalcil_visitor#fundef_calls)Metrics_base.pretty_setcil_visitor#fundef_calls(VInfoMap.cardinalcil_visitor#funspec_calls)Metrics_base.pretty_setcil_visitor#funspec_calls(VInfoMap.cardinalcil_visitor#fundecl_calls)Metrics_base.pretty_setcil_visitor#fundecl_calls(VInfoSet.cardinalcil_visitor#extern_global_vars)Metrics_base.pretty_extern_varscil_visitor#extern_global_vars(Metrics_base.number_entry_pointscil_visitor#fundef_calls)Metrics_base.pretty_entry_pointscil_visitor#fundef_callsinletpr_detailed_resultsfmtcil_visitor=Format.fprintffmt"@[<v 0>\
@{<div style=\"text-align: left;\">\
@[<v 2>@ \
@{<h2>Detailed results@}@ \
@[<v 0>%a@ @]\
@]@}"(funfmtcil_visitor->cil_visitor#print_statsfmt)cil_visitorinFormat.fprintffmt"@[<v 0>\
<!DOCTYPE html>@ \
@{<html>@ \
@{<head>@ \
@{<title>%s@}@ \
<meta charset=\"utf-8\">@ \
@{<style type=\"text/css\">%s@}@ \
@}@ \
@{<body>\
@[<v 2>@ \
%a@ \
%a@ \
%a@ \
@]@}@}@]@?""Metrics"Css_html.csspr_preludecil_visitorpr_statscil_visitorpr_detailed_resultscil_visitor;;letpp_funinfofmtvis=letnfundef=VInfoMap.cardinalvis#fundef_callsinletnfunspec=VInfoMap.cardinalvis#funspec_callsinletnfundecl=VInfoMap.cardinalvis#fundecl_callsinletnextern=VInfoSet.cardinalvis#extern_global_varsinletfundef_hdr=Format.sprintf"Defined functions (%d)"nfundefandfunspec_hdr=Format.sprintf"Specified-only functions (%d)"nfunspecandfundecl_hdr=Format.sprintf"Undefined and unspecified functions (%d)"nfundeclandextern_hdr=Format.sprintf"'Extern' global variables (%d)"nexternandentry_pts_hdr=Format.sprintf"Potential entry points (%d)"(Metrics_base.number_entry_pointsvis#fundef_calls)inFormat.fprintffmt"@[<v 0>@[<v 1>%a@ @[%a@]@]@ @ \
@[<v 1>%a@ @[%a@]@]@ @ \
@[<v 1>%a@ @[%a@]@]@ @ \
@[<v 1>%a@ @[%a@]@]@ @ \
@[<v 1>%a@ @[%a@]@]@ \
@]"(Metrics_base.mk_hdr1)fundef_hdrMetrics_base.pretty_setvis#fundef_calls(Metrics_base.mk_hdr1)funspec_hdrMetrics_base.pretty_setvis#funspec_calls(Metrics_base.mk_hdr1)fundecl_hdrMetrics_base.pretty_setvis#fundecl_calls(Metrics_base.mk_hdr1)extern_hdrMetrics_base.pretty_extern_varsvis#extern_global_vars(Metrics_base.mk_hdr1)entry_pts_hdrMetrics_base.pretty_entry_pointsvis#fundef_calls;;letjson_of_funinfovis=letfundef=("defined-functions",json_of_varinfo_mapvis#fundef_calls)inletfunspec=("specified-only-functions",json_of_varinfo_mapvis#funspec_calls)inletfundecl=("undefined-functions",json_of_varinfo_mapvis#fundecl_calls)inletextern_vars=VInfoSet.fold(funviacc->`Stringvi.vname::acc)vis#extern_global_vars[]inletextern=("extern-global-vars",`List(List.revextern_vars))inletentry_points=("entry-points",json_of_entry_pointsvis#fundef_calls)in`Assoc[fundef;funspec;fundecl;extern;entry_points]letpp_with_funinfofmtcil_visitor=Format.fprintffmt"@[<v 0>%a@ %a@]"pp_funinfocil_visitorpp_base_metricscil_visitor#get_global_metrics;;letget_global_metrics~libc=letfile=Ast.get()in(* Do as before *)letcil_visitor=newslocVisitor~libcinVisitor.visitFramacFileSameGlobals(cil_visitor:>Visitor.frama_c_visitor)file;cil_visitor#get_global_metrics;;letget_metrics_map~libc=letfile=Ast.get()in(* Do as before *)letcil_visitor=newslocVisitor~libcinVisitor.visitFramacFileSameGlobals(cil_visitor:>Visitor.frama_c_visitor)file;cil_visitor#get_metrics_map;;letget_cilast_metrics~libc=letfile=Ast.get()in(* Do as before *)letcil_visitor=newslocVisitor~libcinVisitor.visitFramacFileSameGlobals(cil_visitor:>Visitor.frama_c_visitor)file;{fundecl_calls=cil_visitor#fundecl_calls;funspec_calls=cil_visitor#funspec_calls;fundef_calls=cil_visitor#fundef_calls;extern_global_vars=cil_visitor#extern_global_vars;basic_global_metrics=cil_visitor#get_global_metrics;};;letcompute_on_cilast~libc=letfile=Ast.get()in(* Do as before *)letcil_visitor=newslocVisitor~libcinVisitor.visitFramacFileSameGlobals(cil_visitor:>Visitor.frama_c_visitor)file;ifMetrics_parameters.ByFunction.get()thenMetrics_parameters.result"@[<v 0>Cil AST@ %t@]"cil_visitor#pp_detailed_text_metrics;(* let r = metrics_to_result cil_visitor in *)(* Print the result to file if required *)ifnot(Metrics_parameters.OutputFile.is_empty())thenbeginletout_fname=Metrics_parameters.OutputFile.get()intryletoc=open_out_bin(Filepath.to_string_absout_fname)inletfmt=Format.formatter_of_out_channelocin(matchMetrics_base.get_file_typeout_fnamewith|Html->dump_htmlfmtcil_visitor|Text->pp_with_funinfofmtcil_visitor|Json->letjson=json_of_funinfocil_visitorinYojson.pretty_printfmtjson;Format.fprintffmt"@."(* ensure the file ends with a newline *));close_outoc;withSys_errors->Metrics_parameters.abort"Cannot open file %a (%s)."Filepath.prettyout_fnamesendelseMetrics_parameters.result"%a"pp_with_funinfocil_visitor(* Visitor for the recursive estimation of a stack size.
Its arguments are the function currently being visited and the current
callstack, as a list of kernel functions.
The callstack is used to detect recursive calls.
TODO: this computation is far from optimal; for instance, locals_size could
be cached for each function. Also, it does not consider calls via function
pointers. *)classlocals_size_visitorkfcallstack=objectvalmutablelocals_size_no_temps=Z.zeromethodget_locals_size_no_temps=locals_size_no_tempsvalmutablelocals_size_temps=Z.zeromethodget_locals_size_temps=locals_size_tempsvalmutablemax_size_calls_no_temps=Z.zeromethodget_max_size_calls_no_temps=max_size_calls_no_tempsvalmutablemax_size_calls_temps=Z.zeromethodget_max_size_calls_temps=max_size_calls_tempsinheritVisitor.frama_c_inplacemethod!vinsti=matchiwith|Call(_,Varvi,_,_)|Local_init(_,ConsInit(vi,_,_),_)->begintryletkf'=Globals.Functions.find_by_namevi.vnameinMetrics_parameters.debug"@[function %a:@;computing call to function %a@]"Kernel_function.prettykfKernel_function.prettykf';letnew_cs=kf'::callstackinifList.memkf'callstackthenMetrics_parameters.abort"@[unsupported recursive call detected:@;%a@]"(Pretty_utils.pp_list~sep:"@ <-@ "Kernel_function.pretty)(List.revnew_cs);letnew_vis=newlocals_size_visitorkf'new_csinignore(Visitor.visitFramacKf(new_vis:>Visitor.frama_c_visitor)kf');letcall_size_no_temps=Z.addnew_vis#get_max_size_calls_no_tempsnew_vis#get_locals_size_no_tempsinletcall_size_temps=Z.addnew_vis#get_max_size_calls_tempsnew_vis#get_locals_size_tempsinmax_size_calls_no_temps<-Z.maxmax_size_calls_no_tempscall_size_no_temps;max_size_calls_temps<-Z.maxmax_size_calls_tempscall_size_tempswithNot_found->(* should not happen *)Metrics_parameters.fatal~current:true"@[function not found:@;%s@]"vi.vname;end;Cil.DoChildren|_->Cil.DoChildrenmethod!vvdecvi=ifnotvi.vglob&¬vi.vghost&&vi.vstorage=NoStoragethenbeginletsize_exp=Cil.sizeOf~loc:vi.vdeclvi.vtypeinmatchCil.constFoldToIntsize_expwith|None->Metrics_parameters.error"@[in function %a,@;cannot compute sizeof %a (type %a)@]"Kernel_function.prettykfPrinter.pp_varinfoviPrinter.pp_typvi.vtype|Somesize->Metrics_parameters.debug"@[function %a:@;sizeof(%a) = %a (%s)@]"Kernel_function.prettykfPrinter.pp_varinfoviZ.prettysize(ifvi.vtempthen"temp"else"non-temp");ifvi.vtempthenlocals_size_temps<-Z.addlocals_size_tempssizeelselocals_size_no_temps<-Z.addlocals_size_no_tempssizeend;Cil.DoChildrenend(* Requires a computed Cil AST *)letcompute_locals_sizekf=letvis=newlocals_size_visitorkf[kf]inignore(Visitor.visitFramacKf(vis:>Visitor.frama_c_visitor)kf);Metrics_parameters.result"@[%a\t%a\t%a\t%a\t%a@]"Kernel_function.prettykfZ.prettyvis#get_locals_size_no_tempsZ.pretty(Z.addvis#get_locals_size_no_tempsvis#get_locals_size_temps)Z.prettyvis#get_max_size_calls_no_tempsZ.pretty(Z.addvis#get_max_size_calls_no_tempsvis#get_max_size_calls_temps);;