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)
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)
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)
<a name="title"> </a>
<h1 align=center>Valgrind, snapshot 20020522</h1>
<center>This manual was majorly updated on 20020501</center>
-<center>This manual was minorly updated on 20020522</center>
+<center>This manual was minorly updated on 20020603</center>
<p>
<center>
memory access of 1, 2, 4 or 8 bytes respectively. Or
<code>Param</code>,
meaning an invalid system call parameter error. Or
- <code>Free</code>, meaning an invalid or mismatching free.</li><br>
+ <code>Free</code>, meaning an invalid or mismatching free.
+ Or <code>PThread</code>, meaning any kind of complaint to do
+ with the PThreads API.</li><br>
<p>
<li>The "immediate location" specification. For Value and Addr
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)
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)
#include <sys/time.h> /* 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
------------------------------------------------ */
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;
}
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;
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,
attr->__mutexkind = type;
return 0;
default:
+ pthread_error("pthread_mutexattr_settype: "
+ "invalid type");
return EINVAL;
}
}
{
/* 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;
}
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 */,
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 */,
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 */
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;
}
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() ?!");
}
vg_sem_t* vg_sem;
ensure_valgrind("sem_init");
if (pshared != 0) {
+ pthread_error("sem_init: unsupported pshared value");
errno = ENOSYS;
return -1;
}
<a name="title"> </a>
<h1 align=center>Valgrind, snapshot 20020522</h1>
<center>This manual was majorly updated on 20020501</center>
-<center>This manual was minorly updated on 20020522</center>
+<center>This manual was minorly updated on 20020603</center>
<p>
<center>
memory access of 1, 2, 4 or 8 bytes respectively. Or
<code>Param</code>,
meaning an invalid system call parameter error. Or
- <code>Free</code>, meaning an invalid or mismatching free.</li><br>
+ <code>Free</code>, meaning an invalid or mismatching free.
+ Or <code>PThread</code>, meaning any kind of complaint to do
+ with the PThreads API.</li><br>
<p>
<li>The "immediate location" specification. For Value and Addr
/* Invalid read/write attempt at given size */
Addr1, Addr2, Addr4, Addr8,
/* Invalid or mismatching free */
- FreeS
+ FreeS,
+ /* Pthreading error */
+ PThread
}
SuppressionKind;
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? */
Addr addr;
/* Addr, Free, Param, User */
AddrInfo addrinfo;
- /* Param */
+ /* Param; hijacked for PThread as a description */
Char* syscall_param;
/* Param, User */
Bool isWriteableLack;
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;
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");
}
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 );
+}
+
/*------------------------------*/
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) {
/* 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;
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;
}
#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:
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. */
#include <sys/time.h> /* 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
------------------------------------------------ */
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;
}
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;
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,
attr->__mutexkind = type;
return 0;
default:
+ pthread_error("pthread_mutexattr_settype: "
+ "invalid type");
return EINVAL;
}
}
{
/* 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;
}
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 */,
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 */,
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 */
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;
}
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() ?!");
}
vg_sem_t* vg_sem;
ensure_valgrind("sem_init");
if (pshared != 0) {
+ pthread_error("sem_init: unsupported pshared value");
errno = ENOSYS;
return -1;
}
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);
"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;
}
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;
|| 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;
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;
/* 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;
}
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;
}
&& 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;
}
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;
}
&& 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;
}
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;
}
/* 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;
}
&& 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;
}
&& 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;
}
&& 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;
}
&& 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;
}
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;
}
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:
<a name="title"> </a>
<h1 align=center>Valgrind, snapshot 20020522</h1>
<center>This manual was majorly updated on 20020501</center>
-<center>This manual was minorly updated on 20020522</center>
+<center>This manual was minorly updated on 20020603</center>
<p>
<center>
memory access of 1, 2, 4 or 8 bytes respectively. Or
<code>Param</code>,
meaning an invalid system call parameter error. Or
- <code>Free</code>, meaning an invalid or mismatching free.</li><br>
+ <code>Free</code>, meaning an invalid or mismatching free.
+ Or <code>PThread</code>, meaning any kind of complaint to do
+ with the PThreads API.</li><br>
<p>
<li>The "immediate location" specification. For Value and Addr
# (optionally: caller3 name)
# }
+{
+ __pthread_mutex_unlock/__register_frame_info
+ PThread
+ fun:__pthread_mutex_unlock
+ fun:__register_frame_info
+}
# even more glibc suppressions ?
{
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)
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)
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)
<a name="title"> </a>
<h1 align=center>Valgrind, snapshot 20020522</h1>
<center>This manual was majorly updated on 20020501</center>
-<center>This manual was minorly updated on 20020522</center>
+<center>This manual was minorly updated on 20020603</center>
<p>
<center>
memory access of 1, 2, 4 or 8 bytes respectively. Or
<code>Param</code>,
meaning an invalid system call parameter error. Or
- <code>Free</code>, meaning an invalid or mismatching free.</li><br>
+ <code>Free</code>, meaning an invalid or mismatching free.
+ Or <code>PThread</code>, meaning any kind of complaint to do
+ with the PThreads API.</li><br>
<p>
<li>The "immediate location" specification. For Value and Addr
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)
/* Invalid read/write attempt at given size */
Addr1, Addr2, Addr4, Addr8,
/* Invalid or mismatching free */
- FreeS
+ FreeS,
+ /* Pthreading error */
+ PThread
}
SuppressionKind;
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? */
Addr addr;
/* Addr, Free, Param, User */
AddrInfo addrinfo;
- /* Param */
+ /* Param; hijacked for PThread as a description */
Char* syscall_param;
/* Param, User */
Bool isWriteableLack;
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;
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");
}
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 );
+}
+
/*------------------------------*/
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) {
/* 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;
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;
}
#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:
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. */
#include <sys/time.h> /* 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
------------------------------------------------ */
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;
}
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;
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,
attr->__mutexkind = type;
return 0;
default:
+ pthread_error("pthread_mutexattr_settype: "
+ "invalid type");
return EINVAL;
}
}
{
/* 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;
}
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 */,
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 */,
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 */
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;
}
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() ?!");
}
vg_sem_t* vg_sem;
ensure_valgrind("sem_init");
if (pshared != 0) {
+ pthread_error("sem_init: unsupported pshared value");
errno = ENOSYS;
return -1;
}
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);
"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;
}
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;
|| 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;
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;
/* 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;
}
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;
}
&& 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;
}
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;
}
&& 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;
}
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;
}
/* 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;
}
&& 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;
}
&& 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;
}
&& 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;
}
&& 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;
}
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;
}
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: