1// Test that dyld interposition works in the presence of DYLD_INSERT_LIBRARIES.
2// Additionally, the injected library also has a pthread introspection hook that
3// 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 %t
8// RUN: %clang_tsan %s -o %t.dylib -fno-sanitize=thread -dynamiclib -DSHARED_LIB
9//
10// RUN: env DYLD_INSERT_LIBRARIES=%t.dylib %run %t 2>&1 | FileCheck %s --implicit-check-not='ThreadSanitizer'
11//
12// XFAIL: ios
13
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 library
48 // constructor (see __library_initializer() below) runs after the TSan
49 // initializer. It replaces and forward to the previously-installed TSan
50 // 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, format: "Hello from pthread\n");
92 return NULL;
93}
94
95int main() {
96 fprintf(stderr, format: "main\n");
97 pthread_t t;
98 pthread_create(newthread: &t, NULL, start_routine: Thread, NULL);
99 pthread_join(th: t, NULL);
100 fprintf(stderr, format: "Done.\n");
101}
102#endif // defined(SHARED_LIB)
103
104// CHECK: __library_initializer
105// CHECK: main
106// Ignore TSan background thread.
107// CHECK: THREAD_CREATE
108// CHECK: THREAD_CREATE [[CHILD:0x[0-9a-f]+]]
109// CHECK: THREAD_START [[CHILD]], self: [[CHILD]], name: n/a
110// CHECK: Hello from pthread
111// CHECK: THREAD_TERMINATE [[CHILD]], self: [[CHILD]], name: child thread
112// CHECK: THREAD_DESTROY [[CHILD]]
113

source code of compiler-rt/test/tsan/Darwin/dyld-insert-libraries.c