// Test that dyld interposition works in the presence of DYLD_INSERT_LIBRARIES. // Additionally, the injected library also has a pthread introspection hook that // calls intercepted APIs before and after calling through to the TSan hook. // This mirrors what libBacktraceRecording.dylib (Xcode 'Queue Debugging' // feature) does. // RUN: %clang_tsan %s -o %t // RUN: %clang_tsan %s -o %t.dylib -fno-sanitize=thread -dynamiclib -DSHARED_LIB // // RUN: env DYLD_INSERT_LIBRARIES=%t.dylib %run %t 2>&1 | FileCheck %s --implicit-check-not='ThreadSanitizer' // // XFAIL: ios #include #include #include #include #if defined(SHARED_LIB) enum { PTHREAD_INTROSPECTION_THREAD_CREATE = 1, PTHREAD_INTROSPECTION_THREAD_START, PTHREAD_INTROSPECTION_THREAD_TERMINATE, PTHREAD_INTROSPECTION_THREAD_DESTROY, }; typedef void (*pthread_introspection_hook_t)(unsigned int event, pthread_t thread, void *addr, size_t size); extern pthread_introspection_hook_t pthread_introspection_hook_install( pthread_introspection_hook_t hook); static pthread_introspection_hook_t previous_pthread_hook; static void pthread_introspection_hook(unsigned int event, pthread_t thread, void *addr, size_t size) { pthread_t self; const unsigned k_max_thread_name_size = 64; char name[k_max_thread_name_size]; // Use some intercepted APIs *before* TSan hook runs. { self = pthread_self(); pthread_getname_np(self, name, k_max_thread_name_size); if (strlen(name) == 0) { strlcpy(name, "n/a", 4); } } // This calls through to the TSan-installed hook, because the injected library // constructor (see __library_initializer() below) runs after the TSan // initializer. It replaces and forward to the previously-installed TSan // introspection hook (very similar to what libBacktraceRecording.dylib does). assert(previous_pthread_hook); previous_pthread_hook(event, thread, addr, size); // Use some intercepted APIs *after* TSan hook runs. { assert(self == pthread_self()); char name2[k_max_thread_name_size]; pthread_getname_np(self, name2, k_max_thread_name_size); if (strlen(name2) == 0) { strlcpy(name2, "n/a", 4); } assert(strcmp(name, name2) == 0); } switch (event) { case PTHREAD_INTROSPECTION_THREAD_CREATE: fprintf(stderr, "THREAD_CREATE %p, self: %p, name: %s\n", thread, self, name); break; case PTHREAD_INTROSPECTION_THREAD_START: fprintf(stderr, "THREAD_START %p, self: %p, name: %s\n", thread, self, name); break; case PTHREAD_INTROSPECTION_THREAD_TERMINATE: fprintf(stderr, "THREAD_TERMINATE %p, self: %p, name: %s\n", thread, self, name); break; case PTHREAD_INTROSPECTION_THREAD_DESTROY: fprintf(stderr, "THREAD_DESTROY %p, self: %p, name: %s\n", thread, self, name); break; } } __attribute__((constructor)) static void __library_initializer(void) { fprintf(stderr, "__library_initializer\n"); previous_pthread_hook = pthread_introspection_hook_install(pthread_introspection_hook); } #else // defined(SHARED_LIB) void *Thread(void *a) { pthread_setname_np("child thread"); fprintf(stderr, "Hello from pthread\n"); return NULL; } int main() { fprintf(stderr, "main\n"); pthread_t t; pthread_create(&t, NULL, Thread, NULL); pthread_join(t, NULL); fprintf(stderr, "Done.\n"); } #endif // defined(SHARED_LIB) // CHECK: __library_initializer // CHECK: main // Ignore TSan background thread. // CHECK: THREAD_CREATE // CHECK: THREAD_CREATE [[CHILD:0x[0-9a-f]+]] // CHECK: THREAD_START [[CHILD]], self: [[CHILD]], name: n/a // CHECK: Hello from pthread // CHECK: THREAD_TERMINATE [[CHILD]], self: [[CHILD]], name: child thread // CHECK: THREAD_DESTROY [[CHILD]]