123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303(**************************************************************************)(* *)(* 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). *)(* *)(**************************************************************************)(* To avoid listing declarations several times, we use their locations
as proxies. However, we cannot directly compare locations, since static
(re-)definitions, as well as prototypes included several times,
have locations which are physically different (pos_cnum)
despite being semantically identical (same file/line/column).
The module below provides a hash table using the equality function
corresponding to our needs.
*)moduleSemanticLocs:sigincludeHashtbl.Swithtypekey=Cil_datatype.Location.tvalis_empty:'at->boolvalkeys:'at->keylist(* sorted w.r.t. cmp_start_semantic *)valelements:'at->(key*'a)list(* sorted w.r.t. cmp_start_semantic *)end=structincludeHashtbl.Make(structtypet=Cil_datatype.Location.tletequal=Cil_datatype.Location.equal_start_semanticlethash(b,_e)=Hashtbl.hash(b.Filepath.pos_path,b.Filepath.pos_lnum)end)letis_emptytbl=lengthtbl=0letkeystbl=letl=fold(funloc_acc->loc::acc)tbl[]inList.sortCil_datatype.Location.compare_start_semanticlletelementstbl=letl=fold(funlocvacc->(loc,v)::acc)tbl[]inList.sort(fun(l1,_v1)(l2,_v2)->Cil_datatype.Location.compare_start_semanticl1l2)lendmoduleSelf=Plugin.Register(structletname="list-functions"letshortname="list-functions"lethelp="prints the list of function definitions and declarations, \
along with their locations and number of statements, \
in text or JSON format"end)modulePrintLibc=Self.False(structletoption_name="-list-functions-libc"lethelp="whether to print functions located within Frama-C's libc \
directory. Default: false"end)modulePrintDeclarations=Self.False(structletoption_name="-list-functions-declarations"lethelp="whether to print function declarations. Default: false"end)moduleOutput=Self.Filepath(structletoption_name="-list-functions-output"letarg_name="filename"letexistence=Filepath.Indifferentletfile_kind="json"lethelp="where to save the output, in JSON format. If omitted', \
then output to stdout in text format instead"end)typefuninfo={name:string;declarations:unitSemanticLocs.t;definitions:int(*number of statements*)SemanticLocs.t;(* Note: only static functions can have multiple definitions *)}classstmt_count_visitor=objectinheritVisitor.frama_c_inplacevalcount=ref0method!vstmt_aux_s=incrcount;Cil.DoChildrenmethodget=!countend(* To find good locations for declarations and definitions, we use different
methods:
- For declarations, the Cabs AST information is much better than the Cil
one, which erases declarations when a definition is found;
- For definitions, the information seems to be equivalent, so we use the
one in Kernel_function.
*)(* Due to the fact that the Cabs AST contains no fc_stdlib attributes, we use a
location-based approach. *)letlocated_within_framac_libcloc=letpos=fstlocinFilepath.is_relative~base_name:Fc_config.framac_libcpos.Filepath.pos_pathclassfun_cabs_visitorprint_libc=object(self)inheritCabsvisit.nopCabsVisitorvaldecls:(string,'aSemanticLocs.t)Hashtbl.t=Hashtbl.create7methodget_decls=declsmethodprivateget_single_name(_spec,(name,_,_,_))=namemethodprivateget_name(name,_,_,_)=namemethodprivateadd_loctablenameloc=ifprint_libc||not(located_within_framac_libcloc)thenletlocs_table=tryHashtbl.findtablenamewith|Not_found->lett=SemanticLocs.create1inHashtbl.replacetablenamet;tinSemanticLocs.replacelocs_tableloc()method!vdefdef=letopenCabsinmatchdefwith|FUNDEF_->(* we will use Cil information anyway *)Cil.SkipChildren|DECDEF(_,(_,name_list),loc)->List.iter(function|((name,PROTO_,_,_),_)->self#add_locdeclsnameloc|_->())name_list;Cil.SkipChildren|_->Cil.DoChildrenendletprint_json(fp:Filepath.Normalized.t)funinfos_json=tryletoc=open_out(fp:>string)inletfmt=Format.formatter_of_out_channelocinJson.ppfmtfuninfos_json;Format.fprintffmt"@.";close_outoc;Self.debug"List written to: %a"Filepath.Normalized.prettyfpwithSys_errormsg->Self.abort"cannot write JSON to %a: %s"Filepath.Normalized.prettyfpmsgletpp_semlocsfmtt=Format.fprintffmt"%a"(Pretty_utils.pp_list~sep:", "Cil_datatype.Location.pretty)(SemanticLocs.keyst)letpp_loc_sizefmtloc_size=let(loc,size)=loc_sizeinFormat.fprintffmt"%a (%d statement%s)"Cil_datatype.Location.prettylocsize(ifsize<>1then"s"else"")letpp_definitionsfmtdefs=Format.fprintffmt"%a"(Pretty_utils.pp_list~sep:", "pp_loc_size)(SemanticLocs.elementsdefs)letprint_textfuninfos=List.iter(funfi->ifPrintDeclarations.get()||not(SemanticLocs.is_emptyfi.definitions)thenbeginFormat.printf"%s:"fi.name;beginifnot(SemanticLocs.is_emptyfi.definitions)thenFormat.printf" defined at %a;"pp_definitionsfi.definitionsend;ifPrintDeclarations.get()&¬(SemanticLocs.is_emptyfi.declarations)thenFormat.printf" declared at %a"pp_semlocsfi.declarations;ifSemanticLocs.(is_emptyfi.definitions&&is_emptyfi.declarations)thenFormat.printf" called but never declared nor defined";Format.printf"@."end)funinfosletget_sizekf=letstmt_count_vis=newstmt_count_visitorinignoreVisitor.(visitFramacKf(stmt_count_vis:>frama_c_inplace)kf);stmt_count_vis#getletdefinitions_with_sizename=letkfs=List.filterKernel_function.is_definition(Globals.Functions.find_all_by_orig_namename)inletdefs_with_size=SemanticLocs.create(List.lengthkfs)inList.iter(funkf->letn=get_sizekfinletloc=Kernel_function.get_locationkfinSemanticLocs.adddefs_with_sizelocn)kfs;defs_with_sizeletjson_string_of_locloc=`String(Format.asprintf"%a"Cil_datatype.Location.prettyloc)letjson_list_of_loc_tbltbl=letkeys=SemanticLocs.keystblin`List(List.mapjson_string_of_lockeys)letjson_array_of_loc_size(loc,size)=`Assoc[("location",json_string_of_locloc);("statements",`Intsize)]letjson_list_of_loc_size_tbltbl=letelements=SemanticLocs.elementstblin`List(List.mapjson_array_of_loc_sizeelements)letrun()=ifList.length(File.get_all())<1thenbeginSelf.abort"no input files";end;letcabs_files=Ast.UntypedFiles.get()inletvis=newfun_cabs_visitor(PrintLibc.get())inList.iter(funfile->ignoreCabsvisit.(visitCabsFile(vis:>nopCabsVisitor)file))cabs_files;letdecls=vis#get_declsinletdefs_without_decls=Globals.Functions.fold(funkfacc->ifKernel_function.is_definitionkfthenbeginletorig_name=(Kernel_function.get_vikf).vorig_nameinifHashtbl.memdeclsorig_namethenaccelseDatatype.String.Set.addorig_nameaccendelseacc)Datatype.String.Set.emptyinletfuninfos=Hashtbl.fold(funnamedeclarationsacc->letdefinitions=definitions_with_sizenameinletfi={name;definitions;declarations}infi::acc)decls[]in(* add data for defined functions not present in 'decls' *)letfuninfos=Datatype.String.Set.fold(funorig_nameacc->ifnot(Hashtbl.memdeclsorig_name)thenbeginletdefinitions=definitions_with_sizeorig_nameinletfi={name=orig_name;definitions;declarations=SemanticLocs.create0}infi::accendelseacc)defs_without_declsfuninfosinletfuninfos=List.sort(funfi1fi2->Extlib.compare_ignore_casefi1.namefi2.name)funinfosinletoutfp=Output.get()inifFilepath.Normalized.is_emptyoutfpthenprint_textfuninfoselseletfuninfos_json=`List(List.map(funfi->letdefinitions=ifSemanticLocs.is_emptyfi.definitionsthen[]else[("definitions",json_list_of_loc_size_tblfi.definitions)]inletdeclarations=ifSemanticLocs.is_emptyfi.declarationsthen[]else[("declarations",json_list_of_loc_tblfi.declarations)]in`Assoc[(fi.name,`Assoc(definitions@declarations))])funinfos)inprint_jsonoutfpfuninfos_jsonlet()=Db.Main.extendrun