Index: lib/tsan/CMakeLists.txt =================================================================== --- lib/tsan/CMakeLists.txt +++ lib/tsan/CMakeLists.txt @@ -47,6 +47,7 @@ if(APPLE) list(APPEND TSAN_SOURCES + rtl/tsan_libdispatch_mac.cc rtl/tsan_platform_mac.cc rtl/tsan_platform_posix.cc) elseif(UNIX) Index: lib/tsan/rtl/tsan_interceptors.h =================================================================== --- lib/tsan/rtl/tsan_interceptors.h +++ lib/tsan/rtl/tsan_interceptors.h @@ -26,6 +26,18 @@ (void)pc; \ /**/ +#define SCOPED_TSAN_INTERCEPTOR(func, ...) \ + SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ + if (REAL(func) == 0) { \ + Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ + Die(); \ + } \ + if (thr->ignore_interceptors || thr->in_ignored_lib) \ + return REAL(func)(__VA_ARGS__); \ +/**/ + +#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__) + #if SANITIZER_FREEBSD #define __libc_free __free #define __libc_malloc __malloc Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -264,17 +264,6 @@ } } -#define SCOPED_TSAN_INTERCEPTOR(func, ...) \ - SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ - if (REAL(func) == 0) { \ - Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ - Die(); \ - } \ - if (thr->ignore_interceptors || thr->in_ignored_lib) \ - return REAL(func)(__VA_ARGS__); \ -/**/ - -#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__) #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func) #if SANITIZER_FREEBSD # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func) Index: lib/tsan/rtl/tsan_libdispatch_mac.cc =================================================================== --- lib/tsan/rtl/tsan_libdispatch_mac.cc +++ lib/tsan/rtl/tsan_libdispatch_mac.cc @@ -0,0 +1,188 @@ +//===-- tsan_platform_mac.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Mac-specific libdispatch (GCD) support. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_common/sanitizer_common.h" +#include "interception/interception.h" +#include "tsan_interceptors.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" + +#include +#include + +namespace __tsan { + +typedef struct { + dispatch_queue_t queue; + void *orig_context; + dispatch_function_t orig_work; +} tsan_block_context_t; + +typedef struct { + dispatch_queue_t queue; + void *orig_context; + void (*orig_work)(void *, size_t); +} tsan_block_context_indexed_t; + +static tsan_block_context_t *AllocContext(dispatch_queue_t queue, + void *orig_context, + dispatch_function_t orig_work) { + tsan_block_context_t *new_context = (tsan_block_context_t *)internal_alloc( + MBlockScopedBuf, sizeof(tsan_block_context_t)); + new_context->queue = queue; + new_context->orig_context = orig_context; + new_context->orig_work = orig_work; + return new_context; +} + +static tsan_block_context_indexed_t *AllocContextIndexed( + dispatch_queue_t queue, void *orig_context, + void (*orig_work)(void *, size_t)) { + tsan_block_context_indexed_t *new_context = + (tsan_block_context_indexed_t *)internal_alloc( + MBlockScopedBuf, sizeof(tsan_block_context_t)); + new_context->queue = queue; + new_context->orig_context = orig_context; + new_context->orig_work = orig_work; + return new_context; +} + +static void dispatch_callback_wrap_acquire(void *param) { + SCOPED_INTERCEPTOR_RAW(dispatch_async_f_callback_wrap); + tsan_block_context_t *context = (tsan_block_context_t *)param; + Acquire(thr, pc, (uptr)context->queue); + context->orig_work(context->orig_context); + internal_free(context); +} + +static void dispatch_apply_callback_wrap_acquire(void *param, size_t index) { + SCOPED_INTERCEPTOR_RAW(dispatch_async_f_callback_wrap); + tsan_block_context_indexed_t *context = (tsan_block_context_indexed_t *)param; + Acquire(thr, pc, (uptr)context->queue); + context->orig_work(context->orig_context, index); +} + +#define RELEASE_ACQUIRE_QUEUE_BLOCK(func, queue, block, ...) \ + Release(thr, pc, (uptr)queue); \ + REAL(func)(__VA_ARGS__, ^{ \ + SCOPED_INTERCEPTOR_RAW(func); \ + Acquire(thr, pc, (uptr)queue); \ + block(); \ + }) +#define RELEASE_ACQUIRE_QUEUE_BLOCK_INDEX(func, queue, block, ...) \ + Release(thr, pc, (uptr)queue); \ + REAL(func)(__VA_ARGS__, ^(size_t index) { \ + SCOPED_INTERCEPTOR_RAW(func); \ + Acquire(thr, pc, (uptr)queue); \ + block(index); \ + }) +#define RELEASE_ACQUIRE_QUEUE_FUNCTION(func, queue, context, work, ...) \ + tsan_block_context_t *new_context = AllocContext(queue, context, work); \ + Release(thr, pc, (uptr)queue); \ + REAL(func)(__VA_ARGS__, new_context, dispatch_callback_wrap_acquire) +#define RELEASE_ACQUIRE_QUEUE_FUNCTION_INDEX(func, queue, context, work, ...) \ + tsan_block_context_indexed_t *new_context = \ + AllocContextIndexed(queue, context, work); \ + Release(thr, pc, (uptr)queue); \ + REAL(func)(__VA_ARGS__, new_context, dispatch_apply_callback_wrap_acquire); \ + internal_free(new_context) + +TSAN_INTERCEPTOR(void, dispatch_async, dispatch_queue_t queue, + dispatch_block_t block) { + SCOPED_TSAN_INTERCEPTOR(dispatch_async, queue, block); + RELEASE_ACQUIRE_QUEUE_BLOCK(dispatch_async, queue, block, queue); +} + +TSAN_INTERCEPTOR(void, dispatch_async_f, dispatch_queue_t queue, void *context, + dispatch_function_t work) { + SCOPED_TSAN_INTERCEPTOR(dispatch_async_f, queue, context, work); + RELEASE_ACQUIRE_QUEUE_FUNCTION(dispatch_async_f, queue, context, work, queue); +} + +TSAN_INTERCEPTOR(void, dispatch_sync, dispatch_queue_t queue, + dispatch_block_t block) { + SCOPED_TSAN_INTERCEPTOR(dispatch_sync, queue, block); + RELEASE_ACQUIRE_QUEUE_BLOCK(dispatch_sync, queue, block, queue); +} + +TSAN_INTERCEPTOR(void, dispatch_sync_f, dispatch_queue_t queue, void *context, + dispatch_function_t work) { + SCOPED_TSAN_INTERCEPTOR(dispatch_sync_f, queue, context, work); + RELEASE_ACQUIRE_QUEUE_FUNCTION(dispatch_sync_f, queue, context, work, queue); +} + +TSAN_INTERCEPTOR(void, dispatch_after, dispatch_time_t when, + dispatch_queue_t queue, dispatch_block_t block) { + SCOPED_TSAN_INTERCEPTOR(dispatch_after, when, queue, block); + RELEASE_ACQUIRE_QUEUE_BLOCK(dispatch_after, queue, block, when, queue); +} + +TSAN_INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, + dispatch_queue_t queue, void *context, + dispatch_function_t work) { + SCOPED_TSAN_INTERCEPTOR(dispatch_after_f, when, queue, context, work); + RELEASE_ACQUIRE_QUEUE_FUNCTION(dispatch_after_f, queue, context, work, when, + queue); +} + +TSAN_INTERCEPTOR(void, dispatch_apply, size_t iterations, + dispatch_queue_t queue, void (^block)(size_t)) { + SCOPED_TSAN_INTERCEPTOR(dispatch_apply, iterations, queue, block); + RELEASE_ACQUIRE_QUEUE_BLOCK_INDEX(dispatch_apply, queue, block, iterations, + queue); +} + +TSAN_INTERCEPTOR(void, dispatch_apply_f, size_t iterations, + dispatch_queue_t queue, void *context, + void (*work)(void *, size_t)) { + SCOPED_TSAN_INTERCEPTOR(dispatch_apply_f, iterations, queue, context, work); + RELEASE_ACQUIRE_QUEUE_FUNCTION_INDEX(dispatch_apply_f, queue, context, work, + iterations, queue); +} + +// GCD's dispatch_once implementation contains a fast path that contains an +// intentionally racy read and it's inlined into user's code. Furthermore, this +// fast path doesn't establish a proper happens-before relations between the +// initialization and code following the call to dispatch_once. We could deal +// with this in instrumented code, but there's not much we can do about it in +// system libraries. Let's instead ignore all accesses from the initializer, so +// we don't see false positives. +// Secondly, dispatch_once is both a macro and a real function, we want to +// intercept the function, so we need to undefine the macro. +#undef dispatch_once +TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate, + dispatch_block_t block) { + SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block); + REAL(dispatch_once)(predicate, ^{ + ThreadIgnoreBegin(thr, pc); + block(); + ThreadIgnoreEnd(thr, pc); + }); +} + +#undef dispatch_once_f +TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate, + void *context, dispatch_function_t function) { + SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function); + ThreadIgnoreBegin(thr, pc); + REAL(dispatch_once_f)(predicate, context, function); + ThreadIgnoreEnd(thr, pc); +} + +} // namespace __tsan + +#endif // SANITIZER_MAC Index: lib/tsan/rtl/tsan_suppressions.cc =================================================================== --- lib/tsan/rtl/tsan_suppressions.cc +++ lib/tsan/rtl/tsan_suppressions.cc @@ -31,7 +31,16 @@ // False positive when using std . // Happens because we miss atomic synchronization in libstdc++. // See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. -"race:std::_Sp_counted_ptr_inplace&1 + +#import + +long global; + +int main() { + NSLog(@"Hello world"); + + global = 42; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + global = 43; + }); + + [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]]; +} + +// CHECK: Hello world +// CHECK-NOT: WARNING: ThreadSanitizer