tsan.c   [plain text]


#include <darwintest.h>
#include <dlfcn.h>
#include <pthread.h>
#include <stdlib.h>

T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));

T_DECL(tsan_sanity, "TSan Sanity Check", T_META_CHECK_LEAKS(NO))
{
	void *tsan_dylib = dlopen("@rpath/libclang_rt.tsan_osx_dynamic.dylib", RTLD_NOLOAD);
	T_ASSERT_NOTNULL(tsan_dylib, "TSan dylib loaded");

	void *ptr = malloc(16);
	free(ptr);
		
	T_PASS("I didn't crash!");
}

typedef unsigned long long invisible_barrier_t;
void __tsan_testonly_barrier_init(invisible_barrier_t *barrier, unsigned count);
void __tsan_testonly_barrier_wait(invisible_barrier_t *barrier);
int __tsan_get_report_data(void *report, const char **description, int *count,
						   int *stack_count, int *mop_count, int *loc_count,
						   int *mutex_count, int *thread_count,
						   int *unique_tid_count, void **sleep_trace,
						   unsigned long trace_size);

bool tsan_report_hit = false;
char *tsan_description = NULL;
invisible_barrier_t barrier;

const char *__tsan_default_options() {
	return "abort_on_error=0:exitcode=0";
}

void __tsan_on_report(void *report) {
	tsan_report_hit = true;

	const char *description;
	int count;
	int stack_count, mop_count, loc_count, mutex_count, thread_count,
	unique_tid_count;
	void *sleep_trace[16] = {0};
	__tsan_get_report_data(report, &description, &count, &stack_count, &mop_count,
						   &loc_count, &mutex_count, &thread_count,
						   &unique_tid_count, sleep_trace, 16);
	tsan_description = strdup(description);
}

void *thread1(void *arg) {
	__tsan_testonly_barrier_wait(&barrier);
	*((long *)arg) = 42;
	return NULL;
}

void *thread2(void *arg) {
	*((long *)arg) = 43;
	__tsan_testonly_barrier_wait(&barrier);
	return NULL;
}

T_DECL(tsan_data_race_stack, "TSan Detects data-race on stack", T_META_CHECK_LEAKS(NO))
{
	tsan_description = NULL;
	tsan_report_hit = false;
	__tsan_testonly_barrier_init(&barrier, 2);

	long racy_stack_value = 0;
	pthread_t t1;
	pthread_create(&t1, NULL, thread1, &racy_stack_value);
	pthread_t t2;
	pthread_create(&t2, NULL, thread2, &racy_stack_value);
	pthread_join(t2, NULL);
	pthread_join(t1, NULL);

	T_EXPECT_EQ(tsan_report_hit, true, "tsan finds data-race");
	T_EXPECT_NOTNULL(strstr(tsan_description, "data-race"), "tsan header");
}

T_DECL(tsan_data_race_heap, "TSan Detects data-race on heap", T_META_CHECK_LEAKS(NO))
{
	tsan_description = NULL;
	tsan_report_hit = false;
	__tsan_testonly_barrier_init(&barrier, 2);

	long *racy_heap_value = malloc(sizeof(long));
	pthread_t t1;
	pthread_create(&t1, NULL, thread1, racy_heap_value);
	pthread_t t2;
	pthread_create(&t2, NULL, thread2, racy_heap_value);
	pthread_join(t2, NULL);
	pthread_join(t1, NULL);

	T_EXPECT_EQ(tsan_report_hit, true, "tsan finds data-race");
	T_EXPECT_NOTNULL(strstr(tsan_description, "data-race"), "tsan header");
}