Skip to content

Commit d862442

Browse files
authored
add ThreadSanitizer support (#27167)
- 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 c342ef8 commit d862442

File tree

14 files changed

+145
-44
lines changed

14 files changed

+145
-44
lines changed

Make.inc

Lines changed: 18 additions & 8 deletions
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')

deps/llvm.mk

Lines changed: 3 additions & 0 deletions
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
Lines changed: 28 additions & 0 deletions
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+

doc/src/devdocs/sanitizers.md

Lines changed: 9 additions & 1 deletion
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`.

src/aotcompile.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include "llvm-version.h"
44
#include "platform.h"
5-
#include "options.h"
65

76
// target support
87
#include <llvm/ADT/Triple.h>
@@ -24,6 +23,9 @@
2423
#if defined(JL_ASAN_ENABLED)
2524
#include <llvm/Transforms/Instrumentation/AddressSanitizer.h>
2625
#endif
26+
#if defined(JL_TSAN_ENABLED)
27+
#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
28+
#endif
2729
#include <llvm/Transforms/Scalar/GVN.h>
2830
#include <llvm/Transforms/IPO/AlwaysInliner.h>
2931
#include <llvm/Transforms/InstCombine/InstCombine.h>
@@ -624,6 +626,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
624626
}
625627
PM->add(createMemCpyOptPass());
626628
PM->add(createAlwaysInlinerLegacyPass()); // Respect always_inline
629+
PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
627630
if (lower_intrinsics) {
628631
PM->add(createBarrierNoopPass());
629632
PM->add(createLowerExcHandlersPass());
@@ -641,6 +644,9 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
641644
#endif
642645
#if defined(JL_MSAN_ENABLED)
643646
PM->add(createMemorySanitizerPass(true));
647+
#endif
648+
#if defined(JL_TSAN_ENABLED)
649+
PM->add(createThreadSanitizerLegacyPassPass());
644650
#endif
645651
return;
646652
}
@@ -766,6 +772,9 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
766772
#if defined(JL_MSAN_ENABLED)
767773
PM->add(createMemorySanitizerPass(true));
768774
#endif
775+
#if defined(JL_TSAN_ENABLED)
776+
PM->add(createThreadSanitizerLegacyPassPass());
777+
#endif
769778
}
770779

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

src/cgmemmgr.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include "llvm-version.h"
44
#include "platform.h"
5-
#include "options.h"
65

76
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
87
#include "julia.h"

src/codegen.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include "llvm-version.h"
44
#include "platform.h"
5-
#include "options.h"
65
#if defined(_OS_WINDOWS_)
76
// use ELF because RuntimeDyld COFF i686 support didn't exist
87
// use ELF because RuntimeDyld COFF X86_64 doesn't seem to work (fails to generate function pointers)?
@@ -5787,6 +5786,12 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
57875786
f->addFnAttr(Attribute::StackProtectStrong);
57885787
#endif
57895788

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

src/dlload.c

Lines changed: 1 addition & 1 deletion
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

src/jitlayers.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
#include "llvm-version.h"
66
#include "platform.h"
7-
#include "options.h"
87

98
#include <llvm/Transforms/Utils/Cloning.h>
109
#include <llvm/Transforms/Utils/ModuleUtils.h>

src/julia.expmap

Lines changed: 2 additions & 0 deletions
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_*;

0 commit comments

Comments
 (0)