This is an archive of the discontinued LLVM Phabricator instance.

[tsan] Handle libdispatch worker threads on OS X
ClosedPublic

Authored by kubamracek on Nov 4 2015, 3:36 AM.

Details

Summary

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. If we don't do that, the first interceptor called on such a thread will crash.

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. The worker threads don't have a parent thread, as they are created "spuriously", so a few internal functions need to be modified to accept an invalid (kInvalidTid) parent thread ID.

(This is part of an effort to port TSan to OS X, and it's one the very first steps. Don't expect TSan on OS X to actually work or pass tests at this point.)

Diff Detail

Repository
rL LLVM

Event Timeline

kubamracek updated this revision to Diff 39181.Nov 4 2015, 3:36 AM
kubamracek retitled this revision from to [tsan] Handle libdispatch worker threads on OS X.
kubamracek updated this object.
kubamracek added reviewers: samsonov, kcc, dvyukov, glider.
dvyukov added inline comments.Nov 4 2015, 4:29 AM
lib/tsan/rtl/tsan_platform_mac.cc
95 ↗(On Diff #39181)

Don't we need to pass detached=true?
If a thread is not detached, runtime will wait for pthread_join call before getting rid of all associated state.

97 ↗(On Diff #39181)

call this var 'thr'
each and every reference to the current thread is called 'thr' throughout the runtime code

101 ↗(On Diff #39181)

same here

107 ↗(On Diff #39181)

I guess the order will actually be reversed, because user will install the hook after tsan runtime. So if user hook calls any intercepted functions or is instrumented, this still will crash.
We can solve it later.

lib/tsan/rtl/tsan_rtl.h
668 ↗(On Diff #39181)

I think it belongs to tsan_defs.h
tsan_rtl.h includes a bunch of tsan headers, so we won't be able to use the const in any of these headers. For this reason the very base, non-dependent on anything things are declared in tsan_defs.h

lib/tsan/rtl/tsan_rtl_thread.cc
58 ↗(On Diff #39181)

please-please-please one statement per line

kubamracek updated this revision to Diff 39192.Nov 4 2015, 5:08 AM

Updating patch.

I guess the order will actually be reversed, because user will install the hook after tsan runtime. So if user hook calls any intercepted functions or is instrumented, this still will crash. We can solve it later.

The API is actually private and should not be used by user code (although the header files and API declarations are publicly available on opensource.apple.com). The only user of this hook that I know of is Xcode's debugger, which actually calls the previous function at the beginning of its hook, so we're fine here. Anyway, if this becomes an issue later, I'll take a look at it.

dvyukov accepted this revision.Nov 4 2015, 6:05 AM
dvyukov edited edge metadata.

LGTM

The only user of this hook that I know of is Xcode's debugger, which actually calls the previous function at the beginning of its hook, so we're fine here.

ack

This revision is now accepted and ready to land.Nov 4 2015, 6:05 AM
This revision was automatically updated to reflect the committed changes.

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.

How do we deal with these in ASan?

How do we deal with these in ASan?

In asan we can deal with the case when we don't have an initialized thread:

AsanThread *t = GetCurrentThread();
void *allocated;
bool check_rss_limit = true;
if (t) {
  AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
  allocated =
      allocator.Allocate(cache, needed_size, 8, false, check_rss_limit);
} else {
  SpinMutexLock l(&fallback_mutex);
  AllocatorCache *cache = &fallback_allocator_cache;
  allocated =
      allocator.Allocate(cache, needed_size, 8, false, check_rss_limit);
}

That was added due to the same problems we are now struggling with tsan (bootstrap, thread start/end on different platforms). But it was easier to solve as in asan thread state is used only as an optimization. This path uses a global mutex for all allocations and deallocations, and so is extremely slow, that is fine because it is meant to be used only during said short periods of time.

Now I wonder if this path is actually heavily used in some contexts (e.g. mac + gcd), but nobody noticed.