123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629(**************************************************************************)(* *)(* 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_typesopenMarkdownmoduleEva_info=structletloaded=reffalseletcoverage_md_gen:(unit->elements)ref=Extlib.mk_fun"coverage_md_gen"letdomains_md_gen:(unit->(text*text)list)ref=Extlib.mk_fun"domains_md_gen"endtypeenv={is_draft:bool;remarks:Markdown.elementlistDatatype.String.Map.t;}letinsert_remark_optenvanchorplaceholder=tryDatatype.String.Map.findanchorenv.remarkswithNot_found->placeholderletinsert_remarkenvanchor=insert_remark_optenvanchor[](* apparently, pandoc, or at least its latex output,
does not like anchors beginning with _ *)letsanitize_anchors=ifs=""then"a"elseifs.[0]='_'then"a"^selsesletinsert_marksenvanchor=Comment"BEGIN_REMARK"::insert_remarkenvanchor@[Comment"END_REMARK"]letpluralls=matchlwith|[]|[_]->s|_::_::_->s^"s"letsection_domainsenv=letanchor="domains"inlethead=H3(plain"Eva Domains",Someanchor)inifenv.is_draftthenhead::Comment"You can give more information about the choice of Eva domains"::insert_marksenvanchorelsebeginletl=!Eva_info.domains_md_gen()inhead::Block(matchlwith|[]->[Text(plain"Only the base domain (`cvalue`) \
has been used for the analysis")]|_->[Text(plain"In addition to the base domain (`cvalue`), additional \
domains have been used by Eva");DLl])::insert_remarkenvanchorendletsection_stubsenv=letstubbed_kf=List.concat(List.map(funf->letfilename=Filepath.Normalized.of_stringfinGlobals.FileIndex.get_functions~declarations:falsefilename)(Mdr_params.Stubs.get()))inletstubbed_kf=List.filterKernel_function.is_definitionstubbed_kfinletopt=Dynamic.Parameter.String.get"-eva-use-spec"()inletl=String.split_on_char','optinletuse_spec=List.filter_map(* The option can include categories in Frama-C's List/Set/Map sense,
which begins with a '@'. In particular, @default is included by
default. Theoretically, there could also be some '-' to suppress
the inclusion of a function
*)(funs->ifString.lengths<>0&&s.[0]<>'@'&&s.[0]<>'-'thenletkf=Globals.Functions.find_by_namesinletanchor=sanitize_anchorsinletcontent=ifenv.is_drafttheninsert_marksenvanchorelseletintro=Markdown.text@@Markdown.format"`%s` has the following specification"sinletfunspec=Populate_spec.populate_funspeckf[`Assigns];Markdown.codeblock~lang:"acsl""%a"Printer.pp_funspec(Annotations.funspeckf)inBlock(intro@funspec)::insert_remarkenvanchorinSome(H4(codes,Someanchor)::content)elseNone)linletdescribe_funckf=letname=Kernel_function.get_namekfinletanchor=sanitize_anchornameinletloc=Kernel_function.get_locationkfinletcontent=ifenv.is_drafttheninsert_marksenvanchorelseletintro=Markdown.text@@Markdown.format"`%s` @[<h>is defined at %a@]"nameCil_datatype.Location.prettylocinletfundecl=Markdown.codeblock~lang:"c""%a"Printer.pp_global(GFun(Kernel_function.get_definitionkf,loc))inBlock(intro@fundecl)::insert_remarkenvanchorinH4(codename,Someanchor)::contentinletcontent=ifstubbed_kf<>[]thenbeginList.mapdescribe_funcstubbed_kfendelse[]inletcontent=content@use_specinletcontent=List.concatcontentinifcontent=[]thenifenv.is_draftthen[Comment"No stubs have been used"]else[Block[Text(plain"No stubs have been used for this analysis")]]elsecontentletget_files()=letdir_table=Datatype.String.Hashtbl.create17inletadd_entryf=letf=Filepath.Normalized.to_pretty_stringfinletdir=Filename.dirnamefinletbase=Filename.basenamefinletsuf=tryleti=String.rindexbase'.'inString.subbasei(String.lengthbase-i)withNot_found->""inletentries=tryDatatype.String.Hashtbl.finddir_tabledirwithNot_found->Datatype.String.Map.emptyinletsubentries=tryDatatype.String.Map.findsufentrieswithNot_found->Datatype.String.Set.emptyinDatatype.String.(Hashtbl.replacedir_tabledir(Map.addsuf(Set.addbasesubentries)entries))inList.iteradd_entry(Kernel.Files.get());lettreat_subentrydirdir_filessuffilesl=letdir_files=List.fold_left(funaccs->ifFilename.check_suffixssufthenDatatype.String.Set.addsaccelseacc)Datatype.String.Set.emptydir_filesinifDatatype.String.Set.subsetdir_filesfilesthen(dir^"/*"^suf)::lelseDatatype.String.Set.elementsfiles@linlettreat_entrydirmapl=tryletdir_files=Array.to_list(Sys.readdirdir)inDatatype.String.Map.fold(treat_subentrydirdir_files)maplwithSys_errors->Mdr_params.warning"Unable to find directory %s: %s"dirs;Datatype.String.Map.fold(fun_sl->Datatype.String.Set.elementss@l)maplinDatatype.String.Hashtbl.foldtreat_entrydir_table[]letgen_inputsenv=letanchor="c-input"inletprelude=ifenv.is_draftthenComment"You can add here some remarks about the set of files \
that is considered by Frama-C"::insert_marksenvanchorelseinsert_remarkenvanchorinH2(plain"Input files",Someanchor)::prelude@[Block[Text(plain"The C source files (not including the headers `.h` files)"@plain"that have been considered during the analysis \
are the following:");UL(List.map(funx->text@@codex)(get_files()));]]letgen_configenv=letanchor="options"inletheader=H2(plain"Configuration",Someanchor)inletcontent=ifenv.is_draftthenComment"You can add here some remarks about the options used for the analysis"::insert_marksenvanchorelsebeginletplaceholder=[Block[Text(plain"The options that have been used for this analysis \
are the following.")]]ininsert_remark_optenvanchorplaceholderendinheader::contentletgen_contextenv=letcontext=letanchor="intro"inletheader=H1(plain"Introduction",Someanchor)inifenv.is_draftthenheader::Comment"You can add here some overall introduction to the analysis"::insert_marksenvanchorelsebeginmatchinsert_remarkenvanchorwith|[]->[]|(_::_)asl->header::lendincontext@H1(plain"Context of the analysis",Some"context")::gen_inputsenv@gen_configenv@(if!Eva_info.loadedthensection_domainsenvelse[])@H3(plain"Stubbed Functions",Some"stubs")::(ifenv.is_draftthenComment"You can add here general comments about the stubs that have been used"::insert_marksenv"stubs"elseinsert_remarkenv"stubs")@section_stubsenvletgen_coverageenv=letanchor="coverage"inletheader=H1(plain"Coverage",Someanchor)inletcontent=!Eva_info.coverage_md_gen()inletcontent=ifenv.is_draftthencontent@Comment"You can comment on the coverage obtained by Eva"::insert_marksenvanchorelsecontent@insert_remarkenvanchorinheader::contentletstring_of_pospos=Format.asprintf"%a"Filepath.pp_posposletstring_of_pos_opt=function|None->"Global"|Somepos->string_of_posposletstring_of_loc(l1,_)=string_of_posl1letmake_events_tableprint_kindcaptionevents=letopenLoginletcaption=Somecaptioninletheader=[plain"Location",Left;plain"Description",Left;]inletheader=ifprint_kindthen(plain"Kind",Center)::headerelseheaderinletkind=function|Result->"Result"|Feedback->"Feedback"|Debug->"Debug"|Warning->"Warning"|Error->"User error"|Failure->"Internal error"inlettreat_event{evt_kind;evt_plugin;evt_source;evt_message}=letevt_message=Str.global_replace(Str.regexp_string"\n")" "evt_messageinletline=[plain(string_of_pos_optevt_source);format"`%s` (emitted by `%s`)"evt_messageevt_plugin]inifprint_kindthenplain(kindevt_kind)::lineelselineinletcontent=List.fold_left(funlevt->treat_eventevt::l)[]eventsinTable{caption;header;content}letmake_errors_tableerrs=make_events_tabletrue(plain(pluralerrs"Error"^" reported by Frama-C"))errsletmake_warnings_tablewarnings=make_events_tablefalse(plain(pluralwarnings"Warning"^" reported by Frama-C"))warningsletsection_eventis_errenvnbevent=letopenLoginlettitle=Format.asprintf"@[<h>%s %d (%s)@]"(ifis_errthen"Error"else"Warning")nb(string_of_pos_optevent.evt_source)inletlab=Format.asprintf"@[<h>%s-%d@]"(ifis_errthen"err"else"warn")nbinletcontent=ifenv.is_drafttheninsert_marksenvlabelseinsert_remarkenvlabinH2(plaintitle,Somelab)::Block((text@@plain"Message:")@codeblock"[%s] %s"event.evt_pluginevent.evt_message)::contentletmake_events_listis_errenvl=List.concat(List.mapi(section_eventis_errenv)l)letmake_errors_list=make_events_listtrueletmake_warnings_list=make_events_listfalseletgen_section_warningsenv=letopenLoginMessages.reset_once_flag();leterrs=ref[]inletwarnings=ref[]inletadd_eventevt=matchevt.evt_kindwith|Error|Failure->errs:=evt::!errs|Warning->warnings:=evt::!warnings|_->()inMessages.iteradd_event;leterrs=!errsinletwarnings=!warningsinleterror_section=ifMessages.nb_errors()<>0thenbegin(* Failure are supposed to stop the analyses right away, so that no
report will be generated. On the other hand, Error messages can be
triggered without stopping everything. Applying the same treatment
to a Failure catched by an evil plugin cannot hurt.
*)letprelude=ifenv.is_draftthen[Comment"you can comment on each individual error"]else[Block(text@@glue[bold"Important warning:";plain"Frama-C did not complete its execution ";plain"successfully. Analysis results may be inaccurate.";plain((pluralerrs"The error")^" listed below must be");plain"fixed first before examining other ";plain"warnings and alarms."]);make_errors_tableerrs]inH1(plain"Errors in the analyzer",Some"errors")::prelude@make_errors_listenv(List.reverrs)endelse[]inifMessages.nb_warnings()<>0thenbeginletprelude=ifenv.is_draftthen[Comment"you can comment on each individual error"]else[Block((text@@glue[plain("The table below lists the "^pluralwarnings"warning");plain"that have been emitted by the analyzer.";plain"They might put additional assumptions on the relevance";plain"of the analysis results and must be reviewed carefully";])@(text@@glue[plain"Note that this does not take into account emitted alarms:";plain"they are reported in";link~text:(plain"the next section")~name:"alarms"()]));make_warnings_tablewarnings]inerror_section@H1(plain"Warnings",Some"warnings")::prelude@make_warnings_listenv(List.revwarnings)endelseerror_sectionletgen_section_alarmsenv=lettreat_alarmekfs~rank:_alarmannot(i,sec,content)=letlabel="Alarm-"^string_of_intiinletlink=link~text:(format"%d"i)~name:label()inletkind=code@@Alarms.get_namealarminletfunc=code@@Kernel_function.get_namekfinletloc=string_of_loc@@Cil_datatype.Stmt.locsinletloc_text=plainlocinletemitter=code(Emitter.get_namee)inletdescr=codeblock~lang:"acsl""%a"Printer.pp_code_annotationannotinletsec_title=format"Alarm %d at %s"ilocinletsec_content=ifenv.is_draftthenBlockdescr::insert_marksenvlabelelseBlock((text@@glue[plain"The following ACSL assertion must hold to avoid";plain(Alarms.get_descriptionalarm|>String.lowercase_ascii);format"(undefined behavior)."])@descr)::insert_remarkenvlabelin(i+1,sec@H2(sec_title,Somelabel)::sec_content,[link;kind;emitter;func;loc_text]::content)inlet_,sections,content=Alarms.foldtreat_alarm(0,[],[])inletcontent=List.revcontentinmatchcontentwith|[]->letanchor="alarms"inlettext_content=ifenv.is_draftthenComment"No alarm!"::insert_marksenvanchorelseBlock(text@@glue[bold"No alarm";plain"was found during the analysis";plain"Any execution starting from";code(Kernel.MainFunction.get_function_name());plain"in a context matching the one used for the analysis";plain"will be immune from any undefined behavior."])::insert_remarkenvanchorinH1(plain"Results of the analysis",Someanchor)::text_content|_::l->letalarm=ifl=[]then"alarm"else"alarms"inletcaption=Some(plain(String.capitalize_asciialarm^" emitted by the analysis"))inletheader=[plain"No",Center;plain"Kind",Center;plain"Emitter",Center;plain"Function",Left;plain"Location",Left;]inlettext_content=ifenv.is_draftthenbeginsectionsendelsebeginBlock(text@@glue[plain("The table below lists the "^alarm);plain"that have been emitted during the analysis.";plain"Any execution starting from";code(Kernel.MainFunction.get_function_name());plain"in a context matching the one used for the analysis";plain"will be immune from any other undefined behavior.";plain"More information on each individual alarm is";plain"given in the remainder of this section"])::Table{content;caption;header}::sectionsendinH1(plain"Results of the analysis",Some"alarms")::text_contentletgen_section_callgraphenv=letf=Mdr_params.FlameGraph.get()iniff=""then[]elsebeginletanchor="flamegraph"inletcontent=ifenv.is_draftthenComment"A flamegraph provides a visualization of the functions and \
callstacks whose analysis is the most costly."::insert_marksenvanchorelsepar(plain"The image below shows the flamegraph ("@url"http://www.brendangregg.com/flamegraphs.html"@plain") for the chosen entry point.")@par(image~alt:"Flamegraph visualization."~file:f)@insert_remarkenvanchorinH1(plain"Flamegraph",Someanchor)::contentendletgen_section_postludeenv=letanchor="conclusion"inletheader=H1(plain"Conclusion",Someanchor)inifenv.is_draftthenheader::Comment"You can put here some concluding remarks"::insert_marksenvanchorelsebeginmatchinsert_remarkenvanchorwith|[]->[]|(_::_)asl->header::lendletgen_alarmsenv=gen_section_warningsenv@gen_section_alarmsenv@gen_section_callgraphenv@gen_section_postludeenvletmk_remarksis_draft=ifnot(Mdr_params.Remarks.is_empty())thenParse_remarks.get_remarks(Mdr_params.Remarks.get())elseifis_draftthenbeginletf=Mdr_params.Output.get()inifFilepath.existsfthenbeginMdr_params.feedback"Re-using pre-existing remarks in draft file %a"Filepath.Normalized.prettyf;Parse_remarks.get_remarksfendelseDatatype.String.Map.emptyendelseDatatype.String.Map.emptyletgen_report~draft:is_draft()=letremarks=mk_remarksis_draftinletenv={remarks;is_draft}inletcontext=gen_contextenvinletcoverage=if!Eva_info.loadedthengen_coverageenvelse[]inletalarms=gen_alarmsenvinlettitle=Mdr_params.Title.get()inlettitle=iftitle=""thenbeginifis_draftthenplain"Draft report"elseplain"Frama-C Analysis Report"endelseplaintitleinletauthors=List.map(funx->plainx)(Mdr_params.Authors.get())inletdate=matchMdr_params.Date.get()with|""->None|s->Some(plains)inletelements=context@coverage@alarmsinletelements=ifis_draftthenComment"This file contains additional remarks that will be added to \
automatically generated content by Frama-C's Markdown-report plugin. \
For any section of the document, you can write pandoc markdown \
content between the BEGIN and END comments. In addition, the plug-in \
will consider any \\<!-- INCLUDE file.md --\\> comment (without backslashes) \
as a directive to include the content of file.md in the corresponding \
section. \
Please don't alter the structure \
of the document as it is used by the plugin to associate content to \
the relevant section."::elementselseelementsinletelements=Raw["\\let\\underscore\\_";"\\renewcommand{\\_}{\\discretionary{\\underscore}{}{\\underscore}}"]::elementsinletdoc=Markdown.pandoc~title~authors?dateelementsinletfile=Mdr_params.Output.get()inifFilepath.Normalized.is_emptyfilethenMdr_params.error"No output file specified (use option %s)."Mdr_params.Output.option_nameelsetryCommand.print_filefile(funfmt->Markdown.pp_pandocfmtdoc;Format.pp_print_newlinefmt());Mdr_params.result"Report %a generated"Filepath.Normalized.prettyfilewithSys_errors->Mdr_params.warning"Unable to open %a for writing (%s). No report generated"Filepath.Normalized.prettyfiles