From: Julian Seward Date: Tue, 4 Jun 2002 22:54:20 +0000 (+0000) Subject: A new kind of error: PThread errors. Used to report detected misuse in X-Git-Tag: svn/VALGRIND_1_0_3~92 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a487ef1ce74a1d5b36fdc1f803e38090f9b102f4;p=thirdparty%2Fvalgrind.git A new kind of error: PThread errors. Used to report detected misuse in the pthread_* API. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@379 --- diff --git a/Makefile.am b/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/addrcheck/Makefile.am b/addrcheck/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/addrcheck/Makefile.am +++ b/addrcheck/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/cachegrind/Makefile.am b/cachegrind/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/cachegrind/Makefile.am +++ b/cachegrind/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/cachegrind/docs/manual.html b/cachegrind/docs/manual.html index 3320b2da7d..2a3f20fa89 100644 --- a/cachegrind/docs/manual.html +++ b/cachegrind/docs/manual.html @@ -26,7 +26,7 @@  

Valgrind, snapshot 20020522

This manual was majorly updated on 20020501
-
This manual was minorly updated on 20020522
+
This manual was minorly updated on 20020603

@@ -1021,7 +1021,9 @@ You can ask to add suppressions from another file, by specifying memory access of 1, 2, 4 or 8 bytes respectively. Or Param, meaning an invalid system call parameter error. Or - Free, meaning an invalid or mismatching free.
+ Free, meaning an invalid or mismatching free. + Or PThread, meaning any kind of complaint to do + with the PThreads API.

  • The "immediate location" specification. For Value and Addr diff --git a/corecheck/Makefile.am b/corecheck/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/corecheck/Makefile.am +++ b/corecheck/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c index c2b0f68e61..8a74a5c382 100644 --- a/coregrind/arch/x86-linux/vg_libpthread.c +++ b/coregrind/arch/x86-linux/vg_libpthread.c @@ -216,6 +216,20 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn ) #include /* gettimeofday */ +/* --------------------------------------------------- + Ummm .. + ------------------------------------------------ */ + +static +void pthread_error ( const char* msg ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 0, + VG_USERREQ__PTHREAD_ERROR, + msg, 0, 0, 0); +} + + /* --------------------------------------------------- THREAD ATTRIBUTES ------------------------------------------------ */ @@ -230,8 +244,11 @@ int pthread_attr_init(pthread_attr_t *attr) int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { if (detachstate != PTHREAD_CREATE_JOINABLE - && detachstate != PTHREAD_CREATE_DETACHED) + && detachstate != PTHREAD_CREATE_DETACHED) { + pthread_error("pthread_attr_setdetachstate: " + "detachstate is invalid"); return EINVAL; + } attr->__detachstate = detachstate; return 0; } @@ -301,6 +318,8 @@ int pthread_attr_setscope ( pthread_attr_t *attr, int scope ) ensure_valgrind("pthread_attr_setscope"); if (scope == PTHREAD_SCOPE_SYSTEM) return 0; + pthread_error("pthread_attr_setscope: " + "invalid or unsupported scope"); if (scope == PTHREAD_SCOPE_PROCESS) return ENOTSUP; return EINVAL; @@ -581,10 +600,18 @@ int pthread_detach(pthread_t th) VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, VG_USERREQ__SET_OR_GET_DETACH, 2 /* get */, th, 0, 0); - if (res == -1) /* not found */ + if (res == -1) { + /* not found */ + pthread_error("pthread_detach: " + "invalid target thread"); return ESRCH; - if (res == 1) /* already detached */ + } + if (res == 1) { + /* already detached */ + pthread_error("pthread_detach: " + "target thread is already detached"); return EINVAL; + } if (res == 0) { VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, VG_USERREQ__SET_OR_GET_DETACH, @@ -709,6 +736,8 @@ int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) attr->__mutexkind = type; return 0; default: + pthread_error("pthread_mutexattr_settype: " + "invalid type"); return EINVAL; } } @@ -790,12 +819,15 @@ int __pthread_mutex_destroy(pthread_mutex_t *mutex) { /* Valgrind doesn't hold any resources on behalf of the mutex, so no need to involve it. */ - if (mutex->__m_count > 0) + if (mutex->__m_count > 0) { + pthread_error("pthread_mutex_destroy: " + "mutex is still in use"); return EBUSY; - mutex->__m_count = 0; - mutex->__m_owner = (_pthread_descr)VG_INVALID_THREADID; - mutex->__m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; - return 0; + } + mutex->__m_count = 0; + mutex->__m_owner = (_pthread_descr)VG_INVALID_THREADID; + mutex->__m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; + return 0; } @@ -937,8 +969,11 @@ int pthread_setcancelstate(int state, int *oldstate) int res; ensure_valgrind("pthread_setcancelstate"); if (state != PTHREAD_CANCEL_ENABLE - && state != PTHREAD_CANCEL_DISABLE) + && state != PTHREAD_CANCEL_DISABLE) { + pthread_error("pthread_setcancelstate: " + "invalid state"); return EINVAL; + } my_assert(-1 != PTHREAD_CANCEL_ENABLE); my_assert(-1 != PTHREAD_CANCEL_DISABLE); VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, @@ -955,8 +990,11 @@ int pthread_setcanceltype(int type, int *oldtype) int res; ensure_valgrind("pthread_setcanceltype"); if (type != PTHREAD_CANCEL_DEFERRED - && type != PTHREAD_CANCEL_ASYNCHRONOUS) + && type != PTHREAD_CANCEL_ASYNCHRONOUS) { + pthread_error("pthread_setcanceltype: " + "invalid type"); return EINVAL; + } my_assert(-1 != PTHREAD_CANCEL_DEFERRED); my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, @@ -1038,7 +1076,8 @@ int pthread_sigmask(int how, const sigset_t *newmask, case SIG_SETMASK: how = VKI_SIG_SETMASK; break; case SIG_BLOCK: how = VKI_SIG_BLOCK; break; case SIG_UNBLOCK: how = VKI_SIG_UNBLOCK; break; - default: return EINVAL; + default: pthread_error("pthread_sigmask: invalid how"); + return EINVAL; } /* Crude check */ @@ -1084,9 +1123,9 @@ int pthread_kill(pthread_t thread, int signo) int raise (int sig) { int retcode = pthread_kill(pthread_self(), sig); - if (retcode == 0) + if (retcode == 0) { return 0; - else { + } else { errno = retcode; return -1; } @@ -1153,7 +1192,6 @@ int __pthread_once ( pthread_once_t *once_control, res = __pthread_mutex_lock(&once_masterlock); if (res != 0) { - printf("res = %d\n",res); barf("pthread_once: Looks like your program's " "init routine calls back to pthread_once() ?!"); } @@ -2040,6 +2078,7 @@ int sem_init(sem_t *sem, int pshared, unsigned int value) vg_sem_t* vg_sem; ensure_valgrind("sem_init"); if (pshared != 0) { + pthread_error("sem_init: unsupported pshared value"); errno = ENOSYS; return -1; } diff --git a/coregrind/docs/manual.html b/coregrind/docs/manual.html index 3320b2da7d..2a3f20fa89 100644 --- a/coregrind/docs/manual.html +++ b/coregrind/docs/manual.html @@ -26,7 +26,7 @@  

    Valgrind, snapshot 20020522

    This manual was majorly updated on 20020501
    -
    This manual was minorly updated on 20020522
    +
    This manual was minorly updated on 20020603

    @@ -1021,7 +1021,9 @@ You can ask to add suppressions from another file, by specifying memory access of 1, 2, 4 or 8 bytes respectively. Or Param, meaning an invalid system call parameter error. Or - Free, meaning an invalid or mismatching free.

  • + Free, meaning an invalid or mismatching free. + Or PThread, meaning any kind of complaint to do + with the PThreads API.

  • The "immediate location" specification. For Value and Addr diff --git a/coregrind/vg_errcontext.c b/coregrind/vg_errcontext.c index 61753391c0..d5e0abb43d 100644 --- a/coregrind/vg_errcontext.c +++ b/coregrind/vg_errcontext.c @@ -53,7 +53,9 @@ typedef /* Invalid read/write attempt at given size */ Addr1, Addr2, Addr4, Addr8, /* Invalid or mismatching free */ - FreeS + FreeS, + /* Pthreading error */ + PThread } SuppressionKind; @@ -108,7 +110,9 @@ typedef typedef enum { ValueErr, AddrErr, ParamErr, UserErr, /* behaves like an anonymous ParamErr */ - FreeErr, FreeMismatchErr } + FreeErr, FreeMismatchErr, + PThreadErr /* pthread API error */ + } ErrKind; /* What kind of memory access is involved in the error? */ @@ -138,7 +142,7 @@ typedef Addr addr; /* Addr, Free, Param, User */ AddrInfo addrinfo; - /* Param */ + /* Param; hijacked for PThread as a description */ Char* syscall_param; /* Param, User */ Bool isWriteableLack; @@ -255,6 +259,12 @@ static Bool eq_ErrContext ( Bool cheap_addr_cmp, return False; switch (e1->ekind) { + case PThreadErr: + if (e1->syscall_param == e2->syscall_param) + return True; + if (0 == VG_(strcmp)(e1->syscall_param, e2->syscall_param)) + return True; + return False; case UserErr: case ParamErr: if (e1->isWriteableLack != e2->isWriteableLack) return False; @@ -412,6 +422,10 @@ static void pp_ErrContext ( ErrContext* ec, Bool printCount ) VG_(pp_ExeContext)(ec->where); pp_AddrInfo(ec->addr, &ec->addrinfo); break; + case PThreadErr: + VG_(message)(Vg_UserMsg, "%s", ec->syscall_param ); + VG_(pp_ExeContext)(ec->where); + break; default: VG_(panic)("pp_ErrContext"); } @@ -747,6 +761,25 @@ void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ) VG_(maybe_add_context) ( &ec ); } +void VG_(record_pthread_err) ( ThreadId tid, Char* msg ) +{ + ErrContext ec; + if (vg_ignore_errors) return; + if (!VG_(clo_instrument)) return; + clear_ErrContext( &ec ); + ec.count = 1; + ec.next = NULL; + ec.where = VG_(get_ExeContext)( False, VG_(threads)[tid].m_eip, + VG_(threads)[tid].m_ebp ); + ec.ekind = PThreadErr; + ec.tid = tid; + ec.syscall_param = msg; + ec.m_eip = VG_(threads)[tid].m_eip; + ec.m_esp = VG_(threads)[tid].m_esp; + ec.m_ebp = VG_(threads)[tid].m_ebp; + VG_(maybe_add_context) ( &ec ); +} + /*------------------------------*/ @@ -963,6 +996,7 @@ static void load_one_suppressions_file ( Char* filename ) else if (STREQ(buf, "Addr4")) supp->skind = Addr4; else if (STREQ(buf, "Addr8")) supp->skind = Addr8; else if (STREQ(buf, "Free")) supp->skind = FreeS; + else if (STREQ(buf, "PThread")) supp->skind = PThread; else goto syntax_error; if (supp->skind == Param) { @@ -1100,7 +1134,7 @@ static Suppression* is_suppressible_error ( ErrContext* ec ) /* See if the error context matches any suppression. */ for (su = vg_suppressions; su != NULL; su = su->next) { switch (su->skind) { - case FreeS: + case FreeS: case PThread: case Param: case Value0: su_size = 0; break; case Value1: case Addr1: su_size = 1; break; case Value2: case Addr2: su_size = 2; break; @@ -1122,7 +1156,11 @@ static Suppression* is_suppressible_error ( ErrContext* ec ) if (ec->size != su_size) continue; break; case FreeS: - if (ec->ekind != FreeErr && ec->ekind != FreeMismatchErr) continue; + if (ec->ekind != FreeErr + && ec->ekind != FreeMismatchErr) continue; + break; + case PThread: + if (ec->ekind != PThreadErr) continue; break; } diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h index ba2203063c..ec923b1881 100644 --- a/coregrind/vg_include.h +++ b/coregrind/vg_include.h @@ -487,8 +487,11 @@ extern Bool VG_(is_empty_arena) ( ArenaId aid ); #define VG_USERREQ__NUKE_OTHER_THREADS 0x3023 + /* Cosmetic ... */ #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101 +/* Log a pthread error from client-space. Cosmetic. */ +#define VG_USERREQ__PTHREAD_ERROR 0x3102 /* In vg_constants.h: @@ -1306,6 +1309,8 @@ extern void VG_(record_param_err) ( ThreadState* tst, Char* msg ); extern void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ); +extern void VG_(record_pthread_err) ( ThreadId tid, Char* msg ); + /* The classification of a faulting address. */ diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c index c2b0f68e61..8a74a5c382 100644 --- a/coregrind/vg_libpthread.c +++ b/coregrind/vg_libpthread.c @@ -216,6 +216,20 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn ) #include /* gettimeofday */ +/* --------------------------------------------------- + Ummm .. + ------------------------------------------------ */ + +static +void pthread_error ( const char* msg ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 0, + VG_USERREQ__PTHREAD_ERROR, + msg, 0, 0, 0); +} + + /* --------------------------------------------------- THREAD ATTRIBUTES ------------------------------------------------ */ @@ -230,8 +244,11 @@ int pthread_attr_init(pthread_attr_t *attr) int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { if (detachstate != PTHREAD_CREATE_JOINABLE - && detachstate != PTHREAD_CREATE_DETACHED) + && detachstate != PTHREAD_CREATE_DETACHED) { + pthread_error("pthread_attr_setdetachstate: " + "detachstate is invalid"); return EINVAL; + } attr->__detachstate = detachstate; return 0; } @@ -301,6 +318,8 @@ int pthread_attr_setscope ( pthread_attr_t *attr, int scope ) ensure_valgrind("pthread_attr_setscope"); if (scope == PTHREAD_SCOPE_SYSTEM) return 0; + pthread_error("pthread_attr_setscope: " + "invalid or unsupported scope"); if (scope == PTHREAD_SCOPE_PROCESS) return ENOTSUP; return EINVAL; @@ -581,10 +600,18 @@ int pthread_detach(pthread_t th) VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, VG_USERREQ__SET_OR_GET_DETACH, 2 /* get */, th, 0, 0); - if (res == -1) /* not found */ + if (res == -1) { + /* not found */ + pthread_error("pthread_detach: " + "invalid target thread"); return ESRCH; - if (res == 1) /* already detached */ + } + if (res == 1) { + /* already detached */ + pthread_error("pthread_detach: " + "target thread is already detached"); return EINVAL; + } if (res == 0) { VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, VG_USERREQ__SET_OR_GET_DETACH, @@ -709,6 +736,8 @@ int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) attr->__mutexkind = type; return 0; default: + pthread_error("pthread_mutexattr_settype: " + "invalid type"); return EINVAL; } } @@ -790,12 +819,15 @@ int __pthread_mutex_destroy(pthread_mutex_t *mutex) { /* Valgrind doesn't hold any resources on behalf of the mutex, so no need to involve it. */ - if (mutex->__m_count > 0) + if (mutex->__m_count > 0) { + pthread_error("pthread_mutex_destroy: " + "mutex is still in use"); return EBUSY; - mutex->__m_count = 0; - mutex->__m_owner = (_pthread_descr)VG_INVALID_THREADID; - mutex->__m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; - return 0; + } + mutex->__m_count = 0; + mutex->__m_owner = (_pthread_descr)VG_INVALID_THREADID; + mutex->__m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; + return 0; } @@ -937,8 +969,11 @@ int pthread_setcancelstate(int state, int *oldstate) int res; ensure_valgrind("pthread_setcancelstate"); if (state != PTHREAD_CANCEL_ENABLE - && state != PTHREAD_CANCEL_DISABLE) + && state != PTHREAD_CANCEL_DISABLE) { + pthread_error("pthread_setcancelstate: " + "invalid state"); return EINVAL; + } my_assert(-1 != PTHREAD_CANCEL_ENABLE); my_assert(-1 != PTHREAD_CANCEL_DISABLE); VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, @@ -955,8 +990,11 @@ int pthread_setcanceltype(int type, int *oldtype) int res; ensure_valgrind("pthread_setcanceltype"); if (type != PTHREAD_CANCEL_DEFERRED - && type != PTHREAD_CANCEL_ASYNCHRONOUS) + && type != PTHREAD_CANCEL_ASYNCHRONOUS) { + pthread_error("pthread_setcanceltype: " + "invalid type"); return EINVAL; + } my_assert(-1 != PTHREAD_CANCEL_DEFERRED); my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, @@ -1038,7 +1076,8 @@ int pthread_sigmask(int how, const sigset_t *newmask, case SIG_SETMASK: how = VKI_SIG_SETMASK; break; case SIG_BLOCK: how = VKI_SIG_BLOCK; break; case SIG_UNBLOCK: how = VKI_SIG_UNBLOCK; break; - default: return EINVAL; + default: pthread_error("pthread_sigmask: invalid how"); + return EINVAL; } /* Crude check */ @@ -1084,9 +1123,9 @@ int pthread_kill(pthread_t thread, int signo) int raise (int sig) { int retcode = pthread_kill(pthread_self(), sig); - if (retcode == 0) + if (retcode == 0) { return 0; - else { + } else { errno = retcode; return -1; } @@ -1153,7 +1192,6 @@ int __pthread_once ( pthread_once_t *once_control, res = __pthread_mutex_lock(&once_masterlock); if (res != 0) { - printf("res = %d\n",res); barf("pthread_once: Looks like your program's " "init routine calls back to pthread_once() ?!"); } @@ -2040,6 +2078,7 @@ int sem_init(sem_t *sem, int pshared, unsigned int value) vg_sem_t* vg_sem; ensure_valgrind("sem_init"); if (pshared != 0) { + pthread_error("sem_init: unsupported pshared value"); errno = ENOSYS; return -1; } diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c index fa71fb2b47..93309b8170 100644 --- a/coregrind/vg_scheduler.c +++ b/coregrind/vg_scheduler.c @@ -1754,7 +1754,7 @@ void do__cleanup_pop ( ThreadId tid, CleanupEntry* cu ) sp = VG_(threads)[tid].custack_used; if (VG_(clo_trace_sched)) { VG_(sprintf)(msg_buf, - "cleanup_pop from slot %d", sp); + "cleanup_pop from slot %d", sp-1); print_sched_event(tid, msg_buf); } vg_assert(sp >= 0 && sp <= VG_N_CLEANUPSTACK); @@ -1932,6 +1932,8 @@ void do__set_cancelpend ( ThreadId tid, "set_cancelpend for invalid tid %d", cee); print_sched_event(tid, msg_buf); } + VG_(record_pthread_err)( tid, + "pthread_cancel: target thread does not exist, or invalid"); SET_EDX(tid, -VKI_ESRCH); return; } @@ -1965,6 +1967,8 @@ void do_pthread_join ( ThreadId tid, vg_assert(VG_(threads)[tid].status == VgTs_Runnable); if (jee == tid) { + VG_(record_pthread_err)( tid, + "pthread_join: attempt to join to self"); SET_EDX(tid, EDEADLK); /* libc constant, not a kernel one */ VG_(threads)[tid].status = VgTs_Runnable; return; @@ -1979,6 +1983,8 @@ void do_pthread_join ( ThreadId tid, || jee >= VG_N_THREADS || VG_(threads)[jee].status == VgTs_Empty) { /* Invalid thread to join to. */ + VG_(record_pthread_err)( tid, + "pthread_join: target thread does not exist, or invalid"); SET_EDX(tid, EINVAL); VG_(threads)[tid].status = VgTs_Runnable; return; @@ -1990,6 +1996,9 @@ void do_pthread_join ( ThreadId tid, if (VG_(threads)[i].status == VgTs_WaitJoinee && VG_(threads)[i].joiner_jee_tid == jee) { /* Someone already did join on this thread */ + VG_(record_pthread_err)( tid, + "pthread_join: another thread already " + "in join-wait for target thread"); SET_EDX(tid, EINVAL); VG_(threads)[tid].status = VgTs_Runnable; return; @@ -2270,7 +2279,8 @@ void do_pthread_mutex_lock( ThreadId tid, /* POSIX doesn't mandate this, but for sanity ... */ if (mutex == NULL) { - /* VG_(printf)("NULL mutex\n"); */ + VG_(record_pthread_err)( tid, + "pthread_mutex_lock/trylock: mutex is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2289,7 +2299,8 @@ void do_pthread_mutex_lock( ThreadId tid, if (mutex->__m_count >= 0) break; /* else fall thru */ default: - /* VG_(printf)("unknown __m_kind %d in mutex\n", mutex->__m_kind); */ + VG_(record_pthread_err)( tid, + "pthread_mutex_lock/trylock: mutex is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2367,6 +2378,8 @@ void do_pthread_mutex_unlock ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (mutex == NULL) { + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2385,13 +2398,25 @@ void do_pthread_mutex_unlock ( ThreadId tid, if (mutex->__m_count >= 0) break; /* else fall thru */ default: + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is invalid"); SET_EDX(tid, EINVAL); return; } /* Barf if we don't currently hold the mutex. */ - if (mutex->__m_count == 0 /* nobody holds it */ - || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) { + if (mutex->__m_count == 0) { + /* nobody holds it */ + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is not locked"); + SET_EDX(tid, EPERM); + return; + } + + if ((ThreadId)mutex->__m_owner != tid) { + /* we don't hold it */ + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is locked by a different thread"); SET_EDX(tid, EPERM); return; } @@ -2587,6 +2612,8 @@ void do_pthread_cond_wait ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (mutex == NULL || cond == NULL) { + VG_(record_pthread_err)( tid, + "pthread_cond_wait/timedwait: cond or mutex is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2605,6 +2632,8 @@ void do_pthread_cond_wait ( ThreadId tid, if (mutex->__m_count >= 0) break; /* else fall thru */ default: + VG_(record_pthread_err)( tid, + "pthread_cond_wait/timedwait: mutex is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2612,6 +2641,9 @@ void do_pthread_cond_wait ( ThreadId tid, /* Barf if we don't currently hold the mutex. */ if (mutex->__m_count == 0 /* nobody holds it */ || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) { + VG_(record_pthread_err)( tid, + "pthread_cond_wait/timedwait: mutex is unlocked " + "or is locked but not owned by thread"); SET_EDX(tid, EINVAL); return; } @@ -2655,6 +2687,8 @@ void do_pthread_cond_signal_or_broadcast ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (cond == NULL) { + VG_(record_pthread_err)( tid, + "pthread_cond_signal/broadcast: cond is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2738,6 +2772,8 @@ void do_pthread_key_delete ( ThreadId tid, pthread_key_t key ) && VG_(threads)[tid].status == VgTs_Runnable); if (!is_valid_key(key)) { + VG_(record_pthread_err)( tid, + "pthread_key_delete: key is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2770,6 +2806,8 @@ void do_pthread_getspecific ( ThreadId tid, pthread_key_t key ) && VG_(threads)[tid].status == VgTs_Runnable); if (!is_valid_key(key)) { + VG_(record_pthread_err)( tid, + "pthread_getspecific: key is invalid"); SET_EDX(tid, (UInt)NULL); return; } @@ -2794,6 +2832,8 @@ void do_pthread_setspecific ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (!is_valid_key(key)) { + VG_(record_pthread_err)( tid, + "pthread_setspecific: key is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2917,7 +2957,9 @@ void do_pthread_kill ( ThreadId tid, /* me */ vg_assert(VG_(is_valid_tid)(tid) && VG_(threads)[tid].status == VgTs_Runnable); - if (!VG_(is_valid_tid)(tid)) { + if (!VG_(is_valid_tid)(thread)) { + VG_(record_pthread_err)( tid, + "pthread_kill: invalid target thread"); SET_EDX(tid, -VKI_ESRCH); return; } @@ -3052,6 +3094,11 @@ void do_nontrivial_clientreq ( ThreadId tid ) SET_EDX(tid, 0); break; + case VG_USERREQ__PTHREAD_ERROR: + VG_(record_pthread_err)( tid, (Char*)(arg[1]) ); + SET_EDX(tid, 0); + break; + case VG_USERREQ__MAKE_NOACCESS: case VG_USERREQ__MAKE_WRITABLE: case VG_USERREQ__MAKE_READABLE: diff --git a/docs/manual.html b/docs/manual.html index 3320b2da7d..2a3f20fa89 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -26,7 +26,7 @@  

    Valgrind, snapshot 20020522

    This manual was majorly updated on 20020501
    -
    This manual was minorly updated on 20020522
    +
    This manual was minorly updated on 20020603

    @@ -1021,7 +1021,9 @@ You can ask to add suppressions from another file, by specifying memory access of 1, 2, 4 or 8 bytes respectively. Or Param, meaning an invalid system call parameter error. Or - Free, meaning an invalid or mismatching free.

  • + Free, meaning an invalid or mismatching free. + Or PThread, meaning any kind of complaint to do + with the PThreads API.

  • The "immediate location" specification. For Value and Addr diff --git a/glibc-2.2.supp b/glibc-2.2.supp index e58567cf7e..2c2dafeafd 100644 --- a/glibc-2.2.supp +++ b/glibc-2.2.supp @@ -16,6 +16,12 @@ # (optionally: caller3 name) # } +{ + __pthread_mutex_unlock/__register_frame_info + PThread + fun:__pthread_mutex_unlock + fun:__register_frame_info +} # even more glibc suppressions ? { diff --git a/helgrind/Makefile.am b/helgrind/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/helgrind/Makefile.am +++ b/helgrind/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/lackey/Makefile.am b/lackey/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/lackey/Makefile.am +++ b/lackey/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/memcheck/Makefile.am b/memcheck/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/memcheck/Makefile.am +++ b/memcheck/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/memcheck/docs/manual.html b/memcheck/docs/manual.html index 3320b2da7d..2a3f20fa89 100644 --- a/memcheck/docs/manual.html +++ b/memcheck/docs/manual.html @@ -26,7 +26,7 @@  

    Valgrind, snapshot 20020522

    This manual was majorly updated on 20020501
    -
    This manual was minorly updated on 20020522
    +
    This manual was minorly updated on 20020603

    @@ -1021,7 +1021,9 @@ You can ask to add suppressions from another file, by specifying memory access of 1, 2, 4 or 8 bytes respectively. Or Param, meaning an invalid system call parameter error. Or - Free, meaning an invalid or mismatching free.

  • + Free, meaning an invalid or mismatching free. + Or PThread, meaning any kind of complaint to do + with the PThreads API.

  • The "immediate location" specification. For Value and Addr diff --git a/none/Makefile.am b/none/Makefile.am index 03cc0da839..8db9b73e6c 100644 --- a/none/Makefile.am +++ b/none/Makefile.am @@ -87,6 +87,9 @@ vg_memory.o: vg_memory.c $(MANUAL_DEPS) vg_clientfuncs.o: vg_clientfuncs.c $(MANUAL_DEPS) $(COMPILE) -fno-omit-frame-pointer -c $< +vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS) + $(COMPILE) -fno-omit-frame-pointer -c $< + valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \ $(valgrind_so_OBJECTS) $(valgrind_so_LDADD) diff --git a/vg_errcontext.c b/vg_errcontext.c index 61753391c0..d5e0abb43d 100644 --- a/vg_errcontext.c +++ b/vg_errcontext.c @@ -53,7 +53,9 @@ typedef /* Invalid read/write attempt at given size */ Addr1, Addr2, Addr4, Addr8, /* Invalid or mismatching free */ - FreeS + FreeS, + /* Pthreading error */ + PThread } SuppressionKind; @@ -108,7 +110,9 @@ typedef typedef enum { ValueErr, AddrErr, ParamErr, UserErr, /* behaves like an anonymous ParamErr */ - FreeErr, FreeMismatchErr } + FreeErr, FreeMismatchErr, + PThreadErr /* pthread API error */ + } ErrKind; /* What kind of memory access is involved in the error? */ @@ -138,7 +142,7 @@ typedef Addr addr; /* Addr, Free, Param, User */ AddrInfo addrinfo; - /* Param */ + /* Param; hijacked for PThread as a description */ Char* syscall_param; /* Param, User */ Bool isWriteableLack; @@ -255,6 +259,12 @@ static Bool eq_ErrContext ( Bool cheap_addr_cmp, return False; switch (e1->ekind) { + case PThreadErr: + if (e1->syscall_param == e2->syscall_param) + return True; + if (0 == VG_(strcmp)(e1->syscall_param, e2->syscall_param)) + return True; + return False; case UserErr: case ParamErr: if (e1->isWriteableLack != e2->isWriteableLack) return False; @@ -412,6 +422,10 @@ static void pp_ErrContext ( ErrContext* ec, Bool printCount ) VG_(pp_ExeContext)(ec->where); pp_AddrInfo(ec->addr, &ec->addrinfo); break; + case PThreadErr: + VG_(message)(Vg_UserMsg, "%s", ec->syscall_param ); + VG_(pp_ExeContext)(ec->where); + break; default: VG_(panic)("pp_ErrContext"); } @@ -747,6 +761,25 @@ void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ) VG_(maybe_add_context) ( &ec ); } +void VG_(record_pthread_err) ( ThreadId tid, Char* msg ) +{ + ErrContext ec; + if (vg_ignore_errors) return; + if (!VG_(clo_instrument)) return; + clear_ErrContext( &ec ); + ec.count = 1; + ec.next = NULL; + ec.where = VG_(get_ExeContext)( False, VG_(threads)[tid].m_eip, + VG_(threads)[tid].m_ebp ); + ec.ekind = PThreadErr; + ec.tid = tid; + ec.syscall_param = msg; + ec.m_eip = VG_(threads)[tid].m_eip; + ec.m_esp = VG_(threads)[tid].m_esp; + ec.m_ebp = VG_(threads)[tid].m_ebp; + VG_(maybe_add_context) ( &ec ); +} + /*------------------------------*/ @@ -963,6 +996,7 @@ static void load_one_suppressions_file ( Char* filename ) else if (STREQ(buf, "Addr4")) supp->skind = Addr4; else if (STREQ(buf, "Addr8")) supp->skind = Addr8; else if (STREQ(buf, "Free")) supp->skind = FreeS; + else if (STREQ(buf, "PThread")) supp->skind = PThread; else goto syntax_error; if (supp->skind == Param) { @@ -1100,7 +1134,7 @@ static Suppression* is_suppressible_error ( ErrContext* ec ) /* See if the error context matches any suppression. */ for (su = vg_suppressions; su != NULL; su = su->next) { switch (su->skind) { - case FreeS: + case FreeS: case PThread: case Param: case Value0: su_size = 0; break; case Value1: case Addr1: su_size = 1; break; case Value2: case Addr2: su_size = 2; break; @@ -1122,7 +1156,11 @@ static Suppression* is_suppressible_error ( ErrContext* ec ) if (ec->size != su_size) continue; break; case FreeS: - if (ec->ekind != FreeErr && ec->ekind != FreeMismatchErr) continue; + if (ec->ekind != FreeErr + && ec->ekind != FreeMismatchErr) continue; + break; + case PThread: + if (ec->ekind != PThreadErr) continue; break; } diff --git a/vg_include.h b/vg_include.h index ba2203063c..ec923b1881 100644 --- a/vg_include.h +++ b/vg_include.h @@ -487,8 +487,11 @@ extern Bool VG_(is_empty_arena) ( ArenaId aid ); #define VG_USERREQ__NUKE_OTHER_THREADS 0x3023 + /* Cosmetic ... */ #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101 +/* Log a pthread error from client-space. Cosmetic. */ +#define VG_USERREQ__PTHREAD_ERROR 0x3102 /* In vg_constants.h: @@ -1306,6 +1309,8 @@ extern void VG_(record_param_err) ( ThreadState* tst, Char* msg ); extern void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ); +extern void VG_(record_pthread_err) ( ThreadId tid, Char* msg ); + /* The classification of a faulting address. */ diff --git a/vg_libpthread.c b/vg_libpthread.c index c2b0f68e61..8a74a5c382 100644 --- a/vg_libpthread.c +++ b/vg_libpthread.c @@ -216,6 +216,20 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn ) #include /* gettimeofday */ +/* --------------------------------------------------- + Ummm .. + ------------------------------------------------ */ + +static +void pthread_error ( const char* msg ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 0, + VG_USERREQ__PTHREAD_ERROR, + msg, 0, 0, 0); +} + + /* --------------------------------------------------- THREAD ATTRIBUTES ------------------------------------------------ */ @@ -230,8 +244,11 @@ int pthread_attr_init(pthread_attr_t *attr) int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { if (detachstate != PTHREAD_CREATE_JOINABLE - && detachstate != PTHREAD_CREATE_DETACHED) + && detachstate != PTHREAD_CREATE_DETACHED) { + pthread_error("pthread_attr_setdetachstate: " + "detachstate is invalid"); return EINVAL; + } attr->__detachstate = detachstate; return 0; } @@ -301,6 +318,8 @@ int pthread_attr_setscope ( pthread_attr_t *attr, int scope ) ensure_valgrind("pthread_attr_setscope"); if (scope == PTHREAD_SCOPE_SYSTEM) return 0; + pthread_error("pthread_attr_setscope: " + "invalid or unsupported scope"); if (scope == PTHREAD_SCOPE_PROCESS) return ENOTSUP; return EINVAL; @@ -581,10 +600,18 @@ int pthread_detach(pthread_t th) VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, VG_USERREQ__SET_OR_GET_DETACH, 2 /* get */, th, 0, 0); - if (res == -1) /* not found */ + if (res == -1) { + /* not found */ + pthread_error("pthread_detach: " + "invalid target thread"); return ESRCH; - if (res == 1) /* already detached */ + } + if (res == 1) { + /* already detached */ + pthread_error("pthread_detach: " + "target thread is already detached"); return EINVAL; + } if (res == 0) { VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, VG_USERREQ__SET_OR_GET_DETACH, @@ -709,6 +736,8 @@ int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) attr->__mutexkind = type; return 0; default: + pthread_error("pthread_mutexattr_settype: " + "invalid type"); return EINVAL; } } @@ -790,12 +819,15 @@ int __pthread_mutex_destroy(pthread_mutex_t *mutex) { /* Valgrind doesn't hold any resources on behalf of the mutex, so no need to involve it. */ - if (mutex->__m_count > 0) + if (mutex->__m_count > 0) { + pthread_error("pthread_mutex_destroy: " + "mutex is still in use"); return EBUSY; - mutex->__m_count = 0; - mutex->__m_owner = (_pthread_descr)VG_INVALID_THREADID; - mutex->__m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; - return 0; + } + mutex->__m_count = 0; + mutex->__m_owner = (_pthread_descr)VG_INVALID_THREADID; + mutex->__m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; + return 0; } @@ -937,8 +969,11 @@ int pthread_setcancelstate(int state, int *oldstate) int res; ensure_valgrind("pthread_setcancelstate"); if (state != PTHREAD_CANCEL_ENABLE - && state != PTHREAD_CANCEL_DISABLE) + && state != PTHREAD_CANCEL_DISABLE) { + pthread_error("pthread_setcancelstate: " + "invalid state"); return EINVAL; + } my_assert(-1 != PTHREAD_CANCEL_ENABLE); my_assert(-1 != PTHREAD_CANCEL_DISABLE); VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, @@ -955,8 +990,11 @@ int pthread_setcanceltype(int type, int *oldtype) int res; ensure_valgrind("pthread_setcanceltype"); if (type != PTHREAD_CANCEL_DEFERRED - && type != PTHREAD_CANCEL_ASYNCHRONOUS) + && type != PTHREAD_CANCEL_ASYNCHRONOUS) { + pthread_error("pthread_setcanceltype: " + "invalid type"); return EINVAL; + } my_assert(-1 != PTHREAD_CANCEL_DEFERRED); my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, @@ -1038,7 +1076,8 @@ int pthread_sigmask(int how, const sigset_t *newmask, case SIG_SETMASK: how = VKI_SIG_SETMASK; break; case SIG_BLOCK: how = VKI_SIG_BLOCK; break; case SIG_UNBLOCK: how = VKI_SIG_UNBLOCK; break; - default: return EINVAL; + default: pthread_error("pthread_sigmask: invalid how"); + return EINVAL; } /* Crude check */ @@ -1084,9 +1123,9 @@ int pthread_kill(pthread_t thread, int signo) int raise (int sig) { int retcode = pthread_kill(pthread_self(), sig); - if (retcode == 0) + if (retcode == 0) { return 0; - else { + } else { errno = retcode; return -1; } @@ -1153,7 +1192,6 @@ int __pthread_once ( pthread_once_t *once_control, res = __pthread_mutex_lock(&once_masterlock); if (res != 0) { - printf("res = %d\n",res); barf("pthread_once: Looks like your program's " "init routine calls back to pthread_once() ?!"); } @@ -2040,6 +2078,7 @@ int sem_init(sem_t *sem, int pshared, unsigned int value) vg_sem_t* vg_sem; ensure_valgrind("sem_init"); if (pshared != 0) { + pthread_error("sem_init: unsupported pshared value"); errno = ENOSYS; return -1; } diff --git a/vg_scheduler.c b/vg_scheduler.c index fa71fb2b47..93309b8170 100644 --- a/vg_scheduler.c +++ b/vg_scheduler.c @@ -1754,7 +1754,7 @@ void do__cleanup_pop ( ThreadId tid, CleanupEntry* cu ) sp = VG_(threads)[tid].custack_used; if (VG_(clo_trace_sched)) { VG_(sprintf)(msg_buf, - "cleanup_pop from slot %d", sp); + "cleanup_pop from slot %d", sp-1); print_sched_event(tid, msg_buf); } vg_assert(sp >= 0 && sp <= VG_N_CLEANUPSTACK); @@ -1932,6 +1932,8 @@ void do__set_cancelpend ( ThreadId tid, "set_cancelpend for invalid tid %d", cee); print_sched_event(tid, msg_buf); } + VG_(record_pthread_err)( tid, + "pthread_cancel: target thread does not exist, or invalid"); SET_EDX(tid, -VKI_ESRCH); return; } @@ -1965,6 +1967,8 @@ void do_pthread_join ( ThreadId tid, vg_assert(VG_(threads)[tid].status == VgTs_Runnable); if (jee == tid) { + VG_(record_pthread_err)( tid, + "pthread_join: attempt to join to self"); SET_EDX(tid, EDEADLK); /* libc constant, not a kernel one */ VG_(threads)[tid].status = VgTs_Runnable; return; @@ -1979,6 +1983,8 @@ void do_pthread_join ( ThreadId tid, || jee >= VG_N_THREADS || VG_(threads)[jee].status == VgTs_Empty) { /* Invalid thread to join to. */ + VG_(record_pthread_err)( tid, + "pthread_join: target thread does not exist, or invalid"); SET_EDX(tid, EINVAL); VG_(threads)[tid].status = VgTs_Runnable; return; @@ -1990,6 +1996,9 @@ void do_pthread_join ( ThreadId tid, if (VG_(threads)[i].status == VgTs_WaitJoinee && VG_(threads)[i].joiner_jee_tid == jee) { /* Someone already did join on this thread */ + VG_(record_pthread_err)( tid, + "pthread_join: another thread already " + "in join-wait for target thread"); SET_EDX(tid, EINVAL); VG_(threads)[tid].status = VgTs_Runnable; return; @@ -2270,7 +2279,8 @@ void do_pthread_mutex_lock( ThreadId tid, /* POSIX doesn't mandate this, but for sanity ... */ if (mutex == NULL) { - /* VG_(printf)("NULL mutex\n"); */ + VG_(record_pthread_err)( tid, + "pthread_mutex_lock/trylock: mutex is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2289,7 +2299,8 @@ void do_pthread_mutex_lock( ThreadId tid, if (mutex->__m_count >= 0) break; /* else fall thru */ default: - /* VG_(printf)("unknown __m_kind %d in mutex\n", mutex->__m_kind); */ + VG_(record_pthread_err)( tid, + "pthread_mutex_lock/trylock: mutex is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2367,6 +2378,8 @@ void do_pthread_mutex_unlock ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (mutex == NULL) { + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2385,13 +2398,25 @@ void do_pthread_mutex_unlock ( ThreadId tid, if (mutex->__m_count >= 0) break; /* else fall thru */ default: + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is invalid"); SET_EDX(tid, EINVAL); return; } /* Barf if we don't currently hold the mutex. */ - if (mutex->__m_count == 0 /* nobody holds it */ - || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) { + if (mutex->__m_count == 0) { + /* nobody holds it */ + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is not locked"); + SET_EDX(tid, EPERM); + return; + } + + if ((ThreadId)mutex->__m_owner != tid) { + /* we don't hold it */ + VG_(record_pthread_err)( tid, + "pthread_mutex_unlock: mutex is locked by a different thread"); SET_EDX(tid, EPERM); return; } @@ -2587,6 +2612,8 @@ void do_pthread_cond_wait ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (mutex == NULL || cond == NULL) { + VG_(record_pthread_err)( tid, + "pthread_cond_wait/timedwait: cond or mutex is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2605,6 +2632,8 @@ void do_pthread_cond_wait ( ThreadId tid, if (mutex->__m_count >= 0) break; /* else fall thru */ default: + VG_(record_pthread_err)( tid, + "pthread_cond_wait/timedwait: mutex is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2612,6 +2641,9 @@ void do_pthread_cond_wait ( ThreadId tid, /* Barf if we don't currently hold the mutex. */ if (mutex->__m_count == 0 /* nobody holds it */ || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) { + VG_(record_pthread_err)( tid, + "pthread_cond_wait/timedwait: mutex is unlocked " + "or is locked but not owned by thread"); SET_EDX(tid, EINVAL); return; } @@ -2655,6 +2687,8 @@ void do_pthread_cond_signal_or_broadcast ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (cond == NULL) { + VG_(record_pthread_err)( tid, + "pthread_cond_signal/broadcast: cond is NULL"); SET_EDX(tid, EINVAL); return; } @@ -2738,6 +2772,8 @@ void do_pthread_key_delete ( ThreadId tid, pthread_key_t key ) && VG_(threads)[tid].status == VgTs_Runnable); if (!is_valid_key(key)) { + VG_(record_pthread_err)( tid, + "pthread_key_delete: key is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2770,6 +2806,8 @@ void do_pthread_getspecific ( ThreadId tid, pthread_key_t key ) && VG_(threads)[tid].status == VgTs_Runnable); if (!is_valid_key(key)) { + VG_(record_pthread_err)( tid, + "pthread_getspecific: key is invalid"); SET_EDX(tid, (UInt)NULL); return; } @@ -2794,6 +2832,8 @@ void do_pthread_setspecific ( ThreadId tid, && VG_(threads)[tid].status == VgTs_Runnable); if (!is_valid_key(key)) { + VG_(record_pthread_err)( tid, + "pthread_setspecific: key is invalid"); SET_EDX(tid, EINVAL); return; } @@ -2917,7 +2957,9 @@ void do_pthread_kill ( ThreadId tid, /* me */ vg_assert(VG_(is_valid_tid)(tid) && VG_(threads)[tid].status == VgTs_Runnable); - if (!VG_(is_valid_tid)(tid)) { + if (!VG_(is_valid_tid)(thread)) { + VG_(record_pthread_err)( tid, + "pthread_kill: invalid target thread"); SET_EDX(tid, -VKI_ESRCH); return; } @@ -3052,6 +3094,11 @@ void do_nontrivial_clientreq ( ThreadId tid ) SET_EDX(tid, 0); break; + case VG_USERREQ__PTHREAD_ERROR: + VG_(record_pthread_err)( tid, (Char*)(arg[1]) ); + SET_EDX(tid, 0); + break; + case VG_USERREQ__MAKE_NOACCESS: case VG_USERREQ__MAKE_WRITABLE: case VG_USERREQ__MAKE_READABLE: