123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: 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, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Display the results of the analysis in a textual form. *)openMopsa_utilsopenArgExtopenCore.AllopenLocationopenFormatopenCallstackopenCommon(** Command-line option to enable display of alarms call stacks *)letopt_show_callstacks=reffalse(** Command-line option to specify the number of spaces to use for
printing tabs
*)letopt_tw=ref4letprintoutfmt=letformatter=matchoutwith|None->std_formatter|Somefile->leto=open_outfileinformatter_of_out_channeloinkasprintf(funstr->fprintfformatter"%s%!"str)fmtletcompile_tab_columnstwspos=letrecdoitis=try(matchString.subs01,String.subs1(String.lengths-1)with|"\t",s->(funn->(ifn>ithen(doit(i+1)sn)+tw-1elsedoit(i+1)sn))|_,s->doit(i+1)s)with|Invalid_argument_->funi->iinget_pos_columnpos|>doit0sletreplace_tabstws=letre=Str.regexp"\t"inStr.global_replacere(String.maketw' ')smoduleAlarmKindSet=SetExt.Make(structtypet=alarm_kindletcompare=compare_alarm_kindend)letcolor_of_diag=function|Safe->Debug.green|Unreachable->Debug.gray|Error->Debug.red|Warning->Debug.orangeleticon_of_diag=function|Safe->"✔"|Warning->"⚠"|Error->"✗"|Unreachable->"🛇"(** Highlight source code at a given location range *)lethighlight_rangecolorfmtrange=ifnot@@is_orig_range@@untag_rangerangethen()else(* Print source code at location range *)letstart_pos=get_range_startrangeinletend_pos=get_range_endrangeinletfile=get_pos_filestart_posinassert(file=get_pos_fileend_pos);ifnot@@Sys.file_existsfilethen()elseletis_bug_linei=i>=get_pos_linestart_pos&&i<=get_pos_lineend_posin(* Read the file from disk *)letf=open_infileinletget_bug_linesi=letreciteriacc=letol=trySome(input_linef)withEnd_of_file->Noneinifis_bug_lineitheniter(i+1)(matcholwith|None->acc|Somel->(i,l)::acc)elsematcholwith|None->acc|_->iter(i+1)accinList.rev@@iteri[]in(* Highlight bug region in a line *)lethighlight_bugfmt(i,l)=letget_tabbed_column=compile_tab_columns!opt_twlinletl=replace_tabs!opt_twlinletsafe_sublse=tryString.sublsewithInvalid_argument_->let()=Debug.warn_atrange"issue on sub %s %d %d"lseinString.subl(min0s)(max0(mine((String.lengthl)-s)))inletn=String.lengthlin(* prints from c1 to c2 included *)letc1=get_tabbed_columnstart_posinletc2=get_tabbed_columnend_posinlets1,s2,s3=ifi=get_pos_linestart_pos&&i=get_pos_lineend_posthensafe_subl0c1,safe_sublc1(c2-c1),safe_sublc2(n-c2)elseifi=get_pos_linestart_pos&&i=get_pos_lineend_posthensafe_subl0c1,safe_sublc1(n-c1),""elseifi=get_pos_lineend_posthen"",safe_subl0c2,safe_sublc2(n-c2)else"",l,""infprintffmt"%a: %s%a%s"(Debug.boldpp_print_int)is1(Debug.color_strcolor)s2s3in(* Underline bug region *)letunderline_bugcolorfmt(i,l)=letget_tabbed_column=compile_tab_columns!opt_twlinletl=replace_tabs!opt_twlinletn=string_of_inti|>String.lengthinletc1=get_tabbed_columnstart_pos+n+2inletc2=get_tabbed_columnend_pos+n+2inletc3=String.lengthl+n+2inlets1=String.makec1' 'inlets2=String.make(c2-c1)'^'inlets3=String.make(c3-c2)' 'infprintffmt"@,%s%a%s"s1(Debug.color_strcolor)s2s3in(* Print the highlighted lines *)letlines=get_bug_lines1inclose_inf;pp_print_list~pp_sep:(funfmt()->fprintffmt"@,")highlight_bugfmtlines;(* Underline bug if the number of lines is 1 *)matchlineswith|[bug]->underline_bugcolorfmtbug|_->()letpp_diagnosticoutndiagcallstackskinds=(* Print the alarm instance *)letfile_name=get_range_relative_filediag.diag_rangeinletfun_name=matchCallstackSet.choosecallstackswith|c::_->Somec.call_fun_orig_name|_->Noneinletlen=CallstackSet.cardinalcallstacksinprintout"@.%a Check%s:%a@,@[<v 2>%a: %a: %a%a%a%a@]@.@."(Debug.color_str(color_of_diagdiag.diag_kind))(icon_of_diagdiag.diag_kind)(iflen<=1thenFormat.asprintf" #%d"(n+1)elseFormat.asprintf"s #%d-#%d"(n+1)(n+len))(funfmt->function|None->()|Somef->fprintffmt"@,%a: In function '%a':"(Debug.boldpp_print_string)file_name(Debug.boldpp_print_string)f)fun_name(Debug.boldpp_relative_range)diag.diag_range(Debug.color(color_of_diagdiag.diag_kind)pp_diagnostic_kind)diag.diag_kind(Debug.color(color_of_diagdiag.diag_kind)pp_check)diag.diag_check(funfmtrange->letstart_pos=get_range_startrangeinletfile=get_pos_filestart_posinifnot@@Sys.file_existsfilethen()elsefprintffmt"@,@,%a"(highlight_range(color_of_diagdiag.diag_kind))range)diag.diag_range(funfmt->function|[]->()|l->fprintffmt"@,%a"(pp_print_list~pp_sep:(funfmt()->fprintffmt"@,")pp_alarm_kind)l)kinds(funfmtcallstacks->matchCallstackSet.elementscallstackswith|[]->()|[cs]whenis_empty_callstackcs->()|[cs]->fprintffmt"@,Callstack:@,@[%a@]"pp_callstackcs|cs::tl->ifnot!opt_show_callstacksthenletothers=List.lengthtlinfprintffmt"@,Callstack:@,@[%a@]@,+%d other callstack%a"pp_callstackcsothersDebug.plurial_intotherselseletpp_numbered_callstackifmtcs=fprintffmt"@,@[<v>Callstack%a:@,%a@]"(funfmt->function|None->()|Somei->fprintffmt" %d"(i+1))ipp_callstackcsinletcsl'=List.mapi(funics->(i,cs))(cs::tl)inpp_print_list~pp_sep:(funfmt()->fprintffmt"@,")(funfmt(i,cs)->pp_numbered_callstack(Somei)fmtcs)fmtcsl';)callstacksletincr_check_diaglencheckdiagchecks_map=let(safe,error,warning)=matchCheckMap.find_optcheckchecks_mapwith|Somex->x|None->(0,0,0)inlettotal=matchdiagwith|Safe->safe+len,error,warning|Unreachable->safe,error,warning|Error->safe,error+len,warning|Warning->safe,error,warning+leninCheckMap.addchecktotalchecks_mapletconstruct_checks_summary?(print=false)repout=letdiag_groups=Alarm.group_diagnosticsrep.report_diagnosticsinRangeDiagnosticWoCsMap.fold(fun(range,diagWoCs)cssacc->let(i,safe,error,warning,checks_map)=accinletlen=CallstackSet.cardinalcssinletchecks_map'=incr_check_diaglendiagWoCs.diag_checkdiagWoCs.diag_kindchecks_mapinmatchdiagWoCs.diag_kindwith|Unreachable->i,safe,error,warning,checks_map'|Safe->ifprint&&!opt_show_safe_checksthenpp_diagnosticoutidiagWoCscss[];i+len,safe+len,error,warning,checks_map'|Error|Warning->(* Get the set of alarms kinds and callstacks *)letkinds=AlarmSet.fold(funakinds->AlarmKindSet.adda.alarm_kindkinds)diagWoCs.diag_alarmsAlarmKindSet.emptyin(* Join alarm kinds *)letreciter=function|[]->[]|hd::tl->lethd',tl'=iter_withhdtlinhd'::itertl'anditer_witha=function|[]->a,[]|hd::tl->matchjoin_alarm_kindahdwith|None->leta',tl'=iter_withatlina',hd::tl'|Someaa->letaa',tl'=iter_withaatlinaa',tl'inletkinds'=iter(AlarmKindSet.elementskinds)inifprintthenpp_diagnosticoutidiagWoCscsskinds';leterror',warning'=ifdiagWoCs.diag_kind=Errorthenerror+len,warningelseerror,warning+lenini+len,safe,error',warning',checks_map')diag_groups(0,0,0,0,CheckMap.empty)letprint_checks_summarychecks_maptotalsafeerrorwarningout=letppdiagsingluarpluralfmtn=ifn=0then()elseifn=1thenfprintffmt", %a"(Debug.color(color_of_diagdiag)(funfmtn->fprintffmt"%s %d %s"(icon_of_diagdiag)nsingluar))nelsefprintffmt", %a"(Debug.color(color_of_diagdiag)(funfmtn->fprintffmt"%s %d %s"(icon_of_diagdiag)nplural))ninprintout"@[<v2>Checks summary: %a%a%a%a (selectivity: %.2f%%)@,%a@]@.@."(Debug.bold(funfmttotal->fprintffmt"%d total"total))total(ppSafe"safe""safe")safe(ppError"error""errors")error(ppWarning"warning""warnings")warning(float_of_int(100*safe)/.(float_of_int@@safe+error+warning))(pp_print_list~pp_sep:(funfmt()->fprintffmt"@,")(funfmt(check,(safe,error,warning))->fprintffmt"%a: %d total%a%a%a"pp_checkcheck(safe+error+warning)(ppSafe"safe""safe")safe(ppError"error""errors")error(ppWarning"warning""warnings")warning))(CheckMap.bindingschecks_map)letreportmanflow~time~files~out=letrep=Flow.get_reportflowinifis_sound_reportrepthenprintout"%a@."(Debug.color_strDebug.green)"Analysis terminated successfully"elseprintout"%a@."(Debug.color_strDebug.orange)"Analysis terminated successfully (with assumptions)";if!opt_display_lastflowthenprintout"Last flow =@[@\n%a@]@\n"(* "Context = @[@\n%a@]@\n" *)(format(Core.Flow.printman.lattice.print))flow(* (Core.Context.print man.lattice.print) (Flow.get_ctx f) *);ifis_safe_reportrepthenprintout"%a No alarm@."((Debug.colorDebug.green)pp_print_string)"✔";printout"Analysis time: %.3fs@."time;lettotal,safe,error,warning,checks_map=construct_checks_summary~print:truerepoutinprint_checks_summarychecks_maptotalsafeerrorwarningout;ifnot(is_sound_reportrep)thenletnb=AssumptionSet.cardinalrep.report_assumptionsinprintout"%d assumption%a:@, @[<v>%a@]@.@."nbDebug.plurial_intnb(pp_print_list~pp_sep:(funfmt()->fprintffmt"@,")pp_assumption)(AssumptionSet.elementsrep.report_assumptions)letpanicexn~btrace~time~files~outcontinuation=printout"%a@."(Debug.color_strDebug.red)"Analysis aborted";let()=matchexnwith|Exceptions.Panic(msg,"")->printout"panic: %s@."msg|Exceptions.Panic(msg,loc)->printout"panic raised in %s: %s@."locmsg|Exceptions.PanicAtLocation(range,msg,"")->printout"panic in %a: %s@."Location.pp_rangerangemsg|Exceptions.PanicAtLocation(range,msg,loc)->printout"%a: panic raised in %s: %s@."Location.pp_rangerangelocmsg|Exceptions.PanicAtFrame(range,cs,msg,"")->printout"panic in %a: %s@\nTrace:@\n%a@."Location.pp_rangerangemsgpp_callstackcs;|Exceptions.PanicAtFrame(range,cs,msg,loc)->printout"%a: panic raised in %s: %s@\nTrace:@\n%a@."Location.pp_rangerangelocmsgpp_callstackcs|Exceptions.SyntaxError(range,msg)->printout"%a: syntax error: %s@."Location.pp_rangerangemsg|Exceptions.UnnamedSyntaxErrorrange->printout"%a: syntax error@."Location.pp_rangerange|Exceptions.SyntaxErrorListl->printout"Syntax errors:@\n @[%a@]@."(pp_print_list~pp_sep:(funfmt()->fprintffmt"@\n")(funfmt(range,msg)->fprintffmt"%a: %s"Location.pp_rangerangemsg))l|Exceptions.UnnamedSyntaxErrorListl->printout"Syntax errors:@\n @[%a@]@."(pp_print_list~pp_sep:(funfmt()->fprintffmt"@\n")Location.pp_range)l|_->printout"Uncaught exception: %s@."(Printexc.to_stringexn)inlet()=ifbtrace=""then()elseprintout"Backtrace:@\n%s"btraceincontinuation()letgroup_args_by_categoryargs=letsorted=List.sort(funarg1arg2->comparearg1.categoryarg2.category)argsinletgrouped,_=List.fold_right(funarg(acc,cat)->ifcomparecatarg.category<>0then(arg.category,[arg])::acc,arg.categoryelselet(_,l)=List.hdaccin(cat,arg::l)::(List.tlacc),cat)sorted([],"")ingroupedlethelp(args:ArgExt.arglist)~out=letprint_defaultfmtd=ifd=""then()elsefprintffmt" (default: %s)"dinletgroups=group_args_by_categoryargsinprintout"Options:@.";List.iter(fun(cat,args)->printout" %s@."(String.uppercase_asciicat);List.iter(funarg->matcharg.specwith|Symbol(l,_)->printout" %s={%a} %s%a@."arg.key(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt",")pp_print_string)larg.docprint_defaultarg.default|_->printout" %s %s%a@."arg.keyarg.docprint_defaultarg.default)(List.sortStdlib.compareargs))groupsletlist_domains(domains:stringlist)~out=printout"Domains:@.";List.iter(fund->printout" %s@."d)domainsletlist_reductions(reductions:stringlist)~out=printout"Reductions:@.";List.iter(fund->printout" %s@."d)reductionsletlist_checkschecks~out=printout"Checks:@.";List.iter(funchk->printout" %a@."Core.Alarm.pp_checkchk)checksletlist_hookshooks~out=printout"Hooks:@.";List.iter(funh->printout" %s@."h)hooksletprintprinter~range~out=ifDebug.can_print"print"thenprintout"%a@\n @[%a@]@."Location.pp_relative_rangerangepflushprinterelse()