Skip to content

Commit f5ff4e5

Browse files
vchuravyKeno
authored andcommitted
add ThreadSanitizer support
- enables building with TSAN for the runtime library as well as Julia code - updates the handling of `SANITIZE=1` in Make.inc - moves sanitizer to late in the pipeline, copies what Clang does - cleans up `options.h`, and `julia_internal.h` w.r.t sanitizers - update devdocs for sanitizer - adds a patch for TSAN to deal with Julia's usage of address spaces - don't use COPY_STACKS with TSAN - don't use DEEPBIND by default if a sanitizer is enabled
1 parent 1888e31 commit f5ff4e5

12 files changed

+151
-34
lines changed

Diff for: Make.inc

+18-8
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ ifeq ($(USE_LIBCPP),1)
492492
$(error USE_LIBCPP only supported with clang. Try setting USE_LIBCPP=0)
493493
endif
494494
ifeq ($(SANITIZE),1)
495-
$(error Address Sanitizer only supported with clang. Try setting SANITIZE=0)
495+
$(error Sanitizers are only supported with clang. Try setting SANITIZE=0)
496496
endif
497497
CC := $(CROSS_COMPILE)gcc
498498
CXX := $(CROSS_COMPILE)g++
@@ -539,7 +539,7 @@ ifeq ($(USE_LIBCPP),1)
539539
$(error USE_LIBCPP only supported with clang. Try setting USE_LIBCPP=0)
540540
endif
541541
ifeq ($(SANITIZE),1)
542-
$(error Address Sanitizer only supported with clang. Try setting SANITIZE=0)
542+
$(error Sanitizers only supported with clang. Try setting SANITIZE=0)
543543
endif
544544
CC := icc
545545
CXX := icpc
@@ -670,17 +670,27 @@ endif
670670
endif
671671

672672
ifeq ($(SANITIZE),1)
673+
SANITIZE_OPTS :=
674+
SANITIZE_LDFLAGS :=
673675
ifeq ($(SANITIZE_MEMORY),1)
674-
SANITIZE_OPTS := -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer
675-
SANITIZE_LDFLAGS := $(SANITIZE_OPTS)
676-
else
677-
SANITIZE_OPTS := -fsanitize=address -mllvm -asan-stack=0
678-
SANITIZE_LDFLAGS := -fsanitize=address
676+
SANITIZE_OPTS += -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer
677+
SANITIZE_LDFLAGS += $(SANITIZE_OPTS)
678+
endif
679+
ifeq ($(SANITIZE_ADDRESS),1)
680+
SANITIZE_OPTS += -fsanitize=address -mllvm -asan-stack=0
681+
SANITIZE_LDFLAGS += -fsanitize=address
682+
endif
683+
ifeq ($(SANITIZE_THREAD),1)
684+
SANITIZE_OPTS += -fsanitize=thread
685+
SANITIZE_LDFLAGS += -fsanitize=thread
686+
endif
687+
ifeq ($(SANITIZE_OPTS),)
688+
$(error SANITIZE=1, but no sanitizer selected, set either SANITIZE_MEMORY, SANITIZE_THREAD, or SANITIZE_ADDRESS)
679689
endif
680690
JCXXFLAGS += $(SANITIZE_OPTS)
681691
JCFLAGS += $(SANITIZE_OPTS)
682692
JLDFLAGS += $(SANITIZE_LDFLAGS)
683-
endif
693+
endif # SANITIZE
684694

685695
TAR := $(shell which gtar 2>/dev/null || which tar 2>/dev/null)
686696
TAR_TEST := $(shell $(TAR) --help 2>&1 | egrep 'bsdtar|strip-components')

Diff for: deps/llvm.mk

+3
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ $(eval $(call LLVM_PATCH,llvm-8.0-D66657-codegen-degenerate)) # remove for 10.0
410410
$(eval $(call LLVM_PATCH,llvm-8.0-D71495-vectorize-freduce)) # remove for 10.0
411411
$(eval $(call LLVM_PATCH,llvm-8.0-D75072-SCEV-add-type))
412412
$(eval $(call LLVM_PATCH,llvm-8.0-D65174-limit-merge-stores)) # remove for 10.0
413+
$(eval $(call LLVM_PATCH,llvm-julia-tsan-custom-as))
413414
endif # LLVM_VER 8.0
414415

