Skip to content

Commit 7c0cb30

Browse files
authored
Correct TSAN tasking integration (#36929)
In order for tsan to work, any setjmp/longjmp must be executed while that task's tsan state is active. As a result, we cannot switch the tsan state until said setjmp is completed, but need to do it (as basically the only thing happening) between the setjmp and the subsequent longjmp. To facilitate this without too much disruption, move the tsan state into the jl_ucontext_t, which seems appropriate since it's additional state that needs to be restored on context switch. Also forbid using TSAN from Clang < 11, where the runtime library has bugs that cause us to exhaust the maximum number of allowed mappings.
1 parent de7442e commit 7c0cb30

File tree

8 files changed

+173
-72
lines changed

8 files changed

+173
-72
lines changed

Make.inc

+4
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,10 @@ CXX_DISABLE_ASSERTION := -DJL_NDEBUG
431431
DISABLE_ASSERTIONS := -DNDEBUG -DJL_NDEBUG
432432
endif
433433

434+
ifeq ($(LLVM_ASSERTIONS),0)
435+
CXX_DISABLE_ASSERTION += -DNDEBUG
436+
endif
437+
434438
# Compiler specific stuff
435439

436440
ifeq ($(USEMSVC), 1)

src/aotcompile.cpp

-4
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,8 @@
2020
#include <llvm/Transforms/IPO.h>
2121
#include <llvm/Transforms/Scalar.h>
2222
#include <llvm/Transforms/Vectorize.h>
23-
#if defined(JL_ASAN_ENABLED)
2423
#include <llvm/Transforms/Instrumentation/AddressSanitizer.h>
25-
#endif
26-
#if defined(JL_TSAN_ENABLED)
2724
#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
28-
#endif
2925
#include <llvm/Transforms/Scalar/GVN.h>
3026
#include <llvm/Transforms/IPO/AlwaysInliner.h>
3127
#include <llvm/Transforms/InstCombine/InstCombine.h>

src/gc-stacks.c

+6
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,12 @@ void sweep_stack_pools(void)
226226
t->stkbuf = NULL;
227227
_jl_free_stack(ptls2, stkbuf, bufsz);
228228
}
229+
#ifdef JL_TSAN_ENABLED
230+
if (t->ctx.tsan_state) {
231+
__tsan_destroy_fiber(t->ctx.tsan_state);
232+
t->ctx.tsan_state = NULL;
233+
}
234+
#endif
229235
}
230236
if (n >= l - ndel)
231237
break;

src/julia.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
# define JL_THREAD_LOCAL
6666
#endif
6767

68-
// Duplicated from options.h
6968
#if defined(__has_feature) // Clang flavor
7069
#if __has_feature(address_sanitizer)
7170
#define JL_ASAN_ENABLED
@@ -74,6 +73,9 @@
7473
#define JL_MSAN_ENABLED
7574
#endif
7675
#if __has_feature(thread_sanitizer)
76+
#if __clang_major__ < 11
77+
#error Thread sanitizer runtime libraries in clang < 11 leak memory and cannot be used
78+
#endif
7779
#define JL_TSAN_ENABLED
7880
#endif
7981
#else // GCC flavor
@@ -1810,10 +1812,6 @@ typedef struct _jl_task_t {
18101812
unsigned int copy_stack:31; // sizeof stack for copybuf
18111813
unsigned int started:1;
18121814

1813-
#if defined(JL_TSAN_ENABLED)
1814-
void *tsan_state;
1815-
#endif
1816-
18171815
// current exception handler
18181816
jl_handler_t *eh;
18191817
// saved gc stack top for context switches

src/julia_internal.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ void __sanitizer_finish_switch_fiber(void*, const void**, size_t*);
2121
#endif
2222
#ifdef JL_TSAN_ENABLED
2323
void *__tsan_create_fiber(unsigned flags);
24-
// void __tsan_destroy_fiber(void *fiber);
24+
void *__tsan_get_current_fiber(void);
25+
void __tsan_destroy_fiber(void *fiber);
2526
void __tsan_switch_to_fiber(void *fiber, unsigned flags);
2627
#endif
2728
#ifdef __cplusplus

src/julia_threads.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ typedef win32_ucontext_t jl_ucontext_t;
4747
#if defined(JL_HAVE_ASM) || defined(JL_HAVE_SIGALTSTACK)
4848
typedef struct {
4949
jl_jmp_buf uc_mcontext;
50+
#if defined(JL_TSAN_ENABLED)
51+
void *tsan_state;
52+
#endif
5053
} jl_ucontext_t;
5154
#endif
5255
#if defined(JL_HAVE_ASYNCIFY)
56+
#if defined(JL_TSAN_ENABLED)
57+
#error TSAN not currently supported with asyncify
58+
#endif
5359
typedef struct {
5460
// This is the extent of the asyncify stack, but because the top of the
5561
// asyncify stack (stacktop) is also the bottom of the C stack, we can
@@ -62,7 +68,12 @@ typedef struct {
6268
#if defined(JL_HAVE_UCONTEXT) || defined(JL_HAVE_UNW_CONTEXT)
6369
#define UNW_LOCAL_ONLY
6470
#include <libunwind.h>
65-
typedef ucontext_t jl_ucontext_t;
71+
typedef struct {
72+
ucontext_t ctx;
73+
#if defined(JL_TSAN_ENABLED)
74+
void *tsan_state;
75+
#endif
76+
} jl_ucontext_t;
6677
#endif
6778
#endif
6879

src/support/win32_ucontext.h

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ typedef struct {
1616
size_t ss_size;
1717
} uc_stack;
1818
jmp_buf uc_mcontext;
19+
#ifdef JL_TSAN_ENABLED
20+
void *tsan_state;
21+
#endif
1922
} win32_ucontext_t;
2023
void jl_makecontext(win32_ucontext_t *ucp, void (*func)(void));
2124
void jl_swapcontext(win32_ucontext_t *oucp, const win32_ucontext_t *ucp);

0 commit comments

Comments
 (0)