summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKuba Brecka <kuba.brecka@gmail.com>2015-11-04 15:09:14 +0000
committerKuba Brecka <kuba.brecka@gmail.com>2015-11-04 15:09:14 +0000
commit9798c96cf144cfb203756457a0cdb751b498a625 (patch)
treec4b541f8cc7b0493109ff584cba2e7cce6aab5cc
parentd486db8e9130049bd6717c1c5761e25c83c39840 (diff)
[tsan] Handle libdispatch worker threads on OS X
On OS X, GCD worker threads are created without a call to pthread_create. We need to properly register these threads with ThreadCreate and ThreadStart. This patch uses a libpthread API (`pthread_introspection_hook_install`) to get notifications about new threads and about threads that are about to be destroyed. Differential Revision: http://reviews.llvm.org/D14328 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@252049 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/tsan/rtl/tsan_clock.cc2
-rw-r--r--lib/tsan/rtl/tsan_defs.h2
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc22
-rw-r--r--lib/tsan/rtl/tsan_platform.h2
-rw-r--r--lib/tsan/rtl/tsan_platform_mac.cc40
-rw-r--r--lib/tsan/rtl/tsan_rtl_thread.cc8
6 files changed, 63 insertions, 13 deletions
diff --git a/lib/tsan/rtl/tsan_clock.cc b/lib/tsan/rtl/tsan_clock.cc
index 59e3de435..1e2050d1f 100644
--- a/lib/tsan/rtl/tsan_clock.cc
+++ b/lib/tsan/rtl/tsan_clock.cc
@@ -90,8 +90,6 @@
namespace __tsan {
-const unsigned kInvalidTid = (unsigned)-1;
-
ThreadClock::ThreadClock(unsigned tid, unsigned reused)
: tid_(tid)
, reused_(reused + 1) { // 0 has special meaning
diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h
index d869d95e0..385e366c3 100644
--- a/lib/tsan/rtl/tsan_defs.h
+++ b/lib/tsan/rtl/tsan_defs.h
@@ -83,6 +83,8 @@ const bool kCollectHistory = false;
const bool kCollectHistory = true;
#endif
+const unsigned kInvalidTid = (unsigned)-1;
+
// The following "build consistency" machinery ensures that all source files
// are built in the same configuration. Inconsistent builds lead to
// hard to debug crashes.
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index c9dc34934..0d9bba5b2 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -812,6 +812,18 @@ extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) {
atomic_store(g, 0, memory_order_relaxed);
}
+namespace __tsan {
+void DestroyThreadState() {
+ ThreadState *thr = cur_thread();
+ ThreadFinish(thr);
+ ThreadSignalContext *sctx = thr->signal_ctx;
+ if (sctx) {
+ thr->signal_ctx = 0;
+ UnmapOrDie(sctx, sizeof(*sctx));
+ }
+}
+} // namespace __tsan
+
static void thread_finalize(void *v) {
uptr iter = (uptr)v;
if (iter > 1) {
@@ -821,15 +833,7 @@ static void thread_finalize(void *v) {
}
return;
}
- {
- ThreadState *thr = cur_thread();
- ThreadFinish(thr);
- ThreadSignalContext *sctx = thr->signal_ctx;
- if (sctx) {
- thr->signal_ctx = 0;
- UnmapOrDie(sctx, sizeof(*sctx));
- }
- }
+ DestroyThreadState();
}
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index 87169fc08..57cd86d83 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -355,6 +355,8 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
void *abstime), void *c, void *m, void *abstime,
void(*cleanup)(void *arg), void *arg);
+void DestroyThreadState();
+
} // namespace __tsan
#endif // TSAN_PLATFORM_H
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
index 550f9705e..4677adda3 100644
--- a/lib/tsan/rtl/tsan_platform_mac.cc
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -54,11 +54,51 @@ void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
void InitializeShadowMemoryPlatform() { }
#endif
+// On OS X, GCD worker threads are created without a call to pthread_create. We
+// need to properly register these threads with ThreadCreate and ThreadStart.
+// These threads don't have a parent thread, as they are created "spuriously".
+// We're using a libpthread API that notifies us about a newly created thread.
+// The `thread == pthread_self()` check indicates this is actually a worker
+// thread. If it's just a regular thread, this hook is called on the parent
+// thread.
+typedef void (*pthread_introspection_hook_t)(unsigned int event,
+ pthread_t thread, void *addr,
+ size_t size);
+extern "C" pthread_introspection_hook_t pthread_introspection_hook_install(
+ pthread_introspection_hook_t hook);
+static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1;
+static const uptr PTHREAD_INTROSPECTION_THREAD_DESTROY = 4;
+static pthread_introspection_hook_t prev_pthread_introspection_hook;
+static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
+ void *addr, size_t size) {
+ if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
+ if (thread == pthread_self()) {
+ // The current thread is a newly created GCD worker thread.
+ ThreadState *parent_thread_state = nullptr; // No parent.
+ int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);
+ CHECK_NE(tid, 0);
+ ThreadState *thr = cur_thread();
+ ThreadStart(thr, tid, GetTid());
+ }
+ } else if (event == PTHREAD_INTROSPECTION_THREAD_DESTROY) {
+ ThreadState *thr = cur_thread();
+ if (thr->tctx->parent_tid == kInvalidTid) {
+ DestroyThreadState();
+ }
+ }
+
+ if (prev_pthread_introspection_hook != nullptr)
+ prev_pthread_introspection_hook(event, thread, addr, size);
+}
+
void InitializePlatform() {
DisableCoreDumperIfNecessary();
#ifndef SANITIZER_GO
CheckAndProtect();
#endif
+
+ prev_pthread_introspection_hook =
+ pthread_introspection_hook_install(&my_pthread_introspection_hook);
}
#ifndef SANITIZER_GO
diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index 66c78cfdd..dcae255f7 100644
--- a/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -55,6 +55,8 @@ void ThreadContext::OnCreated(void *arg) {
if (tid == 0)
return;
OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg);
+ if (!args->thr) // GCD workers don't have a parent thread.
+ return;
args->thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0);
@@ -231,8 +233,10 @@ int ThreadCount(ThreadState *thr) {
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
StatInc(thr, StatThreadCreate);
OnCreatedArgs args = { thr, pc };
- int tid = ctx->thread_registry->CreateThread(uid, detached, thr->tid, &args);
- DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid);
+ u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers.
+ int tid =
+ ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args);
+ DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid);
StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads());
return tid;
}