diff options
author | njn <njn@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2006-12-14 02:55:58 +0000 |
---|---|---|
committer | njn <njn@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2006-12-14 02:55:58 +0000 |
commit | a86f29edc3d1daaf084736744a4a88c3c970e3f7 (patch) | |
tree | f3da75ce54ecc59b89f0689e99afebfba2877cd0 | |
parent | 75b5de7222e57e7c76ad3bba4adeddef47376c0b (diff) |
Removed the unused pthread_model and thread_model modules.
Mostly commented out the unused stuff relating to ThreadErrs and MutexErrs,
which no longer exist.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6399 a5019735-40e9-0310-863c-91ae7b9d1cf9
-rw-r--r-- | coregrind/Makefile.am | 4 | ||||
-rw-r--r-- | coregrind/m_errormgr.c | 55 | ||||
-rw-r--r-- | coregrind/m_pthreadmodel.c | 604 | ||||
-rw-r--r-- | coregrind/m_threadmodel.c | 1284 | ||||
-rw-r--r-- | coregrind/pub_core_errormgr.h | 7 | ||||
-rw-r--r-- | coregrind/pub_core_pthreadmodel.h | 50 | ||||
-rw-r--r-- | coregrind/pub_core_threadmodel.h | 70 | ||||
-rw-r--r-- | glibc-2.2.supp | 25 | ||||
-rw-r--r-- | glibc-2.3.supp | 25 |
9 files changed, 35 insertions, 2089 deletions
diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 25a2c9c0..850b6a5c 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -112,7 +112,6 @@ noinst_HEADERS = \ pub_core_mallocfree.h \ pub_core_options.h \ pub_core_oset.h \ - pub_core_pthreadmodel.h \ pub_core_redir.h \ pub_core_replacemalloc.h\ pub_core_scheduler.h \ @@ -122,7 +121,6 @@ noinst_HEADERS = \ pub_core_stacktrace.h \ pub_core_syscall.h \ pub_core_syswrap.h \ - pub_core_threadmodel.h \ pub_core_threadstate.h \ pub_core_tooliface.h \ pub_core_trampoline.h \ @@ -177,13 +175,11 @@ COREGRIND_SOURCES_COMMON = \ m_mallocfree.c \ m_options.c \ m_oset.c \ - m_pthreadmodel.c \ m_redir.c \ m_signals.c \ m_stacks.c \ m_stacktrace.c \ m_syscall.c \ - m_threadmodel.c \ m_threadstate.c \ m_tooliface.c \ m_trampoline.S \ diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c index 369eb49b..0c08225d 100644 --- a/coregrind/m_errormgr.c +++ b/coregrind/m_errormgr.c @@ -170,7 +170,10 @@ UInt VG_(get_n_errs_found)( void ) * effectively extend it by defining their own enums in the (0..) range. */ typedef enum { - PThreadSupp = -1, /* Matches PThreadErr */ + // Nb: thread errors are a relic of the time when Valgrind's core + // could detect them. This example is left commented-out as an + // example should new core errors ever be added. + ThreadSupp = -1, /* Matches ThreadErr */ } CoreSuppKind; @@ -269,10 +272,10 @@ static Bool eq_Error ( VgRes res, Error* e1, Error* e2 ) return False; switch (e1->ekind) { - // case ThreadErr: - // case MutexErr: - // vg_assert(VG_(needs).core_errors); - // return VG_(tm_error_equal)(res, e1, e2); + //(example code, see comment on CoreSuppKind above) + //case ThreadErr: + // vg_assert(VG_(needs).core_errors); + // return <something> default: if (VG_(needs).tool_errors) { return VG_TDICT_CALL(tool_eq_Error, res, e1, e2); @@ -302,11 +305,11 @@ static void pp_Error ( Error* err ) } switch (err->ekind) { - // case ThreadErr: - // case MutexErr: - // vg_assert(VG_(needs).core_errors); - // VG_(tm_error_print)(err); - // break; + //(example code, see comment on CoreSuppKind above) + //case ThreadErr: + // vg_assert(VG_(needs).core_errors); + // VG_(tm_error_print)(err); + // break; default: if (VG_(needs).tool_errors) VG_TDICT_CALL( tool_pp_Error, err ); @@ -420,10 +423,12 @@ static void gen_suppression(Error* err) if (stop_at > VG_MAX_SUPP_CALLERS) stop_at = VG_MAX_SUPP_CALLERS; vg_assert(stop_at > 0); - if (ThreadErr == err->ekind || MutexErr == err->ekind) { - VG_(printf)("{\n"); - VG_(printf)(" <insert a suppression name here>\n"); - VG_(printf)(" core:PThread\n"); + //(example code, see comment on CoreSuppKind above) + if (0) { + //if (0) ThreadErr == err->ekind) { + // VG_(printf)("{\n"); + // VG_(printf)(" <insert a suppression name here>\n"); + // VG_(printf)(" core:Thread\n"); } else { Char* name = VG_TDICT_CALL(tool_get_error_name, err); @@ -601,11 +606,11 @@ void VG_(maybe_record_error) ( ThreadId tid, /* update 'extra' */ switch (ekind) { - // case ThreadErr: - // case MutexErr: - // vg_assert(VG_(needs).core_errors); - // extra_size = VG_(tm_error_update_extra)(p); - // break; + //(example code, see comment on CoreSuppKind above) + //case ThreadErr: + // vg_assert(VG_(needs).core_errors); + // extra_size = <something> + // break; default: vg_assert(VG_(needs).tool_errors); extra_size = VG_TDICT_CALL(tool_update_extra, p); @@ -1013,9 +1018,10 @@ static void load_one_suppressions_file ( Char* filename ) if (VG_(needs).core_errors && tool_name_present("core", tool_names)) { // A core suppression - if (VG_STREQ(supp_name, "PThread")) - supp->skind = PThreadSupp; - else + //(example code, see comment on CoreSuppKind above) + //if (VG_STREQ(supp_name, "Thread")) + // supp->skind = ThreadSupp; + //else BOMB("unknown core suppression type"); } else if (VG_(needs).tool_errors && @@ -1120,8 +1126,9 @@ static Bool supp_matches_error(Supp* su, Error* err) { switch (su->skind) { - case PThreadSupp: - return (err->ekind == ThreadErr || err->ekind == MutexErr); + //(example code, see comment on CoreSuppKind above) + //case ThreadSupp: + // return (err->ekind == ThreadErr); default: if (VG_(needs).tool_errors) { return VG_TDICT_CALL(tool_error_matches_suppression, err, su); diff --git a/coregrind/m_pthreadmodel.c b/coregrind/m_pthreadmodel.c deleted file mode 100644 index c2e8f9a3..00000000 --- a/coregrind/m_pthreadmodel.c +++ /dev/null @@ -1,604 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Pthreads library modelling. m_pthreadmodel.c ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2005 Jeremy Fitzhardinge - jeremy@goop.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -/* - This file wraps the client's use of libpthread functions and calls - on vg_threadmodel.c to model the state of the the client's threads. - The intent is to 1) look for problem's in the client's use of the - pthread API, and 2) tell tools which care about thread events (eg, - helgrind). - - This file is intended to be implementation-independent. It assumes - that the client is using the same pthread.h as the one we include - here, but makes minimal assumptions about the actual structures - defined and so on (ie, the exact nature of pthread_t). - - (For now we assume there's a 1:1 relationship between pthread_t's - and Valgrind-visible threads; N:M implementations will need further - work.) - - The model is based on the pthread standard rather than any - particular implementation, in order to encourage portable use of - libpthread. On the other hand, we will probably need to implement - particular implementation extensions if they're widely used. - - One tricky problem we need to solve is the mapping between - pthread_t identifiers and internal thread identifiers. - */ - -#include "pub_core_basics.h" - -#if 0 - -#define __USE_GNU -#define __USE_UNIX98 -#include <pthread.h> - -static const Bool debug = False; - -static Bool check_wrappings(void); - -#define ENTER(x) \ - do { \ - if (VG_(clo_trace_pthreads)) \ - VG_(message)(Vg_DebugMsg, ">>> %d entering %s", \ - VG_(get_running_tid)(), #x); \ - } while(0) - -static const Char *pp_retval(enum return_type rt, Word retval) -{ - static Char buf[50]; - - switch(rt) { - case RT_RETURN: - VG_(sprintf)(buf, "return %d 0x%x", retval, retval); - return buf; - - case RT_LONGJMP: - return "LONGJMPed out"; - - case RT_EXIT: - return "thread exit"; - } - return "??"; -} - -#define LEAVE(x, rt, retval) \ - do { \ - if (VG_(clo_trace_pthreads)) \ - VG_(message)(Vg_DebugMsg, "<<< %d leaving %s -> %s", \ - VG_(get_running_tid)(), #x, pp_retval(rt, retval)); \ - } while(0) - -struct pthread_map -{ - pthread_t id; - - ThreadId tid; -}; - -static Int pthread_cmp(const void *v1, const void *v2) -{ - const pthread_t *a = (const pthread_t *)v1; - const pthread_t *b = (const pthread_t *)v2; - - return VG_(memcmp)(a, b, sizeof(*a)); -} - -static SkipList sk_pthread_map = VG_SKIPLIST_INIT(struct pthread_map, id, pthread_cmp, - NULL, VG_AR_CORE); - -/* Find a ThreadId for a particular pthread_t; block until it becomes available */ -static ThreadId get_pthread_mapping(pthread_t id) -{ - /* Nasty little spin loop; revise if this turns out to be a - problem. This should only spin for as long as it takes for the - child thread to register the pthread_t. */ - for(;;) { - struct pthread_map *m = VG_(SkipList_Find_Exact)(&sk_pthread_map, &id); - - if (m && m->tid != VG_INVALID_THREADID) - return m->tid; - - //VG_(printf)("find %x -> %p\n", id, m); - VG_(vg_yield)(); - } -} - -/* Create a mapping between a ThreadId and a pthread_t */ -static void pthread_id_mapping(ThreadId tid, Addr idp, UInt idsz) -{ - pthread_t id = *(pthread_t *)idp; - struct pthread_map *m = VG_(SkipList_Find_Exact)(&sk_pthread_map, &id); - - if (debug) - VG_(printf)("Thread %d maps to %p\n", tid, id); - - if (m == NULL) { - m = VG_(SkipNode_Alloc)(&sk_pthread_map); - m->id = id; - m->tid = tid; - VG_(SkipList_Insert)(&sk_pthread_map, m); - } else { - if (m->tid != VG_INVALID_THREADID && m->tid != tid) - VG_(message)(Vg_UserMsg, "Thread %d is creating duplicate mapping for pthread identifier %x; previously mapped to %d\n", - tid, (UInt)id, m->tid); - m->tid = tid; - } -} - -static void check_thread_exists(ThreadId tid) -{ - if (!VG_(tm_thread_exists)(tid)) { - if (debug) - VG_(printf)("creating thread %d\n", tid); - VG_(tm_thread_create)(VG_INVALID_THREADID, tid, False); - } -} - -static Addr startfunc_wrapper = 0; - -void VG_(pthread_startfunc_wrapper)(Addr wrapper) -{ - startfunc_wrapper = wrapper; -} - -struct pthread_create_nonce { - Bool detached; - pthread_t *threadid; -}; - -static void *before_pthread_create(va_list va) -{ - pthread_t *threadp = va_arg(va, pthread_t *); - const pthread_attr_t *attr = va_arg(va, const pthread_attr_t *); - void *(*start)(void *) = va_arg(va, void *(*)(void *)); - void *arg = va_arg(va, void *); - struct pthread_create_nonce *n; - struct vg_pthread_newthread_data *data; - ThreadState *tst; - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_create); - - /* Data is in the client heap and is freed by the client in the - startfunc_wrapper. */ - vg_assert(startfunc_wrapper != 0); - - tst = VG_(get_ThreadState)(VG_(get_running_tid)()); - - // XXX: why use TL_(malloc)() here? What characteristics does this - // allocation require? - // [Possible: When using a tool that replaces malloc(), we want to call - // the replacement version. Otherwise, we want to use VG_(cli_malloc)(). - // So we go via the default version of TL_(malloc)() in vg_default?] - tl_assert2(0, "read the comment in the code about this..."); - - // XXX: These three lines are going to have to change. They relied on - // TL_(malloc) being a weak symbol, and it just doesn't fit with the - // VG_(tdict) approach that we've switched to. The right way to do this - // will be to provide a function in the core that checks if - // VG_(tdict).malloc_malloc has been set; if so, it should - // call it, if not, it should call VG_(cli_malloc)(). -// VG_(tl_malloc_called_deliberately) = True; -// data = TL_(malloc)(sizeof(*data)); -// VG_(tl_malloc_called_deliberately) = False; - - VG_TRACK(pre_mem_write, Vg_CorePThread, tst->tid, "new thread data", - (Addr)data, sizeof(*data)); - data->startfunc = start; - data->arg = arg; - VG_TRACK(post_mem_write, (Addr)data, sizeof(*data)); - - /* Substitute arguments - XXX hack: need an API to do this. */ - ((Word *)tst->arch.m_esp)[3] = startfunc_wrapper; - ((Word *)tst->arch.m_esp)[4] = (Word)data; - - if (debug) - VG_(printf)("starting thread at wrapper %p\n", startfunc_wrapper); - - n = VG_(arena_malloc)(VG_AR_CORE, sizeof(*n)); - n->detached = attr && !!attr->__detachstate; - n->threadid = threadp; - - return n; -} - -static void after_pthread_create(void *nonce, enum return_type rt, Word retval) -{ - struct pthread_create_nonce *n = (struct pthread_create_nonce *)nonce; - ThreadId tid = VG_(get_running_tid)(); - - if (n == NULL) - return; - - if (rt == RT_RETURN && retval == 0) { - if (!VG_(tm_thread_exists)(tid)) - VG_(tm_thread_create)(tid, get_pthread_mapping(*n->threadid), - n->detached); - else { - if (n->detached) - VG_(tm_thread_detach)(tid); - /* XXX set creator tid as well? */ - } - } - - VG_(arena_free)(VG_AR_CORE, n); - - LEAVE(pthread_create, rt, retval); -} - -static void *before_pthread_join(va_list va) -{ - pthread_t pt_joinee = va_arg(va, pthread_t); - ThreadId joinee; - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_join); - - joinee = get_pthread_mapping(pt_joinee); - - VG_(tm_thread_join)(VG_(get_running_tid)(), joinee); - - return NULL; -} - -static void after_pthread_join(void *v, enum return_type rt, Word retval) -{ - /* nothing to be done? */ - if (!check_wrappings()) - return; - - LEAVE(pthread_join, rt, retval); -} - -struct pthread_detach_data { - pthread_t id; -}; - -static void *before_pthread_detach(va_list va) -{ - pthread_t id = va_arg(va, pthread_t); - struct pthread_detach_data *data; - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_detach); - - data = VG_(arena_malloc)(VG_AR_CORE, sizeof(*data)); - data->id = id; - - return data; -} - -static void after_pthread_detach(void *nonce, enum return_type rt, Word retval) -{ - struct pthread_detach_data *data = (struct pthread_detach_data *)nonce; - ThreadId tid; - - if (data == NULL) - return; - - tid = get_pthread_mapping(data->id); - - VG_(arena_free)(VG_AR_CORE, data); - - if (rt == RT_RETURN && retval == 0) - VG_(tm_thread_detach)(tid); - - LEAVE(pthread_detach, rt, retval); -} - - - -static void *before_pthread_self(va_list va) -{ - /* If pthread_t is a structure, then this might be passed a pointer - to the return value. On Linux/glibc, it's a simple scalar, so it is - returned normally. */ - if (!check_wrappings()) - return NULL; - - ENTER(pthread_self); - - check_thread_exists(VG_(get_running_tid)()); - return NULL; -} - -static void after_pthread_self(void *nonce, enum return_type rt, Word retval) -{ - pthread_t ret = (pthread_t)retval; - - if (!check_wrappings()) - return; - - pthread_id_mapping(VG_(get_running_tid)(), (Addr)&ret, sizeof(ret)); - - LEAVE(pthread_self, rt, retval); -} - - -/* If a mutex hasn't been initialized, check it against all the static - initializers to see if it appears to have been statically - initialized. */ -static void check_mutex_init(ThreadId tid, pthread_mutex_t *mx) -{ - static const pthread_mutex_t initializers[] = { - PTHREAD_MUTEX_INITIALIZER, - PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, - PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, - PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, - }; - Int i; - - if (VG_(tm_mutex_exists)((Addr)mx)) - return; - - VG_TRACK(pre_mem_read, Vg_CorePThread, tid, "pthread_mutex_t", (Addr)mx, sizeof(*mx)); - - for(i = 0; i < sizeof(initializers)/sizeof(*initializers); i++) - if (VG_(memcmp)(&initializers[i], mx, sizeof(*mx)) == 0) { - VG_(tm_mutex_init)(tid, (Addr)mx); - break; - } -} - -static void *before_pthread_mutex_init(va_list va) -{ - pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *); - const pthread_mutexattr_t *attr = va_arg(va, const pthread_mutexattr_t *); - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_mutex_init); - - /* XXX look for recursive mutex */ - /* XXX look for non-process scope */ - (void)attr; - - return mx; -} - -static void after_pthread_mutex_init(void *nonce, enum return_type rt, Word retval) -{ - if (!check_wrappings()) - return; - - if (rt == RT_RETURN && retval == 0) - VG_(tm_mutex_init)(VG_(get_running_tid)(), (Addr)nonce); - - LEAVE(pthread_mutex_init, rt, retval); -} - -static void *before_pthread_mutex_destroy(va_list va) -{ - pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *); - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_mutex_destroy); - - VG_(tm_mutex_destroy)(VG_(get_running_tid)(), (Addr)mx); - - return NULL; -} - -static void after_pthread_mutex_destroy(void *nonce, enum return_type rt, Word retval) -{ - if (!check_wrappings()) - return; - - LEAVE(pthread_mutex_destroy, rt, retval); -} - -static void *before_pthread_mutex_lock(va_list va) -{ - pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *); - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_mutex_lock); - - if (debug) - VG_(printf)("%d locking %p\n", VG_(get_running_tid)(), mx); - check_thread_exists(VG_(get_running_tid)()); - check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */ - VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx); - - return mx; -} - -static void after_pthread_mutex_lock(void *nonce, enum return_type rt, Word retval) -{ - if (!check_wrappings()) - return; - - if (rt == RT_RETURN && retval == 0) - VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce); - else { - if (debug) - VG_(printf)("after mutex_lock failed: rt=%d ret=%d\n", rt, retval); - VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce); - } - - LEAVE(pthread_mutex_lock, rt, retval); -} - -static void *before_pthread_mutex_trylock(va_list va) -{ - pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *); - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_mutex_trylock); - - if (debug) - VG_(printf)("%d trylocking %p\n", VG_(get_running_tid)(), mx); - check_thread_exists(VG_(get_running_tid)()); - check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */ - VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx); - - return mx; -} - -static void after_pthread_mutex_trylock(void *nonce, enum return_type rt, Word retval) -{ - if (nonce == NULL) - return; - - if (rt == RT_RETURN && retval == 0) - VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce); - else { - if (debug) - VG_(printf)("after mutex_trylock failed: rt=%d ret=%d\n", rt, retval); - VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce); - } - - LEAVE(pthread_mutex_trylock, rt, retval); -} - -static void *before_pthread_mutex_unlock(va_list va) -{ - pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *); - - if (!check_wrappings()) - return NULL; - - ENTER(pthread_mutex_unlock); - - VG_(tm_mutex_tryunlock)(VG_(get_running_tid)(), (Addr)mx); - - return mx; -} - -static void after_pthread_mutex_unlock(void *nonce, enum return_type rt, Word retval) -{ - if (nonce == NULL) - return; - - if (rt == RT_RETURN && retval == 0) - VG_(tm_mutex_unlock)(VG_(get_running_tid)(), (Addr)nonce); /* complete unlock */ - else - VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce); /* re-acquire */ - - LEAVE(pthread_mutex_unlock, rt, retval); -} - - -static struct pt_wraps { - const Char *name; - FuncWrapper wrapper; - const CodeRedirect *redir; -} wraps[] = { -#define WRAP(func, extra) { #func extra, { before_##func, after_##func } } - WRAP(pthread_create, "@@GLIBC_2.1"), /* XXX TODO: 2.0 ABI (?) */ - WRAP(pthread_join, ""), - WRAP(pthread_detach, ""), - - WRAP(pthread_self, ""), - - WRAP(pthread_mutex_init, ""), - WRAP(pthread_mutex_destroy, ""), - WRAP(pthread_mutex_lock, ""), - WRAP(pthread_mutex_trylock, ""), - WRAP(pthread_mutex_unlock, ""), -#undef WRAP -}; - -/* Check to see if all the wrappers are resolved */ -static Bool check_wrappings() -{ - Int i; - static Bool ok = True; - static Bool checked = False; - - if (checked) - return ok; - - for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) { - if (!VG_(is_resolved)(wraps[i].redir)) { - VG_(message)(Vg_DebugMsg, "Pthread wrapper for \"%s\" is not resolved", - wraps[i].name); - ok = False; - } - } - - if (startfunc_wrapper == 0) { - VG_(message)(Vg_DebugMsg, "Pthread wrapper for thread start function is not resolved"); - ok = False; - } - - if (!ok) - VG_(message)(Vg_DebugMsg, "Missing intercepts; model disabled"); - - checked = True; - return ok; -} - -/* - Set up all the wrappers for interesting functions. - */ -void VG_(pthread_init)() -{ - Int i; - - for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) { - //VG_(printf)("adding pthread wrapper for %s\n", wraps[i].name); - wraps[i].redir = VG_(add_wrapper)("soname:libpthread.so.0", - wraps[i].name, &wraps[i].wrapper); - } - VG_(tm_init)(); - VG_(tm_thread_create)(VG_INVALID_THREADID, VG_(master_tid), True); -} - -#else /* !0 */ -/* Stubs for now */ -//:: void VG_(pthread_init)() -//:: { -//:: } -//:: -//:: void VG_(pthread_startfunc_wrapper)(Addr wrapper) -//:: { -//:: } -#endif /* 0 */ - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_threadmodel.c b/coregrind/m_threadmodel.c deleted file mode 100644 index fa3e2085..00000000 --- a/coregrind/m_threadmodel.c +++ /dev/null @@ -1,1284 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Thread modelling. m_threadmodel.c ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2005-2006 Jeremy Fitzhardinge - jeremy@goop.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -/* - This file implements an abstract thread model, by which a client's - thread usage is validated. The model the file implements is - intended to be enough to contain pthreads, or perhaps a superset, - but it is not pthread-specific; that's done in in vg_pthreadmodel.c - - While the primary client of this file is vg_pthreadmodel.c, it is - also intended that clients can make direct use of this file for - home-grown threading libraries. It is therefore useful for both - validating the library itself as well as the users of the library. - - A note on terminology: - - The states referred to in this file ("blocked state", "zombie - state") are specific to this threads model, and have nothing do to - with the scheduler status for a thread. For example, a thread - could be "blocked" in a lock but be in VgTs_Runnable status, - because the lock is actually a spinlock. - - "Fails" means "reports an error" and possibly means that the model - is getting out of sync with the actual implementation. This model - only reports problems to the user, and doesn't attempt to actually - change the behaviour of the implementation. - - NB: - - This file assumes there's a 1:1 relationship between application - threads and Valgrind threads, which means that 1:N and M:N thread - models are not (yet) supported. At some point we may need to - introduce a separate notion of a "thread" for modelling purposes. - */ - -#include "pub_core_basics.h" -#include "pub_core_vki.h" -#include "pub_core_errormgr.h" -#include "pub_core_execontext.h" -#include "pub_core_libcassert.h" -#include "pub_core_libcbase.h" -#include "pub_core_libcprint.h" -#include "pub_core_mallocfree.h" -#include "pub_core_oset.h" -#include "pub_core_threadmodel.h" -#include "pub_core_threadstate.h" -#include "pub_core_tooliface.h" - -struct thread; -struct mutex; -struct condvar; - -static const Bool debug_thread = False; -static const Bool debug_mutex = False; - -/* -------------------------------------------------- - Thread lifetime - - Threads are all expressed in terms of internal ThreadIds. The - thread library interface needs to map from the library's identifers - to ThreadIds. - -------------------------------------------------- */ - -/* Per-thread state. We maintain our own here rather than hanging it - off ThreadState, so that we have the option of not having a 1:1 - relationship between modelled threads and Valgrind threads. */ -struct thread -{ - ThreadId tid; - ThreadId creator; - - Bool detached; /* thread is detached */ - - enum thread_state { - TS_Alive, /* alive */ - TS_Zombie, /* waiting to be joined on (detached is False) */ - TS_Dead, /* all dead */ - - TS_Running, /* running */ - TS_MutexBlocked, /* blocked on mutex */ - TS_CVBlocked, /* blocked on condvar */ - TS_JoinBlocked, /* blocked in join */ - } state; - - struct mutex *mx_blocked; /* mutex we're blocked on (state==TS_MutexBlocked) */ - struct condvar *cv_blocked; /* condvar we're blocked on (state==TS_CVBlocked) */ - struct thread *th_blocked; /* thread we're blocked on (state==TS_JoinBlocked) */ - - ExeContext *ec_created; /* where created */ - ExeContext *ec_blocked; /* where blocked/unblocked */ -}; - -enum thread_error -{ - THE_NotExist, /* thread doesn't exist */ - THE_NotAlive, /* thread isn't alive (use after death) */ - THE_Rebirth, /* thread already alive */ - THE_Blocked, /* thread not supposed to be blocked */ - THE_NotBlocked, /* thread supposed to be blocked */ - THE_Detached, /* thread is detached */ -}; - -struct thread_error_data -{ - enum thread_error err; - struct thread *th; - const Char *action; -}; - -static const Char *pp_threadstate(const struct thread *th) -{ - if (th == NULL) - return "non-existent"; - - switch(th->state) { - case TS_Alive: return "alive"; - case TS_Zombie: return "zombie"; - case TS_Dead: return "dead"; - case TS_Running: return "running"; - case TS_MutexBlocked:return "mutex-blocked"; - case TS_CVBlocked: return "cv-blocked"; - case TS_JoinBlocked: return "join-blocked"; - default: return "???"; - } -} - -static void thread_validate(struct thread *th) -{ - switch(th->state) { - case TS_Alive: - case TS_Running: - case TS_Dead: - case TS_Zombie: - vg_assert(th->mx_blocked == NULL); - vg_assert(th->cv_blocked == NULL); - vg_assert(th->th_blocked == NULL); - break; - - case TS_MutexBlocked: - vg_assert(th->mx_blocked != NULL); - vg_assert(th->cv_blocked == NULL); - vg_assert(th->th_blocked == NULL); - break; - - case TS_CVBlocked: - vg_assert(th->mx_blocked == NULL); - vg_assert(th->cv_blocked != NULL); - vg_assert(th->th_blocked == NULL); - break; - - case TS_JoinBlocked: - vg_assert(th->mx_blocked == NULL); - vg_assert(th->cv_blocked == NULL); - vg_assert(th->th_blocked != NULL); - break; - } -} - -static void thread_setstate(struct thread *th, enum thread_state state) -{ - ExeContext *ec; - - if (th->state == state) - return; - - ec = VG_(record_ExeContext)(th->tid); - - switch(state) { - case TS_Alive: - case TS_Dead: - th->ec_created = ec; - break; - - case TS_Running: - case TS_MutexBlocked: - case TS_CVBlocked: - case TS_JoinBlocked: - case TS_Zombie: - th->ec_blocked = ec; - } - - th->state = state; - if (debug_thread) - VG_(printf)("setting thread(%d) -> %s\n", th->tid, pp_threadstate(th)); - thread_validate(th); -} - -static void do_thread_run(struct thread *th) -{ - th->mx_blocked = NULL; - th->cv_blocked = NULL; - th->th_blocked = NULL; - thread_setstate(th, TS_Running); -} - -static void do_thread_block_mutex(struct thread *th, struct mutex *mx) -{ - th->mx_blocked = mx; - th->cv_blocked = NULL; - th->th_blocked = NULL; - thread_setstate(th, TS_MutexBlocked); -} - -static void do_thread_block_condvar(struct thread *th, struct condvar *cv) -{ - th->mx_blocked = NULL; - th->cv_blocked = cv; - th->th_blocked = NULL; - thread_setstate(th, TS_CVBlocked); -} - -static void do_thread_block_join(struct thread *th, struct thread *joinee) -{ - th->mx_blocked = NULL; - th->cv_blocked = NULL; - th->th_blocked = joinee; - thread_setstate(th, TS_JoinBlocked); -} - -static void do_thread_block_zombie(struct thread *th) -{ - th->mx_blocked = NULL; - th->cv_blocked = NULL; - th->th_blocked = NULL; - thread_setstate(th, TS_Zombie); -} - -static void do_thread_dead(struct thread *th) -{ - th->mx_blocked = NULL; - th->cv_blocked = NULL; - th->th_blocked = NULL; - thread_setstate(th, TS_Dead); -} - -static OSet *threadSet = NULL; - -static struct thread *thread_get(ThreadId tid) -{ - return VG_(OSet_Lookup)(threadSet, &tid); -} - -static void thread_report(ThreadId tid, enum thread_error err, const Char *action) -{ - Char *errstr = "?"; - struct thread *th = thread_get(tid); - struct thread_error_data errdata; - - switch(err) { - case THE_NotExist: errstr = "non existent"; break; - case THE_NotAlive: errstr = "not alive"; break; - case THE_Rebirth: errstr = "re-born"; break; - case THE_Blocked: errstr = "blocked"; break; - case THE_NotBlocked: errstr = "not blocked"; break; - case THE_Detached: errstr = "detached"; break; - } - - errdata.err = err; - errdata.th = th; - errdata.action = action; - - VG_(maybe_record_error)(VG_(get_running_tid)(), ThreadErr, 0, errstr, &errdata); -} - -static void pp_thread_error(Error *err) -{ - struct thread_error_data *errdata = VG_(get_error_extra)(err); - struct thread *th = errdata->th; - Char *errstr = VG_(get_error_string)(err); - - VG_(message)(Vg_UserMsg, "Found %s thread in state %s while %s", - errstr, pp_threadstate(th), errdata->action); - VG_(pp_ExeContext)(VG_(get_error_where)(err)); - - if (th) { - VG_(message)(Vg_UserMsg, " Thread %d was %s", - th->tid, th->state == TS_Dead ? "destroyed" : "created"); - VG_(pp_ExeContext)(th->ec_created); - } -} - -/* Thread creation */ -void VG_(tm_thread_create)(ThreadId creator, ThreadId tid, Bool detached) -{ - struct thread *th = thread_get(tid); - - if (debug_thread) - VG_(printf)("thread %d creates %d %s\n", creator, tid, detached ? "detached" : ""); - if (th != NULL) { - if (th->state != TS_Dead) - thread_report(tid, THE_Rebirth, "creating"); - } else { - th = VG_(OSet_AllocNode)(threadSet, sizeof(struct thread)); - th->tid = tid; - VG_(OSet_Insert)(threadSet, th); - } - - th->creator = creator; - th->detached = detached; - th->mx_blocked = NULL; - th->cv_blocked = NULL; - th->th_blocked = NULL; - - thread_setstate(th, TS_Alive); - do_thread_run(th); -} - -Bool VG_(tm_thread_exists)(ThreadId tid) -{ - struct thread *th = thread_get(tid); - - return th && th->state != TS_Dead; -} - -/* A thread is terminating itself - - fails if tid has already terminated - - if detached, tid becomes invalid for all further operations - - if not detached, the thread remains in a Zombie state until - someone joins on it - */ -void VG_(tm_thread_exit)(ThreadId tid) -{ - struct thread *th = thread_get(tid); - - if (th == NULL) - thread_report(tid, THE_NotExist, "exiting"); - else { - struct thread *joiner; - - switch(th->state) { - case TS_Dead: - case TS_Zombie: /* already exited once */ - thread_report(tid, THE_NotAlive, "exiting"); - break; - - case TS_MutexBlocked: - case TS_CVBlocked: - case TS_JoinBlocked: - thread_report(tid, THE_Blocked, "exiting"); - break; - - case TS_Alive: - case TS_Running: - /* OK */ - break; - } - - /* ugly - walk all threads to find people joining with us */ - /* In pthreads its an error to have multiple joiners, but that - seems a bit specific to implement here; there should a way - for the thread library binding to handle this. */ - VG_(OSet_ResetIter)(threadSet); - while ((joiner = VG_(OSet_Next)(threadSet)) != NULL) { - if (joiner->state == TS_JoinBlocked && joiner->th_blocked == th) { - /* found someone - wake them up */ - do_thread_run(joiner); - - /* we're dead */ - do_thread_dead(th); - } - } - - if (th->state != TS_Dead) - do_thread_block_zombie(th); - } -} - -void VG_(tm_thread_detach)(ThreadId tid) -{ - struct thread *th = thread_get(tid); - - if (th == NULL) - thread_report(tid, THE_NotExist, "detaching"); - else { - if (th->detached) - thread_report(tid, THE_Detached, "detaching"); - else { - /* XXX look for waiters */ - th->detached = True; - } - } -} - -/* One thread blocks until another has terminated - - fails if joinee is detached - - fails if joinee doesn't exist - - once the join completes, joinee is dead - */ -void VG_(tm_thread_join)(ThreadId joinerid, ThreadId joineeid) -{ - struct thread *joiner = thread_get(joinerid); - struct thread *joinee = thread_get(joineeid); - - /* First, check the joinee thread's state */ - if (joinee == NULL) - thread_report(joineeid, THE_NotExist, "joining as joinee"); - else { - switch(joinee->state) { - case TS_Alive: /* really shouldn't see them in this state... */ - case TS_Running: - case TS_Zombie: - case TS_MutexBlocked: - case TS_CVBlocked: - case TS_JoinBlocked: - /* OK */ - break; - - case TS_Dead: - thread_report(joineeid, THE_NotAlive, "joining as joinee"); - break; - } - } - - /* now the joiner... */ - if (joiner == NULL) - thread_report(joineeid, THE_NotExist, "joining as joiner"); - else { - switch(joiner->state) { - case TS_Alive: /* ? */ - case TS_Running: /* OK */ - break; - - case TS_Zombie: /* back from the dead */ - case TS_Dead: - thread_report(joineeid, THE_NotAlive, "joining as joiner"); - break; - - case TS_MutexBlocked: - case TS_CVBlocked: - case TS_JoinBlocked: - thread_report(joineeid, THE_Blocked, "joining as joiner"); - break; - } - - if (joinee->detached) - thread_report(joineeid, THE_Detached, "joining as joiner"); - else { - /* block if the joinee hasn't exited yet */ - if (joinee) { - switch(joinee->state) { - case TS_Dead: - break; - - default: - if (joinee->state == TS_Zombie) - do_thread_dead(joinee); - else - do_thread_block_join(joiner, joinee); - } - } - } - } -} - -/* Context switch to a new thread */ -void VG_(tm_thread_switchto)(ThreadId tid) -{ - VG_TRACK( thread_run, tid ); -} - -static void thread_block_mutex(ThreadId tid, struct mutex *mx) -{ - struct thread *th = thread_get(tid); - - if (th == NULL) { - /* should an unknown thread doing something make it spring to life? */ - thread_report(tid, THE_NotExist, "blocking on mutex"); - return; - } - switch(th->state) { - case TS_Dead: - case TS_Zombie: - thread_report(th->tid, THE_NotAlive, "blocking on mutex"); - break; - - case TS_MutexBlocked: - case TS_CVBlocked: - case TS_JoinBlocked: - thread_report(th->tid, THE_Blocked, "blocking on mutex"); - break; - - case TS_Alive: - case TS_Running: /* OK */ - break; - } - - do_thread_block_mutex(th, mx); -} - -static void thread_unblock_mutex(ThreadId tid, struct mutex *mx, const Char *action) -{ - struct thread *th = thread_get(tid); - - if (th == NULL) { - /* should an unknown thread doing something make it spring to life? */ - thread_report(tid, THE_NotExist, "giving up on mutex"); - return; - } - - switch(th->state) { - case TS_MutexBlocked: /* OK */ - break; - - case TS_Alive: - case TS_Running: - thread_report(tid, THE_NotBlocked, action); - break; - - case TS_CVBlocked: - case TS_JoinBlocked: - thread_report(tid, THE_Blocked, action); - break; - - case TS_Dead: - case TS_Zombie: - thread_report(tid, THE_NotAlive, action); - break; - } - - do_thread_run(th); -} - -/* -------------------------------------------------- - Mutexes - - This models simple, non-recursive mutexes. - -------------------------------------------------- */ - -struct mutex -{ - Addr mutex; /* address of mutex */ - ThreadId owner; /* owner if state == MX_Locked */ - enum mutex_state { - MX_Init, - MX_Free, - MX_Locked, - MX_Unlocking, /* half-unlocked */ - MX_Dead - } state; /* mutex state */ - - ExeContext *ec_create; /* where created/destroyed */ - ExeContext *ec_locked; /* where last locked/unlocked */ -}; - -enum mutex_error -{ - MXE_NotExist, /* never existed */ - MXE_NotInit, /* not initialized (use after destroy) */ - MXE_ReInit, /* already initialized */ - MXE_NotLocked, /* not locked */ - MXE_Locked, /* is locked */ - MXE_Deadlock, /* deadlock detected */ - MXE_NotOwner, /* non-owner trying to change lock */ -}; - -struct mutex_error_data -{ - enum mutex_error err; - struct mutex *mx; - const Char *action; -}; - -static struct mutex *mutex_get(Addr mutexp); - -static const Char *pp_mutexstate(const struct mutex *mx) -{ - static Char buf[20]; - - switch(mx->state) { - case MX_Init: return "Init"; - case MX_Free: return "Free"; - case MX_Dead: return "Dead"; - - case MX_Locked: - VG_(sprintf)(buf, "Locked by tid %d", mx->owner); - break; - - case MX_Unlocking: - VG_(sprintf)(buf, "Being unlocked by tid %d", mx->owner); - break; - - default: - VG_(sprintf)(buf, "?? %d", mx->state); - break; - } - - return buf; -} - -static void mutex_setstate(ThreadId tid, struct mutex *mx, enum mutex_state st) -{ - ExeContext *ec = VG_(record_ExeContext)(tid); - - switch(st) { - case MX_Init: - case MX_Dead: - mx->ec_create = ec; - break; - - case MX_Unlocking: - case MX_Locked: - case MX_Free: - mx->ec_locked = ec; - break; - } - - mx->state = st; - if (debug_mutex) - VG_(printf)("setting mutex(%p) -> %s\n", mx->mutex, pp_mutexstate(mx)); -} - -static void mutex_report(ThreadId tid, Addr mutexp, enum mutex_error err, const Char *action) -{ - Char *errstr="?"; - struct mutex *mx = mutex_get(mutexp); - struct mutex_error_data errdata; - - switch(err) { - case MXE_NotExist: errstr="non-existent"; break; - case MXE_NotInit: errstr="uninitialized"; break; - case MXE_ReInit: errstr="already initialized"; break; - case MXE_NotLocked: errstr="not locked"; break; - case MXE_Locked: errstr="locked"; break; - case MXE_NotOwner: errstr="unowned"; break; - case MXE_Deadlock: errstr="deadlock on"; break; - } - - errdata.err = err; - errdata.mx = mx; - errdata.action = action; - - VG_(maybe_record_error)(tid, MutexErr, 0, errstr, &errdata); -} - -static void pp_mutex_error(Error *err) -{ - struct mutex_error_data *errdata = VG_(get_error_extra)(err); - struct mutex *mx = errdata->mx; - Char *errstr = VG_(get_error_string)(err); - - VG_(message)(Vg_UserMsg, "Found %s mutex %p while %s", - errstr, mx ? mx->mutex : 0, errdata->action); - VG_(pp_ExeContext)(VG_(get_error_where)(err)); - - switch (mx->state) { - case MX_Init: - case MX_Dead: - break; - case MX_Locked: - VG_(message)(Vg_UserMsg, " Mutex was locked by thread %d", mx->owner); - VG_(pp_ExeContext)(mx->ec_locked); - break; - case MX_Unlocking: - VG_(message)(Vg_UserMsg, " Mutex being unlocked"); - VG_(pp_ExeContext)(mx->ec_locked); - break; - case MX_Free: - VG_(message)(Vg_UserMsg, " Mutex was unlocked"); - VG_(pp_ExeContext)(mx->ec_locked); - break; - } - - VG_(message)(Vg_UserMsg, " Mutex was %s", - mx->state == MX_Dead ? "destroyed" : "created"); - VG_(pp_ExeContext)(mx->ec_create); -} - -static OSet *mutexSet = NULL; - -static struct mutex *mutex_get(Addr mutexp) -{ - return VG_(OSet_Lookup)(mutexSet, &mutexp); -} - -static Bool mx_is_initialized(Addr mutexp) -{ - const struct mutex *mx = mutex_get(mutexp); - - return mx && mx->state != MX_Dead; -} - -static struct mutex *mutex_check_initialized(ThreadId tid, Addr mutexp, const Char *action) -{ - struct mutex *mx; - - vg_assert(tid != VG_INVALID_THREADID); - - if (!mx_is_initialized(mutexp)) { - mutex_report(tid, mutexp, MXE_NotInit, action); - VG_(tm_mutex_init)(tid, mutexp); - } - - mx = mutex_get(mutexp); - vg_assert(mx != NULL); - - return mx; -} - -#if 0 -static Bool mx_is_locked(Addr mutexp) -{ - const struct mutex *mx = mutex_get(mutexp); - - return mx && (mx->state == MX_Locked); -} -#endif - -/* Mutex at mutexp is initialized. This must be done before any - further mutex operations are OK. Fails if: - - mutexp already exists (and is locked?) -*/ -void VG_(tm_mutex_init)(ThreadId tid, Addr mutexp) -{ - struct mutex *mx = mutex_get(mutexp); - - if (mx == NULL) { - mx = VG_(OSet_AllocNode)(mutexSet, sizeof(struct mutex)); - mx->mutex = mutexp; - VG_(OSet_Insert)(mutexSet, mx); - } else if (mx->state != MX_Dead) - mutex_report(tid, mutexp, MXE_ReInit, "initializing"); - - mx->owner = VG_INVALID_THREADID; - - mutex_setstate(tid, mx, MX_Init); - mutex_setstate(tid, mx, MX_Free); -} - -Bool VG_(tm_mutex_exists)(Addr mutexp) -{ - return mx_is_initialized(mutexp); -} - -/* Mutex is being destroyed. Fails if: - - mutex was not initialized - - mutex is locked (?) - */ -void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp) -{ - struct mutex *mx = mutex_get(mutexp); - - if (mx == NULL) - mutex_report(tid, mutexp, MXE_NotExist, "destroying"); - else { - switch(mx->state) { - case MX_Dead: - mutex_report(tid, mutexp, MXE_NotInit, "destroying"); - break; - - case MX_Locked: - case MX_Unlocking: - mutex_report(tid, mutexp, MXE_Locked, "destroying"); - VG_(tm_mutex_unlock)(tid, mutexp); - break; - - case MX_Init: - case MX_Free: - /* OK */ - break; - } - mutex_setstate(tid, mx, MX_Dead); - } -} - -/* A thread attempts to lock a mutex. If "blocking" then the thread - is put into a blocked state until the lock is acquired. Fails if: - - tid is invalid - - mutex has not been initialized - - thread is blocked on another object (?) - - blocking on this mutex could cause a deadlock - (Lock rank detection?) - */ -void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp) -{ - struct mutex *mx; - - mx = mutex_check_initialized(tid, mutexp, "trylocking"); - - thread_block_mutex(tid, mx); - - if (mx->state == MX_Locked && mx->owner == tid) /* deadlock */ - mutex_report(tid, mutexp, MXE_Deadlock, "trylocking"); - - VG_TRACK( pre_mutex_lock, tid, mutexp ); -} - -/* Give up waiting for a mutex. Fails if: - - thread is not currently blocked on the mutex - */ -void VG_(tm_mutex_giveup)(ThreadId tid, Addr mutexp) -{ - struct mutex *mx; - - mx = mutex_check_initialized(tid, mutexp, "giving up"); - - thread_unblock_mutex(tid, mx, "giving up on mutex"); -} - -/* A thread acquires a mutex. Fails if: - - thread is not blocked waiting for the mutex - - mutex is held by another thread - */ -void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp) -{ - struct mutex *mx; - - mx = mutex_check_initialized(tid, mutexp, "acquiring"); - - switch(mx->state) { - case MX_Unlocking: /* ownership transfer or relock */ - VG_TRACK( post_mutex_unlock, mx->owner, mutexp ); - if (mx->owner != tid) - thread_unblock_mutex(tid, mx, "acquiring mutex"); - break; - - case MX_Free: - thread_unblock_mutex(tid, mx, "acquiring mutex"); - break; - - case MX_Locked: - if (debug_mutex) - VG_(printf)("mutex=%p mx->state=%s\n", mutexp, pp_mutexstate(mx)); - VG_TRACK( post_mutex_unlock, mx->owner, mutexp ); - mutex_report(tid, mutexp, MXE_Locked, "acquiring"); - thread_unblock_mutex(tid, mx, "acquiring mutex"); - break; - - case MX_Init: - case MX_Dead: - vg_assert(0); - } - - mx->owner = tid; - mutex_setstate(tid, mx, MX_Locked); - - VG_TRACK( post_mutex_lock, tid, mutexp ); -} - -/* Try unlocking a lock. This will move it into a state where it can - either be unlocked, or change ownership to another thread. If - unlock fails, it will remain locked. */ -void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp) -{ - struct thread *th; - struct mutex *mx; - - mx = mutex_check_initialized(tid, mutexp, "try-unlocking"); - - th = thread_get(tid); - - if (th == NULL) - thread_report(tid, THE_NotExist, "try-unlocking mutex"); - else { - switch(th->state) { - case TS_Alive: - case TS_Running: /* OK */ - break; - - case TS_Dead: - case TS_Zombie: - thread_report(tid, THE_NotAlive, "try-unlocking mutex"); - break; - - case TS_JoinBlocked: - case TS_CVBlocked: - case TS_MutexBlocked: - thread_report(tid, THE_Blocked, "try-unlocking mutex"); - do_thread_run(th); - break; - } - } - - switch(mx->state) { - case MX_Locked: - if (mx->owner != tid) - mutex_report(tid, mutexp, MXE_NotOwner, "try-unlocking"); - break; - - case MX_Free: - mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking"); - break; - - case MX_Unlocking: - mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking"); - break; - - case MX_Init: - case MX_Dead: - vg_assert(0); - } - - mutex_setstate(tid, mx, MX_Unlocking); -} - -/* Finish unlocking a Mutex. The mutex can validly be in one of three - states: - - Unlocking - - Locked, owned by someone else (someone else got it in the meantime) - - Free (someone else completed a lock-unlock cycle) - */ -void VG_(tm_mutex_unlock)(ThreadId tid, Addr mutexp) -{ - struct mutex *mx; - struct thread *th; - - mx = mutex_check_initialized(tid, mutexp, "unlocking mutex"); - - th = thread_get(tid); - - if (th == NULL) - thread_report(tid, THE_NotExist, "unlocking mutex"); - else { - switch(th->state) { - case TS_Alive: - case TS_Running: /* OK */ - break; - - case TS_Dead: - case TS_Zombie: - thread_report(tid, THE_NotAlive, "unlocking mutex"); - break; - - case TS_JoinBlocked: - case TS_CVBlocked: - case TS_MutexBlocked: - thread_report(tid, THE_Blocked, "unlocking mutex"); - do_thread_run(th); - break; - } - } - - switch(mx->state) { - case MX_Locked: - /* Someone else might have taken ownership in the meantime */ - if (mx->owner == tid) - mutex_report(tid, mutexp, MXE_Locked, "unlocking"); - break; - - case MX_Free: - /* OK - nothing to do */ - break; - - case MX_Unlocking: - /* OK - we need to complete the unlock */ - VG_TRACK( post_mutex_unlock, tid, mutexp ); - mutex_setstate(tid, mx, MX_Free); - break; - - case MX_Init: - case MX_Dead: - vg_assert(0); - } -} - -/* -------------------------------------------------- - Condition variables - -------------------------------------------------- */ - -struct condvar_waiter -{ - ThreadId waiter; - - struct condvar *condvar; - struct mutex *mutex; - - struct condvar_waiter *next; -}; - -struct condvar -{ - Addr condvar; - - enum condvar_state { - CV_Dead, - CV_Alive, - } state; - - struct condvar_waiter *waiters; // XXX skiplist? - - - ExeContext *ec_created; // where created - ExeContext *ec_signalled; // where last signalled -}; - -enum condvar_err { - CVE_NotExist, - CVE_NotInit, - CVE_ReInit, - CVE_Busy, - CVE_Blocked, -}; - -static OSet *condvarSet = NULL; - -static struct condvar *condvar_get(Addr condp) -{ - return VG_(OSet_Lookup)(condvarSet, &condp); -} - -static Bool condvar_is_initialized(Addr condp) -{ - const struct condvar *cv = condvar_get(condp); - - return cv && cv->state != CV_Dead; -} - -static void condvar_report(ThreadId tid, Addr condp, enum condvar_err err, const Char *action) -{ -} - -static struct condvar *condvar_check_initialized(ThreadId tid, Addr condp, const Char *action) -{ - struct condvar *cv; - vg_assert(tid != VG_INVALID_THREADID); - - if (!condvar_is_initialized(condp)) { - condvar_report(tid, condp, CVE_NotInit, action); - VG_(tm_cond_init)(tid, condp); - } - - cv = condvar_get(condp); - vg_assert(cv != NULL); - - return cv; -} - -/* Initialize a condition variable. Fails if: - - condp has already been initialized - */ -void VG_(tm_cond_init)(ThreadId tid, Addr condp) -{ - struct condvar *cv = condvar_get(condp); - - if (cv == NULL) { - cv = VG_(OSet_AllocNode)(condvarSet, sizeof(struct condvar)); - cv->condvar = condp; - cv->waiters = NULL; - VG_(OSet_Insert)(condvarSet, cv); - } else if (cv->state != CV_Dead) { - condvar_report(tid, condp, CVE_ReInit, "initializing"); - /* ? what about existing waiters? */ - } - - cv->state = CV_Alive; -} - -/* Destroy a condition variable. Fails if: - - condp has not been initialized - - condp is currently being waited on - */ -void VG_(tm_cond_destroy)(ThreadId tid, Addr condp) -{ - struct condvar *cv = condvar_get(condp); - - if (cv == NULL) - condvar_report(tid, condp, CVE_NotExist, "destroying"); - else { - if (cv->state != CV_Alive) - condvar_report(tid, condp, CVE_NotInit, "destroying"); - if (cv->waiters != NULL) - condvar_report(tid, condp, CVE_Busy, "destroying"); - cv->state = CV_Dead; - } -} - -static struct condvar_waiter *get_waiter(const struct condvar *cv, ThreadId tid) -{ - struct condvar_waiter *w; - - for(w = cv->waiters; w; w = w->next) - if (w->waiter == tid) - return w; - return NULL; -} - -/* Wait for a condition, putting thread into blocked state. Fails if: - - condp has not been initialized - - thread doesn't hold mutexp - - thread is blocked on some other object - - thread is already blocked on mutex - */ -void VG_(tm_cond_wait)(ThreadId tid, Addr condp, Addr mutexp) -{ - struct thread *th = thread_get(tid); - struct mutex *mx; - struct condvar *cv; - struct condvar_waiter *waiter; - - /* Condvar must exist */ - cv = condvar_check_initialized(tid, condp, "waiting"); - - /* Mutex must exist */ - mx = mutex_check_initialized(tid, mutexp, "waiting on condvar"); - - /* Thread must own mutex */ - if (mx->state != MX_Locked) { - mutex_report(tid, mutexp, MXE_NotLocked, "waiting on condvar"); - VG_(tm_mutex_trylock)(tid, mutexp); - VG_(tm_mutex_acquire)(tid, mutexp); - } else if (mx->owner != tid) { - mutex_report(tid, mutexp, MXE_NotOwner, "waiting on condvar"); - mx->owner = tid; - } - - /* Thread must not be already waiting for condvar */ - waiter = get_waiter(cv, tid); - if (waiter != NULL) - condvar_report(tid, condp, CVE_Blocked, "waiting"); - else { - waiter = VG_(arena_malloc)(VG_AR_CORE, sizeof(*waiter)); - waiter->condvar = cv; - waiter->mutex = mx; - waiter->next = cv->waiters; - cv->waiters = waiter; - } - - /* Thread is now blocking on condvar */ - do_thread_block_condvar(th, cv); - - /* (half) release mutex */ - VG_(tm_mutex_tryunlock)(tid, mutexp); -} - -/* Wake from a condition, either because we've been signalled, or - because of timeout. Fails if: - - thread is not waiting on condp - */ -void VG_(tm_cond_wakeup)(ThreadId tid, Addr condp, Addr mutexp) -{ -} - -/* Signal a condition variable. Fails if: - - condp has not been initialized - */ -void VG_(tm_cond_signal)(ThreadId tid, Addr condp) -{ -} - -/* -------------------------------------------------- - Error handling - -------------------------------------------------- */ - -UInt VG_(tm_error_update_extra)(Error *err) -{ - switch (VG_(get_error_kind)(err)) { - case ThreadErr: { - struct thread_error_data *errdata = VG_(get_error_extra)(err); - struct thread *new_th = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct thread)); - - VG_(memcpy)(new_th, errdata->th, sizeof(struct thread)); - - errdata->th = new_th; - - return sizeof(struct thread_error_data); - } - - case MutexErr: { - struct mutex_error_data *errdata = VG_(get_error_extra)(err); - struct mutex *new_mx = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct mutex)); - - VG_(memcpy)(new_mx, errdata->mx, sizeof(struct mutex)); - - errdata->mx = new_mx; - - return sizeof(struct mutex_error_data); - } - - default: - return 0; - } -} - -Bool VG_(tm_error_equal)(VgRes res, Error *e1, Error *e2) -{ - /* Guaranteed by calling function */ - vg_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); - - switch (VG_(get_error_kind)(e1)) { - case ThreadErr: { - struct thread_error_data *errdata1 = VG_(get_error_extra)(e1); - struct thread_error_data *errdata2 = VG_(get_error_extra)(e2); - - return errdata1->err == errdata2->err; - } - - case MutexErr: { - struct mutex_error_data *errdata1 = VG_(get_error_extra)(e1); - struct mutex_error_data *errdata2 = VG_(get_error_extra)(e2); - - return errdata1->err == errdata2->err; - } - - default: - VG_(printf)("Error:\n unknown error code %d\n", - VG_(get_error_kind)(e1)); - VG_(core_panic)("unknown error code in VG_(tm_error_equal)"); - } -} - -void VG_(tm_error_print)(Error *err) -{ - switch (VG_(get_error_kind)(err)) { - case ThreadErr: - pp_thread_error(err); - break; - case MutexErr: - pp_mutex_error(err); - break; - } -} - -/* -------------------------------------------------- - Initialisation - -------------------------------------------------- */ - -static Word tm_compare_tid(ThreadId *tid1, ThreadId *tid2) -{ - if (*tid1 < *tid2) return -1; - if (*tid1 > *tid2) return 1; - return 0; -} - -static Word tm_compare_addr(Addr *addr1, Addr *addr2) -{ - if (*addr1 < *addr2) return -1; - if (*addr1 > *addr2) return 1; - return 0; -} - -static void* oset_malloc(SizeT szB) -{ - return VG_(arena_malloc)(VG_AR_CORE, szB); -} - -static void oset_free(void * p) -{ - VG_(arena_free)(VG_AR_CORE, p); -} - -void VG_(tm_init)() -{ - VG_(needs_core_errors)(); - - threadSet = VG_(OSet_Create)(offsetof(struct thread, tid), - (OSetCmp_t)tm_compare_tid, - oset_malloc, oset_free); - mutexSet = VG_(OSet_Create)(offsetof(struct mutex, mutex), - (OSetCmp_t)tm_compare_addr, - oset_malloc, oset_free); - condvarSet = VG_(OSet_Create)(offsetof(struct condvar, condvar), - (OSetCmp_t)tm_compare_addr, - oset_malloc, oset_free); -} - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/ diff --git a/coregrind/pub_core_errormgr.h b/coregrind/pub_core_errormgr.h index 69634130..6055c5e2 100644 --- a/coregrind/pub_core_errormgr.h +++ b/coregrind/pub_core_errormgr.h @@ -39,12 +39,13 @@ #include "pub_tool_errormgr.h" -// XXX: should this be in pthreadmodel.c? // These must be negative, so as to not overlap with tool error kinds. typedef enum { - ThreadErr = -1, // Thread error - MutexErr = -2, // Mutex error + // Nb: thread errors are a relic of the time when Valgrind's core + // could detect them. This example is left as an example should new + // core errors ever be added. + ThreadErr = -1, } CoreErrorKind; diff --git a/coregrind/pub_core_pthreadmodel.h b/coregrind/pub_core_pthreadmodel.h deleted file mode 100644 index 9b3a9ebc..00000000 --- a/coregrind/pub_core_pthreadmodel.h +++ /dev/null @@ -1,50 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Pthread modelling. pub_core_pthreadmodel.h ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2005 Jeremy Fitzhardinge - jeremy@goop.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -#ifndef __PUB_CORE_PTHREADMODEL_H -#define __PUB_CORE_PTHREADMODEL_H - -//-------------------------------------------------------------------- -// PURPOSE: This module does pthread modelling stuff... -//-------------------------------------------------------------------- - -//extern void VG_(pthread_init) (); -//extern void VG_(pthread_startfunc_wrapper)(Addr wrapper); -// -//struct vg_pthread_newthread_data { -// void *(*startfunc)(void *arg); -// void *arg; -//}; - -#endif // __PUB_CORE_PTHREADMODEL_H - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/ diff --git a/coregrind/pub_core_threadmodel.h b/coregrind/pub_core_threadmodel.h deleted file mode 100644 index f003a6c9..00000000 --- a/coregrind/pub_core_threadmodel.h +++ /dev/null @@ -1,70 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Thread modelling. pub_core_threadmodel.h ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2005 Jeremy Fitzhardinge - jeremy@goop.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -#ifndef __PUB_CORE_THREADMODEL_H -#define __PUB_CORE_THREADMODEL_H - -//-------------------------------------------------------------------- -// PURPOSE: This module does thread modelling stuff... -//-------------------------------------------------------------------- - -extern void VG_(tm_thread_create) (ThreadId creator, ThreadId tid, Bool detached); -extern void VG_(tm_thread_exit) (ThreadId tid); -extern Bool VG_(tm_thread_exists) (ThreadId tid); -extern void VG_(tm_thread_detach) (ThreadId tid); -extern void VG_(tm_thread_join) (ThreadId joiner, ThreadId joinee); -extern void VG_(tm_thread_switchto)(ThreadId tid); - -extern void VG_(tm_mutex_init) (ThreadId tid, Addr mutexp); -extern void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp); -extern void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp); -extern void VG_(tm_mutex_giveup) (ThreadId tid, Addr mutexp); -extern void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp); -extern void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp); -extern void VG_(tm_mutex_unlock) (ThreadId tid, Addr mutexp); -extern Bool VG_(tm_mutex_exists) (Addr mutexp); - -extern UInt VG_(tm_error_update_extra) (Error *err); -extern Bool VG_(tm_error_equal) (VgRes res, Error *e1, Error *e2); -extern void VG_(tm_error_print) (Error *err); - -extern void VG_(tm_init) (void); - -extern void VG_(tm_cond_init) (ThreadId tid, Addr condp); -extern void VG_(tm_cond_destroy) (ThreadId tid, Addr condp); -extern void VG_(tm_cond_wait) (ThreadId tid, Addr condp, Addr mutexp); -extern void VG_(tm_cond_wakeup) (ThreadId tid, Addr condp, Addr mutexp); -extern void VG_(tm_cond_signal) (ThreadId tid, Addr condp); - -#endif // __PUB_CORE_THREADMODEL_H - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/ diff --git a/glibc-2.2.supp b/glibc-2.2.supp index 617ef73c..5af5c097 100644 --- a/glibc-2.2.supp +++ b/glibc-2.2.supp @@ -229,31 +229,6 @@ fun:_dl_open } -#-------- Threading bugs? -# glibc 'knows' that destroying a locked mutex will unlock it -{ - pthread_error/pthread_mutex_destroy/__closedir - core:PThread - fun:pthread_error - fun:pthread_mutex_destroy - fun:__closedir -} - -{ - pthread_error/pthread_mutex_destroy/_IO_default_finish - core:PThread - fun:pthread_error - fun:pthread_mutex_destroy - fun:_IO_default_finish* -} - -{ - pthread_mutex_unlock/_IO_funlockfile - core:PThread - fun:pthread_mutex_unlock - fun:_IO_funlockfile -} - # even more glibc suppressions ? { libc-2.2.4.so/libc-2.2.4.so/libc-2.2.4.so(Cond) diff --git a/glibc-2.3.supp b/glibc-2.3.supp index 46489061..62066808 100644 --- a/glibc-2.3.supp +++ b/glibc-2.3.supp @@ -183,31 +183,6 @@ fun:__libc_start_main } -#-------- Threading bugs? -# glibc 'knows' that destroying a locked mutex will unlock it -{ - pthread_error/pthread_mutex_destroy/__closedir - core:PThread - fun:pthread_error - fun:pthread_mutex_destroy - fun:__closedir -} - -{ - pthread_error/pthread_mutex_destroy/_IO_default_finish - core:PThread - fun:pthread_error - fun:pthread_mutex_destroy - fun:_IO_default_finish* -} - -{ - pthread_mutex_unlock/_IO_funlockfile - core:PThread - fun:pthread_mutex_unlock - fun:_IO_funlockfile -} - ##----------------------------------------------------------------------## ## Bugs in helper library supplied with Intel Icc 7.0 (65) ## in /opt/intel/compiler70/ia32/lib/libcxa.so.3 |