forked from OSchip/llvm-project
114 lines
3.8 KiB
C
114 lines
3.8 KiB
C
// 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 <assert.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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]+]], self: [[MAIN:0x[0-9a-f]+]], name: n/a
|
|
// 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]], self: [[MAIN]]
|
|
// CHECK: Done.
|