123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625(**************************************************************************)(* *)(* 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_typesopenGui_typesopenLattice_boundsletresults_kf_computedkf=Analysis.is_computed()&&Results.are_availablekfletkf_calledkf=Analysis.is_computed()&&Results.is_calledkfletterm_c_typet=Logic_const.plain_or_set(funltyp->matchLogic_utils.unroll_typeltypwith|Ctypetyp->Sometyp|_->None)(Logic_utils.unroll_typet.term_type)letclassify_pre_postkfip=letopenPropertyinmatchipwith|IPPredicate{ip_kind=PKEnsures(_,Normal)}->Some(GL_Postkf)|IPPredicate{ip_kind=PKEnsures_}|IPAxiomatic_|IPLemma_|IPTypeInvariant_|IPGlobalInvariant_|IPOther_|IPCodeAnnot_|IPAllocation_|IPReachable_|IPExtended_|IPBehavior_->None|IPPropertyInstance{ii_kf;ii_stmt}->Some(GL_Stmt(ii_kf,ii_stmt))|IPPredicate{ip_kind=PKRequires_|PKAssumes_|PKTerminates}|IPComplete_|IPDisjoint_|IPAssigns_|IPFrom_|IPDecrease_->Some(GL_Prekf)letgui_loc_logic_envlm=(* According to the ACSL spec, 'Pre' is not available in preconditions,
but in practice it is parsed *)letpre()=lete=Logic_typing.Lenv.empty()inLogic_typing.(append_pre_label(append_init_label(append_here_labele)))inletstmt()=pre()in(*TODO: add LoopEntry and LoopCurrent when supported*)letpost()=Logic_typing.append_old_and_post_labels(stmt())inmatchlmwith|GL_Stmt_->stmt()|GL_Pre_->pre()|GL_Post_->post()type'agui_selection_data={alarm:bool;red:bool;before:'agui_res;before_string:stringLazy.t;after:'agui_after;after_string:stringLazy.t;}letgui_selection_data_empty={alarm=false;red=false;before=GR_Empty;before_string=lazy"";after=GA_NA;after_string=lazy"";}letclear_caches()=Cvalue.V_Offsetmap.clear_caches();Cvalue.Model.clear_caches();Locations.Location_Bytes.clear_caches();Locations.Zone.clear_caches();Function_Froms.Memory.clear_caches()moduletypeS=sigmoduleAnalysis:Analysis.Stype('env,'expr,'v)evaluation_functions={eval_and_warn:'env->'expr->'v*bool(* alarm *)*bool(* red *);env:Analysis.Dom.t->Callstack.t->'env;equal:'v->'v->bool;bottom:'v;join:'v->'v->'v;expr_to_gui_selection:'expr->gui_selection;res_to_gui_res:'expr->'v->Analysis.Val.tgui_res;}vallval_as_offsm_ev:(Analysis.Dom.t,lval,gui_offsetmap_res)evaluation_functionsvallval_zone_ev:(Analysis.Dom.t,lval,Locations.Zone.t)evaluation_functionsvalnull_ev:(Analysis.Dom.t,unit,gui_offsetmap_res)evaluation_functionsvalexp_ev:(Analysis.Dom.t,exp,Analysis.Val.tor_bottom)evaluation_functionsvallval_ev:(Analysis.Dom.t,lval,Analysis.Val.tEval.flagged_value)evaluation_functionsvaltlval_ev:gui_loc->(Eval_terms.eval_env,term,gui_offsetmap_res)evaluation_functionsvaltlval_zone_ev:gui_loc->(Eval_terms.eval_env,term,Locations.Zone.t)evaluation_functionsvalterm_ev:gui_loc->(Eval_terms.eval_env,term,Analysis.Val.tor_bottom)evaluation_functionsvalpredicate_ev:gui_loc->(Eval_terms.eval_env,predicate,Eval_terms.predicate_statusor_bottom)evaluation_functionsvalpredicate_with_red:gui_loc->(Eval_terms.eval_env*(kinstr*Callstack.t),Red_statuses.alarm_or_property*predicate,Eval_terms.predicate_statusor_bottom)evaluation_functionsvalmake_data_all_callstacks:('a,'b,'c)evaluation_functions->gui_loc->'b->(gui_callstack*Analysis.Val.tgui_selection_data)list*exnlistendmoduleMake(X:Analysis.S)=structmoduleAnalysis=XincludeCvalue_domain.Getters(X.Dom)letget_precise_loc=matchX.Loc.getMain_locations.PLoc.keywith|None->fun_->Precise_locs.loc_top|Someget->funloc->getlocmoduleAGui_types=Gui_types.Make(X.Val)openAGui_typestype('env,'expr,'v)evaluation_functions={eval_and_warn:'env->'expr->'v*bool*bool;env:X.Dom.t->Callstack.t->'env;equal:'v->'v->bool;bottom:'v;join:'v->'v->'v;expr_to_gui_selection:'expr->gui_selection;res_to_gui_res:'expr->'v->X.Val.tgui_res;}(* Special function for l-values (Var vi, NoOffset). Since allocated variables
may have an incomplete array type, it is simpler to extract the entire
offsetmap and return it (instead of performing a copy of the offsetmap with
a wacky size). For "normal" variables, this code is correct too.
The returned boolean 'ok' means that the operation was possible. *)letextract_single_varstatevi=letb=Base.of_varinfoviintrymatchCvalue.Model.find_basebstatewith|`Bottom->GO_InvalidLoc,false|`Valuem->GO_Offsetmapm,true|`Top->GO_Top,falsewithNot_found->GO_InvalidLoc,false(* Evaluate the given location in [state]. Catch an unreachable state, an
invalid location, or another error during the evaluation. The returned
boolean means 'ok', i.e. that no error occurred. *)letreduce_loc_and_evalstateloc=ifCvalue.Model.is_topstatethenGO_Top,falseelseifCvalue.Model.is_reachablestatethenifInt_Base.(equalloc.Locations.sizezero)thenGO_Empty,trueelseletloc'=Locations.(valid_partReadloc)inifLocations.is_bottom_locloc'thenGO_InvalidLoc,falseelsetryletsize=Int_Base.projectloc'.Locations.sizeinmatchCvalue.Model.copy_offsetmaploc'.Locations.locsizestatewith|`Bottom->GO_Bottom,false|`Valueoffsm->letok=Locations.(is_validReadloc)inGO_Offsetmapoffsm,okwithAbstract_interp.Error_Top->GO_Top,falseelse(* Bottom state *)GO_Bottom,trueletlval_to_offsetmapstatelv=letloc,alarms=X.eval_lval_to_locstatelvinletok=Alarmset.is_emptyalarmsinletstate=get_cvalue_or_topstateinletauxloc(acc_res,acc_ok)=letres,ok=matchlvwith(* catch simplest pattern *)|Varvi,NoOffset->extract_single_varstatevi|_->reduce_loc_and_evalstatelocinmatchacc_res,reswith|GO_Offsetmapo1,GO_Offsetmapo2->GO_Offsetmap(Cvalue.V_Offsetmap.joino1o2),acc_ok&&ok|GO_Bottom,v|v,GO_Bottom->v,acc_ok&&ok|GO_Empty,v|v,GO_Empty->v,acc_ok&&ok|GO_Top,GO_Top->GO_Top,acc_ok&&ok|GO_InvalidLoc,GO_InvalidLoc->GO_InvalidLoc,false|GO_InvalidLoc,GO_Offsetmap_->res,false|GO_Offsetmap_,GO_InvalidLoc->acc_res,false|GO_Top,(GO_InvalidLoc|GO_Offsetmap_asr)|(GO_InvalidLoc|GO_Offsetmap_asr),GO_Top->r,acc_ok&&ok(* cannot happen, we should get Top everywhere *)inmatchlocwith|`Bottom->GO_InvalidLoc,ok,false|`Valueloc->letploc=get_precise_loclocinletr,ok=Precise_locs.foldauxploc(GO_Bottom,ok)inr,ok,falseletlv_offsetmap_res_to_gui_reslvoffsm=lettyp=Some(Cil.unrollType(Cil.typeOfLvallv))inGR_Offsm(offsm,typ)letid_envstate_=stateletlval_as_offsm_ev={eval_and_warn=lval_to_offsetmap;env=id_env;equal=equal_gui_offsetmap_res;bottom=GO_Bottom;join=join_gui_offsetmap_res;expr_to_gui_selection=(funlv->GS_LVallv);res_to_gui_res=lv_offsetmap_res_to_gui_res;}letlval_zone_ev=letlv_to_zonestatelv=letloc,_alarms=X.eval_lval_to_locstatelvinmatchlocwith|`Bottom->Locations.Zone.bottom,false,false|`Valueloc->letploc=get_precise_loclocinletz=Precise_locs.enumerate_valid_bitsLocations.Readplocinz,false,falsein{eval_and_warn=lv_to_zone;env=id_env;equal=Locations.Zone.equal;bottom=Locations.Zone.bottom;join=Locations.Zone.join;expr_to_gui_selection=(funlv->GS_LVallv);res_to_gui_res=(fun_z->GR_Zonez);}letnull_to_offsetmapstate(_:unit)=letstate=get_cvalue_or_topstateinmatchCvalue.Model.find_base_or_defaultBase.nullstatewith|`Bottom->GO_InvalidLoc,false,false|`Top->GO_Top,false,false|`Valuem->GO_Offsetmapm,true,falseletnull_ev={eval_and_warn=null_to_offsetmap;env=id_env;equal=equal_gui_offsetmap_res;bottom=GO_Bottom;join=join_gui_offsetmap_res;expr_to_gui_selection=(fun_->GS_AbsoluteMem);res_to_gui_res=(fun_offsm->GR_Offsm(offsm,None));}letexp_ev=leteval_exp_and_warnstatee=letr=X.eval_exprstateeinfstr,Alarmset.is_empty(sndr),falseinletres_to_gui_resev=letflagged_value=Eval.{v;initialized=true;escaping=false;}inGR_Value(flagged_value,Some(Cil.typeOfe))in{eval_and_warn=eval_exp_and_warn;env=id_env;equal=Bottom.equalX.Val.equal;bottom=`Bottom;join=Bottom.joinX.Val.join;expr_to_gui_selection=(fune->GS_Expre);res_to_gui_res;}letlval_ev=leteval_and_warnstatelval=letr=X.copy_lvaluestatelvalinletflagged_value=matchfstrwith|`Bottom->Eval.Flagged_Value.bottom|`Valuev->vinflagged_value,Alarmset.is_empty(sndr),falsein{eval_and_warn;env=id_env;bottom=Eval.Flagged_Value.bottom;equal=Eval.Flagged_Value.equalX.Val.equal;join=Eval.Flagged_Value.joinX.Val.join;expr_to_gui_selection=(funlv->GS_LVallv);res_to_gui_res=(funlvv->GR_Value(v,Some(Cil.typeOfLvallv)));}letpre_kfkfcallstack=matchCvalue_results.get_initial_state_by_callstackkfwith|`Top->Cvalue.Model.top(* should not happen *)|`Bottom->Cvalue.Model.bottom|`Valueh->tryCallstack.Hashtbl.findhcallstackwithNot_found->Cvalue.Model.top(* should not happen either *)letenv_herekfherecallstack=letpre=pre_kfkfcallstackinlethere=get_cvalue_or_tophereinletc_labels=Eval_annots.c_labelskfcallstackinEval_terms.env_annot~c_labels~pre~here()letenv_pre_kfhere_callstack=lethere=get_cvalue_or_tophereinEval_terms.env_pre_f~pre:here()letenv_postkfpostcallstack=letpre=pre_kfkfcallstackinletpost=get_cvalue_or_toppostinletresult=ifFunction_calls.use_spec_instead_of_definitionkfthenNoneelseletret_stmt=Kernel_function.find_returnkfinmatchret_stmt.skindwith|Return(Some({enode=Lval(Varvi,NoOffset)}),_)->Somevi|Return(None,_)->None|_->assertfalseinletc_labels=Eval_annots.c_labelskfcallstackinEval_terms.env_post_f~c_labels~pre~post~result()(* Maps from callstacks to Value states before and after a GUI location.
The 'after' map is not always available. *)typestates_by_callstack={states_before:X.Dom.tCallstack.Hashtbl.tor_top_bottom;states_after:X.Dom.tCallstack.Hashtbl.tor_top_bottom;}lettop_states_by_callstacks={states_before=`Top;states_after=`Top}(* For statements: results are available only if the statement is reachable.
After states are available only for instructions. *)letcallstacks_at_stmtkfstmt=ifresults_kf_computedkfthen(* Show 'after' states only in instructions. On blocks and if/switch
statements, the notion of 'after' is counter-intuitive. *)letis_instr=matchstmt.skindwithInstr_->true|_->falseinletstates_before=X.get_stmt_state_by_callstack~after:falsestmtinletstates_after=matchstates_beforewith|`Top|`Bottomasx->x|`Value_->ifis_instrthenX.get_stmt_state_by_callstack~after:truestmtelse`Topin{states_before;states_after}elsetop_states_by_callstacks(* For pre-states: results are available only if the function is called,
and correspond to the states before reduction by any precondition.
After states are not available. *)letcallstacks_at_prekf=ifkf_calledkfthenletstates_before=X.get_initial_state_by_callstackkfin{states_before;states_after=`Top}elsetop_states_by_callstacks(* For post-states: results are available only for functions with a body, for
normal termination, and only when the function is called.
After states are not available. *)letcallstacks_at_postkf=ifnot(Function_calls.use_spec_instead_of_definitionkf)&&results_kf_computedkfthenletret=Kernel_function.find_returnkfinletstates_before=X.get_stmt_state_by_callstack~after:trueretin{states_before;states_after=`Top}elsetop_states_by_callstacksletcallstacks_at_gui_loc=function|GL_Stmt(kf,stmt)->callstacks_at_stmtkfstmt|GL_Prekf->callstacks_at_prekf|GL_Postkf->callstacks_at_postkfletenv_gui_loc=function|GL_Stmt(kf,_)->env_herekf|GL_Prekf->env_prekf|GL_Postkf->env_postkflettlval_evlm=lettlval_to_offsetmapenvtlv=letalarms=reffalseinletalarm_mode=Eval_terms.Trackalarmsinletloc=Eval_terms.eval_tlval_as_locationenv~alarm_modetlvinletstate=Eval_terms.env_current_stateenvinletoffsm,ok=reduce_loc_and_evalstatelocinoffsm,not!alarms&&ok,falsein{eval_and_warn=tlval_to_offsetmap;env=env_gui_loclm;equal=equal_gui_offsetmap_res;bottom=GO_Bottom;join=join_gui_offsetmap_res;expr_to_gui_selection=(funtlv->GS_TLValtlv);res_to_gui_res=(funtlvoffsm->GR_Offsm(offsm,term_c_typetlv))}lettlval_zone_evgl=lettlv_to_zoneenvtlv=letalarms=reffalseinletalarm_mode=Eval_terms.Trackalarmsinletz=Eval_terms.eval_tlval_as_zoneLocations.Readenv~alarm_modetlvinz,not!alarms,falsein{eval_and_warn=tlv_to_zone;env=env_gui_locgl;equal=Locations.Zone.equal;bottom=Locations.Zone.bottom;join=Locations.Zone.join;expr_to_gui_selection=(funtlv->GS_TLValtlv);res_to_gui_res=(fun_z->GR_Zonez);}letterm_evlm=leteval_term_and_warnenvt=letalarms=reffalseinletalarm_mode=Eval_terms.Trackalarmsinletr=Eval_terms.(eval_term~alarm_modeenvt)in`Value(from_cvaluer.Eval_terms.eover),not!alarms,falseinletres_to_gui_restv=letflagged_value=Eval.{v;initialized=true;escaping=false;}inGR_Value(flagged_value,term_c_typet)in{eval_and_warn=eval_term_and_warn;env=env_gui_loclm;equal=Bottom.equalX.Val.equal;bottom=`Bottom;join=Bottom.joinX.Val.join;expr_to_gui_selection=(funt->GS_Termt);res_to_gui_res;}letpredicate_evlm=leteval_predicate_and_warnenvt=letr=Eval_terms.eval_predicateenvtin`Valuer,true(* TODO *),falseinletto_status=function|`Bottom->Eval_terms.True|`Values->sin{eval_and_warn=eval_predicate_and_warn;env=env_gui_loclm;equal=(=);bottom=`Bottom;join=Bottom.joinEval_terms.join_predicate_status;expr_to_gui_selection=(funp->GS_Predicatep);res_to_gui_res=(fun_s->GR_Status(to_statuss));}(* Evaluation of a predicate, while tracking red alarms inside the dedicated
column. *)letpredicate_with_redlm=(* We need the statement and the callstack in the environment to
determine whether a red status was emitted then during the analysis. *)letenv_alarm_loclmstatecs=env_gui_loclmstatecs,matchlmwith|GL_Stmt(_,stmt)->Kstmtstmt,cs|GL_Pre_|GL_Post_->Kglobal,csinleteval_alarm_and_warn(env,(kinstr,cs))(ap,p)=letr=Eval_terms.eval_predicateenvpinletred=Red_statuses.is_red_in_callstackkinstrapcsin`Valuer,true(* TODO *),redinletto_status=function|`Bottom->Eval_terms.True|`Values->sin{eval_and_warn=eval_alarm_and_warn;env=env_alarm_loclm;equal=(=);bottom=`Bottom;join=Bottom.joinEval_terms.join_predicate_status;expr_to_gui_selection=(fun(_,p)->GS_Predicatep);res_to_gui_res=(fun_s->GR_Status(to_statuss));}letdata~ok~before~after~red={before;after;alarm=notok;red;before_string=lazy(Pretty_utils.to_stringpretty_gui_resbefore);after_string=(matchafterwith|GA_NA|GA_Unchanged|GA_Bottom->lazy""(* won't be used *)|GA_Afterafter->lazy(Pretty_utils.to_stringpretty_gui_resafter));}typebefore_after=BABefore|BAAfter(* Evaluation of [exp] in [before] and [after] using [ev]. [set_ba] must
be called before each evaluation, with the state in which the evaluation
will be done. *)letmake_dataevset_ba~before~afterexp=set_baBABefore;letvbefore,ok,red=ev.eval_and_warnbeforeexpinletbefore=ev.res_to_gui_resexpvbeforeinmatchafterwith|`Top->data~before~after:GA_NA~ok~red|`Bottom->data~before~after:(GA_Bottom)~ok~red|`Valueafter->set_baBAAfter;(* Currently, we do not warn for alarms in the post-state. *)letvafter,_okafter,_redafter=ev.eval_and_warnafterexpinifev.equalvbeforevafterthendata~before~after:GA_Unchanged~ok~redelsedata~before~after:(GA_After(ev.res_to_gui_resexpvafter))~ok~redletmake_data_all_callstacks_from_statesev~before~afterexpr=letexn=ref[]inletsingle_callstack=(Callstack.Hashtbl.lengthbefore)=1inletv_join_before=refev.bottominletv_join_after=refev.bottominletok_join=reftrueinletred_join=reffalseinletrba=refBABeforeinletset_baba=rba:=bain(* Change [ev] to store intermediate results for 'consolidated' line *)leteval_and_warnstatese=letv,ok,redasr=ev.eval_and_warnstateseinbeginmatch!rbawith|BABefore->v_join_before:=ev.join!v_join_beforev;ok_join:=!ok_join&&ok;red_join:=!red_join||red;|BAAfter->v_join_after:=ev.join!v_join_afterv;end;rinletev={evwitheval_and_warn}in(* Rows by callstack *)letlist=Callstack.Hashtbl.fold(funcallstackbeforeacc->letbefore=ev.envbeforecallstackinletafter=matchafterwith|`Top|`Bottomasx->x|`Valueafter->tryletafter=Callstack.Hashtbl.findaftercallstackin`Value(ev.envaftercallstack)(* If a callstack exists before the statement but is not found
after, then the post state for this callstack is bottom. *)withNot_found->`Bottominletcallstack=ifsingle_callstackthenGC_SinglecallstackelseGC_Callstackcallstackintry(callstack,(make_dataevset_ba~before~afterexpr))::accwithe->exn:=e::!exn;acc)before[]in(* Consolidated row, only if there are multiple callstacks *)letlist=ifsingle_callstackthenlistelseletcallstack=GC_Consolidatedinletbefore=ev.res_to_gui_resexpr!v_join_beforeinletafter=matchafterwith|`Top|`Bottom->GA_NA|`Value_->ifev.equal!v_join_before!v_join_afterthenGA_UnchangedelseGA_After(ev.res_to_gui_resexpr!v_join_after)in(callstack,(data~before~after~ok:!ok_join~red:!red_join))::listinlist,!exnletmake_data_all_callstacksevlocv=let{states_before;states_after}=callstacks_at_gui_loclocinmatchstates_beforewith|`Top->[],[](* Happens if none of the domains has saved its states.
In this case, nothing is displayed by the GUI. *)|`Bottom->[],[](* Bottom case: nothing is displayed either. *)|`Valuebefore->Cil.CurrentLoc.set(gui_loc_locloc);clear_caches();make_data_all_callstacks_from_statesev~before~after:states_aftervend(*
Local Variables:
compile-command: "make -C ../../../.."
End:
*)