123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)openCil_typesopenMt_typesopenMt_shared_vars_typesopenMt_cfg_types(* -------------------------------------------------------------------------- *)(* --- Threads --- *)(* -------------------------------------------------------------------------- *)typerecompute_reason=|FirstIteration|NewMsgReceived|PotentialSharedVarsChanged|SharedVarsValuesChanged|InitialArgsChanged|InitialEnvChanged|InterferencesChanged;;moduleRecomputeReason=structtypet=recompute_reason(* Used for comparison. *)letrank=function|FirstIteration->0|NewMsgReceived->1|PotentialSharedVarsChanged->2|SharedVarsValuesChanged->3|InitialArgsChanged->4|InitialEnvChanged->5|InterferencesChanged->6letcomparer1r2=rankr1-rankr2letprettyfmt=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_stateletlabelth=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_self.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_self.fatal"Invalid analysis stack"|h::_->hleton_current_traceanalysisf=matchanalysis.curr_events_stackwith|[]->Mt_self.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_self.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_self.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)