- Performance improvement: eliminated busy waiting from thread creation.

- Applied DRD_() prefix to all names of functions that are not
  intercepts of client code.
- Removed superfluous include directive, namely #include <inttypes.h>.
- Removed hack for suppressing false positive reports on stdio / stderr
  because recently a suppression pattern was added for these races.
- Removed unused code and declarations.
- Added more comments.
- Updated copyright statement.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@9149
This commit is contained in:
Bart Van Assche 2009-02-14 12:12:57 +00:00
parent 99249d56d6
commit 0a804bd7c6

View File

@ -1,13 +1,12 @@
/*--------------------------------------------------------------------*/
/*--- Client-space code for drd. drd_pthread_intercepts.c ---*/
/*--- Client-space code for DRD. drd_pthread_intercepts.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of drd, a data race detector.
This file is part of DRD, a data race detector.
Copyright (C) 2006-2008 Bart Van Assche
bart.vanassche@gmail.com
Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@ -40,26 +39,27 @@
originates from Valgrind.
------------------------------------------------------------------ */
// Make sure pthread_spinlock_t is available when compiling with older glibc
// versions (2.3 or before).
/*
Define _GNU_SOURCE to make sure that pthread_spinlock_t is available when
compiling with older glibc versions (2.3 or before).
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <assert.h>
#include <inttypes.h> // uintptr_t
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // confstr()
#include "config.h"
#include "drd_basics.h"
#include <assert.h> /* assert() */
#include <pthread.h> /* pthread_mutex_t */
#include <semaphore.h> /* sem_t */
#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* malloc(), free() */
#include <unistd.h> /* confstr() */
#include "config.h" /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP etc. */
#include "drd_basics.h" /* DRD_() */
#include "drd_clientreq.h"
#include "pub_tool_redir.h"
#include "pub_tool_redir.h" /* VG_WRAP_FUNCTION_ZZ() */
// Defines.
/* Defines. */
#define PTH_FUNC(ret_ty, f, args...) \
ret_ty VG_WRAP_FUNCTION_ZZ(libpthreadZdsoZd0,f)(args); \
@ -73,74 +73,78 @@ typedef struct
void* (*start)(void*);
void* arg;
int detachstate;
#if 0
pthread_mutex_t mutex;
pthread_cond_t cond;
#else
int wrapper_started;
#endif
} VgPosixThreadArgs;
/* Function declarations. */
/* Local function declarations. */
static void init(void) __attribute__((constructor));
static void check_threading_library(void);
static void vg_set_main_thread_state(void);
static void DRD_(init)(void) __attribute__((constructor));
static void DRD_(check_threading_library)(void);
static void DRD_(set_main_thread_state)(void);
/* Function definitions. */
/** Shared library initialization function: the _init() function is called
* after dlopen() has loaded the shared library. This function must not
* be declared static.
/**
* Shared library initialization function. The function init() is called after
* dlopen() has loaded the shared library with DRD client intercepts because
* the constructor attribute was specified in the declaration of this function.
* Note: do specify the -nostdlib option to gcc when linking this code into a
* shared library because doing so would cancel the effect of the constructor
* attribute ! Using the gcc option -nodefaultlibs is fine because this last
* option preserves the shared library initialization code that calls
* constructor and destructor functions.
*/
static void init(void)
static void DRD_(init)(void)
{
check_threading_library();
vg_set_main_thread_state();
/* glibc up to and including version 2.8 triggers conflicting accesses */
/* on stdout and stderr when sending output to one of these streams from */
/* more than one thread. Suppress data race reports on these objects. */
DRD_IGNORE_VAR(*stdout);
DRD_IGNORE_VAR(*stderr);
#if defined(HAVE_LIBC_FILE_LOCK)
DRD_IGNORE_VAR(*(pthread_mutex_t*)(stdout->_lock));
DRD_IGNORE_VAR(*(pthread_mutex_t*)(stderr->_lock));
#endif
DRD_(check_threading_library)();
DRD_(set_main_thread_state)();
}
static MutexT pthread_to_drd_mutex_type(const int kind)
/**
* POSIX threads and DRD each have their own mutex type identification.
* Convert POSIX threads' mutex type to DRD's mutex type. In the code below
* if-statements are used to test the value of 'kind' instead of a switch
* statement because some of the PTHREAD_MUTEX_ macro's may have the same
* value.
*/
static MutexT DRD_(pthread_to_drd_mutex_type)(const int kind)
{
switch (kind)
{
/* PTHREAD_MUTEX_RECURSIVE_NP */
case PTHREAD_MUTEX_RECURSIVE:
if (kind == PTHREAD_MUTEX_RECURSIVE)
return mutex_type_recursive_mutex;
/* PTHREAD_MUTEX_ERRORCHECK_NP */
case PTHREAD_MUTEX_ERRORCHECK:
else if (kind == PTHREAD_MUTEX_ERRORCHECK)
return mutex_type_errorcheck_mutex;
/* PTHREAD_MUTEX_TIMED_NP */
/* PTHREAD_MUTEX_NORMAL */
case PTHREAD_MUTEX_DEFAULT:
#if defined(HAVE_PTHREAD_MUTEX_ADAPTIVE_NP)
case PTHREAD_MUTEX_ADAPTIVE_NP:
#endif
else if (kind == PTHREAD_MUTEX_NORMAL)
return mutex_type_default_mutex;
else if (kind == PTHREAD_MUTEX_DEFAULT)
return mutex_type_default_mutex;
#if defined(HAVE_PTHREAD_MUTEX_ADAPTIVE_NP)
else if (kind == PTHREAD_MUTEX_ADAPTIVE_NP)
return mutex_type_default_mutex;
#endif
else
{
return mutex_type_invalid_mutex;
}
return mutex_type_invalid_mutex;
}
/** @note The function mutex_type() has been declared inline in order
* to avoid that it shows up in call stacks.
/**
* Read the mutex type stored in the client memory used for the mutex
* implementation.
*
* @note This function depends on the implementation of the POSIX threads
* library -- the POSIX standard does not define the name of the member in
* which the mutex type is stored.
* @note The function mutex_type() has been declared inline in order
* to avoid that it shows up in call stacks (drd/tests/...exp* files).
*/
static __inline__ MutexT mutex_type(pthread_mutex_t* mutex)
static __inline__ MutexT DRD_(mutex_type)(pthread_mutex_t* mutex)
{
#if defined(HAVE_PTHREAD_MUTEX_T__M_KIND)
/* LinuxThreads. */
/* glibc + LinuxThreads. */
const int kind = mutex->__m_kind;
#elif defined(HAVE_PTHREAD_MUTEX_T__DATA__KIND)
/* NPTL. */
/* glibc + NPTL. */
const int kind = mutex->__data.__kind;
#else
/* Another POSIX threads implementation. Regression tests will fail. */
@ -149,17 +153,13 @@ static __inline__ MutexT mutex_type(pthread_mutex_t* mutex)
"Did not recognize your POSIX threads implementation. Giving up.\n");
assert(0);
#endif
return pthread_to_drd_mutex_type(kind);
return DRD_(pthread_to_drd_mutex_type)(kind);
}
static void vg_start_suppression(const void* const p, size_t const size)
{
int res;
VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_START_SUPPRESSION,
p, size, 0, 0, 0);
}
static void vg_set_joinable(const pthread_t tid, const int joinable)
/**
* Tell DRD whether 'tid' is a joinable thread or a detached thread.
*/
static void DRD_(set_joinable)(const pthread_t tid, const int joinable)
{
int res;
assert(joinable == 0 || joinable == 1);
@ -167,37 +167,42 @@ static void vg_set_joinable(const pthread_t tid, const int joinable)
tid, joinable, 0, 0, 0);
}
/**
* The function called from the thread created by pthread_create().
*/
static void* DRD_(thread_wrapper)(void* arg)
{
int res;
VgPosixThreadArgs* arg_ptr;
VgPosixThreadArgs arg_copy;
VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_SUPPRESS_CURRENT_STACK,
0, 0, 0, 0, 0);
{
VgPosixThreadArgs* const arg_ptr = (VgPosixThreadArgs*)arg;
VgPosixThreadArgs const arg_copy = *arg_ptr;
void* result;
arg_ptr = (VgPosixThreadArgs*)arg;
arg_copy = *arg_ptr;
#if 0
pthread_mutex_lock(arg_ptr->mutex);
pthread_cond_signal(arg_ptr->cond);
pthread_mutex_unlock(arg_ptr->mutex);
#else
arg_ptr->wrapper_started = 1;
#endif
/*
* Free the memory 'arg_ptr' points at now such that it does not get
* leaked when the function called below throws a C++ exception.
*/
free(arg_ptr);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SET_PTHREADID,
pthread_self(), 0, 0, 0, 0);
vg_set_joinable(pthread_self(),
arg_copy.detachstate == PTHREAD_CREATE_JOINABLE);
result = (arg_copy.start)(arg_copy.arg);
return result;
}
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SET_PTHREADID,
pthread_self(), 0, 0, 0, 0);
DRD_(set_joinable)(pthread_self(),
arg_copy.detachstate == PTHREAD_CREATE_JOINABLE);
return (arg_copy.start)(arg_copy.arg);
}
/** Return 1 if LinuxThread has been detected, and 0 otherwise. */
static int detected_linuxthreads(void)
/**
* Return 1 if the LinuxThread implementation has been detected, and 0
* otherwise. For more information about the confstr() function, see also
* http://www.opengroup.org/onlinepubs/009695399/functions/confstr.html
*/
static int DRD_(detected_linuxthreads)(void)
{
#if defined(linux)
#if defined(_CS_GNU_LIBPTHREAD_VERSION)
@ -217,35 +222,40 @@ static int detected_linuxthreads(void)
#endif
}
/** Stop and print an error message in case a non-supported threading
* library (LinuxThreads) has been detected.
/**
* Stop and print an error message in case a non-supported threading
* library implementation (LinuxThreads) has been detected.
*/
static void check_threading_library(void)
static void DRD_(check_threading_library)(void)
{
if (detected_linuxthreads())
if (DRD_(detected_linuxthreads)())
{
if (getenv("LD_ASSUME_KERNEL"))
{
fprintf(stderr,
"Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
"the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
"after having unset the environment variable LD_ASSUME_KERNEL. Giving up.\n"
"Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
"the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
"after having unset the environment variable LD_ASSUME_KERNEL. Giving up.\n"
);
}
else
{
fprintf(stderr,
"Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
"the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
"after having upgraded to a newer version of your Linux distribution.\n"
"Giving up.\n"
"Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
"the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
"after having upgraded to a newer version of your Linux distribution.\n"
"Giving up.\n"
);
}
abort();
}
}
static void vg_set_main_thread_state(void)
/**
* The main thread is the only thread not created by pthread_create().
* Update DRD's state information about the main thread.
*/
static void DRD_(set_main_thread_state)(void)
{
int res;
@ -266,55 +276,43 @@ PTH_FUNC(int, pthreadZucreateZa, // pthread_create*
int res;
int ret;
OrigFn fn;
VgPosixThreadArgs vgargs;
VgPosixThreadArgs* vgargs;
VALGRIND_GET_ORIG_FN(fn);
vg_start_suppression(&vgargs.wrapper_started,
sizeof(vgargs.wrapper_started));
vgargs.start = start;
vgargs.arg = arg;
vgargs.wrapper_started = 0;
vgargs.detachstate = PTHREAD_CREATE_JOINABLE;
vgargs = malloc(sizeof *vgargs);
assert(vgargs);
vgargs->start = start;
vgargs->arg = arg;
/*
* Find out whether the thread will be started as a joinable thread
* or as a detached thread. If no thread attributes have been specified,
* the new thread will be started as a joinable thread.
*/
vgargs->detachstate = PTHREAD_CREATE_JOINABLE;
if (attr)
{
if (pthread_attr_getdetachstate(attr, &vgargs.detachstate) != 0)
if (pthread_attr_getdetachstate(attr, &vgargs->detachstate) != 0)
{
assert(0);
}
}
assert(vgargs.detachstate == PTHREAD_CREATE_JOINABLE
|| vgargs.detachstate == PTHREAD_CREATE_DETACHED);
#if 0
pthread_mutex_init(&vgargs.mutex, 0);
pthread_cond_init(&vgargs.cond, 0);
pthread_mutex_lock(&vgargs.mutex);
#endif
assert(vgargs->detachstate == PTHREAD_CREATE_JOINABLE
|| vgargs->detachstate == PTHREAD_CREATE_DETACHED);
/* Suppress NPTL-specific conflicts between creator and created thread. */
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__DRD_STOP_RECORDING,
0, 0, 0, 0, 0);
CALL_FN_W_WWWW(ret, fn, thread, attr, DRD_(thread_wrapper), &vgargs);
CALL_FN_W_WWWW(ret, fn, thread, attr, DRD_(thread_wrapper), vgargs);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__DRD_START_RECORDING,
0, 0, 0, 0, 0);
#if 0
pthread_cond_wait(&vgargs.cond, &vgargs.mutex);
pthread_mutex_unlock(&vgargs.mutex);
pthread_cond_destroy(&vgargs.cond);
pthread_mutex_destroy(&vgargs.mutex);
#else
// Yes, you see it correctly, busy waiting ... The problem is that
// POSIX threads functions cannot be called here -- the functions defined
// in this file (drd_intercepts.c) would be called instead of those in
// libpthread.so. This loop is necessary because vgargs is allocated on the
// stack, and the created thread reads it.
if (ret == 0)
{
while (! vgargs.wrapper_started)
{
sched_yield();
}
}
#endif
/* Free the memory 'vgargs' points at if pthread_create() failed. */
if (ret != 0)
free(vgargs);
return ret;
}
@ -346,7 +344,7 @@ PTH_FUNC(int, pthreadZudetach, pthread_t pt_thread)
CALL_FN_W_W(ret, fn, pt_thread);
if (ret == 0)
{
vg_set_joinable(pt_thread, 0);
DRD_(set_joinable)(pt_thread, 0);
}
}
return ret;
@ -381,7 +379,8 @@ PTH_FUNC(int, pthreadZumutexZuinit,
if (attr)
pthread_mutexattr_gettype(attr, &mt);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_MUTEX_INIT,
mutex, pthread_to_drd_mutex_type(mt), 0, 0, 0);
mutex, DRD_(pthread_to_drd_mutex_type)(mt),
0, 0, 0);
CALL_FN_W_WW(ret, fn, mutex, attr);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_INIT,
mutex, 0, 0, 0, 0);
@ -400,7 +399,7 @@ PTH_FUNC(int, pthreadZumutexZudestroy,
mutex, 0, 0, 0, 0);
CALL_FN_W_W(ret, fn, mutex);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_DESTROY,
mutex, mutex_type(mutex), 0, 0, 0);
mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
return ret;
}
@ -413,7 +412,7 @@ PTH_FUNC(int, pthreadZumutexZulock, // pthread_mutex_lock
OrigFn fn;
VALGRIND_GET_ORIG_FN(fn);
VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__PRE_MUTEX_LOCK,
mutex, mutex_type(mutex), 0, 0, 0);
mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
CALL_FN_W_W(ret, fn, mutex);
VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__POST_MUTEX_LOCK,
mutex, ret == 0, 0, 0, 0);
@ -429,7 +428,7 @@ PTH_FUNC(int, pthreadZumutexZutrylock, // pthread_mutex_trylock
OrigFn fn;
VALGRIND_GET_ORIG_FN(fn);
VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__PRE_MUTEX_LOCK,
mutex, mutex_type(mutex), 1, 0, 0);
mutex, DRD_(mutex_type)(mutex), 1, 0, 0);
CALL_FN_W_W(ret, fn, mutex);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_LOCK,
mutex, ret == 0, 0, 0, 0);
@ -446,7 +445,7 @@ PTH_FUNC(int, pthreadZumutexZutimedlock, // pthread_mutex_timedlock
OrigFn fn;
VALGRIND_GET_ORIG_FN(fn);
VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__PRE_MUTEX_LOCK,
mutex, mutex_type(mutex), 0, 0, 0);
mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
CALL_FN_W_WW(ret, fn, mutex, abs_timeout);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_LOCK,
mutex, ret == 0, 0, 0, 0);
@ -463,7 +462,7 @@ PTH_FUNC(int, pthreadZumutexZuunlock, // pthread_mutex_unlock
VALGRIND_GET_ORIG_FN(fn);
VALGRIND_DO_CLIENT_REQUEST(res, -1,
VG_USERREQ__PRE_MUTEX_UNLOCK,
mutex, mutex_type(mutex), 0, 0, 0);
mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
CALL_FN_W_W(ret, fn, mutex);
VALGRIND_DO_CLIENT_REQUEST(res, -1,
VG_USERREQ__POST_MUTEX_UNLOCK,
@ -514,7 +513,7 @@ PTH_FUNC(int, pthreadZucondZuwaitZa, // pthread_cond_wait*
OrigFn fn;
VALGRIND_GET_ORIG_FN(fn);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_COND_WAIT,
cond, mutex, mutex_type(mutex), 0, 0);
cond, mutex, DRD_(mutex_type)(mutex), 0, 0);
CALL_FN_W_WW(ret, fn, cond, mutex);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_COND_WAIT,
cond, mutex, 1, 0, 0);
@ -532,7 +531,7 @@ PTH_FUNC(int, pthreadZucondZutimedwaitZa, // pthread_cond_timedwait*
OrigFn fn;
VALGRIND_GET_ORIG_FN(fn);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_COND_WAIT,
cond, mutex, mutex_type(mutex), 0, 0);
cond, mutex, DRD_(mutex_type)(mutex), 0, 0);
CALL_FN_W_WWW(ret, fn, cond, mutex, abstime);
VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_COND_WAIT,
cond, mutex, 1, 0, 0);