123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2025 *)(* 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_typesopenMt_typesopenMt_shared_vars_typesopenMt_cfg_types(* -------------------------------------------------------------------------- *)(* --- Threads --- *)(* -------------------------------------------------------------------------- *)typerecompute_reason=|FirstIteration|NewMsgReceived|PotentialSharedVarsChanged|SharedVarsValuesChanged|InitialArgsChanged|InitialEnvChanged|InterferencesChanged;;moduleRecomputeReason=structtypet=recompute_reasonletcomparer1r2=matchr1,r2with|FirstIteration,FirstIteration|NewMsgReceived,NewMsgReceived|SharedVarsValuesChanged,SharedVarsValuesChanged|PotentialSharedVarsChanged,PotentialSharedVarsChanged|InitialArgsChanged,InitialArgsChanged|InitialEnvChanged,InitialEnvChanged|InterferencesChanged,InterferencesChanged->0|(FirstIteration|NewMsgReceived|SharedVarsValuesChanged|PotentialSharedVarsChanged|InitialArgsChanged|InitialEnvChanged|InterferencesChanged),_->Mt_lib.compare_tagr1r2letprettyfmt=function|FirstIteration->Format.fprintffmt"first@ iteration"|NewMsgReceived->Format.fprintffmt"new@ message@ received"|SharedVarsValuesChanged->Format.fprintffmt"shared@ vars@ values@ changed"|PotentialSharedVarsChanged->Format.fprintffmt"potential@ shared@ vars@ changed"|InitialArgsChanged->Format.fprintffmt"thread@ initial@ arguments@ changed"|InitialEnvChanged->Format.fprintffmt"thread@ initial@ memory@ state@ changed"|InterferencesChanged->Format.fprintffmt"interferences@ changed"endmoduleSetRecomputeReason=Set.Make(structtypet=recompute_reasonletcompare=RecomputeReason.compareend)typepriority=PDefault|PUnknown|PPriorityofintmodulePriority=Datatype.Make_with_collections(structtypet=priorityletname="Mt_thread.priority"letreprs=[PPriority0;PDefault;PUnknown]includeDatatype.Undefinedletcompare:t->t->int=Extlib.compare_basicletequal=Datatype.from_comparelethash=Hashtbl.hashend)typethread=Thread.ttypethread_state={th_eva_thread:Thread.t;th_parent:thread_stateoption;th_fun:kernel_function;th_stack:Callstack.t;mutableth_init_state:Cvalue.Model.t;mutableth_params:Cvalue.V.tlist;mutableth_amap:Trace.t;mutableth_to_recompute:SetRecomputeReason.t;mutableth_read_written:AccessesByZone.map;mutableth_cfg:CfgNode.t;mutableth_read_written_cfg:AccessesByZoneNode.map;mutableth_values_written:Mt_memory.Types.state;mutableth_projects:Project.tlist;mutableth_value_results:Eva_results.resultsoption;mutableth_priority:priority;}moduleThreadState=structtypet=thread_state(*
open Unmarshal
let help =
let l = t_list Locations.Location_Bytes.Datatype.descr in
let rec descr = Structure (Sum [|
[| Id.descr;
Structure (Sum [| [| descr|] |]);
Kernel_function.Datatype.descr;
Stack.descr;
Relations_type.Model.Datatype.descr;
l;
Trace.descr;
t_bool;
MapVarAccesses.Datatype.descr;
CfgNode.descr
|] |]) in
descr
*)letlabelth=Thread.labelth.th_eva_threadletis_mainth=Thread.is_mainth.th_eva_threadletprettyfmtth=Thread.prettyfmtth.th_eva_threadletequalth1th2=Thread.equalth1.th_eva_threadth2.th_eva_threadletpretty_detailedfmtth=letpp_parentfmt=function|None->()|Somep->Format.fprintffmt",@ parent %a,@ args %a"prettyp(Pretty_utils.pp_list~sep:",@ "Cvalue.V.pretty)th.th_paramsinFormat.fprintffmt"%a,@ fun %s%a"prettyth(Kernel_function.get_nameth.th_fun)pp_parentth.th_parentletone_creates_otherth1th2=letcreatesthpths=letrecin_parentsths'=matchths'.th_parentwith|None->`Unrelated|Somethwhenequalthpth->`Creates(thp,ths)|Someth->in_parentsthinin_parentsthsinmatchcreatesth1th2with|`Unrelated->createsth2th1|_asr->rletrecompute_becausethr=ifnot(SetRecomputeReason.equalth.th_to_recompute(SetRecomputeReason.singletonFirstIteration))(* Can happen if the control-flow leading to the thread creation
is split by the value analysis *)thenth.th_to_recompute<-SetRecomputeReason.addrth.th_to_recomputeend(* -------------------------------------------------------------------------- *)(* --- Thread analysis --- *)(* -------------------------------------------------------------------------- *)typethreads_table=thread_stateThread.Hashtbl.ttypeanalysis_state={all_threads:threads_table(* List of all threads. Is kept (and can thus
increase) from one iteration to the next *);mutableall_mutexes:Mutex.Set.t;(** Set of all mutexes of the analysis *)mutableall_queues:Mqueue.Set.t;(** Set of all queues of the analysis *)mutableiteration:int(* Current iteration of the analysis *);mutablemain_thread:thread_state(* Starting thread *);mutablecurr_thread:thread_state(* Thread currently running. *);mutablecurr_events_stack:Trace.tlist(* Mthread events that have been
found during the current analysis of the current thread. The list
has the same height as [curr_stack]. The top of the list is the trace
containing the events for the function being analyzed by Value, and
so on until the top of the list. When the list is popped, the events
of the callee are merged inside the trace of the caller. *);mutablememexec_cache:Trace.tDatatype.Int.Hashtbl.t(* Cache for the results obtained during the analysis of the current
thread *);mutablecurr_stack:Callstack.t(* stack of a multithread event. Asynchronously set by a callback and used
by another, because of a slightly too restricted signature in the
value analysis. *);mutableconcurrent_accesses:Locations.Zone.t(* Shared variables that have been detected in the analysis so far
in a global manner *);mutableprecise_concurrent_accesses:Locations.Zone.t(* Shared variables that have been detected in the analysis so far,
through the various cfgs. Subset of the previous field *);mutableconcurrent_accesses_by_nodes:(Locations.Zone.t*SetNodeIdAccess.t)list(* List of concurrent accesses that have been found. Used to
compute the field [precise_concurrent_accesses] *);}(* Iterators on threads. We presave the current list of threads so that
the iterators do not accidentally capture new added threads. (This is not
important for correctness, but is slightly cleaner.). Threads are sorted,
agains for cleanliness reasons. *)letthreadsanalysis=(* the main thread always have the least id and will always be in front of the
list *)Thread.Hashtbl.fold_sorted(fun_thl->th::l)analysis.all_threads[]|>List.revletthread_stateanalysisth=tryThread.Hashtbl.findanalysis.all_threadsthwithNot_found->Mt_options.fatal"Unknown thread %a"Thread.prettythletfold_threadsanalysisvf=List.fold_left(funaccth->fthacc)v(threadsanalysis)letiter_threadsanalysisf=List.iter(funth->fth)(threadsanalysis)letcalling_kianalysis=Callstack.top_callsiteanalysis.curr_stackletcurrent_funanalysis=Callstack.top_kfanalysis.curr_stackletcurr_eventsanalysis=matchanalysis.curr_events_stackwith|[]->Mt_options.fatal"Invalid analysis stack"|h::_->hleton_current_traceanalysisf=matchanalysis.curr_events_stackwith|[]->Mt_options.fatal"Invalid analysis stack"|h::q->analysis.curr_events_stack<-fhq::q(* Store a mthread event into the state of our analysis. *)letregister_eventanalysis?(top=Callstack.top_callanalysis.curr_stack)evt=on_current_traceanalysis(funcur_->Trace.add_eventcurtopevt);;letregister_multiple_eventsanalysisevts=on_current_traceanalysis(funcur_->Trace.unionevtscur);;(* Store the memory state for the function which we finished analyzing *)letregister_memory_statesanalysis~before~after=Mt_options.debug~level:2"Recording states for %a"Kernel_function.pretty(current_funanalysis);on_current_traceanalysis(funcur_->Trace.add_statescur~before~after);;;letpush_function_callanalysis=analysis.curr_events_stack<-Trace.empty::analysis.curr_events_stackletpop_function_callanalysis=lettop=Callstack.top_callanalysis.curr_stackinmatchanalysis.curr_stack.stackwith|[]->assert(List.lengthanalysis.curr_events_stack=1);on_current_traceanalysis(funcur_->Trace.add_prefixtopcur);|_::_->matchanalysis.curr_events_stackwith|[]|[_]->Mt_options.fatal"Invalid analysis stack when popping calling"|trace_callee::trace_caller::q->lettrace_callee'=Trace.add_prefixtoptrace_calleeinletnew_trace=Trace.uniontrace_callertrace_callee'inanalysis.curr_events_stack<-new_trace::qmoduleOrderedThreads=structletfamily_treeanalysis=letth_tbl=analysis.all_threadsin(* The inheritance table has at most as many entries as the general
thread table *)lettree=Thread.Hashtbl.(create(lengthth_tbl))inThread.Hashtbl.iter_sorted(funthstate->matchstate.th_parentwith|None->()(* This is the main thread *)|Someparent->letchildren=tryThread.Hashtbl.findtreeparent.th_eva_threadwithNot_found->[]inThread.Hashtbl.replacetreeparent.th_eva_thread(th::children))th_tbl;tree;;letcreation_mapanalysis=lettree=family_treeanalysisin(* Not really optimized, but we don't really care here. Mostly,
threads are created by one single thread, the main one *)letrecall_childrenaccth=letimmediate_children=tryThread.Hashtbl.findtreethwithNot_found->[]anddo_childaccth'=letacc'=Thread.Set.addth'accinall_childrenacc'th'inList.fold_leftdo_childaccimmediate_childreninfold_threadsanalysisThread.Map.empty(funthmap->letchildren=all_childrenThread.Set.emptyth.th_eva_threadinThread.Map.addth.th_eva_threadchildrenmap)(* Iter a function f over program threads following the an order compatible
with the partial order induced by thread creation *)letordered_iteranalysis=lettree=family_treeanalysisinfunfinitial->letrecdo_threadvalueth=letv=fthvalueintryletchildren=Thread.Hashtbl.findtreethinList.iter(do_threadv)children;withNot_found->()indo_threadinitialThread.main;;letordered_foldfaccanalysis=lettree=family_treeanalysisinletrecdo_thread_id_listaccthlist=matchthlistwith|[]->acc|_::_->letnew_acc,next_level=List.fold_left(fun(glob_acc,next_acc)th->letchildren=tryThread.Hashtbl.findtreethwithNot_found->[]in(fglob_accth,children@next_acc))(acc,[])thlistindo_thread_id_listnew_accnext_levelindo_thread_id_listacc[Thread.main];;endletshould_compute_threadth=(Thread.is_mainth.th_eva_thread)||(letname=ThreadState.labelthin(not(Datatype.String.Set.memname(Mt_options.SkipThreads.get())))&&letonly=Mt_options.OnlyThreads.get()inDatatype.String.Set.is_emptyonly||Datatype.String.Set.memnameonly)