123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233(**************************************************************************)(* *)(* 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). *)(* *)(**************************************************************************)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_options.warning"Mthread is an experimental plugin and is still in development.";ifnot(Mt_options.ConcatDotFilesTo.is_empty())&¬(Mt_options.ExtractModels.mem"html")thenMt_options.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_options.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_options.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.initf_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();Mt_lib.clear_value_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_options.feedback"*** Computing value analysis for main thread";Analysis.compute();Mt_options.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_options.feedback"******* Outputting model for %s"s;(matchswith|"html"->Mt_outputs.Html.output_threadsanalysis;|_->Mt_options.error"Unknown model %s specified"s;);Mt_options.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()))(* We do a best effort to add [mthread.h] to the list of our files.
This should work even if a plugin requests the computation of
the AST before we have started running *)let()=Cmdline.run_after_setting_files(fun_l->ifMt_options.Enabled.get()thenletf=File.from_filename(Mt_lib.mthread_h())inFile.pre_registerf;)letmain()=ifMt_options.Enabled.get()then(compute_mthread_once())let()=Boot.Main.extendmain