123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)openMt_thread(* Mthread registers once and for all a few callbacks inside the value
analysis. Depending on whether the plugin is active and the mthread analysis
has not already been computed, the callbacks are set to functions that
do nothing, or to real functions
*)letno_hook=fun____->()letref_hook_call_function=refno_hookletref_hook_end_function=refno_hooklet()=Cvalue_callbacks.register_call_hook(funargs->!ref_hook_call_functionargs)let()=Cvalue_callbacks.register_call_results_hook(funargs->!ref_hook_end_functionargs)lethook_builtins=lazy((* a generic function that does nothing *)letnopst_args=st,Nonein(* We create a reference for each of our builtin functions *)letlref=List.map(fune->(refnop,e))Mt_analysis_hooks.mthread_builtinsin(* We register one hook by function, that simply dereferences the
corresponding reference (up to some interface conversion) *)List.iter(fun(r,(s,_))->(* Conversion from the simplified type of an mthread function
into one suitable as a hook *)Builtins.register_builtinsNoCacheCallers(funstargs->letst,res=try!rstargswithMt_analysis_hooks.Hook_failureres->st,Some(Mt_memory.int_to_valueres)inBuiltins.Full{c_values=[res,st];c_clobbered=Base.SetLattice.bottom;c_assigns=None;}))lref;(* And finally, we return a function that sets all the references either
to nop, or to the suitable hook function *)(function|None->(* Disable the hook *)List.iter(fun(r,_)->r:=nop)lref;ref_hook_call_function:=no_hook;ref_hook_end_function:=no_hook;|Someanalysis->List.iter(fun(r,(_s,f))->r:=fanalysis)lref;ref_hook_call_function:=Mt_analysis_hooks.catch_functions_callsanalysis;ref_hook_end_function:=Mt_analysis_hooks.catch_functions_recordanalysis;))letref_analysis=refNoneletanalysis_hook=ref[]letregister_analysis_hookhook=analysis_hook:=hook::!analysis_hookletapply_analysis_hooks()=letapplyanalysis=List.iter(funhook->hookanalysis)!analysis_hookinOption.iterapply!ref_analysis(* Perform an entire mthread execution, based on the ast and options of the
given project *)letmthread_runproject=Mt_self.warning"Mthread is an experimental plugin and is still in development.";Mt_lib.check_mthread_library();ifnot(Mt_options.ConcatDotFilesTo.is_empty())&¬(Mt_options.ExtractModels.mem"html")thenMt_self.error"Option %S needs option \"%s html\" to work."Mt_options.ConcatDotFilesTo.option_nameMt_options.ExtractModels.option_name;letold_project=Project.current()inProject.set_currentproject;lethook_builtins=Lazy.forcehook_builtinsinMt_self.feedback"******* Starting mthread";(* We force the computation of the AST before this stage, so that it does not
get recomputed in some other projects later *)Ast.compute();(* Make sure that Mthread won't restart in one of the other projects
or once the fixpoint is reached. *)Mt_options.Enabled.setfalse;(* We create the record containing the state of the analysis (which must
remain unique, as it is used to define the callback for the value
analysis.)
For the current thread field, we use a dummy main thread, that will get
overwritten once the real one is determined *)letf_main=tryfst(Globals.entry_point())withGlobals.No_such_entry_points->Mt_self.abort"%s Mthread cannot run"sinletdummy_main_thread=Mt_analysis_hooks.main_threadf_mainCvalue.Model.empty_mapinletanalysis={all_threads=Thread.Hashtbl.create17;all_mutexes=Mutex.Set.empty;all_queues=Mqueue.Set.empty;iteration=0;curr_thread=dummy_main_thread;main_thread=dummy_main_thread;curr_events_stack=[];memexec_cache=Datatype.Int.Hashtbl.create16;curr_stack=Callstack.init~thread:(Thread.(idmain))~entry_point:f_main;concurrent_accesses=Locations.Zone.bottom;precise_concurrent_accesses=Locations.Zone.bottom;concurrent_accesses_by_nodes=[];}in(* We register our callback function *)hook_builtins(Someanalysis);ref_analysis:=Someanalysis;apply_analysis_hooks();(* Cleanup function, called at the end or in case of failure or success. *)letcleanup()=apply_analysis_hooks();hook_builtinsNone;Project.set_currentold_project;intry(* We analyse the main thread *)letmoduleAnalyzer=(valAnalysis.current_analyzer())inAnalyzer.Interferences.reset();Self.clear_results();Thread.reset_state();Mutex.reset_state();Mqueue.reset_state();(* Let Eva know about interrupt handlers. *)Thread.register_interrupt_handlers(Mt_options.InterruptHandlers.get());Mt_self.feedback"*** Computing value analysis for main thread";Analysis.compute();Mt_self.feedback"*** First value analysis for main thread done.";(* The hooks of the value analysis have now found the real main thread *)letmain_th=analysis.curr_threadinletresults=Eva_results.get_results()inmain_th.th_value_results<-Someresults;Mt_analysis_fixpoint.record_end_of_thread_analysisanalysis;(* We perform the analysis iterations *)Mt_analysis_fixpoint.reach_fixpointanalysis;(* In the cfgs, mark whether the accesses are concurrent or not,
and remove superfluous node *)Mt_analysis_fixpoint.mark_shared_nodes_kindanalysis;(* We display the combination of all analyses *)Mt_outputs.Eva_results.displayanalysis;(* Printing results to files *)Mt_options.ExtractModels.iter(funs->Mt_self.feedback"******* Outputting model for %s"s;(matchswith|"html"->Mt_outputs.Html.output_threadsanalysis;|_->Mt_self.error"Unknown model %s specified"s;);Mt_self.feedback"******* %s output done."(String.capitalize_asciis););cleanup();withe->cleanup();raiseeletcompute_mthread_once,_self=State_builder.apply_once"mthread_compute"[Ast.self(*; Kernel.MainFunction.self *)](fun()->mthread_run(Project.current()))let()=(* Automatically add Mthread shared directory to the include path and add the
threads lib to the parsed sources if either -mt-threads-lib or -mthread
is used.
We do a best effort to add the stubbed files to the list of our files.
This should work even if a plugin requests the computation of
the AST before we have started running *)Cmdline.run_after_setting_files(fun_l->ifMt_options.ThreadsLib.is_set()||Mt_options.Enabled.get()thenMt_lib.load_threads_library(Mt_options.ThreadsLib.get()))let()=(* Check that the threads lib stays consistent if the AST has already been
computed with a specific variant. *)Mt_options.ThreadsLib.add_set_hook(funold_valuenew_value->ifold_value<>new_value&&Ast.is_computed()thenMt_self.warning"ignoring option %s specified after parsing"Mt_options.ThreadsLib.option_name)letmain()=ifMt_options.Enabled.get()then(compute_mthread_once())let()=Boot.Main.extendmain