415416
ifeq ($(LLVM_VER_SHORT),9.0)
@@ -427,6 +428,7 @@ $(eval $(call LLVM_PATCH,llvm-D75072-SCEV-add-type))
427428
$(eval $(call LLVM_PATCH,llvm-9.0-D65174-limit-merge-stores)) # remove for 10.0
428429
$(eval $(call LLVM_PATCH,llvm9-D71443-PPC-MC-redef-symbol)) # remove for 10.0
429430
$(eval $(call LLVM_PATCH,llvm-9.0-D78196)) # remove for 11.0
431+
$(eval $(call LLVM_PATCH,llvm-julia-tsan-custom-as))
430432
endif # LLVM_VER 9.0
431433

432434
ifeq ($(LLVM_VER_SHORT),10.0)
@@ -441,6 +443,7 @@ $(eval $(call LLVM_PATCH,llvm7-revert-D44485))
441443
$(eval $(call LLVM_PATCH,llvm-D75072-SCEV-add-type))
442444
$(eval $(call LLVM_PATCH,llvm-10.0-PPC_SELECT_CC)) # delete for LLVM 11
443445
$(eval $(call LLVM_PATCH,llvm-10.0-PPC-LI-Elimination)) # delete for LLVM 11
446+
$(eval $(call LLVM_PATCH,llvm-julia-tsan-custom-as))
444447
endif # LLVM_VER 10.0
445448

446449
# Add a JL prefix to the version map. DO NOT REMOVE

Diff for: deps/patches/llvm-julia-tsan-custom-as.patch

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
From bd41be423127b8946daea805290ad2eb19e66be4 Mon Sep 17 00:00:00 2001
2+
From: Valentin Churavy <v.churavy@gmail.com>
3+
Date: Sat, 19 May 2018 11:56:55 -0400
4+
Subject: [PATCH] [TSAN] Allow for custom address spaces
5+
6+
Julia uses addressspaces for GC and we want these to be sanitized as well.
7+
---
8+
lib/Transforms/Instrumentation/ThreadSanitizer.cpp | 4 +++-
9+
1 file changed, 3 insertions(+), 1 deletion(-)
10+
11+
diff --git a/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
12+
index ec6904486e1..9d673353f43 100644
13+
--- a/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
14+
+++ b/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
15+
@@ -296,7 +296,9 @@ static bool shouldInstrumentReadWriteFromAddress(const Module *M, Value *Addr) {
16+
// with them.
17+
if (Addr) {
18+
Type *PtrTy = cast<PointerType>(Addr->getType()->getScalarType());
19+
- if (PtrTy->getPointerAddressSpace() != 0)
20+
+ auto AS = PtrTy->getPointerAddressSpace();
21+
+ // Allow for custom addresspaces
22+
+ if (AS != 0 && AS < 10)
23+
return false;
24+
}
25+
26+
--
27+
2.17.0
28+

Diff for: doc/src/devdocs/sanitizers.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ An easy solution is to have an dedicated build folder for providing a matching t
1111
with `BUILD_LLVM_CLANG=1`. You can then refer to this toolchain from another build
1212
folder by specifying `USECLANG=1` while overriding the `CC` and `CXX` variables.
1313

14+
To use one of of the sanitizers set `SANITIZE=1` and then the appropriate flag for the sanitizer you
15+
want to use.
16+
1417
## Address Sanitizer (ASAN)
1518

1619
For detecting or debugging memory bugs, you can use Clang's [address sanitizer (ASAN)](http://clang.llvm.org/docs/AddressSanitizer.html).
17-
By compiling with `SANITIZE=1` you enable ASAN for the Julia compiler and its generated code.
20+
By compiling with `SANITIZE_ADDRESS=1` you enable ASAN for the Julia compiler and its generated code.
1821
In addition, you can specify `LLVM_SANITIZE=1` to sanitize the LLVM library as well. Note that
1922
these options incur a high performance and memory cost. For example, using ASAN for Julia and
2023
LLVM makes `testall1` takes 8-10 times as long while using 20 times as much memory (this can be
@@ -31,3 +34,8 @@ the future.
3134

3235
For detecting use of uninitialized memory, you can use Clang's [memory sanitizer (MSAN)](http://clang.llvm.org/docs/MemorySanitizer.html)
3336
by compiling with `SANITIZE_MEMORY=1`.
37+
38+
## Thread Sanitizer (TSAN)
39+
40+
For debugging data-races and other threading related issues you can use Clang's [thread sanitizer (TSAN)](https://clang.llvm.org/docs/ThreadSanitizer.html)
41+
by compiling with `SANITIZE_THREAD=1`.

Diff for: src/aotcompile.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#if defined(JL_ASAN_ENABLED)
2525
#include <llvm/Transforms/Instrumentation/AddressSanitizer.h>
2626
#endif
27+
#if defined(JL_TSAN_ENABLED)
28+
#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
29+
#endif
2730
#include <llvm/Transforms/Scalar/GVN.h>
2831
#include <llvm/Transforms/IPO/AlwaysInliner.h>
2932
#include <llvm/Transforms/InstCombine/InstCombine.h>
@@ -624,6 +627,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
624627
}
625628
PM->add(createMemCpyOptPass());
626629
PM->add(createAlwaysInlinerLegacyPass()); // Respect always_inline
630+
PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
627631
if (lower_intrinsics) {
628632
PM->add(createBarrierNoopPass());
629633
PM->add(createLowerExcHandlersPass());
@@ -641,6 +645,9 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
641645
#endif
642646
#if defined(JL_MSAN_ENABLED)
643647
PM->add(createMemorySanitizerPass(true));
648+
#endif
649+
#if defined(JL_TSAN_ENABLED)
650+
PM->add(createThreadSanitizerLegacyPassPass());
644651
#endif
645652
return;
646653
}
@@ -764,6 +771,9 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
764771
#if defined(JL_MSAN_ENABLED)
765772
PM->add(createMemorySanitizerPass(true));
766773
#endif
774+
#if defined(JL_TSAN_ENABLED)
775+
PM->add(createThreadSanitizerLegacyPassPass());
776+
#endif
767777
}
768778

