mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-22 07:16:17 +00:00
3336 lines
103 KiB
C
3336 lines
103 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- pthread intercepts for thread checking. ---*/
|
|
/*--- hg_intercepts.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Helgrind, a Valgrind tool for detecting errors
|
|
in threaded programs.
|
|
|
|
Copyright (C) 2007-2015 OpenWorks LLP
|
|
info@open-works.co.uk
|
|
|
|
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.
|
|
|
|
Neither the names of the U.S. Department of Energy nor the
|
|
University of California nor the names of its contributors may be
|
|
used to endorse or promote products derived from this software
|
|
without prior written permission.
|
|
*/
|
|
|
|
/* RUNS ON SIMULATED CPU
|
|
Interceptors for pthread_* functions, so that tc_main can see
|
|
significant thread events.
|
|
|
|
Important: when adding a function wrapper to this file, remember to
|
|
add a test case to tc20_verifywrap.c. A common cause of failure is
|
|
for wrappers to not engage on different distros, and
|
|
tc20_verifywrap essentially checks that each wrapper is really
|
|
doing something.
|
|
*/
|
|
|
|
// DDD: for Darwin, need to have non-"@*"-suffixed versions for all pthread
|
|
// functions that currently have them.
|
|
// Note also, in the comments and code below, all Darwin symbols start
|
|
// with a leading underscore, which is not shown either in the comments
|
|
// nor in the redirect specs.
|
|
|
|
|
|
#include "pub_tool_basics.h"
|
|
#include "pub_tool_redir.h"
|
|
#include "pub_tool_clreq.h"
|
|
#include "helgrind.h"
|
|
#include "config.h"
|
|
|
|
|
|
#if defined(VGO_solaris)
|
|
/* See porting comments in drd/drd_pthread_intercepts.c
|
|
However when a POSIX threads API function (for example pthread_cond_init)
|
|
is built upon the Solaris one (cond_init), intercept only the bottom one.
|
|
Helgrind does not contain generic synchronization nesting like DRD
|
|
and double intercept confuses it. */
|
|
#include <synch.h>
|
|
#include <thread.h>
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
#define TRACE_PTH_FNS 0
|
|
#define TRACE_QT4_FNS 0
|
|
#define TRACE_GNAT_FNS 0
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
#if defined(VGO_solaris)
|
|
/* On Solaris, libpthread is just a filter library on top of libc.
|
|
* Threading and synchronization functions in runtime linker are not
|
|
* intercepted.
|
|
*/
|
|
#define PTH_FUNC(ret_ty, f, args...) \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZZ(VG_Z_LIBC_SONAME,f)(args); \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZZ(VG_Z_LIBC_SONAME,f)(args)
|
|
|
|
/* pthread_t is typedef'd to 'unsigned int' but in DO_CREQ_* macros
|
|
sizeof(Word) is expected. */
|
|
#define CREQ_PTHREAD_T Word
|
|
#define SEM_ERROR ret
|
|
#else
|
|
#define PTH_FUNC(ret_ty, f, args...) \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZZ(VG_Z_LIBPTHREAD_SONAME,f)(args); \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZZ(VG_Z_LIBPTHREAD_SONAME,f)(args)
|
|
#define CREQ_PTHREAD_T pthread_t
|
|
#define SEM_ERROR errno
|
|
#endif /* VGO_solaris */
|
|
|
|
// Do a client request. These are macros rather than a functions so
|
|
// as to avoid having an extra frame in stack traces.
|
|
|
|
// NB: these duplicate definitions in helgrind.h. But here, we
|
|
// can have better typing (Word etc) and assertions, whereas
|
|
// in helgrind.h we can't. Obviously it's important the two
|
|
// sets of definitions are kept in sync.
|
|
|
|
// nuke the previous definitions
|
|
#undef DO_CREQ_v_W
|
|
#undef DO_CREQ_v_WW
|
|
#undef DO_CREQ_W_WW
|
|
#undef DO_CREQ_v_WWW
|
|
|
|
#define DO_CREQ_v_W(_creqF, _ty1F,_arg1F) \
|
|
do { \
|
|
Word _arg1; \
|
|
assert(sizeof(_ty1F) == sizeof(Word)); \
|
|
_arg1 = (Word)(_arg1F); \
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT((_creqF), \
|
|
_arg1, 0,0,0,0); \
|
|
} while (0)
|
|
|
|
#define DO_CREQ_v_WW(_creqF, _ty1F,_arg1F, _ty2F,_arg2F) \
|
|
do { \
|
|
Word _arg1, _arg2; \
|
|
assert(sizeof(_ty1F) == sizeof(Word)); \
|
|
assert(sizeof(_ty2F) == sizeof(Word)); \
|
|
_arg1 = (Word)(_arg1F); \
|
|
_arg2 = (Word)(_arg2F); \
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT((_creqF), \
|
|
_arg1,_arg2,0,0,0); \
|
|
} while (0)
|
|
|
|
#define DO_CREQ_W_WW(_resF, _creqF, _ty1F,_arg1F, \
|
|
_ty2F,_arg2F) \
|
|
do { \
|
|
Word _res, _arg1, _arg2; \
|
|
assert(sizeof(_ty1F) == sizeof(Word)); \
|
|
assert(sizeof(_ty2F) == sizeof(Word)); \
|
|
_arg1 = (Word)(_arg1F); \
|
|
_arg2 = (Word)(_arg2F); \
|
|
_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(2, \
|
|
(_creqF), \
|
|
_arg1,_arg2,0,0,0); \
|
|
_resF = _res; \
|
|
} while (0)
|
|
|
|
#define DO_CREQ_v_WWW(_creqF, _ty1F,_arg1F, \
|
|
_ty2F,_arg2F, _ty3F, _arg3F) \
|
|
do { \
|
|
Word _arg1, _arg2, _arg3; \
|
|
assert(sizeof(_ty1F) == sizeof(Word)); \
|
|
assert(sizeof(_ty2F) == sizeof(Word)); \
|
|
assert(sizeof(_ty3F) == sizeof(Word)); \
|
|
_arg1 = (Word)(_arg1F); \
|
|
_arg2 = (Word)(_arg2F); \
|
|
_arg3 = (Word)(_arg3F); \
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT((_creqF), \
|
|
_arg1,_arg2,_arg3,0,0); \
|
|
} while (0)
|
|
|
|
#define DO_CREQ_v_WWWW(_creqF, _ty1F,_arg1F, \
|
|
_ty2F, _arg2F, _ty3F, _arg3F, \
|
|
_ty4F, _arg4F) \
|
|
do { \
|
|
Word _arg1, _arg2, _arg3, _arg4; \
|
|
assert(sizeof(_ty1F) == sizeof(Word)); \
|
|
assert(sizeof(_ty2F) == sizeof(Word)); \
|
|
assert(sizeof(_ty3F) == sizeof(Word)); \
|
|
assert(sizeof(_ty4F) == sizeof(Word)); \
|
|
_arg1 = (Word)(_arg1F); \
|
|
_arg2 = (Word)(_arg2F); \
|
|
_arg3 = (Word)(_arg3F); \
|
|
_arg4 = (Word)(_arg4F); \
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT((_creqF), \
|
|
_arg1,_arg2,_arg3,_arg4,0); \
|
|
} while (0)
|
|
|
|
#define DO_PthAPIerror(_fnnameF, _errF) \
|
|
do { \
|
|
const char* _fnname = (_fnnameF); \
|
|
long _err = (long)(int)(_errF); \
|
|
const char* _errstr = lame_strerror(_err); \
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTH_API_ERROR, \
|
|
char*,_fnname, \
|
|
long,_err, char*,_errstr); \
|
|
} while (0)
|
|
|
|
|
|
/* Needed for older glibcs (2.3 and older, at least) who don't
|
|
otherwise "know" about pthread_rwlock_anything or about
|
|
PTHREAD_MUTEX_RECURSIVE (amongst things). */
|
|
#define _GNU_SOURCE 1
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
|
|
/* A standalone memcmp. */
|
|
__attribute__((noinline))
|
|
static int my_memcmp ( const void* ptr1, const void* ptr2, size_t size)
|
|
{
|
|
const unsigned char* uchar_ptr1 = (const unsigned char*) ptr1;
|
|
const unsigned char* uchar_ptr2 = (const unsigned char*) ptr2;
|
|
size_t i;
|
|
for (i = 0; i < size; ++i) {
|
|
if (uchar_ptr1[i] != uchar_ptr2[i])
|
|
return (uchar_ptr1[i] < uchar_ptr2[i]) ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* A lame version of strerror which doesn't use the real libc
|
|
strerror_r, since using the latter just generates endless more
|
|
threading errors (glibc goes off and does tons of crap w.r.t.
|
|
locales etc) */
|
|
static const HChar* lame_strerror ( long err )
|
|
{
|
|
switch (err) {
|
|
case EPERM: return "EPERM: Operation not permitted";
|
|
case ENOENT: return "ENOENT: No such file or directory";
|
|
case ESRCH: return "ESRCH: No such process";
|
|
case EINTR: return "EINTR: Interrupted system call";
|
|
case EBADF: return "EBADF: Bad file number";
|
|
case EAGAIN: return "EAGAIN: Try again";
|
|
case ENOMEM: return "ENOMEM: Out of memory";
|
|
case EACCES: return "EACCES: Permission denied";
|
|
case EFAULT: return "EFAULT: Bad address";
|
|
case EEXIST: return "EEXIST: File exists";
|
|
case EINVAL: return "EINVAL: Invalid argument";
|
|
case EMFILE: return "EMFILE: Too many open files";
|
|
case ENOSYS: return "ENOSYS: Function not implemented";
|
|
case EOVERFLOW: return "EOVERFLOW: Value too large "
|
|
"for defined data type";
|
|
case EBUSY: return "EBUSY: Device or resource busy";
|
|
case ETIMEDOUT: return "ETIMEDOUT: Connection timed out";
|
|
case EDEADLK: return "EDEADLK: Resource deadlock would occur";
|
|
case EOPNOTSUPP: return "EOPNOTSUPP: Operation not supported on "
|
|
"transport endpoint"; /* honest, guv */
|
|
case ETIME: return "ETIME: Timer expired";
|
|
default: return "hg_intercepts.c: lame_strerror(): "
|
|
"unhandled case -- please fix me!";
|
|
}
|
|
}
|
|
|
|
#if defined(VGO_solaris)
|
|
/*
|
|
* Solaris provides higher throughput, parallelism and scalability than other
|
|
* operating systems, at the cost of more fine-grained locking activity.
|
|
* This means for example that when a thread is created under Linux, just one
|
|
* big lock in glibc is used for all thread setup. Solaris libc uses several
|
|
* fine-grained locks and the creator thread resumes its activities as soon
|
|
* as possible, leaving for example stack and TLS setup activities to the
|
|
* created thread.
|
|
*
|
|
* This situation confuses Helgrind as it assumes there is some false ordering
|
|
* in place between creator and created thread; and therefore many types of
|
|
* race conditions in the application would not be reported. To prevent such
|
|
* false ordering, command line option --ignore-thread-creation is set to
|
|
* 'yes' by default on Solaris. All activity (loads, stores, client requests)
|
|
* is therefore ignored during:
|
|
* - pthread_create() call in the creator thread [libc.so]
|
|
* - thread creation phase (stack and TLS setup) in the created thread [libc.so]
|
|
*
|
|
* As explained in the comments for _ti_bind_guard(), whenever the runtime
|
|
* linker has to perform any activity (such as resolving a symbol), it protects
|
|
* its data structures by calling into rt_bind_guard() which in turn invokes
|
|
* _ti_bind_guard() in libc. Pointers to _ti_bind_guard() and _ti_bind_clear()
|
|
* are passed from libc to runtime linker in _ld_libc() call during libc_init().
|
|
* All activity is also ignored during:
|
|
* - runtime dynamic linker work between rt_bind_guard() and rt_bind_clear()
|
|
* calls [ld.so]
|
|
*
|
|
* This also means that Helgrind does not report race conditions in libc (when
|
|
* --ignore-thread-creation=yes) and runtime linker itself (unconditionally)
|
|
* during these ignored sequences.
|
|
*/
|
|
|
|
#include "pub_tool_libcassert.h"
|
|
#include "pub_tool_vki.h"
|
|
|
|
/*
|
|
* Original function pointers for _ti_bind_guard() and _ti_bind_clear()
|
|
* from libc. They are intercepted in function wrapper of _ld_libc().
|
|
*/
|
|
typedef int (*hg_rtld_guard_fn)(int flags);
|
|
static hg_rtld_guard_fn hg_rtld_bind_guard = NULL;
|
|
static hg_rtld_guard_fn hg_rtld_bind_clear = NULL;
|
|
|
|
static void hg_init(void) __attribute__((constructor));
|
|
static void hg_init(void)
|
|
{
|
|
if ((hg_rtld_bind_guard == NULL) || (hg_rtld_bind_clear == NULL)) {
|
|
fprintf(stderr,
|
|
"Bind guard functions for the runtime linker (ld.so.1) were not intercepted.\n"
|
|
"This means the interface between libc and runtime linker changed\n"
|
|
"and Helgrind needs to be ported properly. Giving up.\n");
|
|
tl_assert(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Intercepts for _ti_bind_guard() and _ti_bind_clear() functions from libc.
|
|
* These are intercepted during _ld_libc() call by identifying CI_BIND_GUARD
|
|
* and CI_BIND_CLEAR, to provide resilience against function renaming.
|
|
*/
|
|
static int _ti_bind_guard_intercept_WRK(int flags)
|
|
{
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT(_VG_USERREQ__HG_RTLD_BIND_GUARD,
|
|
flags, 0, 0, 0, 0);
|
|
return hg_rtld_bind_guard(flags);
|
|
}
|
|
|
|
static int _ti_bind_clear_intercept_WRK(int flags)
|
|
{
|
|
int ret = hg_rtld_bind_clear(flags);
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT(_VG_USERREQ__HG_RTLD_BIND_CLEAR,
|
|
flags, 0, 0, 0, 0);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Wrapped _ld_libc() from the runtime linker ld.so.1.
|
|
*/
|
|
void I_WRAP_SONAME_FNNAME_ZZ(VG_Z_LD_SO_1, ZuldZulibc)(vki_Lc_interface *ptr);
|
|
void I_WRAP_SONAME_FNNAME_ZZ(VG_Z_LD_SO_1, ZuldZulibc)(vki_Lc_interface *ptr)
|
|
{
|
|
OrigFn fn;
|
|
int tag;
|
|
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
vki_Lc_interface *funcs = ptr;
|
|
for (tag = funcs->ci_tag; tag != 0; tag = (++funcs)->ci_tag) {
|
|
switch (tag) {
|
|
case VKI_CI_BIND_GUARD:
|
|
if (funcs->vki_ci_un.ci_func != _ti_bind_guard_intercept_WRK) {
|
|
hg_rtld_bind_guard = funcs->vki_ci_un.ci_func;
|
|
funcs->vki_ci_un.ci_func = _ti_bind_guard_intercept_WRK;
|
|
}
|
|
break;
|
|
case VKI_CI_BIND_CLEAR:
|
|
if (funcs->vki_ci_un.ci_func != _ti_bind_clear_intercept_WRK) {
|
|
hg_rtld_bind_clear = funcs->vki_ci_un.ci_func;
|
|
funcs->vki_ci_un.ci_func = _ti_bind_clear_intercept_WRK;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
CALL_FN_v_W(fn, ptr);
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- pthread_create, pthread_join, pthread_exit ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
static void* mythread_wrapper ( void* xargsV )
|
|
{
|
|
volatile Word* xargs = (volatile Word*) xargsV;
|
|
void*(*fn)(void*) = (void*(*)(void*))xargs[0];
|
|
void* arg = (void*)xargs[1];
|
|
pthread_t me = pthread_self();
|
|
/* Tell the tool what my pthread_t is. */
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_SET_MY_PTHREAD_T, CREQ_PTHREAD_T, me);
|
|
/* allow the parent to proceed. We can't let it proceed until
|
|
we're ready because (1) we need to make sure it doesn't exit and
|
|
hence deallocate xargs[] while we still need it, and (2) we
|
|
don't want either parent nor child to proceed until the tool has
|
|
been notified of the child's pthread_t.
|
|
|
|
Note that parent and child access args[] without a lock,
|
|
effectively using args[2] as a spinlock in order to get the
|
|
parent to wait until the child passes this point. The parent
|
|
disables checking on xargs[] before creating the child and
|
|
re-enables it once the child goes past this point, so the user
|
|
never sees the race. The previous approach (suppressing the
|
|
resulting error) was flawed, because it could leave shadow
|
|
memory for args[] in a state in which subsequent use of it by
|
|
the parent would report further races. */
|
|
xargs[2] = 0;
|
|
/* Now we can no longer safely use xargs[]. */
|
|
return (void*) fn( (void*)arg );
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_create@GLIBC_2.0
|
|
// glibc: pthread_create@@GLIBC_2.1
|
|
// glibc: pthread_create@@GLIBC_2.2.5
|
|
// darwin: pthread_create
|
|
// darwin: pthread_create_suspended_np (trapped)
|
|
//
|
|
/* ensure this has its own frame, so as to make it more distinguishable
|
|
in suppressions */
|
|
__attribute__((noinline))
|
|
static int pthread_create_WRK(pthread_t *thread, const pthread_attr_t *attr,
|
|
void *(*start) (void *), void *arg)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
volatile Word xargs[3];
|
|
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_create wrapper"); fflush(stderr);
|
|
}
|
|
xargs[0] = (Word)start;
|
|
xargs[1] = (Word)arg;
|
|
xargs[2] = 1; /* serves as a spinlock -- sigh */
|
|
/* Disable checking on the spinlock and the two words used to
|
|
convey args to the child. Basically we need to make it appear
|
|
as if the child never accessed this area, since merely
|
|
suppressing the resulting races does not address the issue that
|
|
that piece of the parent's stack winds up in the "wrong" state
|
|
and therefore may give rise to mysterious races when the parent
|
|
comes to re-use this piece of stack in some other frame. */
|
|
VALGRIND_HG_DISABLE_CHECKING(&xargs, sizeof(xargs));
|
|
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT(_VG_USERREQ__HG_PTHREAD_CREATE_BEGIN,
|
|
0, 0, 0, 0, 0);
|
|
CALL_FN_W_WWWW(ret, fn, thread,attr,mythread_wrapper,&xargs[0]);
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT(_VG_USERREQ__HG_PTHREAD_CREATE_END,
|
|
0, 0, 0, 0, 0);
|
|
|
|
if (ret == 0) {
|
|
/* we have to wait for the child to notify the tool of its
|
|
pthread_t before continuing */
|
|
while (xargs[2] != 0) {
|
|
/* Do nothing. We need to spin until the child writes to
|
|
xargs[2]. However, that can lead to starvation in the
|
|
child and very long delays (eg, tc19_shadowmem on
|
|
ppc64-linux Fedora Core 6). So yield the cpu if we can,
|
|
to let the child run at the earliest available
|
|
opportunity. */
|
|
sched_yield();
|
|
}
|
|
} else {
|
|
DO_PthAPIerror( "pthread_create", ret );
|
|
}
|
|
|
|
/* Reenable checking on the area previously used to communicate
|
|
with the child. */
|
|
VALGRIND_HG_ENABLE_CHECKING(&xargs, sizeof(xargs));
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: pth_create -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZucreateZAZa, // pthread_create@*
|
|
pthread_t *thread, const pthread_attr_t *attr,
|
|
void *(*start) (void *), void *arg) {
|
|
return pthread_create_WRK(thread, attr, start, arg);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZucreate, // pthread_create
|
|
pthread_t *thread, const pthread_attr_t *attr,
|
|
void *(*start) (void *), void *arg) {
|
|
return pthread_create_WRK(thread, attr, start, arg);
|
|
}
|
|
PTH_FUNC(int, pthreadZucreateZuZa, // pthread_create_*
|
|
pthread_t *thread, const pthread_attr_t *attr,
|
|
void *(*start) (void *), void *arg) {
|
|
// trap anything else
|
|
assert(0);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZucreate, // pthread_create
|
|
pthread_t *thread, const pthread_attr_t *attr,
|
|
void *(*start) (void *), void *arg) {
|
|
return pthread_create_WRK(thread, attr, start, arg);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#if defined(VGO_solaris)
|
|
/* Solaris also provides thr_create() in addition to pthread_create().
|
|
* Both pthread_create(3C) and thr_create(3C) are based on private
|
|
* _thrp_create().
|
|
*/
|
|
__attribute__((noinline))
|
|
static int thr_create_WRK(void *stk, size_t stksize, void *(*start)(void *),
|
|
void *arg, long flags, thread_t *new_thread)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
volatile Word xargs[3];
|
|
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< thr_create wrapper"); fflush(stderr);
|
|
}
|
|
xargs[0] = (Word)start;
|
|
xargs[1] = (Word)arg;
|
|
xargs[2] = 1; /* serves as a spinlock -- sigh */
|
|
/* See comments in pthread_create_WRK() */
|
|
VALGRIND_HG_DISABLE_CHECKING(&xargs, sizeof(xargs));
|
|
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT(_VG_USERREQ__HG_PTHREAD_CREATE_BEGIN,
|
|
0, 0, 0, 0, 0);
|
|
CALL_FN_W_6W(ret, fn, stk, stksize, mythread_wrapper, start, flags,
|
|
new_thread);
|
|
VALGRIND_DO_CLIENT_REQUEST_STMT(_VG_USERREQ__HG_PTHREAD_CREATE_END,
|
|
0, 0, 0, 0, 0);
|
|
|
|
if (ret == 0) {
|
|
while (xargs[2] != 0) {
|
|
/* See comments in pthread_create_WRK(). */
|
|
sched_yield();
|
|
}
|
|
} else {
|
|
DO_PthAPIerror("thr_create", ret);
|
|
}
|
|
|
|
VALGRIND_HG_ENABLE_CHECKING(&xargs, sizeof(xargs));
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: thr_create -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
PTH_FUNC(int, thrZucreate, // thr_create
|
|
void *stk, size_t stksize, void *(*start)(void *),
|
|
void *arg, long flags, thread_t *new_thread) {
|
|
return thr_create_WRK(stk, stksize, start, arg, flags, new_thread);
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_join
|
|
// darwin: pthread_join
|
|
// darwin: pthread_join$NOCANCEL$UNIX2003
|
|
// darwin pthread_join$UNIX2003
|
|
__attribute__((noinline))
|
|
static int pthread_join_WRK(pthread_t thread, void** value_pointer)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_join wrapper"); fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WW(ret, fn, thread,value_pointer);
|
|
|
|
/* At least with NPTL as the thread library, this is safe because
|
|
it is guaranteed (by NPTL) that the joiner will completely gone
|
|
before pthread_join (the original) returns. See email below.*/
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_JOIN_POST, CREQ_PTHREAD_T, thread);
|
|
} else {
|
|
DO_PthAPIerror( "pthread_join", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: pth_join -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZujoin, // pthread_join
|
|
pthread_t thread, void** value_pointer) {
|
|
return pthread_join_WRK(thread, value_pointer);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZujoinZa, // pthread_join*
|
|
pthread_t thread, void** value_pointer) {
|
|
return pthread_join_WRK(thread, value_pointer);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZujoin, // pthread_join
|
|
pthread_t thread, void** value_pointer) {
|
|
return pthread_join_WRK(thread, value_pointer);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
/* Behaviour of pthread_join on NPTL:
|
|
|
|
Me:
|
|
I have a question re the NPTL pthread_join implementation.
|
|
|
|
Suppose I am the thread 'stayer'.
|
|
|
|
If I call pthread_join(quitter), is it guaranteed that the
|
|
thread 'quitter' has really exited before pthread_join returns?
|
|
|
|
IOW, is it guaranteed that 'quitter' will not execute any further
|
|
instructions after pthread_join returns?
|
|
|
|
I believe this is true based on the following analysis of
|
|
glibc-2.5 sources. However am not 100% sure and would appreciate
|
|
confirmation.
|
|
|
|
'quitter' will be running start_thread() in nptl/pthread_create.c
|
|
|
|
The last action of start_thread() is to exit via
|
|
__exit_thread_inline(0), which simply does sys_exit
|
|
(nptl/pthread_create.c:403)
|
|
|
|
'stayer' meanwhile is waiting for lll_wait_tid (pd->tid)
|
|
(call at nptl/pthread_join.c:89)
|
|
|
|
As per comment at nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h:536,
|
|
lll_wait_tid will not return until kernel notifies via futex
|
|
wakeup that 'quitter' has terminated.
|
|
|
|
Hence pthread_join cannot return until 'quitter' really has
|
|
completely disappeared.
|
|
|
|
Drepper:
|
|
> As per comment at nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h:536,
|
|
> lll_wait_tid will not return until kernel notifies via futex
|
|
> wakeup that 'quitter' has terminated.
|
|
That's the key. The kernel resets the TID field after the thread is
|
|
done. No way the joiner can return before the thread is gone.
|
|
*/
|
|
|
|
#if defined(VGO_solaris)
|
|
/* Solaris also provides thr_join() in addition to pthread_join().
|
|
* Both pthread_join(3C) and thr_join(3C) are based on private _thrp_join().
|
|
*
|
|
* :TODO: No functionality is currently provided for joinee == 0 and departed.
|
|
* This would require another client request, of course.
|
|
*/
|
|
__attribute__((noinline))
|
|
static int thr_join_WRK(thread_t joinee, thread_t *departed, void **thread_return)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< thr_join wrapper"); fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WWW(ret, fn, joinee, departed, thread_return);
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_JOIN_POST, CREQ_PTHREAD_T, joinee);
|
|
} else {
|
|
DO_PthAPIerror("thr_join", ret);
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: thr_join -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
PTH_FUNC(int, thrZujoin, // thr_join
|
|
thread_t joinee, thread_t *departed, void **thread_return) {
|
|
return thr_join_WRK(joinee, departed, thread_return);
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// Ada gcc gnat runtime:
|
|
// The gnat gcc Ada runtime does not use pthread_join. Instead, it uses
|
|
// a combination of other pthread primitives to ensure a child thread
|
|
// is gone. This combination is somewhat functionally equivalent to a
|
|
// pthread_join.
|
|
// We wrap two hook procedures called by the gnat gcc Ada runtime
|
|
// that allows helgrind to understand the semantic of Ada task dependencies
|
|
// and termination.
|
|
|
|
// System.Tasking.Debug.Master_Hook is called by a task Dependent to
|
|
// indicate that its master is identified by master+master_level.
|
|
void I_WRAP_SONAME_FNNAME_ZU
|
|
(Za,
|
|
system__tasking__debug__master_hook)
|
|
(void *dependent, void *master, int master_level);
|
|
void I_WRAP_SONAME_FNNAME_ZU
|
|
(Za,
|
|
system__tasking__debug__master_hook)
|
|
(void *dependent, void *master, int master_level)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_GNAT_FNS) {
|
|
fprintf(stderr, "<< GNAT master_hook wrapper "
|
|
"dependent %p master %p master_level %d\n",
|
|
dependent, master, master_level); fflush(stderr);
|
|
}
|
|
|
|
// We call the wrapped function, even if it is a null body.
|
|
CALL_FN_v_WWW(fn, dependent, master, master_level);
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_GNAT_MASTER_HOOK,
|
|
void*,dependent, void*,master,
|
|
Word, (Word)master_level);
|
|
|
|
if (TRACE_GNAT_FNS) {
|
|
fprintf(stderr, " :: GNAT master_hook >>\n");
|
|
}
|
|
}
|
|
|
|
// System.Tasking.Debug.Master_Completed_Hook is called by a task to
|
|
// indicate that it has completed a master.
|
|
// This indicates that all its Dependent tasks (that identified themselves
|
|
// with the Master_Hook call) are terminated. Helgrind can consider
|
|
// at this point that the equivalent of a 'pthread_join' has been done
|
|
// between self_id and all dependent tasks at master_level.
|
|
void I_WRAP_SONAME_FNNAME_ZU
|
|
(Za,
|
|
system__tasking__debug__master_completed_hook)
|
|
(void *self_id, int master_level);
|
|
void I_WRAP_SONAME_FNNAME_ZU
|
|
(Za,
|
|
system__tasking__debug__master_completed_hook)
|
|
(void *self_id, int master_level)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_GNAT_FNS) {
|
|
fprintf(stderr, "<< GNAT master_completed_hook wrapper "
|
|
"self_id %p master_level %d\n",
|
|
self_id, master_level); fflush(stderr);
|
|
}
|
|
|
|
// We call the wrapped function, even if it is a null body.
|
|
CALL_FN_v_WW(fn, self_id, master_level);
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_GNAT_MASTER_COMPLETED_HOOK,
|
|
void*,self_id, Word,(Word)master_level);
|
|
|
|
if (TRACE_GNAT_FNS) {
|
|
fprintf(stderr, " :: GNAT master_completed_hook >>\n");
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- pthread_mutex_t functions ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
/* Handled: pthread_mutex_init pthread_mutex_destroy
|
|
pthread_mutex_lock
|
|
pthread_mutex_trylock pthread_mutex_timedlock
|
|
pthread_mutex_unlock
|
|
*/
|
|
|
|
//-----------------------------------------------------------
|
|
#if !defined(VGO_solaris)
|
|
// glibc: pthread_mutex_init
|
|
// darwin: pthread_mutex_init
|
|
PTH_FUNC(int, pthreadZumutexZuinit, // pthread_mutex_init
|
|
pthread_mutex_t *mutex,
|
|
pthread_mutexattr_t* attr)
|
|
{
|
|
int ret;
|
|
long mbRec;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_mxinit %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
mbRec = 0;
|
|
if (attr) {
|
|
int ty, zzz;
|
|
zzz = pthread_mutexattr_gettype(attr, &ty);
|
|
if (zzz == 0 && ty == PTHREAD_MUTEX_RECURSIVE)
|
|
mbRec = 1;
|
|
}
|
|
|
|
CALL_FN_W_WW(ret, fn, mutex,attr);
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST,
|
|
pthread_mutex_t*,mutex, long,mbRec);
|
|
} else {
|
|
DO_PthAPIerror( "pthread_mutex_init", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: mxinit -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#else /* VGO_solaris */
|
|
|
|
// Solaris: mutex_init (pthread_mutex_init calls here)
|
|
PTH_FUNC(int, mutexZuinit, // mutex_init
|
|
mutex_t *mutex, int type, void *arg)
|
|
{
|
|
int ret;
|
|
long mbRec;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< mxinit %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
mbRec = ((type & LOCK_RECURSIVE) != 0) ? 1 : 0;
|
|
|
|
CALL_FN_W_WWW(ret, fn, mutex, type, arg);
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST,
|
|
mutex_t *, mutex, long, mbRec);
|
|
} else {
|
|
DO_PthAPIerror("mutex_init", ret);
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: mxinit -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_mutex_destroy
|
|
// darwin: pthread_mutex_destroy
|
|
// Solaris: mutex_destroy (pthread_mutex_destroy is a weak alias)
|
|
__attribute__((noinline))
|
|
static int mutex_destroy_WRK(pthread_mutex_t *mutex)
|
|
{
|
|
int ret;
|
|
unsigned long mutex_is_init;
|
|
OrigFn fn;
|
|
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_mxdestroy %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
if (mutex != NULL) {
|
|
static const pthread_mutex_t mutex_init = PTHREAD_MUTEX_INITIALIZER;
|
|
mutex_is_init = my_memcmp(mutex, &mutex_init, sizeof(*mutex)) == 0;
|
|
} else {
|
|
mutex_is_init = 0;
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE,
|
|
pthread_mutex_t*, mutex, unsigned long, mutex_is_init);
|
|
|
|
CALL_FN_W_W(ret, fn, mutex);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_mutex_destroy", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: mxdestroy -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if defined(VGO_linux) || defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZumutexZudestroy, // pthread_mutex_destroy
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_destroy_WRK(mutex);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, mutexZudestroy, // mutex_destroy
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_destroy_WRK(mutex);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_mutex_lock
|
|
// darwin: pthread_mutex_lock
|
|
// Solaris: mutex_lock (pthread_mutex_lock is a weak alias)
|
|
__attribute__((noinline))
|
|
static int mutex_lock_WRK(pthread_mutex_t *mutex)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_mxlock %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE,
|
|
pthread_mutex_t*,mutex, long,0/*!isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, mutex);
|
|
|
|
/* There's a hole here: libpthread now knows the lock is locked,
|
|
but the tool doesn't, so some other thread could run and detect
|
|
that the lock has been acquired by someone (this thread). Does
|
|
this matter? Not sure, but I don't think so. */
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
pthread_mutex_t *, mutex, long, (ret == 0) ? True : False);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_mutex_lock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: mxlock -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if defined(VGO_linux) || defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZumutexZulock, // pthread_mutex_lock
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_lock_WRK(mutex);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, mutexZulock, // mutex_lock
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_lock_WRK(mutex);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#if defined(VGO_solaris)
|
|
/* Internal to libc. Mutex is usually initialized only implicitly,
|
|
* by zeroing mutex_t structure.
|
|
*/
|
|
__attribute__((noinline))
|
|
PTH_FUNC(void, lmutexZulock, // lmutex_lock
|
|
mutex_t *mutex)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< lmxlock %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE,
|
|
mutex_t *, mutex, long, 0 /*!isTryLock*/);
|
|
CALL_FN_v_W(fn, mutex);
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
mutex_t *, mutex, long, True);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: lmxlock >>\n");
|
|
}
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_mutex_trylock
|
|
// darwin: pthread_mutex_trylock
|
|
// Solaris: mutex_trylock (pthread_mutex_trylock is a weak alias)
|
|
//
|
|
// pthread_mutex_trylock. The handling needed here is very similar
|
|
// to that for pthread_mutex_lock, except that we need to tell
|
|
// the pre-lock creq that this is a trylock-style operation, and
|
|
// therefore not to complain if the lock is nonrecursive and
|
|
// already locked by this thread -- because then it'll just fail
|
|
// immediately with EBUSY.
|
|
__attribute__((noinline))
|
|
static int mutex_trylock_WRK(pthread_mutex_t *mutex)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_mxtrylock %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE,
|
|
pthread_mutex_t*,mutex, long,1/*isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, mutex);
|
|
|
|
/* There's a hole here: libpthread now knows the lock is locked,
|
|
but the tool doesn't, so some other thread could run and detect
|
|
that the lock has been acquired by someone (this thread). Does
|
|
this matter? Not sure, but I don't think so. */
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
pthread_mutex_t *, mutex, long, (ret == 0) ? True : False);
|
|
|
|
if (ret != 0) {
|
|
if (ret != EBUSY)
|
|
DO_PthAPIerror( "pthread_mutex_trylock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: mxtrylock -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if defined(VGO_linux) || defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZumutexZutrylock, // pthread_mutex_trylock
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_trylock_WRK(mutex);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, mutexZutrylock, // mutex_trylock
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_trylock_WRK(mutex);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_mutex_timedlock
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_mutex_timedlock
|
|
//
|
|
// pthread_mutex_timedlock. Identical logic to pthread_mutex_trylock.
|
|
__attribute__((noinline))
|
|
static int mutex_timedlock_WRK(pthread_mutex_t *mutex,
|
|
void *timeout)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_mxtimedlock %p %p", mutex, timeout);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE,
|
|
pthread_mutex_t*,mutex, long,1/*isTryLock-ish*/);
|
|
|
|
CALL_FN_W_WW(ret, fn, mutex,timeout);
|
|
|
|
/* There's a hole here: libpthread now knows the lock is locked,
|
|
but the tool doesn't, so some other thread could run and detect
|
|
that the lock has been acquired by someone (this thread). Does
|
|
this matter? Not sure, but I don't think so. */
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
pthread_mutex_t *, mutex, long, (ret == 0) ? True : False);
|
|
|
|
if (ret != 0) {
|
|
if (ret != ETIMEDOUT)
|
|
DO_PthAPIerror( "pthread_mutex_timedlock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: mxtimedlock -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
PTH_FUNC(int, pthreadZumutexZutimedlock, // pthread_mutex_timedlock
|
|
pthread_mutex_t *mutex,
|
|
void *timeout) {
|
|
return mutex_timedlock_WRK(mutex, timeout);
|
|
}
|
|
#if defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZumutexZureltimedlock, // pthread_mutex_reltimedlock
|
|
pthread_mutex_t *mutex,
|
|
void *timeout) {
|
|
return mutex_timedlock_WRK(mutex, timeout);
|
|
}
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_mutex_unlock
|
|
// darwin: pthread_mutex_unlock
|
|
// Solaris: mutex_unlock (pthread_mutex_unlock is a weak alias)
|
|
__attribute__((noinline))
|
|
static int mutex_unlock_WRK(pthread_mutex_t *mutex)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_mxunlk %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE,
|
|
pthread_mutex_t*,mutex);
|
|
|
|
CALL_FN_W_W(ret, fn, mutex);
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST,
|
|
pthread_mutex_t*,mutex);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_mutex_unlock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " mxunlk -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if defined(VGO_linux) || defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZumutexZuunlock, // pthread_mutex_unlock
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_unlock_WRK(mutex);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, mutexZuunlock, // mutex_unlock
|
|
pthread_mutex_t *mutex) {
|
|
return mutex_unlock_WRK(mutex);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
#if defined(VGO_solaris)
|
|
/* Internal to libc. */
|
|
__attribute__((noinline))
|
|
PTH_FUNC(void, lmutexZuunlock, // lmutex_unlock
|
|
mutex_t *mutex)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< lmxunlk %p", mutex); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE,
|
|
mutex_t *, mutex);
|
|
CALL_FN_v_W(fn, mutex);
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST,
|
|
mutex_t*, mutex);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " lmxunlk >>\n");
|
|
}
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- pthread_cond_t functions ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
/* Handled: pthread_cond_wait pthread_cond_timedwait
|
|
pthread_cond_signal pthread_cond_broadcast
|
|
pthread_cond_init
|
|
pthread_cond_destroy
|
|
*/
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_cond_wait@GLIBC_2.2.5
|
|
// glibc: pthread_cond_wait@@GLIBC_2.3.2
|
|
// darwin: pthread_cond_wait
|
|
// darwin: pthread_cond_wait$NOCANCEL$UNIX2003
|
|
// darwin: pthread_cond_wait$UNIX2003
|
|
// Solaris: cond_wait (pthread_cond_wait is built atop of cond_wait)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_cond_wait_WRK(pthread_cond_t* cond,
|
|
pthread_mutex_t* mutex)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
unsigned long mutex_is_valid;
|
|
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_cond_wait %p %p", cond, mutex);
|
|
fflush(stderr);
|
|
}
|
|
|
|
/* Tell the tool a cond-wait is about to happen, so it can check
|
|
for bogus argument values. In return it tells us whether it
|
|
thinks the mutex is valid or not. */
|
|
DO_CREQ_W_WW(mutex_is_valid,
|
|
_VG_USERREQ__HG_PTHREAD_COND_WAIT_PRE,
|
|
pthread_cond_t*,cond, pthread_mutex_t*,mutex);
|
|
assert(mutex_is_valid == 1 || mutex_is_valid == 0);
|
|
|
|
/* Tell the tool we're about to drop the mutex. This reflects the
|
|
fact that in a cond_wait, we show up holding the mutex, and the
|
|
call atomically drops the mutex and waits for the cv to be
|
|
signalled. */
|
|
if (mutex_is_valid) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE,
|
|
pthread_mutex_t*,mutex);
|
|
}
|
|
|
|
CALL_FN_W_WW(ret, fn, cond,mutex);
|
|
|
|
/* this conditional look stupid, but compare w/ same logic for
|
|
pthread_cond_timedwait below */
|
|
if (mutex_is_valid) {
|
|
/* and now we have the mutex again if (ret == 0) */
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
pthread_mutex_t *, mutex, long, (ret == 0) ? True : False);
|
|
}
|
|
|
|
DO_CREQ_v_WWWW(_VG_USERREQ__HG_PTHREAD_COND_WAIT_POST,
|
|
pthread_cond_t*,cond, pthread_mutex_t*,mutex, long,0,
|
|
long, (ret == 0 && mutex_is_valid) ? True : False);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_cond_wait", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " cowait -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZucondZuwaitZAZa, // pthread_cond_wait@*
|
|
pthread_cond_t* cond, pthread_mutex_t* mutex) {
|
|
return pthread_cond_wait_WRK(cond, mutex);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZucondZuwaitZa, // pthread_cond_wait*
|
|
pthread_cond_t* cond, pthread_mutex_t* mutex) {
|
|
return pthread_cond_wait_WRK(cond, mutex);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, condZuwait, // cond_wait
|
|
pthread_cond_t *cond, pthread_mutex_t *mutex) {
|
|
return pthread_cond_wait_WRK(cond, mutex);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_cond_timedwait@@GLIBC_2.3.2
|
|
// glibc: pthread_cond_timedwait@GLIBC_2.2.5
|
|
// glibc: pthread_cond_timedwait@GLIBC_2.0
|
|
// darwin: pthread_cond_timedwait
|
|
// darwin: pthread_cond_timedwait$NOCANCEL$UNIX2003
|
|
// darwin: pthread_cond_timedwait$UNIX2003
|
|
// darwin: pthread_cond_timedwait_relative_np (trapped)
|
|
// Solaris: cond_timedwait (pthread_cond_timedwait is built on cond_timedwait)
|
|
// Solaris: cond_reltimedwait (pthread_cond_reltimedwait_np is built on this)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_cond_timedwait_WRK(pthread_cond_t* cond,
|
|
pthread_mutex_t* mutex,
|
|
struct timespec* abstime,
|
|
int timeout_error)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
unsigned long mutex_is_valid;
|
|
Bool abstime_is_valid;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_cond_timedwait %p %p %p",
|
|
cond, mutex, abstime);
|
|
fflush(stderr);
|
|
}
|
|
|
|
/* Tell the tool a cond-wait is about to happen, so it can check
|
|
for bogus argument values. In return it tells us whether it
|
|
thinks the mutex is valid or not. */
|
|
DO_CREQ_W_WW(mutex_is_valid,
|
|
_VG_USERREQ__HG_PTHREAD_COND_WAIT_PRE,
|
|
pthread_cond_t*,cond, pthread_mutex_t*,mutex);
|
|
assert(mutex_is_valid == 1 || mutex_is_valid == 0);
|
|
|
|
abstime_is_valid = abstime->tv_nsec >= 0 && abstime->tv_nsec < 1000000000;
|
|
|
|
/* Tell the tool we're about to drop the mutex. This reflects the
|
|
fact that in a cond_wait, we show up holding the mutex, and the
|
|
call atomically drops the mutex and waits for the cv to be
|
|
signalled. */
|
|
if (mutex_is_valid && abstime_is_valid) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE,
|
|
pthread_mutex_t*,mutex);
|
|
}
|
|
|
|
CALL_FN_W_WWW(ret, fn, cond,mutex,abstime);
|
|
|
|
if (mutex_is_valid && !abstime_is_valid && ret != EINVAL) {
|
|
DO_PthAPIerror("Bug in libpthread: pthread_cond_timedwait "
|
|
"invalid abstime did not cause"
|
|
" EINVAL", ret);
|
|
}
|
|
|
|
if (mutex_is_valid && abstime_is_valid) {
|
|
/* and now we have the mutex again if (ret == 0 || ret == timeout) */
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
pthread_mutex_t *, mutex,
|
|
long, (ret == 0 || ret == timeout_error) ? True : False);
|
|
}
|
|
|
|
DO_CREQ_v_WWWW(_VG_USERREQ__HG_PTHREAD_COND_WAIT_POST,
|
|
pthread_cond_t*,cond, pthread_mutex_t*,mutex,
|
|
long,ret == timeout_error,
|
|
long, (ret == 0 || ret == timeout_error) && mutex_is_valid
|
|
? True : False);
|
|
|
|
if (ret != 0 && ret != timeout_error) {
|
|
DO_PthAPIerror( "pthread_cond_timedwait", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " cotimedwait -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZucondZutimedwaitZAZa, // pthread_cond_timedwait@*
|
|
pthread_cond_t* cond, pthread_mutex_t* mutex,
|
|
struct timespec* abstime) {
|
|
return pthread_cond_timedwait_WRK(cond, mutex, abstime, ETIMEDOUT);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZucondZutimedwait, // pthread_cond_timedwait
|
|
pthread_cond_t* cond, pthread_mutex_t* mutex,
|
|
struct timespec* abstime) {
|
|
return pthread_cond_timedwait_WRK(cond, mutex, abstime, ETIMEDOUT);
|
|
}
|
|
PTH_FUNC(int, pthreadZucondZutimedwaitZDZa, // pthread_cond_timedwait$*
|
|
pthread_cond_t* cond, pthread_mutex_t* mutex,
|
|
struct timespec* abstime) {
|
|
return pthread_cond_timedwait_WRK(cond, mutex, abstime, ETIMEDOUT);
|
|
}
|
|
PTH_FUNC(int, pthreadZucondZutimedwaitZuZa, // pthread_cond_timedwait_*
|
|
pthread_cond_t* cond, pthread_mutex_t* mutex,
|
|
struct timespec* abstime) {
|
|
assert(0);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, condZutimedwait, // cond_timedwait
|
|
pthread_cond_t *cond, pthread_mutex_t *mutex,
|
|
struct timespec *abstime) {
|
|
return pthread_cond_timedwait_WRK(cond, mutex, abstime, ETIME);
|
|
}
|
|
PTH_FUNC(int, condZureltimedwait, // cond_reltimedwait
|
|
pthread_cond_t *cond, pthread_mutex_t *mutex,
|
|
struct timespec *reltime) {
|
|
return pthread_cond_timedwait_WRK(cond, mutex, reltime, ETIME);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_cond_signal@GLIBC_2.0
|
|
// glibc: pthread_cond_signal@GLIBC_2.2.5
|
|
// glibc: pthread_cond_signal@@GLIBC_2.3.2
|
|
// darwin: pthread_cond_signal
|
|
// darwin: pthread_cond_signal_thread_np (don't intercept this)
|
|
// Solaris: cond_signal (pthread_cond_signal is a weak alias)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_cond_signal_WRK(pthread_cond_t* cond)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_cond_signal %p", cond);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_COND_SIGNAL_PRE,
|
|
pthread_cond_t*,cond);
|
|
|
|
CALL_FN_W_W(ret, fn, cond);
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_COND_SIGNAL_POST,
|
|
pthread_cond_t*,cond);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_cond_signal", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " cosig -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZucondZusignalZAZa, // pthread_cond_signal@*
|
|
pthread_cond_t* cond) {
|
|
return pthread_cond_signal_WRK(cond);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZucondZusignal, // pthread_cond_signal
|
|
pthread_cond_t* cond) {
|
|
return pthread_cond_signal_WRK(cond);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, condZusignal, // cond_signal
|
|
pthread_cond_t *cond) {
|
|
return pthread_cond_signal_WRK(cond);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_cond_broadcast@GLIBC_2.0
|
|
// glibc: pthread_cond_broadcast@GLIBC_2.2.5
|
|
// glibc: pthread_cond_broadcast@@GLIBC_2.3.2
|
|
// darwin: pthread_cond_broadcast
|
|
// Solaris: cond_broadcast (pthread_cond_broadcast is a weak alias)
|
|
//
|
|
// Note, this is pretty much identical, from a dependency-graph
|
|
// point of view, with cond_signal, so the code is duplicated.
|
|
// Maybe it should be commoned up.
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_cond_broadcast_WRK(pthread_cond_t* cond)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_cond_broadcast %p", cond);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_COND_BROADCAST_PRE,
|
|
pthread_cond_t*,cond);
|
|
|
|
CALL_FN_W_W(ret, fn, cond);
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_COND_BROADCAST_POST,
|
|
pthread_cond_t*,cond);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_cond_broadcast", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " cobro -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZucondZubroadcastZAZa, // pthread_cond_broadcast@*
|
|
pthread_cond_t* cond) {
|
|
return pthread_cond_broadcast_WRK(cond);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZucondZubroadcast, // pthread_cond_broadcast
|
|
pthread_cond_t* cond) {
|
|
return pthread_cond_broadcast_WRK(cond);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, condZubroadcast, // cond_broadcast
|
|
pthread_cond_t *cond) {
|
|
return pthread_cond_broadcast_WRK(cond);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
// glibc: pthread_cond_init@GLIBC_2.0
|
|
// glibc: pthread_cond_init@GLIBC_2.2.5
|
|
// glibc: pthread_cond_init@@GLIBC_2.3.2
|
|
// darwin: pthread_cond_init
|
|
// Solaris: cond_init (pthread_cond_init is built atop on this function)
|
|
// Easy way out: Handling of attr could have been messier.
|
|
// It turns out that pthread_cond_init under linux ignores
|
|
// all information in cond_attr, so do we.
|
|
// FIXME: MacOS X?
|
|
#if !defined(VGO_solaris)
|
|
__attribute__((noinline))
|
|
static int pthread_cond_init_WRK(pthread_cond_t* cond, pthread_condattr_t *cond_attr)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_cond_init %p", cond);
|
|
fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WW(ret, fn, cond, cond_attr);
|
|
|
|
if (ret == 0) {
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_COND_INIT_POST,
|
|
pthread_cond_t*,cond, pthread_condattr_t*, cond_attr);
|
|
} else {
|
|
DO_PthAPIerror( "pthread_cond_init", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " coinit -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZucondZuinitZAZa, // pthread_cond_init@*
|
|
pthread_cond_t* cond, pthread_condattr_t* cond_attr) {
|
|
return pthread_cond_init_WRK(cond, cond_attr);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZucondZuinit, // pthread_cond_init
|
|
pthread_cond_t* cond, pthread_condattr_t * cond_attr) {
|
|
return pthread_cond_init_WRK(cond, cond_attr);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#else /* VGO_solaris */
|
|
__attribute__((noinline))
|
|
PTH_FUNC(int, condZuinit, // cond_init
|
|
cond_t *cond, int type, void *arg)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< cond_init %p", cond); fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WWW(ret, fn, cond, type, arg);
|
|
|
|
if (ret == 0) {
|
|
/* Luckily evh__HG_PTHREAD_COND_INIT_POST() ignores cond_attr.
|
|
See also comment for pthread_cond_init_WRK(). */
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_COND_INIT_POST,
|
|
cond_t *, cond, void *, NULL);
|
|
} else {
|
|
DO_PthAPIerror("cond_init", ret);
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " cond_init -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_cond_destroy@@GLIBC_2.3.2
|
|
// glibc: pthread_cond_destroy@GLIBC_2.2.5
|
|
// glibc: pthread_cond_destroy@GLIBC_2.0
|
|
// darwin: pthread_cond_destroy
|
|
// Solaris: cond_destroy (pthread_cond_destroy is a weak alias)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_cond_destroy_WRK(pthread_cond_t* cond)
|
|
{
|
|
int ret;
|
|
unsigned long cond_is_init;
|
|
OrigFn fn;
|
|
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_cond_destroy %p", cond);
|
|
fflush(stderr);
|
|
}
|
|
|
|
if (cond != NULL) {
|
|
const pthread_cond_t cond_init = PTHREAD_COND_INITIALIZER;
|
|
cond_is_init = my_memcmp(cond, &cond_init, sizeof(*cond)) == 0;
|
|
} else {
|
|
cond_is_init = 0;
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_COND_DESTROY_PRE,
|
|
pthread_cond_t*, cond, unsigned long, cond_is_init);
|
|
|
|
CALL_FN_W_W(ret, fn, cond);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_cond_destroy", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " codestr -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZucondZudestroyZAZa, // pthread_cond_destroy@*
|
|
pthread_cond_t* cond) {
|
|
return pthread_cond_destroy_WRK(cond);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZucondZudestroy, // pthread_cond_destroy
|
|
pthread_cond_t* cond) {
|
|
return pthread_cond_destroy_WRK(cond);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, condZudestroy, // cond_destroy
|
|
pthread_cond_t *cond) {
|
|
return pthread_cond_destroy_WRK(cond);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- pthread_barrier_t functions ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
#if defined(HAVE_PTHREAD_BARRIER_INIT)
|
|
|
|
/* Handled: pthread_barrier_init
|
|
pthread_barrier_wait
|
|
pthread_barrier_destroy
|
|
|
|
Unhandled: pthread_barrierattr_destroy
|
|
pthread_barrierattr_getpshared
|
|
pthread_barrierattr_init
|
|
pthread_barrierattr_setpshared
|
|
-- are these important?
|
|
*/
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_barrier_init
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_barrier_init
|
|
PTH_FUNC(int, pthreadZubarrierZuinit, // pthread_barrier_init
|
|
pthread_barrier_t* bar,
|
|
pthread_barrierattr_t* attr, unsigned long count)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_barrier_init %p %p %lu",
|
|
bar, attr, count);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE,
|
|
pthread_barrier_t*, bar,
|
|
unsigned long, count,
|
|
unsigned long, 0/*!resizable*/);
|
|
|
|
CALL_FN_W_WWW(ret, fn, bar,attr,count);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_barrier_init", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " pthread_barrier_init -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_barrier_wait
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_barrier_wait
|
|
PTH_FUNC(int, pthreadZubarrierZuwait, // pthread_barrier_wait
|
|
pthread_barrier_t* bar)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_barrier_wait %p", bar);
|
|
fflush(stderr);
|
|
}
|
|
|
|
/* That this works correctly, and doesn't screw up when a thread
|
|
leaving the barrier races round to the front and re-enters while
|
|
other threads are still leaving it, is quite subtle. See
|
|
comments in the handler for PTHREAD_BARRIER_WAIT_PRE in
|
|
hg_main.c. */
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE,
|
|
pthread_barrier_t*,bar);
|
|
|
|
CALL_FN_W_W(ret, fn, bar);
|
|
|
|
if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) {
|
|
DO_PthAPIerror( "pthread_barrier_wait", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " pthread_barrier_wait -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_barrier_destroy
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_barrier_destroy
|
|
PTH_FUNC(int, pthreadZubarrierZudestroy, // pthread_barrier_destroy
|
|
pthread_barrier_t* bar)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_barrier_destroy %p", bar);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE,
|
|
pthread_barrier_t*,bar);
|
|
|
|
CALL_FN_W_W(ret, fn, bar);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_barrier_destroy", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " pthread_barrier_destroy -> %d >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif // defined(HAVE_PTHREAD_BARRIER_INIT)
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- pthread_spinlock_t functions ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
#if defined(HAVE_PTHREAD_SPIN_LOCK) \
|
|
&& !defined(DISABLE_PTHREAD_SPINLOCK_INTERCEPT)
|
|
|
|
/* Handled: pthread_spin_init pthread_spin_destroy
|
|
pthread_spin_lock pthread_spin_trylock
|
|
pthread_spin_unlock
|
|
|
|
Unhandled:
|
|
*/
|
|
|
|
/* This is a nasty kludge, in that glibc "knows" that initialising a
|
|
spin lock unlocks it, and pthread_spin_{init,unlock} are names for
|
|
the same function. Hence we have to have a wrapper which does both
|
|
things, without knowing which the user intended to happen.
|
|
Solaris has distinct functions for init/unlock but client requests
|
|
are immutable in helgrind.h so follow the glibc lead. */
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_spin_init
|
|
// glibc: pthread_spin_unlock
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_spin_init
|
|
// Solaris: pthread_spin_unlock
|
|
__attribute__((noinline))
|
|
static int pthread_spin_init_or_unlock_WRK(pthread_spinlock_t* lock,
|
|
int pshared) {
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_spin_iORu %p", lock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE,
|
|
pthread_spinlock_t*, lock);
|
|
|
|
CALL_FN_W_WW(ret, fn, lock,pshared);
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST,
|
|
pthread_spinlock_t*,lock);
|
|
} else {
|
|
DO_PthAPIerror( "pthread_spinlock_{init,unlock}", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: spiniORu -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZuspinZuinit, // pthread_spin_init
|
|
pthread_spinlock_t* lock, int pshared) {
|
|
return pthread_spin_init_or_unlock_WRK(lock, pshared);
|
|
}
|
|
PTH_FUNC(int, pthreadZuspinZuunlock, // pthread_spin_unlock
|
|
pthread_spinlock_t* lock) {
|
|
/* this is never actually called */
|
|
return pthread_spin_init_or_unlock_WRK(lock, 0/*pshared*/);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZuspinZuinit, // pthread_spin_init
|
|
pthread_spinlock_t *lock, int pshared) {
|
|
return pthread_spin_init_or_unlock_WRK(lock, pshared);
|
|
}
|
|
PTH_FUNC(int, pthreadZuspinZuunlock, // pthread_spin_unlock
|
|
pthread_spinlock_t *lock) {
|
|
return pthread_spin_init_or_unlock_WRK(lock, 0/*pshared*/);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_spin_destroy
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_spin_destroy
|
|
__attribute__((noinline))
|
|
static int pthread_spin_destroy_WRK(pthread_spinlock_t *lock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_spin_destroy %p", lock);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_DESTROY_PRE,
|
|
pthread_spinlock_t*,lock);
|
|
|
|
CALL_FN_W_W(ret, fn, lock);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_spin_destroy", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: spindestroy -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZuspinZusdestroy, // pthread_spin_destroy
|
|
pthread_spinlock_t *lock) {
|
|
return pthread_spin_destroy_WRK(lock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZuspinZusdestroy, // pthread_spin_destroy
|
|
pthread_spinlock_t *lock) {
|
|
return pthread_spin_destroy_WRK(lock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_spin_lock
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_spin_lock
|
|
__attribute__((noinline))
|
|
static int pthread_spin_lock_WRK(pthread_spinlock_t *lock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_spinlock %p", lock);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE,
|
|
pthread_spinlock_t*,lock, long,0/*!isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, lock);
|
|
|
|
/* There's a hole here: libpthread now knows the lock is locked,
|
|
but the tool doesn't, so some other thread could run and detect
|
|
that the lock has been acquired by someone (this thread). Does
|
|
this matter? Not sure, but I don't think so. */
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST,
|
|
pthread_spinlock_t*,lock);
|
|
} else {
|
|
DO_PthAPIerror( "pthread_spin_lock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: spinlock -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZuspinZulock, // pthread_spin_lock
|
|
pthread_spinlock_t *lock) {
|
|
return pthread_spin_lock_WRK(lock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZuspinZulock, // pthread_spin_lock
|
|
pthread_spinlock_t *lock) {
|
|
return pthread_spin_lock_WRK(lock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_spin_trylock
|
|
// darwin: (doesn't appear to exist)
|
|
// Solaris: pthread_spin_trylock
|
|
__attribute__((noinline))
|
|
static int pthread_spin_trylock_WRK(pthread_spinlock_t *lock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_spin_trylock %p", lock);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE,
|
|
pthread_spinlock_t*,lock, long,1/*isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, lock);
|
|
|
|
/* There's a hole here: libpthread now knows the lock is locked,
|
|
but the tool doesn't, so some other thread could run and detect
|
|
that the lock has been acquired by someone (this thread). Does
|
|
this matter? Not sure, but I don't think so. */
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST,
|
|
pthread_spinlock_t*,lock);
|
|
} else {
|
|
if (ret != EBUSY)
|
|
DO_PthAPIerror( "pthread_spin_trylock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: spin_trylock -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZuspinZutrylock, // pthread_spin_trylock
|
|
pthread_spinlock_t *lock) {
|
|
return pthread_spin_trylock_WRK(lock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZuspinZutrylock, // pthread_spin_trylock
|
|
pthread_spinlock_t *lock) {
|
|
return pthread_spin_trylock_WRK(lock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#endif // defined(HAVE_PTHREAD_SPIN_LOCK)
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- pthread_rwlock_t functions ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
/* Android's pthread.h doesn't say anything about rwlocks, hence these
|
|
functions have to be conditionally compiled. */
|
|
#if defined(HAVE_PTHREAD_RWLOCK_T)
|
|
|
|
/* Handled: pthread_rwlock_init pthread_rwlock_destroy
|
|
pthread_rwlock_rdlock
|
|
pthread_rwlock_wrlock
|
|
pthread_rwlock_unlock
|
|
pthread_rwlock_tryrdlock
|
|
pthread_rwlock_trywrlock
|
|
|
|
Unhandled: pthread_rwlock_timedrdlock
|
|
pthread_rwlock_timedwrlock
|
|
*/
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_rwlock_init
|
|
// darwin: pthread_rwlock_init
|
|
// darwin: pthread_rwlock_init$UNIX2003
|
|
// Solaris: rwlock_init (pthread_rwlock_init is built atop of rwlock_init)
|
|
__attribute__((noinline))
|
|
static int pthread_rwlock_init_WRK(pthread_rwlock_t *rwl,
|
|
pthread_rwlockattr_t* attr)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_init %p", rwl); fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WW(ret, fn, rwl,attr);
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST,
|
|
pthread_rwlock_t*,rwl);
|
|
} else {
|
|
DO_PthAPIerror( "pthread_rwlock_init", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_init -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZurwlockZuinit, // pthread_rwlock_init
|
|
pthread_rwlock_t *rwl,
|
|
pthread_rwlockattr_t* attr) {
|
|
return pthread_rwlock_init_WRK(rwl, attr);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZurwlockZuinitZa, // pthread_rwlock_init*
|
|
pthread_rwlock_t *rwl,
|
|
pthread_rwlockattr_t* attr) {
|
|
return pthread_rwlock_init_WRK(rwl, attr);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
static int pthread_rwlock_init_WRK(pthread_rwlock_t *rwl,
|
|
pthread_rwlockattr_t* attr)
|
|
__attribute__((unused));
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#if defined(VGO_solaris)
|
|
PTH_FUNC(int, rwlockZuinit, // rwlock_init
|
|
rwlock_t *rwlock,
|
|
int type,
|
|
void *arg)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< rwl_init %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WWW(ret, fn, rwlock, type, arg);
|
|
|
|
if (ret == 0 /*success*/) {
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST,
|
|
rwlock_t *, rwlock);
|
|
} else {
|
|
DO_PthAPIerror("rwlock_init", ret);
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_init -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_rwlock_destroy
|
|
// darwin: pthread_rwlock_destroy
|
|
// darwin: pthread_rwlock_destroy$UNIX2003
|
|
// Solaris: rwlock_destroy (pthread_rwlock_destroy is a weak alias)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_rwlock_destroy_WRK(pthread_rwlock_t* rwl)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_destroy %p", rwl); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE,
|
|
pthread_rwlock_t*,rwl);
|
|
|
|
CALL_FN_W_W(ret, fn, rwl);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_rwlock_destroy", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_destroy -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZurwlockZudestroy, // pthread_rwlock_destroy
|
|
pthread_rwlock_t *rwl) {
|
|
return pthread_rwlock_destroy_WRK(rwl);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZurwlockZudestroyZa, // pthread_rwlock_destroy*
|
|
pthread_rwlock_t *rwl) {
|
|
return pthread_rwlock_destroy_WRK(rwl);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, rwlockZudestroy, // rwlock_destroy
|
|
pthread_rwlock_t *rwl) {
|
|
return pthread_rwlock_destroy_WRK(rwl);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_rwlock_wrlock
|
|
// darwin: pthread_rwlock_wrlock
|
|
// darwin: pthread_rwlock_wrlock$UNIX2003
|
|
// Solaris: rw_wrlock (pthread_rwlock_wrlock is a weak alias)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_rwlock_wrlock_WRK(pthread_rwlock_t* rwlock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_wlk %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t*,rwlock,
|
|
long,1/*isW*/, long,0/*!isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, rwlock);
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t*,rwlock, long,1/*isW*/,
|
|
long, (ret == 0) ? True : False);
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_rwlock_wrlock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_wlk -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZurwlockZuwrlock, // pthread_rwlock_wrlock
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_wrlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZurwlockZuwrlockZa, // pthread_rwlock_wrlock*
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_wrlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, rwZuwrlock, // rw_wrlock
|
|
pthread_rwlock_t *rwlock) {
|
|
return pthread_rwlock_wrlock_WRK(rwlock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#if defined(VGO_solaris)
|
|
/* Internal to libc. */
|
|
PTH_FUNC(void, lrwZuwrlock, // lrw_wrlock
|
|
rwlock_t *rwlock)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< lrw_wlk %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t *, rwlock,
|
|
long, 1/*isW*/, long, 0/*!isTryLock*/);
|
|
|
|
CALL_FN_v_W(fn, rwlock);
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t *, rwlock, long, 1/*isW*/, long, True);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: lrw_wlk >>\n");
|
|
}
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_rwlock_rdlock
|
|
// darwin: pthread_rwlock_rdlock
|
|
// darwin: pthread_rwlock_rdlock$UNIX2003
|
|
// Solaris: rw_rdlock (pthread_rwlock_rdlock is a weak alias)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_rwlock_rdlock_WRK(pthread_rwlock_t* rwlock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_rlk %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t*,rwlock,
|
|
long,0/*!isW*/, long,0/*!isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, rwlock);
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t*,rwlock, long,0/*!isW*/,
|
|
long, (ret == 0) ? True : False);
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_rwlock_rdlock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_rlk -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZurwlockZurdlock, // pthread_rwlock_rdlock
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_rdlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZurwlockZurdlockZa, // pthread_rwlock_rdlock*
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_rdlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, rwZurdlock, // rw_rdlock
|
|
pthread_rwlock_t *rwlock) {
|
|
return pthread_rwlock_rdlock_WRK(rwlock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#if defined(VGO_solaris)
|
|
/* Internal to libc. */
|
|
PTH_FUNC(void, lrwZurdlock, // lrw_rdlock
|
|
rwlock_t *rwlock)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< lrw_rlk %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t *, rwlock,
|
|
long, 0/*!isW*/, long, 0/*!isTryLock*/);
|
|
|
|
CALL_FN_v_W(fn, rwlock);
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t *, rwlock, long, 0/*!isW*/, long, True);
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: lrw_rlk ->>\n");
|
|
}
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_rwlock_trywrlock
|
|
// darwin: pthread_rwlock_trywrlock
|
|
// darwin: pthread_rwlock_trywrlock$UNIX2003
|
|
// Solaris: rw_trywrlock (pthread_rwlock_trywrlock is a weak alias)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_rwlock_trywrlock_WRK(pthread_rwlock_t* rwlock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_trywlk %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t*,rwlock,
|
|
long,1/*isW*/, long,1/*isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, rwlock);
|
|
|
|
/* There's a hole here: libpthread now knows the lock is locked,
|
|
but the tool doesn't, so some other thread could run and detect
|
|
that the lock has been acquired by someone (this thread). Does
|
|
this matter? Not sure, but I don't think so. */
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t*,rwlock, long,1/*isW*/,
|
|
long, (ret == 0) ? True : False);
|
|
if (ret != 0) {
|
|
if (ret != EBUSY)
|
|
DO_PthAPIerror( "pthread_rwlock_trywrlock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_trywlk -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZurwlockZutrywrlock, // pthread_rwlock_trywrlock
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_trywrlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZurwlockZutrywrlockZa, // pthread_rwlock_trywrlock*
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_trywrlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, rwZutrywrlock, // rw_trywrlock
|
|
pthread_rwlock_t *rwlock) {
|
|
return pthread_rwlock_trywrlock_WRK(rwlock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_rwlock_tryrdlock
|
|
// darwin: pthread_rwlock_tryrdlock
|
|
// darwin: pthread_rwlock_tryrdlock$UNIX2003
|
|
// Solaris: rw_tryrdlock (pthread_rwlock_tryrdlock is a weak alias)
|
|
//
|
|
__attribute__((noinline))
|
|
static int pthread_rwlock_tryrdlock_WRK(pthread_rwlock_t* rwlock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_tryrlk %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t*,rwlock,
|
|
long,0/*!isW*/, long,1/*isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, rwlock);
|
|
|
|
/* There's a hole here: libpthread now knows the lock is locked,
|
|
but the tool doesn't, so some other thread could run and detect
|
|
that the lock has been acquired by someone (this thread). Does
|
|
this matter? Not sure, but I don't think so. */
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t*,rwlock, long,0/*!isW*/,
|
|
long, (ret == 0) ? True : False);
|
|
|
|
if (ret != 0) {
|
|
if (ret != EBUSY)
|
|
DO_PthAPIerror( "pthread_rwlock_tryrdlock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_tryrlk -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZurwlockZutryrdlock, // pthread_rwlock_tryrdlock
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_tryrdlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZurwlockZutryrdlockZa, // pthread_rwlock_tryrdlock*
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_tryrdlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, rwZutryrdlock, // rw_tryrdlock
|
|
pthread_rwlock_t *rwlock) {
|
|
return pthread_rwlock_tryrdlock_WRK(rwlock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: Unhandled
|
|
// darwin: Unhandled
|
|
// Solaris: pthread_rwlock_timedrdlock
|
|
// Solaris: pthread_rwlock_reltimedrdlock_np
|
|
//
|
|
__attribute__((noinline)) __attribute__((unused))
|
|
static int pthread_rwlock_timedrdlock_WRK(pthread_rwlock_t *rwlock,
|
|
const struct timespec *timeout)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_timedrdl %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t *, rwlock,
|
|
long, 0/*isW*/, long, 0/*isTryLock*/);
|
|
|
|
CALL_FN_W_WW(ret, fn, rwlock, timeout);
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t *, rwlock, long, 0/*isW*/,
|
|
long, (ret == 0) ? True : False);
|
|
if (ret != 0) {
|
|
DO_PthAPIerror("pthread_rwlock_timedrdlock", ret);
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_timedrdl -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
#elif defined(VGO_darwin)
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZurwlockZutimedrdlock, // pthread_rwlock_timedrdlock
|
|
pthread_rwlock_t *rwlock,
|
|
const struct timespec *timeout) {
|
|
return pthread_rwlock_timedrdlock_WRK(rwlock, timeout);
|
|
}
|
|
PTH_FUNC(int, pthreadZurwlockZureltimedrdlockZunp, // pthread_rwlock_timedrdlock_np
|
|
pthread_rwlock_t *rwlock,
|
|
const struct timespec *timeout) {
|
|
return pthread_rwlock_timedrdlock_WRK(rwlock, timeout);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: Unhandled
|
|
// darwin: Unhandled
|
|
// Solaris: pthread_rwlock_timedwrlock
|
|
// Solaris: pthread_rwlock_reltimedwrlock_np
|
|
//
|
|
__attribute__((noinline)) __attribute__((unused))
|
|
static int pthread_rwlock_timedwrlock_WRK(pthread_rwlock_t *rwlock,
|
|
const struct timespec *timeout)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_timedwrl %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
pthread_rwlock_t *, rwlock,
|
|
long, 1/*isW*/, long, 0/*isTryLock*/);
|
|
|
|
CALL_FN_W_WW(ret, fn, rwlock, timeout);
|
|
|
|
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
pthread_rwlock_t *, rwlock, long, 1/*isW*/,
|
|
long, (ret == 0) ? True : False);
|
|
if (ret != 0) {
|
|
DO_PthAPIerror("pthread_rwlock_timedwrlock", ret);
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_timedwrl -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
#elif defined(VGO_darwin)
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, pthreadZurwlockZutimedwrlock, // pthread_rwlock_timedwrlock
|
|
pthread_rwlock_t *rwlock,
|
|
const struct timespec *timeout) {
|
|
return pthread_rwlock_timedwrlock_WRK(rwlock, timeout);
|
|
}
|
|
PTH_FUNC(int, pthreadZurwlockZureltimedwrlockZunp, // pthread_rwlock_timedwrlock_np
|
|
pthread_rwlock_t *rwlock,
|
|
const struct timespec *timeout) {
|
|
return pthread_rwlock_timedwrlock_WRK(rwlock, timeout);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: pthread_rwlock_unlock
|
|
// darwin: pthread_rwlock_unlock
|
|
// darwin: pthread_rwlock_unlock$UNIX2003
|
|
// Solaris: rw_unlock (pthread_rwlock_unlock is a weak alias)
|
|
__attribute__((noinline))
|
|
static int pthread_rwlock_unlock_WRK(pthread_rwlock_t* rwlock)
|
|
{
|
|
int ret;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, "<< pthread_rwl_unlk %p", rwlock); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE,
|
|
pthread_rwlock_t*,rwlock);
|
|
|
|
CALL_FN_W_W(ret, fn, rwlock);
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_POST,
|
|
pthread_rwlock_t*,rwlock);
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "pthread_rwlock_unlock", ret );
|
|
}
|
|
|
|
if (TRACE_PTH_FNS) {
|
|
fprintf(stderr, " :: rwl_unlk -> %d >>\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, pthreadZurwlockZuunlock, // pthread_rwlock_unlock
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_unlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, pthreadZurwlockZuunlockZa, // pthread_rwlock_unlock*
|
|
pthread_rwlock_t* rwlock) {
|
|
return pthread_rwlock_unlock_WRK(rwlock);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, rwZuunlock, // rw_unlock
|
|
pthread_rwlock_t *rwlock) {
|
|
return pthread_rwlock_unlock_WRK(rwlock);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#endif /* defined(HAVE_PTHREAD_RWLOCK_T) */
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- POSIX semaphores ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
#include <semaphore.h>
|
|
#include <fcntl.h> /* O_CREAT */
|
|
|
|
#define TRACE_SEM_FNS 0
|
|
|
|
/* Handled:
|
|
int sem_init(sem_t *sem, int pshared, unsigned value);
|
|
int sem_destroy(sem_t *sem);
|
|
int sem_wait(sem_t *sem);
|
|
int sem_post(sem_t *sem);
|
|
sem_t* sem_open(const char *name, int oflag,
|
|
... [mode_t mode, unsigned value]);
|
|
[complete with its idiotic semantics]
|
|
int sem_close(sem_t* sem);
|
|
|
|
Unhandled:
|
|
int sem_trywait(sem_t *sem);
|
|
int sem_timedwait(sem_t *restrict sem,
|
|
const struct timespec *restrict abs_timeout);
|
|
*/
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: sem_init@@GLIBC_2.2.5
|
|
// glibc: sem_init@@GLIBC_2.1
|
|
// glibc: sem_init@GLIBC_2.0
|
|
// darwin: sem_init
|
|
// Solaris: sema_init (sem_init is built on top of sem_init)
|
|
//
|
|
#if !defined(VGO_solaris)
|
|
__attribute__((noinline))
|
|
static int sem_init_WRK(sem_t* sem, int pshared, unsigned long value)
|
|
{
|
|
OrigFn fn;
|
|
int ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, "<< sem_init(%p,%d,%lu) ", sem,pshared,value);
|
|
fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WWW(ret, fn, sem,pshared,value);
|
|
|
|
if (ret == 0) {
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_INIT_POST,
|
|
sem_t*, sem, unsigned long, value);
|
|
} else {
|
|
DO_PthAPIerror( "sem_init", errno );
|
|
}
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, " sem_init -> %d >>\n", ret);
|
|
fflush(stderr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, semZuinitZAZa, // sem_init@*
|
|
sem_t* sem, int pshared, unsigned long value) {
|
|
return sem_init_WRK(sem, pshared, value);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, semZuinit, // sem_init
|
|
sem_t* sem, int pshared, unsigned long value) {
|
|
return sem_init_WRK(sem, pshared, value);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
#else /* VGO_solaris */
|
|
PTH_FUNC(int, semaZuinit, // sema_init
|
|
sema_t *sem,
|
|
unsigned int value,
|
|
int type,
|
|
void *arg)
|
|
{
|
|
OrigFn fn;
|
|
int ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, "<< sema_init(%p, %d, %u) ", sem, type, value);
|
|
fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WWWW(ret, fn, sem, value, type, arg);
|
|
|
|
if (ret == 0) {
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_INIT_POST,
|
|
sema_t *, sem, Word, value);
|
|
} else {
|
|
DO_PthAPIerror("sema_init", ret);
|
|
}
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, " sema_init -> %d >>\n", ret);
|
|
fflush(stderr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* VGO_solaris */
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: sem_destroy@GLIBC_2.0
|
|
// glibc: sem_destroy@@GLIBC_2.1
|
|
// glibc: sem_destroy@@GLIBC_2.2.5
|
|
// darwin: sem_destroy
|
|
// Solaris: sema_destroy (sem_destroy is built on top of sema_destroy)
|
|
__attribute__((noinline))
|
|
static int sem_destroy_WRK(sem_t* sem)
|
|
{
|
|
OrigFn fn;
|
|
int ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, "<< sem_destroy(%p) ", sem);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, sem_t*, sem);
|
|
|
|
CALL_FN_W_W(ret, fn, sem);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "sem_destroy", SEM_ERROR );
|
|
}
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, " sem_destroy -> %d >>\n", ret);
|
|
fflush(stderr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, semZudestroyZAZa, // sem_destroy*
|
|
sem_t* sem) {
|
|
return sem_destroy_WRK(sem);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, semZudestroy, // sem_destroy
|
|
sem_t* sem) {
|
|
return sem_destroy_WRK(sem);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, semaZudestroy, // sema_destroy
|
|
sem_t *sem) {
|
|
return sem_destroy_WRK(sem);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: sem_wait
|
|
// glibc: sem_wait@GLIBC_2.0
|
|
// glibc: sem_wait@@GLIBC_2.1
|
|
// darwin: sem_wait
|
|
// darwin: sem_wait$NOCANCEL$UNIX2003
|
|
// darwin: sem_wait$UNIX2003
|
|
// Solaris: sema_wait (sem_wait is built on top of sema_wait)
|
|
//
|
|
/* wait: decrement semaphore - acquire lockage */
|
|
__attribute__((noinline))
|
|
static int sem_wait_WRK(sem_t* sem)
|
|
{
|
|
OrigFn fn;
|
|
int ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, "<< sem_wait(%p) ", sem);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_WAIT_PRE, sem_t*,sem);
|
|
|
|
CALL_FN_W_W(ret, fn, sem);
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_WAIT_POST, sem_t*,sem,
|
|
long, (ret == 0) ? True : False);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "sem_wait", SEM_ERROR );
|
|
}
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, " sem_wait -> %d >>\n", ret);
|
|
fflush(stderr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, semZuwait, sem_t* sem) { /* sem_wait */
|
|
return sem_wait_WRK(sem);
|
|
}
|
|
PTH_FUNC(int, semZuwaitZAZa, sem_t* sem) { /* sem_wait@* */
|
|
return sem_wait_WRK(sem);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, semZuwait, sem_t* sem) { /* sem_wait */
|
|
return sem_wait_WRK(sem);
|
|
}
|
|
PTH_FUNC(int, semZuwaitZDZa, sem_t* sem) { /* sem_wait$* */
|
|
return sem_wait_WRK(sem);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, semaZuwait, sem_t *sem) { /* sema_wait */
|
|
return sem_wait_WRK(sem);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: sem_post
|
|
// glibc: sem_post@GLIBC_2.0
|
|
// glibc: sem_post@@GLIBC_2.1
|
|
// darwin: sem_post
|
|
// Solaris: sema_post (sem_post is built on top of sema_post)
|
|
//
|
|
/* post: increment semaphore - release lockage */
|
|
__attribute__((noinline))
|
|
static int sem_post_WRK(sem_t* sem)
|
|
{
|
|
OrigFn fn;
|
|
int ret;
|
|
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, "<< sem_post(%p) ", sem);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_POST_PRE, sem_t*,sem);
|
|
|
|
CALL_FN_W_W(ret, fn, sem);
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_POST_POST, sem_t*,sem);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "sem_post", SEM_ERROR );
|
|
}
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, " sem_post -> %d >>\n", ret);
|
|
fflush(stderr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if defined(VGO_linux)
|
|
PTH_FUNC(int, semZupost, sem_t* sem) { /* sem_post */
|
|
return sem_post_WRK(sem);
|
|
}
|
|
PTH_FUNC(int, semZupostZAZa, sem_t* sem) { /* sem_post@* */
|
|
return sem_post_WRK(sem);
|
|
}
|
|
#elif defined(VGO_darwin)
|
|
PTH_FUNC(int, semZupost, sem_t* sem) { /* sem_post */
|
|
return sem_post_WRK(sem);
|
|
}
|
|
#elif defined(VGO_solaris)
|
|
PTH_FUNC(int, semaZupost, sem_t *sem) { /* sema_post */
|
|
return sem_post_WRK(sem);
|
|
}
|
|
#else
|
|
# error "Unsupported OS"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: sem_open
|
|
// darwin: sem_open
|
|
// Solaris: sem_open
|
|
//
|
|
PTH_FUNC(sem_t*, semZuopen,
|
|
const char* name, long oflag,
|
|
long mode, unsigned long value)
|
|
{
|
|
/* A copy of sem_init_WRK (more or less). Is this correct? */
|
|
OrigFn fn;
|
|
sem_t* ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, "<< sem_open(\"%s\",%ld,%lx,%lu) ",
|
|
name,oflag,mode,value);
|
|
fflush(stderr);
|
|
}
|
|
|
|
CALL_FN_W_WWWW(ret, fn, name,oflag,mode,value);
|
|
|
|
if (ret != SEM_FAILED && (oflag & O_CREAT)) {
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_INIT_POST,
|
|
sem_t*, ret, unsigned long, value);
|
|
}
|
|
if (ret == SEM_FAILED) {
|
|
DO_PthAPIerror( "sem_open", errno );
|
|
}
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, " sem_open -> %p >>\n", ret);
|
|
fflush(stderr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// glibc: sem_close
|
|
// darwin: sem_close
|
|
// Solaris: sem_close
|
|
PTH_FUNC(int, sem_close, sem_t* sem)
|
|
{
|
|
OrigFn fn;
|
|
int ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, "<< sem_close(%p) ", sem);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, sem_t*, sem);
|
|
|
|
CALL_FN_W_W(ret, fn, sem);
|
|
|
|
if (ret != 0) {
|
|
DO_PthAPIerror( "sem_close", errno );
|
|
}
|
|
|
|
if (TRACE_SEM_FNS) {
|
|
fprintf(stderr, " close -> %d >>\n", ret);
|
|
fflush(stderr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- Qt 4 threading functions (w/ GNU name mangling) ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
/* Handled:
|
|
QMutex::lock()
|
|
QMutex::unlock()
|
|
QMutex::tryLock()
|
|
QMutex::tryLock(int)
|
|
|
|
QMutex::QMutex(QMutex::RecursionMode) _ZN6QMutexC1ENS_13RecursionModeE
|
|
QMutex::QMutex(QMutex::RecursionMode) _ZN6QMutexC2ENS_13RecursionModeE
|
|
QMutex::~QMutex() _ZN6QMutexD1Ev
|
|
QMutex::~QMutex() _ZN6QMutexD2Ev
|
|
|
|
Unhandled:
|
|
QReadWriteLock::lockForRead()
|
|
QReadWriteLock::lockForWrite()
|
|
QReadWriteLock::unlock()
|
|
QReadWriteLock::tryLockForRead(int)
|
|
QReadWriteLock::tryLockForRead()
|
|
QReadWriteLock::tryLockForWrite(int)
|
|
QReadWriteLock::tryLockForWrite()
|
|
|
|
QWaitCondition::wait(QMutex*, unsigned long)
|
|
QWaitCondition::wakeAll()
|
|
QWaitCondition::wakeOne()
|
|
|
|
QSemaphore::*
|
|
*/
|
|
/* More comments, 19 Nov 08, based on assessment of qt-4.5.0TP1,
|
|
at least on Unix:
|
|
|
|
It's apparently only necessary to intercept QMutex, since that is
|
|
not implemented using pthread_mutex_t; instead Qt4 has its own
|
|
implementation based on atomics (to check the non-contended case)
|
|
and pthread_cond_wait (to wait in the contended case).
|
|
|
|
QReadWriteLock is built on top of QMutex, counters, and a wait
|
|
queue. So we don't need to handle it specially once QMutex
|
|
handling is correct -- presumably the dependencies through QMutex
|
|
are sufficient to avoid any false race reports. On the other hand,
|
|
it is an open question whether too many dependencies are observed
|
|
-- in which case we may miss races (false negatives). I suspect
|
|
this is likely to be the case, unfortunately.
|
|
|
|
QWaitCondition is built on pthread_cond_t, pthread_mutex_t, QMutex
|
|
and QReadWriteLock. Same compositional-correctness justificiation
|
|
and limitations as fro QReadWriteLock.
|
|
|
|
Ditto QSemaphore (from cursory examination).
|
|
|
|
Does it matter that only QMutex is handled directly? Open
|
|
question. From testing with drd/tests/qt4_* and with KDE4 apps, it
|
|
appears that no false errors are reported; however it is not clear
|
|
if this is causing false negatives.
|
|
|
|
Another problem with Qt4 is thread exiting. Threads are created
|
|
with pthread_create (fine); but they detach and simply exit when
|
|
done. There is no use of pthread_join, and the provided
|
|
wait-for-a-thread-to-exit mechanism (QThread::wait, I believe)
|
|
relies on a system of mutexes and flags. I suspect this also
|
|
causes too many dependencies to appear. Consequently H sometimes
|
|
fails to detect races at exit in some very short-lived racy
|
|
programs, because it appears that a thread can exit _and_ have an
|
|
observed dependency edge back to the main thread (presumably)
|
|
before the main thread reaps the child (that is, calls
|
|
QThread::wait).
|
|
|
|
This theory is supported by the observation that if all threads are
|
|
made to wait at a pthread_barrier_t immediately before they exit,
|
|
then H's detection of races in such programs becomes reliable;
|
|
without the barrier, it is varies from run to run, depending
|
|
(according to investigation) on whether aforementioned
|
|
exit-before-reaping behaviour happens or not.
|
|
|
|
Finally, why is it necessary to intercept the QMutex constructors
|
|
and destructors? The constructors are intercepted only as a matter
|
|
of convenience, so H can print accurate "first observed at"
|
|
clauses. However, it is actually necessary to intercept the
|
|
destructors (as it is with pthread_mutex_destroy) in order that
|
|
locks get removed from LAOG when they are destroyed.
|
|
*/
|
|
|
|
// soname is libQtCore.so.4 ; match against libQtCore.so*
|
|
#define QT4_FUNC(ret_ty, f, args...) \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZU(libQtCoreZdsoZa,f)(args); \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZU(libQtCoreZdsoZa,f)(args)
|
|
|
|
// soname is libQt5Core.so.4 ; match against libQt5Core.so*
|
|
#define QT5_FUNC(ret_ty, f, args...) \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZU(libQt5CoreZdsoZa,f)(args); \
|
|
ret_ty I_WRAP_SONAME_FNNAME_ZU(libQt5CoreZdsoZa,f)(args)
|
|
|
|
//-----------------------------------------------------------
|
|
// QMutex::lock()
|
|
__attribute__((noinline))
|
|
static void QMutex_lock_WRK(void* self)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, "<< QMutex::lock %p", self); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE,
|
|
void*,self, long,0/*!isTryLock*/);
|
|
|
|
CALL_FN_v_W(fn, self);
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
void *, self, long, True);
|
|
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, " :: Q::lock done >>\n");
|
|
}
|
|
}
|
|
|
|
QT4_FUNC(void, _ZN6QMutex4lockEv, void* self) {
|
|
QMutex_lock_WRK(self);
|
|
}
|
|
QT5_FUNC(void, _ZN6QMutex4lockEv, void* self) {
|
|
QMutex_lock_WRK(self);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// QMutex::unlock()
|
|
__attribute__((noinline))
|
|
static void QMutex_unlock_WRK(void* self)
|
|
{
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, "<< QMutex::unlock %p", self); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE,
|
|
void*, self);
|
|
|
|
CALL_FN_v_W(fn, self);
|
|
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST,
|
|
void*, self);
|
|
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, " Q::unlock done >>\n");
|
|
}
|
|
}
|
|
|
|
QT4_FUNC(void, _ZN6QMutex6unlockEv, void* self) {
|
|
QMutex_unlock_WRK(self);
|
|
}
|
|
QT5_FUNC(void, _ZN6QMutex6unlockEv, void* self) {
|
|
QMutex_unlock_WRK(self);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// bool QMutex::tryLock()
|
|
// using 'long' to mimic C++ 'bool'
|
|
__attribute__((noinline))
|
|
static long QMutex_tryLock_WRK(void* self)
|
|
{
|
|
OrigFn fn;
|
|
long ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, "<< QMutex::tryLock %p", self); fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE,
|
|
void*,self, long,1/*isTryLock*/);
|
|
|
|
CALL_FN_W_W(ret, fn, self);
|
|
|
|
// assumes that only the low 8 bits of the 'bool' are significant
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
void *, self, long, (ret & 0xFF) ? True : False);
|
|
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, " :: Q::tryLock -> %lu >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QT4_FUNC(long, _ZN6QMutex7tryLockEv, void* self) {
|
|
return QMutex_tryLock_WRK(self);
|
|
}
|
|
QT5_FUNC(long, _ZN6QMutex7tryLockEv, void* self) {
|
|
return QMutex_tryLock_WRK(self);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// bool QMutex::tryLock(int)
|
|
// using 'long' to mimic C++ 'bool'
|
|
__attribute__((noinline))
|
|
static long QMutex_tryLock_int_WRK(void* self, long arg2)
|
|
{
|
|
OrigFn fn;
|
|
long ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, "<< QMutex::tryLock(int) %p %d", self, (int)arg2);
|
|
fflush(stderr);
|
|
}
|
|
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE,
|
|
void*,self, long,1/*isTryLock*/);
|
|
|
|
CALL_FN_W_WW(ret, fn, self,arg2);
|
|
|
|
// assumes that only the low 8 bits of the 'bool' are significant
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST,
|
|
void *, self, long, (ret & 0xFF) ? True : False);
|
|
|
|
if (TRACE_QT4_FNS) {
|
|
fprintf(stderr, " :: Q::tryLock(int) -> %lu >>\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QT4_FUNC(long, _ZN6QMutex7tryLockEi, void* self, long arg2) {
|
|
return QMutex_tryLock_int_WRK(self, arg2);
|
|
}
|
|
QT5_FUNC(long, _ZN6QMutex7tryLockEi, void* self, long arg2) {
|
|
return QMutex_tryLock_int_WRK(self, arg2);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// It's not really very clear what the args are here. But from
|
|
// a bit of dataflow analysis of the generated machine code of
|
|
// the original function, it appears this takes two args, and
|
|
// returns nothing. Nevertheless preserve return value just in
|
|
// case. A bit of debug printing indicates that the first arg
|
|
// is that of the mutex and the second is either zero or one,
|
|
// probably being the recursion mode, therefore.
|
|
// QMutex::QMutex(QMutex::RecursionMode) ("C1ENS" variant)
|
|
__attribute__((noinline))
|
|
static void* QMutex_constructor_WRK(void* mutex, long recmode)
|
|
{
|
|
OrigFn fn;
|
|
long ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
CALL_FN_W_WW(ret, fn, mutex, recmode);
|
|
// fprintf(stderr, "QMutex constructor 1: %p <- %p %p\n", ret, arg1, arg2);
|
|
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST,
|
|
void*,mutex, long,1/*mbRec*/);
|
|
return (void*)ret;
|
|
}
|
|
|
|
QT4_FUNC(void*, _ZN6QMutexC1ENS_13RecursionModeE, void* self, long recmode) {
|
|
return QMutex_constructor_WRK(self, recmode);
|
|
}
|
|
QT5_FUNC(void*, _ZN6QMutexC1ENS_13RecursionModeE, void* self, long recmode) {
|
|
return QMutex_constructor_WRK(self, recmode);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// QMutex::~QMutex() ("D1Ev" variant)
|
|
__attribute__((noinline))
|
|
static void* QMutex_destructor_WRK(void* mutex)
|
|
{
|
|
OrigFn fn;
|
|
long ret;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE,
|
|
void*,mutex);
|
|
CALL_FN_W_W(ret, fn, mutex);
|
|
return (void*)ret;
|
|
}
|
|
|
|
QT4_FUNC(void*, _ZN6QMutexD1Ev, void* self) {
|
|
return QMutex_destructor_WRK(self);
|
|
}
|
|
QT5_FUNC(void*, _ZN6QMutexD1Ev, void* self) {
|
|
return QMutex_destructor_WRK(self);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// QMutex::QMutex(QMutex::RecursionMode) ("C2ENS" variant)
|
|
QT4_FUNC(void*, _ZN6QMutexC2ENS_13RecursionModeE,
|
|
void* mutex,
|
|
long recmode)
|
|
{
|
|
assert(0);
|
|
/*NOTREACHED*/
|
|
/* Android's gcc behaves like it doesn't know that assert(0)
|
|
never returns. Hence: */
|
|
return NULL;
|
|
}
|
|
|
|
QT5_FUNC(void*, _ZN6QMutexC2ENS_13RecursionModeE, void* self, long recmode)
|
|
{
|
|
assert(0);
|
|
/*NOTREACHED*/
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// QMutex::~QMutex() ("D2Ev" variant)
|
|
QT4_FUNC(void*, _ZN6QMutexD2Ev, void* mutex)
|
|
{
|
|
assert(0);
|
|
/* Android's gcc behaves like it doesn't know that assert(0)
|
|
never returns. Hence: */
|
|
return NULL;
|
|
}
|
|
|
|
QT5_FUNC(void*, _ZN6QMutexD2Ev, void* self)
|
|
{
|
|
assert(0);
|
|
/*NOTREACHED*/
|
|
return NULL;
|
|
}
|
|
|
|
// QReadWriteLock is not intercepted directly. See comments
|
|
// above.
|
|
|
|
//// QReadWriteLock::lockForRead()
|
|
//// _ZN14QReadWriteLock11lockForReadEv == QReadWriteLock::lockForRead()
|
|
//QT4_FUNC(void, ZuZZN14QReadWriteLock11lockForReadEv,
|
|
// // _ZN14QReadWriteLock11lockForReadEv
|
|
// void* self)
|
|
//{
|
|
// OrigFn fn;
|
|
// VALGRIND_GET_ORIG_FN(fn);
|
|
// if (TRACE_QT4_FNS) {
|
|
// fprintf(stderr, "<< QReadWriteLock::lockForRead %p", self);
|
|
// fflush(stderr);
|
|
// }
|
|
//
|
|
// DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
// void*,self,
|
|
// long,0/*!isW*/, long,0/*!isTryLock*/);
|
|
//
|
|
// CALL_FN_v_W(fn, self);
|
|
//
|
|
// DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
// void*,self, long,0/*!isW*/, long, True);
|
|
//
|
|
// if (TRACE_QT4_FNS) {
|
|
// fprintf(stderr, " :: Q::lockForRead :: done >>\n");
|
|
// }
|
|
//}
|
|
//
|
|
//// QReadWriteLock::lockForWrite()
|
|
//// _ZN14QReadWriteLock12lockForWriteEv == QReadWriteLock::lockForWrite()
|
|
//QT4_FUNC(void, ZuZZN14QReadWriteLock12lockForWriteEv,
|
|
// // _ZN14QReadWriteLock12lockForWriteEv
|
|
// void* self)
|
|
//{
|
|
// OrigFn fn;
|
|
// VALGRIND_GET_ORIG_FN(fn);
|
|
// if (TRACE_QT4_FNS) {
|
|
// fprintf(stderr, "<< QReadWriteLock::lockForWrite %p", self);
|
|
// fflush(stderr);
|
|
// }
|
|
//
|
|
// DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE,
|
|
// void*,self,
|
|
// long,1/*isW*/, long,0/*!isTryLock*/);
|
|
//
|
|
// CALL_FN_v_W(fn, self);
|
|
//
|
|
// DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST,
|
|
// void*,self, long,1/*isW*/, long, True);
|
|
//
|
|
// if (TRACE_QT4_FNS) {
|
|
// fprintf(stderr, " :: Q::lockForWrite :: done >>\n");
|
|
// }
|
|
//}
|
|
//
|
|
//// QReadWriteLock::unlock()
|
|
//// _ZN14QReadWriteLock6unlockEv == QReadWriteLock::unlock()
|
|
//QT4_FUNC(void, ZuZZN14QReadWriteLock6unlockEv,
|
|
// // _ZN14QReadWriteLock6unlockEv
|
|
// void* self)
|
|
//{
|
|
// OrigFn fn;
|
|
// VALGRIND_GET_ORIG_FN(fn);
|
|
// if (TRACE_QT4_FNS) {
|
|
// fprintf(stderr, "<< QReadWriteLock::unlock %p", self);
|
|
// fflush(stderr);
|
|
// }
|
|
//
|
|
// DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE,
|
|
// void*,self);
|
|
//
|
|
// CALL_FN_v_W(fn, self);
|
|
//
|
|
// DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_POST,
|
|
// void*,self);
|
|
//
|
|
// if (TRACE_QT4_FNS) {
|
|
// fprintf(stderr, " :: Q::unlock :: done >>\n");
|
|
// }
|
|
//}
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/*--- Replacements for basic string functions, that don't ---*/
|
|
/*--- overrun the input arrays. ---*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
#include "../shared/vg_replace_strmem.c"
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end hg_intercepts.c ---*/
|
|
/*--------------------------------------------------------------------*/
|