From: Julian Seward Date: Wed, 3 Mar 2010 23:03:40 +0000 (+0000) Subject: Rollup fixes for Helgrind: X-Git-Tag: svn/VALGRIND_3_6_0~362 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f885d9f81ec125a0438759bf2a7cdf8856bf0dc;p=thirdparty%2Fvalgrind.git Rollup fixes for Helgrind: * tracking of barriers: add support for resizable barriers * resync TSan-compatible client requests with latest changes * add direct access to the client requests used in hg_intercepts.c * add a client request pair to disable and re-enable tracking of arbitrary address ranges git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11062 --- diff --git a/helgrind/helgrind.h b/helgrind/helgrind.h index 317695ba6b..000a0775f2 100644 --- a/helgrind/helgrind.h +++ b/helgrind/helgrind.h @@ -97,7 +97,7 @@ typedef _VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, /* sem_t* */ _VG_USERREQ__HG_POSIX_SEM_POST_PRE, /* sem_t* */ _VG_USERREQ__HG_POSIX_SEM_WAIT_POST, /* sem_t* */ - _VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, /* pth_bar_t*, ulong */ + _VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, /* pth_bar_t*, ulong, ulong */ _VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, /* pth_bar_t* */ _VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, /* pth_bar_t* */ _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE, /* pth_slk_t* */ @@ -109,29 +109,192 @@ typedef _VG_USERREQ__HG_USERSO_SEND_PRE, /* arbitrary UWord SO-tag */ _VG_USERREQ__HG_USERSO_RECV_POST, /* arbitrary UWord SO-tag */ _VG_USERREQ__HG_RESERVED1, /* Do not use */ - _VG_USERREQ__HG_RESERVED2 /* Do not use */ + _VG_USERREQ__HG_RESERVED2, /* Do not use */ + _VG_USERREQ__HG_RESERVED3, /* Do not use */ + _VG_USERREQ__HG_RESERVED4, /* Do not use */ + _VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, /* Addr a, ulong len */ + _VG_USERREQ__HG_ARANGE_MAKE_TRACKED, /* Addr a, ulong len */ + _VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE /* pth_bar_t*, ulong */ } Vg_TCheckClientRequest; /*----------------------------------------------------------------*/ -/*--- An implementation-only request -- not for end user use ---*/ +/*--- ---*/ +/*--- Implementation-only facilities. Not for end-user use. ---*/ +/*--- For end-user facilities see below (the next section in ---*/ +/*--- this file.) ---*/ +/*--- ---*/ /*----------------------------------------------------------------*/ -#define _HG_CLIENTREQ_UNIMP(_qzz_str) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ - _VG_USERREQ__HG_CLIENTREQ_UNIMP, \ - _qzz_str, 0, 0, 0, 0); \ - (void)0; \ - } while(0) +/* 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 hg_intercepts.c. But here, we + have to make do with weaker typing (no definition of Word etc) and + no assertions, whereas in helgrind.h we can use those facilities. + Obviously it's important the two sets of definitions are kept in + sync. + + The commented-out asserts should actually hold, but unfortunately + they can't be allowed to be visible here, because that would + require the end-user code to #include . +*/ + +#define DO_CREQ_v_W(_creqF, _ty1F,_arg1F) \ + do { \ + long int _unused_res, _arg1; \ + /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ + _arg1 = (long int)(_arg1F); \ + VALGRIND_DO_CLIENT_REQUEST(_unused_res, 0, \ + (_creqF), \ + _arg1, 0,0,0,0); \ + } while (0) + +#define DO_CREQ_v_WW(_creqF, _ty1F,_arg1F, _ty2F,_arg2F) \ + do { \ + long int _unused_res, _arg1, _arg2; \ + /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ + /* assert(sizeof(_ty2F) == sizeof(long int)); */ \ + _arg1 = (long int)(_arg1F); \ + _arg2 = (long int)(_arg2F); \ + VALGRIND_DO_CLIENT_REQUEST(_unused_res, 0, \ + (_creqF), \ + _arg1,_arg2,0,0,0); \ + } while (0) + +#define DO_CREQ_v_WWW(_creqF, _ty1F,_arg1F, \ + _ty2F,_arg2F, _ty3F, _arg3F) \ + do { \ + long int _unused_res, _arg1, _arg2, _arg3; \ + /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ + /* assert(sizeof(_ty2F) == sizeof(long int)); */ \ + /* assert(sizeof(_ty3F) == sizeof(long int)); */ \ + _arg1 = (long int)(_arg1F); \ + _arg2 = (long int)(_arg2F); \ + _arg3 = (long int)(_arg3F); \ + VALGRIND_DO_CLIENT_REQUEST(_unused_res, 0, \ + (_creqF), \ + _arg1,_arg2,_arg3,0,0); \ + } while (0) + + +#define _HG_CLIENTREQ_UNIMP(_qzz_str) \ + DO_CREQ_v_W(_VG_USERREQ__HG_CLIENTREQ_UNIMP, \ + (char*),(_qzz_str)) /*----------------------------------------------------------------*/ -/*--- Misc requests ---*/ +/*--- ---*/ +/*--- Helgrind-native requests. These allow access to ---*/ +/*--- the same set of annotation primitives that are used ---*/ +/*--- to build the POSIX pthread wrappers. ---*/ +/*--- ---*/ /*----------------------------------------------------------------*/ +/* ---------------------------------------------------------- + For describing ordinary mutexes (non-rwlocks). For rwlock + descriptions see ANNOTATE_RWLOCK_* below. + ---------------------------------------------------------- */ + +/* Notify here immediately after mutex creation. _mbRec == 0 for a + non-recursive mutex, 1 for a recursive mutex. */ +#define VALGRIND_HG_MUTEX_INIT_POST(_mutex, _mbRec) \ + DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST, \ + void*,(_mutex), long,(_mbRec)) + +/* Notify here immediately before mutex acquisition. _isTryLock == 0 + for a normal acquisition, 1 for a "try" style acquisition. */ +#define VALGRIND_HG_MUTEX_LOCK_PRE(_mutex, _isTryLock) \ + DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE, \ + void*,(_mutex), long,(_isTryLock)) + +/* Notify here immediately after a successful mutex acquisition. */ +#define VALGRIND_HG_MUTEX_LOCK_POST(_mutex) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST, \ + void*,(_mutex)) + +/* Notify here immediately before a mutex release. */ +#define VALGRIND_HG_MUTEX_UNLOCK_PRE(_mutex) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE, \ + void*,(_mutex)) + +/* Notify here immediately after a mutex release. */ +#define VALGRIND_HG_MUTEX_UNLOCK_POST(_mutex) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST, \ + void*,(_mutex)) + +/* Notify here immediately before mutex destruction. */ +#define VALGRIND_HG_MUTEX_DESTROY_PRE(_mutex) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE, \ + void*,(_mutex)) + +/* ---------------------------------------------------------- + For describing semaphores. + ---------------------------------------------------------- */ + +/* Notify here immediately after semaphore creation. */ +#define VALGRIND_HG_SEM_INIT_POST(_sem, _value) \ + DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_INIT_POST, \ + void*, (_sem), unsigned long, (_value)) + +/* Notify here immediately after a semaphore wait (an acquire-style + operation) */ +#define VALGRIND_HG_SEM_WAIT_POST(_sem) \ + DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_WAIT_POST, \ + void*,(_sem)) + +/* Notify here immediately before semaphore post (a release-style + operation) */ +#define VALGRIND_HG_SEM_POST_PRE(_sem) \ + DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_POST_PRE, \ + void*,(_sem)) + +/* Notify here immediately before semaphore destruction. */ +#define VALGRIND_HG_SEM_DESTROY_PRE(_sem) \ + DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, \ + void*, (_sem)) + +/* ---------------------------------------------------------- + For describing barriers. + ---------------------------------------------------------- */ + +/* Notify here immediately before barrier creation. _count is the + capacity. _resizable == 0 means the barrier may not be resized, 1 + means it may be. */ +#define VALGRIND_HG_BARRIER_INIT_PRE(_bar, _count, _resizable) \ + DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, \ + void*,(_bar), \ + unsigned long,(_count), \ + unsigned long,(_resizable)) + +/* Notify here immediately before arrival at a barrier. */ +#define VALGRIND_HG_BARRIER_WAIT_PRE(_bar) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, \ + void*,(_bar)) + +/* Notify here immediately before a resize (change of barrier + capacity). If _newcount >= the existing capacity, then there is no + change in the state of any threads waiting at the barrier. If + _newcount < the existing capacity, and >= _newcount threads are + currently waiting at the barrier, then this notification is + considered to also have the effect of telling the checker that all + waiting threads have now moved past the barrier. (I can't think of + any other sane semantics.) */ +#define VALGRIND_HG_BARRIER_RESIZE_PRE(_bar, _newcount) \ + DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE, \ + void*,(_bar), \ + unsigned long,(_newcount)) + +/* Notify here immediately before barrier destruction. */ +#define VALGRIND_HG_BARRIER_DESTROY_PRE(_bar) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, \ + void*,(_bar)) + +/* ---------------------------------------------------------- + For describing memory ownership changes. + ---------------------------------------------------------- */ + /* Clean memory state. This makes Helgrind forget everything it knew about the specified memory range. Effectively this announces that the specified memory range now "belongs" to the calling thread, so @@ -139,27 +302,51 @@ typedef synchronisation, and (2) all other threads must sync with this one to access it safely. This is particularly useful for memory allocators that wish to recycle memory. */ -#define VALGRIND_HG_CLEAN_MEMORY(_qzz_start, _qzz_len) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST( \ - (_qzz_res), 0, VG_USERREQ__HG_CLEAN_MEMORY, \ - (_qzz_start), (_qzz_len), 0, 0, 0 \ - ); \ - (void)0; \ - } while(0) +#define VALGRIND_HG_CLEAN_MEMORY(_qzz_start, _qzz_len) \ + DO_CREQ_v_WW(VG_USERREQ__HG_CLEAN_MEMORY, \ + void*,(_qzz_start), \ + unsigned long,(_qzz_len)) + +/* ---------------------------------------------------------- + For error control. + ---------------------------------------------------------- */ + +/* Tell H that an address range is not to be "tracked" until further + notice. This puts it in the NOACCESS state, in which case we + ignore all reads and writes to it. Useful for ignoring ranges of + memory where there might be races we don't want to see. If the + memory is subsequently reallocated via malloc/new/stack allocation, + then it is put back in the trackable state. Hence it is safe in + the situation where checking is disabled, the containing area is + deallocated and later reallocated for some other purpose. */ +#define VALGRIND_HG_DISABLE_CHECKING(_qzz_start, _qzz_len) \ + DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, \ + void*,(_qzz_start), \ + unsigned long,(_qzz_len)) + +/* And put it back into the normal "tracked" state, that is, make it + once again subject to the normal race-checking machinery. This + puts it in the same state as new memory allocated by this thread -- + that is, basically owned exclusively by this thread. */ +#define VALGRIND_HG_ENABLE_CHECKING(_qzz_start, _qzz_len) \ + DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_TRACKED, \ + void*,(_qzz_start), \ + unsigned long,(_qzz_len)) /*----------------------------------------------------------------*/ +/*--- ---*/ /*--- ThreadSanitizer-compatible requests ---*/ +/*--- (mostly unimplemented) ---*/ +/*--- ---*/ /*----------------------------------------------------------------*/ /* A quite-broad set of annotations, as used in the ThreadSanitizer project. This implementation aims to be a (source-level) compatible implementation of the macros defined in: - http://code.google.com/p/google-perftools/source \ - /browse/trunk/src/base/dynamic_annotations.h + http://code.google.com/p/data-race-test/source + /browse/trunk/dynamic_annotations/dynamic_annotations.h (some of the comments below are taken from the above file) @@ -240,22 +427,10 @@ typedef ---------------------------------------------------------------- */ #define ANNOTATE_HAPPENS_BEFORE(obj) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ - _VG_USERREQ__HG_USERSO_SEND_PRE, \ - obj, 0, 0, 0, 0); \ - (void)0; \ - } while (0) + DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_SEND_PRE, void*,(obj)) #define ANNOTATE_HAPPENS_AFTER(obj) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ - _VG_USERREQ__HG_USERSO_RECV_POST, \ - obj, 0, 0, 0, 0); \ - (void)0; \ - } while (0) + DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_RECV_POST, void*,(obj)) /* ---------------------------------------------------------------- @@ -274,6 +449,12 @@ typedef #define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_PUBLISH_MEMORY_RANGE") +/* DEPRECATED. Don't use it. */ +/* #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) */ + +/* DEPRECATED. Don't use it. */ +/* #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) */ + /* ---------------------------------------------------------------- TSan sources say: @@ -289,8 +470,11 @@ typedef behaviour. ---------------------------------------------------------------- */ -#define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) \ - _HG_CLIENTREQ_UNIMP("ANNOTATE_MUTEX_IS_USED_AS_CONDVAR") +#define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \ + _HG_CLIENTREQ_UNIMP("ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX") + +/* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */ +/* #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) */ /* ---------------------------------------------------------------- @@ -354,16 +538,21 @@ typedef ---------------------------------------------------------------- */ -/* Report that we may have a benign race on ADDRESS. Insert at the - point where ADDRESS has been allocated, preferably close to the - point where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. +/* Report that we may have a benign race at "pointer", with size + "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the + point where "pointer" has been allocated, preferably close to the point + where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. XXX: what's this actually supposed to do? And what's the type of DESCRIPTION? When does the annotation stop having an effect? */ -#define ANNOTATE_BENIGN_RACE(address, description) \ +#define ANNOTATE_BENIGN_RACE(pointer, description) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_BENIGN_RACE") - + +/* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to + the memory range [address, address+size). */ +#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + _HG_CLIENTREQ_UNIMP("ANNOTATE_BENIGN_RACE_SIZED") /* Request the analysis tool to ignore all reads in the current thread until ANNOTATE_IGNORE_READS_END is called. Useful to ignore @@ -429,49 +618,51 @@ typedef ---------------------------------------------------------------- */ /* Report that a lock has just been created at address LOCK. */ -#define ANNOTATE_RWLOCK_CREATE(lock) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST( \ - _qzz_res, 0, _VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, \ - lock, 0, 0, 0, 0 \ - ); \ - (void)0; \ - } while(0) +#define ANNOTATE_RWLOCK_CREATE(lock) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, \ + void*,(lock)) /* Report that the lock at address LOCK is about to be destroyed. */ -#define ANNOTATE_RWLOCK_DESTROY(lock) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST( \ - _qzz_res, 0, _VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, \ - lock, 0, 0, 0, 0 \ - ); \ - (void)0; \ - } while(0) +#define ANNOTATE_RWLOCK_DESTROY(lock) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, \ + void*,(lock)) /* Report that the lock at address LOCK has just been acquired. is_w=1 for writer lock, is_w=0 for reader lock. */ -#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST( \ - _qzz_res, 0, _VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST, \ - lock, is_w ? 1 : 0, 0, 0, 0 \ - ); \ - (void)0; \ - } while(0) +#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST, \ + void*,(lock), unsigned long,(is_w)) /* Report that the lock at address LOCK is about to be released. */ - #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ - do { \ - unsigned long _qzz_res; \ - VALGRIND_DO_CLIENT_REQUEST( \ - _qzz_res, 0, _VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE, \ - lock, 0, 0, 0, 0 \ - ); \ - (void)0; \ - } while(0) +#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE, \ + void*,(lock)) /* is_w is ignored */ + + +/* ------------------------------------------------------------- + Annotations useful when implementing barriers. They are not + normally needed by modules that merely use barriers. + The "barrier" argument is a pointer to the barrier object. + ---------------------------------------------------------------- +*/ + +/* Report that the "barrier" has been initialized with initial + "count". If 'reinitialization_allowed' is true, initialization is + allowed to happen multiple times w/o calling barrier_destroy() */ +#define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \ + _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_INIT") + +/* Report that we are about to enter barrier_wait("barrier"). */ +#define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \ + _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY") + +/* Report that we just exited barrier_wait("barrier"). */ +#define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \ + _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY") + +/* Report that the "barrier" has been destroyed. */ +#define ANNOTATE_BARRIER_DESTROY(barrier) \ + _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY") /* ---------------------------------------------------------------- @@ -488,5 +679,9 @@ typedef #define ANNOTATE_NO_OP(arg) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_NO_OP") +/* Force the race detector to flush its state. The actual effect depends on + * the implementation of the detector. */ +#define ANNOTATE_FLUSH_STATE() \ + _HG_CLIENTREQ_UNIMP("ANNOTATE_FLUSH_STATE") #endif /* __HELGRIND_H */ diff --git a/helgrind/hg_intercepts.c b/helgrind/hg_intercepts.c index 8c17cc784b..f5dc2838be 100644 --- a/helgrind/hg_intercepts.c +++ b/helgrind/hg_intercepts.c @@ -70,8 +70,19 @@ 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) -// Do a client request. This is a macro rather than a function -// so as to avoid having an extra function in the stack trace. +// 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 { \ @@ -957,9 +968,10 @@ PTH_FUNC(int, pthreadZubarrierZuinit, // pthread_barrier_init fflush(stderr); } - DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, - pthread_barrier_t*,bar, - unsigned long,count); + 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); diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c index 294dcc8da2..19dd1f968e 100644 --- a/helgrind/hg_main.c +++ b/helgrind/hg_main.c @@ -1084,6 +1084,13 @@ static void shadow_mem_make_NoAccess ( Thread* thr, Addr aIN, SizeT len ) libhb_srange_noaccess( thr->hbthr, aIN, len ); } +static void shadow_mem_make_Untracked ( Thread* thr, Addr aIN, SizeT len ) +{ + if (0 && len > 500) + VG_(printf)("make Untracked ( %#lx, %ld )\n", aIN, len ); + libhb_srange_untrack( thr->hbthr, aIN, len ); +} + /*----------------------------------------------------------------*/ /*--- Event handlers (evh__* functions) ---*/ @@ -1531,6 +1538,7 @@ void evh__set_perms ( Addr a, SizeT len, static void evh__die_mem ( Addr a, SizeT len ) { + // urr, libhb ignores this. if (SHOW_EVENTS >= 2) VG_(printf)("evh__die_mem(%p, %lu)\n", (void*)a, len ); shadow_mem_make_NoAccess( get_current_Thread(), a, len ); @@ -1538,6 +1546,16 @@ void evh__die_mem ( Addr a, SizeT len ) { all__sanity_check("evh__die_mem-post"); } +static +void evh__untrack_mem ( Addr a, SizeT len ) { + // whereas it doesn't ignore this + if (SHOW_EVENTS >= 2) + VG_(printf)("evh__untrack_mem(%p, %lu)\n", (void*)a, len ); + shadow_mem_make_Untracked( get_current_Thread(), a, len ); + if (len >= SCE_BIGRANGE_T && (HG_(clo_sanity_flags) & SCE_BIGRANGE)) + all__sanity_check("evh__untrack_mem-post"); +} + static void evh__copy_mem ( Addr src, Addr dst, SizeT len ) { if (SHOW_EVENTS >= 2) @@ -2701,6 +2719,7 @@ static void evh__HG_POSIX_SEM_WAIT_POST ( ThreadId tid, void* sem ) typedef struct { Bool initted; /* has it yet been initted by guest? */ + Bool resizable; /* is resizing allowed? */ UWord size; /* declared size */ XArray* waiting; /* XA of Thread*. # present is 0 .. .size */ } @@ -2760,15 +2779,16 @@ static void map_barrier_to_Bar_delete ( void* barrier ) { static void evh__HG_PTHREAD_BARRIER_INIT_PRE ( ThreadId tid, void* barrier, - UWord count ) + UWord count, + UWord resizable ) { Thread* thr; Bar* bar; if (SHOW_EVENTS >= 1) VG_(printf)("evh__HG_PTHREAD_BARRIER_INIT_PRE" - "(tid=%d, barrier=%p, count=%lu)\n", - (Int)tid, (void*)barrier, count ); + "(tid=%d, barrier=%p, count=%lu, resizable=%lu)\n", + (Int)tid, (void*)barrier, count, resizable ); thr = map_threads_maybe_lookup( tid ); tl_assert(thr); /* cannot fail - Thread* must already exist */ @@ -2779,6 +2799,12 @@ static void evh__HG_PTHREAD_BARRIER_INIT_PRE ( ThreadId tid, ); } + if (resizable != 0 && resizable != 1) { + HG_(record_error_Misc)( + thr, "pthread_barrier_init: invalid 'resizable' argument" + ); + } + bar = map_barrier_to_Bar_lookup_or_alloc(barrier); tl_assert(bar); @@ -2802,8 +2828,9 @@ static void evh__HG_PTHREAD_BARRIER_INIT_PRE ( ThreadId tid, tl_assert(bar->waiting); tl_assert(VG_(sizeXA)(bar->waiting) == 0); - bar->initted = True; - bar->size = count; + bar->initted = True; + bar->resizable = resizable == 1 ? True : False; + bar->size = count; } @@ -2851,6 +2878,43 @@ static void evh__HG_PTHREAD_BARRIER_DESTROY_PRE ( ThreadId tid, } +/* All the threads have arrived. Now do the Interesting Bit. Get a + new synchronisation object and do a weak send to it from all the + participating threads. This makes its vector clocks be the join of + all the individual threads' vector clocks. Then do a strong + receive from it back to all threads, so that their VCs are a copy + of it (hence are all equal to the join of their original VCs.) */ +static void do_barrier_cross_sync_and_empty ( Bar* bar ) +{ + /* XXX check bar->waiting has no duplicates */ + UWord i; + SO* so = libhb_so_alloc(); + + tl_assert(bar->waiting); + tl_assert(VG_(sizeXA)(bar->waiting) == bar->size); + + /* compute the join ... */ + for (i = 0; i < bar->size; i++) { + Thread* t = *(Thread**)VG_(indexXA)(bar->waiting, i); + Thr* hbthr = t->hbthr; + libhb_so_send( hbthr, so, False/*weak send*/ ); + } + /* ... and distribute to all threads */ + for (i = 0; i < bar->size; i++) { + Thread* t = *(Thread**)VG_(indexXA)(bar->waiting, i); + Thr* hbthr = t->hbthr; + libhb_so_recv( hbthr, so, True/*strong recv*/ ); + } + + /* finally, we must empty out the waiting vector */ + VG_(dropTailXA)(bar->waiting, VG_(sizeXA)(bar->waiting)); + + /* and we don't need this any more. Perhaps a stack-allocated + SO would be better? */ + libhb_so_dealloc(so); +} + + static void evh__HG_PTHREAD_BARRIER_WAIT_PRE ( ThreadId tid, void* barrier ) { @@ -2896,8 +2960,7 @@ static void evh__HG_PTHREAD_BARRIER_WAIT_PRE ( ThreadId tid, */ Thread* thr; Bar* bar; - SO* so; - UWord present, i; + UWord present; if (SHOW_EVENTS >= 1) VG_(printf)("evh__HG_PTHREAD_BARRIER_WAIT_PRE" @@ -2930,39 +2993,74 @@ static void evh__HG_PTHREAD_BARRIER_WAIT_PRE ( ThreadId tid, if (present < bar->size) return; - /* All the threads have arrived. Now do the Interesting Bit. Get - a new synchronisation object and do a weak send to it from all - the participating threads. This makes its vector clocks be the - join of all the individual threads' vector clocks. Then do a - strong receive from it back to all threads, so that their VCs - are a copy of it (hence are all equal to the join of their - original VCs.) */ - so = libhb_so_alloc(); + do_barrier_cross_sync_and_empty(bar); +} - /* XXX check ->waiting has no duplicates */ - tl_assert(bar->waiting); - tl_assert(VG_(sizeXA)(bar->waiting) == bar->size); +static void evh__HG_PTHREAD_BARRIER_RESIZE_PRE ( ThreadId tid, + void* barrier, + UWord newcount ) +{ + Thread* thr; + Bar* bar; + UWord present; - /* compute the join ... */ - for (i = 0; i < bar->size; i++) { - Thread* t = *(Thread**)VG_(indexXA)(bar->waiting, i); - Thr* hbthr = t->hbthr; - libhb_so_send( hbthr, so, False/*weak send*/ ); + if (SHOW_EVENTS >= 1) + VG_(printf)("evh__HG_PTHREAD_BARRIER_RESIZE_PRE" + "(tid=%d, barrier=%p, newcount=%lu)\n", + (Int)tid, (void*)barrier, newcount ); + + thr = map_threads_maybe_lookup( tid ); + tl_assert(thr); /* cannot fail - Thread* must already exist */ + + bar = map_barrier_to_Bar_lookup_or_alloc(barrier); + tl_assert(bar); + + if (!bar->initted) { + HG_(record_error_Misc)( + thr, "pthread_barrier_resize: barrier is uninitialised" + ); + return; /* client is broken .. avoid assertions below */ } - /* ... and distribute to all threads */ - for (i = 0; i < bar->size; i++) { - Thread* t = *(Thread**)VG_(indexXA)(bar->waiting, i); - Thr* hbthr = t->hbthr; - libhb_so_recv( hbthr, so, True/*strong recv*/ ); + + if (!bar->resizable) { + HG_(record_error_Misc)( + thr, "pthread_barrier_resize: barrier is may not be resized" + ); + return; /* client is broken .. avoid assertions below */ } - /* finally, we must empty out the waiting vector */ - VG_(dropTailXA)(bar->waiting, VG_(sizeXA)(bar->waiting)); + if (newcount == 0) { + HG_(record_error_Misc)( + thr, "pthread_barrier_resize: 'newcount' argument is zero" + ); + return; /* client is broken .. avoid assertions below */ + } - /* and we don't need this any more. Perhaps a stack-allocated - SO would be better? */ - libhb_so_dealloc(so); + /* guaranteed by _INIT_PRE above */ + tl_assert(bar->size > 0); + tl_assert(bar->waiting); + /* Guaranteed by this fn */ + tl_assert(newcount > 0); + + if (newcount >= bar->size) { + /* Increasing the capacity. There's no possibility of threads + moving on from the barrier in this situation, so just note + the fact and do nothing more. */ + bar->size = newcount; + } else { + /* Decreasing the capacity. If we decrease it to be equal or + below the number of waiting threads, they will now move past + the barrier, so need to mess with dep edges in the same way + as if the barrier had filled up normally. */ + present = VG_(sizeXA)(bar->waiting); + tl_assert(present >= 0 && present <= bar->size); + if (newcount <= present) { + bar->size = present; /* keep the cross_sync call happy */ + do_barrier_cross_sync_and_empty(bar); + } + bar->size = newcount; + } } @@ -4157,6 +4255,22 @@ Bool hg_handle_client_request ( ThreadId tid, UWord* args, UWord* ret) } break; + case _VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED: + if (0) VG_(printf)("HG_ARANGE_MAKE_UNTRACKED(%#lx,%ld)\n", + args[1], args[2]); + if (args[2] > 0) { /* length */ + evh__untrack_mem(args[1], args[2]); + } + break; + + case _VG_USERREQ__HG_ARANGE_MAKE_TRACKED: + if (0) VG_(printf)("HG_ARANGE_MAKE_TRACKED(%#lx,%ld)\n", + args[1], args[2]); + if (args[2] > 0) { /* length */ + evh__new_mem(args[1], args[2]); + } + break; + /* --- --- Client requests for Helgrind's use only --- --- */ /* Some thread is telling us its pthread_t value. Record the @@ -4327,8 +4441,15 @@ Bool hg_handle_client_request ( ThreadId tid, UWord* args, UWord* ret) break; case _VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE: - /* pth_bar_t*, ulong */ - evh__HG_PTHREAD_BARRIER_INIT_PRE( tid, (void*)args[1], args[2] ); + /* pth_bar_t*, ulong count, ulong resizable */ + evh__HG_PTHREAD_BARRIER_INIT_PRE( tid, (void*)args[1], + args[2], args[3] ); + break; + + case _VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE: + /* pth_bar_t*, ulong newcount */ + evh__HG_PTHREAD_BARRIER_RESIZE_PRE ( tid, (void*)args[1], + args[2] ); break; case _VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE: diff --git a/helgrind/libhb.h b/helgrind/libhb.h index 9ba465d8d7..ab4ba46ed3 100644 --- a/helgrind/libhb.h +++ b/helgrind/libhb.h @@ -126,7 +126,8 @@ void libhb_Thr_resumes ( Thr* thr ); /* Set memory address ranges to new (freshly allocated), or noaccess (no longer accessible). */ void libhb_srange_new ( Thr*, Addr, SizeT ); -void libhb_srange_noaccess ( Thr*, Addr, SizeT ); +void libhb_srange_noaccess ( Thr*, Addr, SizeT ); /* IS IGNORED */ +void libhb_srange_untrack ( Thr*, Addr, SizeT ); /* For the convenience of callers, we offer to store one void* item in a Thr, which we ignore, but the caller can get or set any time. */ diff --git a/helgrind/libhb_core.c b/helgrind/libhb_core.c index 0a07f4bedf..e5a058241b 100644 --- a/helgrind/libhb_core.c +++ b/helgrind/libhb_core.c @@ -5716,6 +5716,16 @@ void libhb_srange_noaccess ( Thr* thr, Addr a, SizeT szB ) /* do nothing */ } +void libhb_srange_untrack ( Thr* thr, Addr a, SizeT szB ) +{ + SVal sv = SVal_NOACCESS; + tl_assert(is_sane_SVal_C(sv)); + if (0 && TRACEME(a,szB)) trace(thr,a,szB,"untrack-before"); + zsm_sset_range( a, szB, sv ); + Filter__clear_range( thr->filter, a, szB ); + if (0 && TRACEME(a,szB)) trace(thr,a,szB,"untrack-after "); +} + void* libhb_get_Thr_opaque ( Thr* thr ) { tl_assert(thr); return thr->opaque;