769779
// An LLVM module pass that just runs all julia passes in order. Useful for

Diff for: src/codegen.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -5787,6 +5787,12 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
57875787
f->addFnAttr(Attribute::StackProtectStrong);
57885788
#endif
57895789

5790+
#ifdef JL_TSAN_ENABLED
5791+
// TODO: enable this only when a argument like `-race` is passed to Julia
5792+
// add a macro for no_sanitize_thread
5793+
f->addFnAttr(llvm::Attribute::SanitizeThread);
5794+
#endif
5795+
57905796
// add the optimization level specified for this module, if any
57915797
int optlevel = jl_get_module_optlevel(ctx.module);
57925798
if (optlevel >= 0 && optlevel <= 3) {

Diff for: src/dlload.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ JL_DLLEXPORT void *jl_dlopen(const char *filename, unsigned flags)
110110
#ifdef RTLD_NOLOAD
111111
| JL_RTLD(flags, NOLOAD)
112112
#endif
113-
#if defined(RTLD_DEEPBIND) && !defined(JL_ASAN_ENABLED)
113+
#if defined(RTLD_DEEPBIND) && !(defined(JL_ASAN_ENABLED) || defined(JL_TSAN_ENABLED) || defined(JL_MSAN_ENABLED))
114114
| JL_RTLD(flags, DEEPBIND)
115115
#endif
116116
#ifdef RTLD_FIRST

Diff for: src/julia.expmap

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
global:
33
__asan*;
4+
__tsan*;
5+
pthread*;
46
__stack_chk_guard;
57
asprintf;
68
bitvector_*;

Diff for: src/julia.h

+21
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@
6565
# define JL_THREAD_LOCAL
6666
#endif
6767

68+
// Duplicated from options.h
69+
#if defined(__has_feature) // Clang flavor
70+
#if __has_feature(address_sanitizer)
71+
#define JL_ASAN_ENABLED
72+
#endif
73+
#if __has_feature(memory_sanitizer)
74+
#define JL_MSAN_ENABLED
75+
#endif
76+
#if __has_feature(thread_sanitizer)
77+
#define JL_TSAN_ENABLED
78+
#endif
79+
#else // GCC flavor
80+
#if defined(__SANITIZE_ADDRESS__)
81+
#define JL_ASAN_ENABLED
82+
#endif
83+
#endif // __has_feature
84+
6885
#define container_of(ptr, type, member) \
6986
((type *) ((char *)(ptr) - offsetof(type, member)))
7087

@@ -1788,6 +1805,10 @@ typedef struct _jl_task_t {
17881805
unsigned int copy_stack:31; // sizeof stack for copybuf
17891806
unsigned int started:1;
17901807

1808+
#if defined(JL_TSAN_ENABLED)
1809+
void *tsan_state;
1810+
#endif
1811+
17911812
// current exception handler
17921813
jl_handler_t *eh;
17931814
// saved gc stack top for context switches

Diff for: src/julia_internal.h

+9-17
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,20 @@
1212
#define sleep(x) Sleep(1000*x)
1313
#endif
1414

15-
#if defined(__has_feature)
16-
#if __has_feature(address_sanitizer)
17-
#define JL_ASAN_ENABLED // Clang flavor
18-
#endif
19-
#elif defined(__SANITIZE_ADDRESS__)
20-
#define JL_ASAN_ENABLED // GCC flavor
21-
#endif
22-
23-
#ifdef JL_ASAN_ENABLED
2415
#ifdef __cplusplus
2516
extern "C" {
2617
#endif
18+
#ifdef JL_ASAN_ENABLED
2719
void __sanitizer_start_switch_fiber(void**, const void*, size_t);
2820
void __sanitizer_finish_switch_fiber(void*, const void**, size_t*);
29-
#ifdef __cplusplus
30-
}
31-
#endif
3221
#endif
33-
34-
#if defined(__has_feature)
35-
#if __has_feature(memory_sanitizer)
36-
#define JL_MSAN_ENABLED
22+
#ifdef JL_TSAN_ENABLED
23+
void *__tsan_create_fiber(unsigned flags);
24+
// void __tsan_destroy_fiber(void *fiber);
25+
void __tsan_switch_to_fiber(void *fiber, unsigned flags);
3726
#endif
27+
#ifdef __cplusplus
28+
}
3829
#endif
3930

4031
// Remove when C11 is required for C code.
@@ -321,7 +312,8 @@ jl_value_t *jl_permbox32(jl_datatype_t *t, int32_t x);
321312
jl_value_t *jl_permbox64(jl_datatype_t *t, int64_t x);
322313
jl_svec_t *jl_perm_symsvec(size_t n, ...);
323314

324-
#if !defined(__clang_analyzer__) && !defined(JL_ASAN_ENABLED) // this sizeof(__VA_ARGS__) trick can't be computed until C11, but that only matters to Clang in some situations
315+
// this sizeof(__VA_ARGS__) trick can't be computed until C11, but that only matters to Clang in some situations
316+
#if !defined(__clang_analyzer__) && !(defined(JL_ASAN_ENABLED) || defined(JL_TSAN_ENABLED))
325317
#ifdef __GNUC__
326318
#define jl_perm_symsvec(n, ...) \
327319
(jl_perm_symsvec)(__extension__({ \

Diff for: src/options.h

+13-6
Original file line numberDiff line numberDiff line change
@@ -154,26 +154,33 @@
154154

155155
// sanitizer defaults ---------------------------------------------------------
156156

157-
// XXX: these macros are duplicated from julia_internal.h
158-
#if defined(__has_feature)
157+
#if defined(__has_feature) // Clang flavor
159158
#if __has_feature(address_sanitizer)
160159
#define JL_ASAN_ENABLED
161160
#endif
162-
#elif defined(__SANITIZE_ADDRESS__)
163-
#define JL_ASAN_ENABLED
164-
#endif
165-
#if defined(__has_feature)
166161
#if __has_feature(memory_sanitizer)
167162
#define JL_MSAN_ENABLED
168163
#endif
164+
#if __has_feature(thread_sanitizer)
165+
#define JL_TSAN_ENABLED
166+
#endif
167+
#else // GCC flavor
168+
#if defined(__SANITIZE_ADDRESS__)
169+
#define JL_ASAN_ENABLED
169170
#endif
171+
#endif // __has_feature
170172

171173
// Automatically enable MEMDEBUG and KEEP_BODIES for the sanitizers
172174
#if defined(JL_ASAN_ENABLED) || defined(JL_MSAN_ENABLED)
173175
#define MEMDEBUG
174176
#define KEEP_BODIES
175177
#endif
176178

179+
// TSAN doesn't like COPY_STACKS
180+
#if defined(JL_TSAN_ENABLED) && defined(COPY_STACKS)
181+
#undef COPY_STACKS
182+
#endif
183+
177184
// Memory sanitizer needs TLS, which llvm only supports for the small memory model
178185
#if defined(JL_MSAN_ENABLED)
179186
// todo: fix the llvm MemoryManager to work with small memory model

0 commit comments

Comments
 (0)