113 lines · c
1// Test that dyld interposition works in the presence of DYLD_INSERT_LIBRARIES.2// Additionally, the injected library also has a pthread introspection hook that3// calls intercepted APIs before and after calling through to the TSan hook.4// This mirrors what libBacktraceRecording.dylib (Xcode 'Queue Debugging'5// feature) does.6 7// RUN: %clang_tsan %s -o %t8// RUN: %clang_tsan %s -o %t.dylib -fno-sanitize=thread -dynamiclib -DSHARED_LIB9//10// RUN: env DYLD_INSERT_LIBRARIES=%t.dylib %run %t 2>&1 | FileCheck %s --implicit-check-not='ThreadSanitizer'11//12// XFAIL: ios13 14#include <assert.h>15#include <pthread.h>16#include <stdio.h>17#include <string.h>18 19#if defined(SHARED_LIB)20enum {21 PTHREAD_INTROSPECTION_THREAD_CREATE = 1,22 PTHREAD_INTROSPECTION_THREAD_START,23 PTHREAD_INTROSPECTION_THREAD_TERMINATE,24 PTHREAD_INTROSPECTION_THREAD_DESTROY,25};26typedef void (*pthread_introspection_hook_t)(unsigned int event,27 pthread_t thread, void *addr,28 size_t size);29extern pthread_introspection_hook_t pthread_introspection_hook_install(30 pthread_introspection_hook_t hook);31 32static pthread_introspection_hook_t previous_pthread_hook;33static void pthread_introspection_hook(unsigned int event, pthread_t thread, void *addr, size_t size) {34 pthread_t self;35 const unsigned k_max_thread_name_size = 64;36 char name[k_max_thread_name_size];37 38 // Use some intercepted APIs *before* TSan hook runs.39 {40 self = pthread_self();41 pthread_getname_np(self, name, k_max_thread_name_size);42 if (strlen(name) == 0) {43 strlcpy(name, "n/a", 4);44 }45 }46 47 // This calls through to the TSan-installed hook, because the injected library48 // constructor (see __library_initializer() below) runs after the TSan49 // initializer. It replaces and forward to the previously-installed TSan50 // introspection hook (very similar to what libBacktraceRecording.dylib does).51 assert(previous_pthread_hook);52 previous_pthread_hook(event, thread, addr, size);53 54 // Use some intercepted APIs *after* TSan hook runs.55 {56 assert(self == pthread_self());57 char name2[k_max_thread_name_size];58 pthread_getname_np(self, name2, k_max_thread_name_size);59 if (strlen(name2) == 0) {60 strlcpy(name2, "n/a", 4);61 }62 assert(strcmp(name, name2) == 0);63 }64 65 switch (event) {66 case PTHREAD_INTROSPECTION_THREAD_CREATE:67 fprintf(stderr, "THREAD_CREATE %p, self: %p, name: %s\n", thread, self, name);68 break;69 case PTHREAD_INTROSPECTION_THREAD_START:70 fprintf(stderr, "THREAD_START %p, self: %p, name: %s\n", thread, self, name);71 break;72 case PTHREAD_INTROSPECTION_THREAD_TERMINATE:73 fprintf(stderr, "THREAD_TERMINATE %p, self: %p, name: %s\n", thread, self, name);74 break;75 case PTHREAD_INTROSPECTION_THREAD_DESTROY:76 fprintf(stderr, "THREAD_DESTROY %p, self: %p, name: %s\n", thread, self, name);77 break;78 }79}80 81__attribute__((constructor))82static void __library_initializer(void) {83 fprintf(stderr, "__library_initializer\n");84 previous_pthread_hook = pthread_introspection_hook_install(pthread_introspection_hook);85}86 87#else // defined(SHARED_LIB)88 89void *Thread(void *a) {90 pthread_setname_np("child thread");91 fprintf(stderr, "Hello from pthread\n");92 return NULL;93}94 95int main() {96 fprintf(stderr, "main\n");97 pthread_t t;98 pthread_create(&t, NULL, Thread, NULL);99 pthread_join(t, NULL);100 fprintf(stderr, "Done.\n");101}102#endif // defined(SHARED_LIB)103 104// CHECK: __library_initializer105// CHECK: main106// Ignore TSan background thread.107// CHECK: THREAD_CREATE108// CHECK: THREAD_CREATE [[CHILD:0x[0-9a-f]+]]109// CHECK: THREAD_START [[CHILD]], self: [[CHILD]], name: n/a110// CHECK: Hello from pthread111// CHECK: THREAD_TERMINATE [[CHILD]], self: [[CHILD]], name: child thread112// CHECK: THREAD_DESTROY [[CHILD]]113