aboutsummaryrefslogtreecommitdiff
path: root/lib/tsan
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tsan')
-rw-r--r--lib/tsan/CMakeLists.txt8
-rw-r--r--lib/tsan/Makefile.mk18
-rw-r--r--lib/tsan/Makefile.old106
-rwxr-xr-xlib/tsan/analyze_libtsan.sh43
-rw-r--r--lib/tsan/benchmarks/mini_bench_local.cc49
-rw-r--r--lib/tsan/benchmarks/mini_bench_shared.cc51
-rw-r--r--lib/tsan/benchmarks/start_many_threads.cc52
-rw-r--r--lib/tsan/benchmarks/vts_many_threads_bench.cc120
-rwxr-xr-xlib/tsan/check_analyze.sh43
-rwxr-xr-xlib/tsan/go/buildgo.sh78
-rw-r--r--lib/tsan/go/test.c51
-rw-r--r--lib/tsan/go/tsan_go.cc185
-rw-r--r--lib/tsan/output_tests/free_race.c43
-rw-r--r--lib/tsan/output_tests/free_race2.c26
-rw-r--r--lib/tsan/output_tests/heap_race.cc19
-rw-r--r--lib/tsan/output_tests/memcpy_race.cc40
-rw-r--r--lib/tsan/output_tests/mop_with_offset.cc36
-rw-r--r--lib/tsan/output_tests/mop_with_offset2.cc36
-rw-r--r--lib/tsan/output_tests/race_on_barrier.c31
-rw-r--r--lib/tsan/output_tests/race_on_barrier2.c30
-rw-r--r--lib/tsan/output_tests/race_on_mutex.c41
-rw-r--r--lib/tsan/output_tests/race_with_finished_thread.cc43
-rw-r--r--lib/tsan/output_tests/simple_race.c25
-rw-r--r--lib/tsan/output_tests/simple_race.cc24
-rw-r--r--lib/tsan/output_tests/simple_stack.c65
-rw-r--r--lib/tsan/output_tests/simple_stack2.cc46
-rw-r--r--lib/tsan/output_tests/static_init1.cc25
-rw-r--r--lib/tsan/output_tests/static_init2.cc31
-rw-r--r--lib/tsan/output_tests/static_init3.cc46
-rw-r--r--lib/tsan/output_tests/static_init4.cc35
-rw-r--r--lib/tsan/output_tests/static_init5.cc40
-rw-r--r--lib/tsan/output_tests/suppress_same_address.cc27
-rw-r--r--lib/tsan/output_tests/suppress_same_stacks.cc27
-rwxr-xr-xlib/tsan/output_tests/test_output.sh49
-rw-r--r--lib/tsan/output_tests/thread_leak.c15
-rw-r--r--lib/tsan/output_tests/thread_leak2.c15
-rw-r--r--lib/tsan/output_tests/thread_leak3.c14
-rw-r--r--lib/tsan/output_tests/tiny_race.c14
-rw-r--r--lib/tsan/output_tests/virtual_inheritance_compile_bug.cc13
-rw-r--r--lib/tsan/output_tests/vptr_benign_race.cc50
-rw-r--r--lib/tsan/output_tests/vptr_harmful_race.cc48
-rw-r--r--lib/tsan/rtl/Makefile.mk23
-rw-r--r--lib/tsan/rtl/Makefile.old59
-rw-r--r--lib/tsan/rtl/tsan_clock.cc118
-rw-r--r--lib/tsan/rtl/tsan_clock.h82
-rw-r--r--lib/tsan/rtl/tsan_defs.h139
-rw-r--r--lib/tsan/rtl/tsan_flags.cc79
-rw-r--r--lib/tsan/rtl/tsan_flags.h71
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc1596
-rw-r--r--lib/tsan/rtl/tsan_interface.cc42
-rw-r--r--lib/tsan/rtl/tsan_interface.h51
-rw-r--r--lib/tsan/rtl/tsan_interface_ann.cc352
-rw-r--r--lib/tsan/rtl/tsan_interface_ann.h31
-rw-r--r--lib/tsan/rtl/tsan_interface_atomic.cc321
-rw-r--r--lib/tsan/rtl/tsan_interface_atomic.h121
-rw-r--r--lib/tsan/rtl/tsan_interface_inl.h65
-rw-r--r--lib/tsan/rtl/tsan_md5.cc245
-rw-r--r--lib/tsan/rtl/tsan_mman.cc123
-rw-r--r--lib/tsan/rtl/tsan_mman.h114
-rw-r--r--lib/tsan/rtl/tsan_mutex.cc259
-rw-r--r--lib/tsan/rtl/tsan_mutex.h78
-rw-r--r--lib/tsan/rtl/tsan_platform.h101
-rw-r--r--lib/tsan/rtl/tsan_platform_linux.cc238
-rw-r--r--lib/tsan/rtl/tsan_platform_mac.cc112
-rw-r--r--lib/tsan/rtl/tsan_printf.cc39
-rw-r--r--lib/tsan/rtl/tsan_report.cc167
-rw-r--r--lib/tsan/rtl/tsan_report.h102
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc534
-rw-r--r--lib/tsan/rtl/tsan_rtl.h491
-rw-r--r--lib/tsan/rtl/tsan_rtl_amd64.S71
-rw-r--r--lib/tsan/rtl/tsan_rtl_mutex.cc220
-rw-r--r--lib/tsan/rtl/tsan_rtl_report.cc372
-rw-r--r--lib/tsan/rtl/tsan_rtl_thread.cc394
-rw-r--r--lib/tsan/rtl/tsan_stat.cc249
-rw-r--r--lib/tsan/rtl/tsan_stat.h254
-rw-r--r--lib/tsan/rtl/tsan_suppressions.cc163
-rw-r--r--lib/tsan/rtl/tsan_suppressions.h43
-rw-r--r--lib/tsan/rtl/tsan_symbolize.cc78
-rw-r--r--lib/tsan/rtl/tsan_symbolize.h31
-rw-r--r--lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc193
-rw-r--r--lib/tsan/rtl/tsan_sync.cc219
-rw-r--r--lib/tsan/rtl/tsan_sync.h106
-rw-r--r--lib/tsan/rtl/tsan_trace.h71
-rw-r--r--lib/tsan/rtl/tsan_update_shadow_word_inl.h79
-rw-r--r--lib/tsan/rtl/tsan_vector.h110
-rw-r--r--lib/tsan/rtl_tests/tsan_bench.cc105
-rw-r--r--lib/tsan/rtl_tests/tsan_mop.cc233
-rw-r--r--lib/tsan/rtl_tests/tsan_mutex.cc221
-rw-r--r--lib/tsan/rtl_tests/tsan_posix.cc146
-rw-r--r--lib/tsan/rtl_tests/tsan_string.cc82
-rw-r--r--lib/tsan/rtl_tests/tsan_test.cc44
-rw-r--r--lib/tsan/rtl_tests/tsan_test_util.h122
-rw-r--r--lib/tsan/rtl_tests/tsan_test_util_linux.cc465
-rw-r--r--lib/tsan/rtl_tests/tsan_thread.cc59
-rw-r--r--lib/tsan/unit_tests/tsan_clock_test.cc123
-rw-r--r--lib/tsan/unit_tests/tsan_flags_test.cc38
-rw-r--r--lib/tsan/unit_tests/tsan_mman_test.cc109
-rw-r--r--lib/tsan/unit_tests/tsan_mutex_test.cc126
-rw-r--r--lib/tsan/unit_tests/tsan_platform_test.cc88
-rw-r--r--lib/tsan/unit_tests/tsan_printf_test.cc106
-rw-r--r--lib/tsan/unit_tests/tsan_shadow_test.cc47
-rw-r--r--lib/tsan/unit_tests/tsan_suppressions_test.cc128
-rw-r--r--lib/tsan/unit_tests/tsan_sync_test.cc65
-rw-r--r--lib/tsan/unit_tests/tsan_vector_test.cc45
104 files changed, 12516 insertions, 0 deletions
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
new file mode 100644
index 000000000000..acfb854d2606
--- /dev/null
+++ b/lib/tsan/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Build for the AddressSanitizer runtime support library.
+
+file(GLOB TSAN_SOURCES "*.cc")
+
+if(CAN_TARGET_X86_64)
+ add_library(clang_rt.tsan-x86_64 STATIC ${TSAN_SOURCES})
+ set_target_properties(clang_rt.tsan-x86_64 PROPERTIES COMPILE_FLAGS "${TARGET_X86_64_CFLAGS}")
+endif()
diff --git a/lib/tsan/Makefile.mk b/lib/tsan/Makefile.mk
new file mode 100644
index 000000000000..70fb610bdc61
--- /dev/null
+++ b/lib/tsan/Makefile.mk
@@ -0,0 +1,18 @@
+#===- lib/tsan/Makefile.mk ---------------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := tsan
+SubDirs := rtl
+Sources :=
+ObjNames :=
+Dependencies :=
+
+Implementation := Generic
+
+TsanFunctions :=
diff --git a/lib/tsan/Makefile.old b/lib/tsan/Makefile.old
new file mode 100644
index 000000000000..2091f61335e9
--- /dev/null
+++ b/lib/tsan/Makefile.old
@@ -0,0 +1,106 @@
+DEBUG=0
+LDFLAGS=-ldl -lpthread -pie
+CXXFLAGS = -fPIE -g -Wall -Werror -DTSAN_DEBUG=$(DEBUG)
+# Silence warnings that Clang produces for gtest code.
+# Use -Wno-attributes so that gcc doesn't complain about unknown warning types.
+CXXFLAGS += -Wno-attributes
+ifeq ($(DEBUG), 0)
+ CXXFLAGS += -O3
+endif
+ifeq ($(CXX), clang++)
+ CXXFLAGS+= -Wno-unused-private-field -Wno-static-in-inline
+endif
+
+LIBTSAN=rtl/libtsan.a
+GTEST_ROOT=third_party/googletest
+GTEST_INCLUDE=-I$(GTEST_ROOT)/include
+GTEST_BUILD_DIR=$(GTEST_ROOT)/build
+GTEST_LIB=$(GTEST_BUILD_DIR)/gtest-all.o
+
+SANITIZER_COMMON_TESTS_SRC=$(wildcard ../sanitizer_common/tests/*_test.cc)
+SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_TESTS_SRC))
+RTL_TEST_SRC=$(wildcard rtl_tests/*.cc)
+RTL_TEST_OBJ=$(patsubst %.cc,%.o,$(RTL_TEST_SRC))
+UNIT_TEST_SRC=$(wildcard unit_tests/*_test.cc)
+UNIT_TEST_OBJ=$(patsubst %.cc,%.o,$(UNIT_TEST_SRC))
+UNIT_TEST_HDR=$(wildcard rtl/*.h) $(wildcard ../sanitizer_common/*.h)
+
+INCLUDES=-Irtl -I.. $(GTEST_INCLUDE)
+
+all: libtsan test
+
+help:
+ @ echo "A little help is always welcome!"
+ @ echo "The most useful targets are:"
+ @ echo " make install_deps # Install third-party dependencies required for building"
+ @ echo " make presubmit # Run it every time before committing"
+ @ echo " make lint # Run the style checker"
+ @ echo
+ @ echo "For more info, see http://code.google.com/p/data-race-test/wiki/ThreadSanitizer2"
+
+$(LIBTSAN): libtsan
+
+libtsan:
+ $(MAKE) -C rtl -f Makefile.old DEBUG=$(DEBUG)
+
+%.o: %.cc $(UNIT_TEST_HDR) $(LIBTSAN)
+ $(CXX) $(CXXFLAGS) $(CFLAGS) $(INCLUDES) -o $@ -c $<
+
+tsan_test: $(UNIT_TEST_OBJ) $(RTL_TEST_OBJ) \
+ $(SANITIZER_COMMON_TESTS_OBJ) $(LIBTSAN) $(GTEST_LIB)
+ $(CXX) $^ -o $@ $(LDFLAGS)
+
+test: libtsan tsan_test
+
+run: all
+ (ulimit -s 8192; ./tsan_test)
+ ./output_tests/test_output.sh
+
+presubmit:
+ $(MAKE) -f Makefile.old lint -j 4
+ # Debug build with clang.
+ $(MAKE) -f Makefile.old clean
+ $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=clang CXX=clang++
+ # Release build with clang.
+ $(MAKE) -f Makefile.old clean
+ $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=clang CXX=clang++
+ # Debug build with gcc
+ $(MAKE) -f Makefile.old clean
+ $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++
+ # Release build with gcc
+ $(MAKE) -f Makefile.old clean
+ $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++
+ ./check_analyze.sh
+ @ echo PRESUBMIT PASSED
+
+RTL_LINT_FITLER=-legal/copyright,-build/include,-readability/casting,-build/header_guard,-build/namespaces
+
+lint: lint_tsan lint_tests
+lint_tsan:
+ third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) rtl/*.{cc,h} \
+ ../sanitizer_common/*.{cc,h}
+lint_tests:
+ third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) \
+ rtl_tests/*.{cc,h} unit_tests/*.cc ../sanitizer_common/tests/*.cc
+
+install_deps:
+ rm -rf third_party
+ mkdir third_party
+ (cd third_party && \
+ svn co -r613 http://googletest.googlecode.com/svn/trunk googletest && \
+ svn co -r82 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint \
+ )
+
+# Remove verbose printf from lint. Not strictly necessary.
+hack_cpplint:
+ sed -i "s/ sys.stderr.write('Done processing.*//g" third_party/cpplint/cpplint.py
+
+$(GTEST_LIB):
+ mkdir -p $(GTEST_BUILD_DIR) && \
+ cd $(GTEST_BUILD_DIR) && \
+ $(MAKE) -f ../make/Makefile CXXFLAGS="$(CXXFLAGS)" CFLAGS="$(CFLAGS)" CC=$(CC) CXX=$(CXX)
+
+clean:
+ rm -f asm_*.s libtsan.nm libtsan.objdump */*.o tsan_test
+ rm -rf $(GTEST_BUILD_DIR)
+ $(MAKE) clean -C rtl -f Makefile.old
diff --git a/lib/tsan/analyze_libtsan.sh b/lib/tsan/analyze_libtsan.sh
new file mode 100755
index 000000000000..e0805610714b
--- /dev/null
+++ b/lib/tsan/analyze_libtsan.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+set -e
+set -u
+
+get_asm() {
+ grep tsan_$1.: -A 10000 libtsan.objdump | \
+ awk "/[^:]$/ {print;} />:/ {c++; if (c == 2) {exit}}"
+}
+
+list="write1 \
+ write2 \
+ write4 \
+ write8 \
+ read1 \
+ read2 \
+ read4 \
+ read8 \
+ func_entry \
+ func_exit"
+
+BIN=`dirname $0`/tsan_test
+objdump -d $BIN > libtsan.objdump
+nm -S $BIN | grep "__tsan_" > libtsan.nm
+
+for f in $list; do
+ file=asm_$f.s
+ get_asm $f > $file
+ tot=$(wc -l < $file)
+ size=$(grep $f$ libtsan.nm | awk --non-decimal-data '{print ("0x"$2)+0}')
+ rsp=$(grep '(%rsp)' $file | wc -l)
+ push=$(grep 'push' $file | wc -l)
+ pop=$(grep 'pop' $file | wc -l)
+ call=$(grep 'call' $file | wc -l)
+ load=$(egrep 'mov .*\,.*\(.*\)|cmp .*\,.*\(.*\)' $file | wc -l)
+ store=$(egrep 'mov .*\(.*\),' $file | wc -l)
+ mov=$(grep 'mov' $file | wc -l)
+ lea=$(grep 'lea' $file | wc -l)
+ sh=$(grep 'shr\|shl' $file | wc -l)
+ cmp=$(grep 'cmp\|test' $file | wc -l)
+ printf "%10s tot %3d; size %4d; rsp %d; push %d; pop %d; call %d; load %2d; store %2d; sh %3d; mov %3d; lea %3d; cmp %3d\n" \
+ $f $tot $size $rsp $push $pop $call $load $store $sh $mov $lea $cmp;
+done
diff --git a/lib/tsan/benchmarks/mini_bench_local.cc b/lib/tsan/benchmarks/mini_bench_local.cc
new file mode 100644
index 000000000000..accdcb63878f
--- /dev/null
+++ b/lib/tsan/benchmarks/mini_bench_local.cc
@@ -0,0 +1,49 @@
+// Mini-benchmark for tsan: non-shared memory writes.
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+int len;
+int *a;
+const int kNumIter = 1000;
+
+__attribute__((noinline))
+void Run(int idx) {
+ for (int i = 0, n = len; i < n; i++)
+ a[i + idx * n] = i;
+}
+
+void *Thread(void *arg) {
+ long idx = (long)arg;
+ printf("Thread %ld started\n", idx);
+ for (int i = 0; i < kNumIter; i++)
+ Run(idx);
+ printf("Thread %ld done\n", idx);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int n_threads = 0;
+ if (argc != 3) {
+ n_threads = 4;
+ len = 1000000;
+ } else {
+ n_threads = atoi(argv[1]);
+ assert(n_threads > 0 && n_threads <= 32);
+ len = atoi(argv[2]);
+ }
+ printf("%s: n_threads=%d len=%d iter=%d\n",
+ __FILE__, n_threads, len, kNumIter);
+ a = new int[n_threads * len];
+ pthread_t *t = new pthread_t[n_threads];
+ for (int i = 0; i < n_threads; i++) {
+ pthread_create(&t[i], 0, Thread, (void*)i);
+ }
+ for (int i = 0; i < n_threads; i++) {
+ pthread_join(t[i], 0);
+ }
+ delete [] t;
+ delete [] a;
+ return 0;
+}
diff --git a/lib/tsan/benchmarks/mini_bench_shared.cc b/lib/tsan/benchmarks/mini_bench_shared.cc
new file mode 100644
index 000000000000..f9b9f42f78a4
--- /dev/null
+++ b/lib/tsan/benchmarks/mini_bench_shared.cc
@@ -0,0 +1,51 @@
+// Mini-benchmark for tsan: shared memory reads.
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+int len;
+int *a;
+const int kNumIter = 1000;
+
+__attribute__((noinline))
+void Run(int idx) {
+ for (int i = 0, n = len; i < n; i++)
+ if (a[i] != i) abort();
+}
+
+void *Thread(void *arg) {
+ long idx = (long)arg;
+ printf("Thread %ld started\n", idx);
+ for (int i = 0; i < kNumIter; i++)
+ Run(idx);
+ printf("Thread %ld done\n", idx);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int n_threads = 0;
+ if (argc != 3) {
+ n_threads = 4;
+ len = 1000000;
+ } else {
+ n_threads = atoi(argv[1]);
+ assert(n_threads > 0 && n_threads <= 32);
+ len = atoi(argv[2]);
+ }
+ printf("%s: n_threads=%d len=%d iter=%d\n",
+ __FILE__, n_threads, len, kNumIter);
+ a = new int[len];
+ for (int i = 0, n = len; i < n; i++)
+ a[i] = i;
+ pthread_t *t = new pthread_t[n_threads];
+ for (int i = 0; i < n_threads; i++) {
+ pthread_create(&t[i], 0, Thread, (void*)i);
+ }
+ for (int i = 0; i < n_threads; i++) {
+ pthread_join(t[i], 0);
+ }
+ delete [] t;
+ delete [] a;
+ return 0;
+}
diff --git a/lib/tsan/benchmarks/start_many_threads.cc b/lib/tsan/benchmarks/start_many_threads.cc
new file mode 100644
index 000000000000..1e86fa6c502e
--- /dev/null
+++ b/lib/tsan/benchmarks/start_many_threads.cc
@@ -0,0 +1,52 @@
+// Mini-benchmark for creating a lot of threads.
+//
+// Some facts:
+// a) clang -O1 takes <15ms to start N=500 threads,
+// consuming ~4MB more RAM than N=1.
+// b) clang -O1 -ftsan takes ~26s to start N=500 threads,
+// eats 5GB more RAM than N=1 (which is somewhat expected but still a lot)
+// but then it consumes ~4GB of extra memory when the threads shut down!
+// (definitely not in the barrier_wait interceptor)
+// Also, it takes 26s to run with N=500 vs just 1.1s to run with N=1.
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+pthread_barrier_t all_threads_ready;
+
+void* Thread(void *unused) {
+ pthread_barrier_wait(&all_threads_ready);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int n_threads;
+ if (argc == 1) {
+ n_threads = 100;
+ } else if (argc == 2) {
+ n_threads = atoi(argv[1]);
+ } else {
+ printf("Usage: %s n_threads\n", argv[0]);
+ return 1;
+ }
+ printf("%s: n_threads=%d\n", __FILE__, n_threads);
+
+ pthread_barrier_init(&all_threads_ready, NULL, n_threads + 1);
+
+ pthread_t *t = new pthread_t[n_threads];
+ for (int i = 0; i < n_threads; i++) {
+ int status = pthread_create(&t[i], 0, Thread, (void*)i);
+ assert(status == 0);
+ }
+ // sleep(5); // FIXME: simplify measuring the memory usage.
+ pthread_barrier_wait(&all_threads_ready);
+ for (int i = 0; i < n_threads; i++) {
+ pthread_join(t[i], 0);
+ }
+ // sleep(5); // FIXME: simplify measuring the memory usage.
+ delete [] t;
+
+ return 0;
+}
diff --git a/lib/tsan/benchmarks/vts_many_threads_bench.cc b/lib/tsan/benchmarks/vts_many_threads_bench.cc
new file mode 100644
index 000000000000..f1056e20c874
--- /dev/null
+++ b/lib/tsan/benchmarks/vts_many_threads_bench.cc
@@ -0,0 +1,120 @@
+// Mini-benchmark for tsan VTS worst case performance
+// Idea:
+// 1) Spawn M + N threads (M >> N)
+// We'll call the 'M' threads as 'garbage threads'.
+// 2) Make sure all threads have created thus no TIDs were reused
+// 3) Join the garbage threads
+// 4) Do many sync operations on the remaining N threads
+//
+// It turns out that due to O(M+N) VTS complexity the (4) is much slower with
+// when N is large.
+//
+// Some numbers:
+// a) clang++ native O1 with n_iterations=200kk takes
+// 5s regardless of M
+// clang++ tsanv2 O1 with n_iterations=20kk takes
+// 23.5s with M=200
+// 11.5s with M=1
+// i.e. tsanv2 is ~23x to ~47x slower than native, depends on M.
+// b) g++ native O1 with n_iterations=200kk takes
+// 5.5s regardless of M
+// g++ tsanv1 O1 with n_iterations=2kk takes
+// 39.5s with M=200
+// 20.5s with M=1
+// i.e. tsanv1 is ~370x to ~720x slower than native, depends on M.
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+class __attribute__((aligned(64))) Mutex {
+ public:
+ Mutex() { pthread_mutex_init(&m_, NULL); }
+ ~Mutex() { pthread_mutex_destroy(&m_); }
+ void Lock() { pthread_mutex_lock(&m_); }
+ void Unlock() { pthread_mutex_unlock(&m_); }
+
+ private:
+ pthread_mutex_t m_;
+};
+
+const int kNumMutexes = 1024;
+Mutex mutexes[kNumMutexes];
+
+int n_threads, n_iterations;
+
+pthread_barrier_t all_threads_ready, main_threads_ready;
+
+void* GarbageThread(void *unused) {
+ pthread_barrier_wait(&all_threads_ready);
+ return 0;
+}
+
+void *Thread(void *arg) {
+ long idx = (long)arg;
+ pthread_barrier_wait(&all_threads_ready);
+
+ // Wait for the main thread to join the garbage threads.
+ pthread_barrier_wait(&main_threads_ready);
+
+ printf("Thread %ld go!\n", idx);
+ int offset = idx * kNumMutexes / n_threads;
+ for (int i = 0; i < n_iterations; i++) {
+ mutexes[(offset + i) % kNumMutexes].Lock();
+ mutexes[(offset + i) % kNumMutexes].Unlock();
+ }
+ printf("Thread %ld done\n", idx);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int n_garbage_threads;
+ if (argc == 1) {
+ n_threads = 2;
+ n_garbage_threads = 200;
+ n_iterations = 20000000;
+ } else if (argc == 4) {
+ n_threads = atoi(argv[1]);
+ assert(n_threads > 0 && n_threads <= 32);
+ n_garbage_threads = atoi(argv[2]);
+ assert(n_garbage_threads > 0 && n_garbage_threads <= 16000);
+ n_iterations = atoi(argv[3]);
+ } else {
+ printf("Usage: %s n_threads n_garbage_threads n_iterations\n", argv[0]);
+ return 1;
+ }
+ printf("%s: n_threads=%d n_garbage_threads=%d n_iterations=%d\n",
+ __FILE__, n_threads, n_garbage_threads, n_iterations);
+
+ pthread_barrier_init(&all_threads_ready, NULL, n_garbage_threads + n_threads + 1);
+ pthread_barrier_init(&main_threads_ready, NULL, n_threads + 1);
+
+ pthread_t *t = new pthread_t[n_threads];
+ {
+ pthread_t *g_t = new pthread_t[n_garbage_threads];
+ for (int i = 0; i < n_garbage_threads; i++) {
+ int status = pthread_create(&g_t[i], 0, GarbageThread, NULL);
+ assert(status == 0);
+ }
+ for (int i = 0; i < n_threads; i++) {
+ int status = pthread_create(&t[i], 0, Thread, (void*)i);
+ assert(status == 0);
+ }
+ pthread_barrier_wait(&all_threads_ready);
+ printf("All threads started! Killing the garbage threads.\n");
+ for (int i = 0; i < n_garbage_threads; i++) {
+ pthread_join(g_t[i], 0);
+ }
+ delete [] g_t;
+ }
+ printf("Resuming the main threads.\n");
+ pthread_barrier_wait(&main_threads_ready);
+
+
+ for (int i = 0; i < n_threads; i++) {
+ pthread_join(t[i], 0);
+ }
+ delete [] t;
+ return 0;
+}
diff --git a/lib/tsan/check_analyze.sh b/lib/tsan/check_analyze.sh
new file mode 100755
index 000000000000..39d570b97673
--- /dev/null
+++ b/lib/tsan/check_analyze.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+set -u
+
+RES=$(./analyze_libtsan.sh)
+PrintRes() {
+ printf "%s\n" "$RES"
+}
+
+PrintRes
+
+mops="write1 \
+ write2 \
+ write4 \
+ write8 \
+ read1 \
+ read2 \
+ read4 \
+ read8"
+func="func_entry \
+ func_exit"
+
+check() {
+ res=$(PrintRes | egrep "$1 .* $2 $3; ")
+ if [ "$res" == "" ]; then
+ echo FAILED $1 must contain $2 $3
+ exit 1
+ fi
+}
+
+for f in $mops; do
+ check $f rsp 1 # To read caller pc.
+ check $f push 0
+ check $f pop 0
+done
+
+for f in $func; do
+ check $f rsp 0
+ check $f push 0
+ check $f pop 0
+ check $f call 1 # TraceSwitch()
+done
+
+echo LGTM
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh
new file mode 100755
index 000000000000..a0d2f6761b41
--- /dev/null
+++ b/lib/tsan/go/buildgo.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+set -e
+
+if [ "`uname -a | grep Linux`" != "" ]; then
+ LINUX=1
+ SUFFIX="linux_amd64"
+elif [ "`uname -a | grep Darwin`" != "" ]; then
+ MAC=1
+ SUFFIX="darwin_amd64"
+else
+ echo Unknown platform
+ exit 1
+fi
+
+SRCS="
+ tsan_go.cc
+ ../rtl/tsan_clock.cc
+ ../rtl/tsan_flags.cc
+ ../rtl/tsan_md5.cc
+ ../rtl/tsan_mutex.cc
+ ../rtl/tsan_printf.cc
+ ../rtl/tsan_report.cc
+ ../rtl/tsan_rtl.cc
+ ../rtl/tsan_rtl_mutex.cc
+ ../rtl/tsan_rtl_report.cc
+ ../rtl/tsan_rtl_thread.cc
+ ../rtl/tsan_stat.cc
+ ../rtl/tsan_suppressions.cc
+ ../rtl/tsan_sync.cc
+ ../../sanitizer_common/sanitizer_allocator.cc
+ ../../sanitizer_common/sanitizer_common.cc
+ ../../sanitizer_common/sanitizer_flags.cc
+ ../../sanitizer_common/sanitizer_libc.cc
+ ../../sanitizer_common/sanitizer_posix.cc
+ ../../sanitizer_common/sanitizer_printf.cc
+ ../../sanitizer_common/sanitizer_symbolizer.cc
+"
+
+if [ "$LINUX" != "" ]; then
+ SRCS+="
+ ../rtl/tsan_platform_linux.cc
+ ../../sanitizer_common/sanitizer_linux.cc
+ "
+elif [ "$MAC" != "" ]; then
+ SRCS+="
+ ../rtl/tsan_platform_mac.cc
+ ../../sanitizer_common/sanitizer_mac.cc
+ "
+fi
+
+SRCS+=$ADD_SRCS
+#ASMS="../rtl/tsan_rtl_amd64.S"
+
+rm -f gotsan.cc
+for F in $SRCS; do
+ cat $F >> gotsan.cc
+done
+
+FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -fPIC -g -Wall -Werror -fno-exceptions -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4"
+if [ "$DEBUG" == "" ]; then
+ FLAGS+=" -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer"
+else
+ FLAGS+=" -DTSAN_DEBUG=1 -g"
+fi
+
+if [ "$LINUX" != "" ]; then
+ FLAGS+=" -ffreestanding"
+fi
+
+echo gcc gotsan.cc -S -o tmp.s $FLAGS $CFLAGS
+gcc gotsan.cc -S -o tmp.s $FLAGS $CFLAGS
+cat tmp.s $ASMS > gotsan.s
+echo as gotsan.s -o race_$SUFFIX.syso
+as gotsan.s -o race_$SUFFIX.syso
+
+gcc test.c race_$SUFFIX.syso -lpthread -o test
+TSAN_OPTIONS="exitcode=0" ./test
+
diff --git a/lib/tsan/go/test.c b/lib/tsan/go/test.c
new file mode 100644
index 000000000000..a9a5b3dbfcad
--- /dev/null
+++ b/lib/tsan/go/test.c
@@ -0,0 +1,51 @@
+//===-- test.c ------------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Sanity test for Go runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+
+void __tsan_init();
+void __tsan_fini();
+void __tsan_go_start(int pgoid, int chgoid, void *pc);
+void __tsan_go_end(int goid);
+void __tsan_read(int goid, void *addr, void *pc);
+void __tsan_write(int goid, void *addr, void *pc);
+void __tsan_func_enter(int goid, void *pc);
+void __tsan_func_exit(int goid);
+void __tsan_malloc(int goid, void *p, unsigned long sz, void *pc);
+void __tsan_free(void *p);
+void __tsan_acquire(int goid, void *addr);
+void __tsan_release(int goid, void *addr);
+void __tsan_release_merge(int goid, void *addr);
+
+int __tsan_symbolize(void *pc, char **img, char **rtn, char **file, int *l) {
+ return 0;
+}
+
+char buf[10];
+
+int main(void) {
+ __tsan_init();
+ __tsan_func_enter(0, &main);
+ __tsan_malloc(0, buf, 10, 0);
+ __tsan_release(0, buf);
+ __tsan_release_merge(0, buf);
+ __tsan_go_start(0, 1, 0);
+ __tsan_write(1, buf, 0);
+ __tsan_acquire(1, buf);
+ __tsan_go_end(1);
+ __tsan_read(0, buf, 0);
+ __tsan_free(buf);
+ __tsan_func_exit(0);
+ __tsan_fini();
+ return 0;
+}
diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc
new file mode 100644
index 000000000000..4b3076c46ce7
--- /dev/null
+++ b/lib/tsan/go/tsan_go.cc
@@ -0,0 +1,185 @@
+//===-- tsan_go.cc --------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// ThreadSanitizer runtime for Go language.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_rtl.h"
+#include "tsan_symbolize.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include <stdlib.h>
+
+namespace __tsan {
+
+static ThreadState *goroutines[kMaxTid];
+
+void InitializeInterceptors() {
+}
+
+void InitializeDynamicAnnotations() {
+}
+
+bool IsExpectedReport(uptr addr, uptr size) {
+ return false;
+}
+
+void internal_start_thread(void(*func)(void*), void *arg) {
+}
+
+ReportStack *SymbolizeData(uptr addr) {
+ return 0;
+}
+
+ReportStack *NewReportStackEntry(uptr addr) {
+ ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
+ sizeof(ReportStack));
+ internal_memset(ent, 0, sizeof(*ent));
+ ent->pc = addr;
+ return ent;
+}
+
+void *internal_alloc(MBlockType typ, uptr sz) {
+ return InternalAlloc(sz);
+}
+
+void internal_free(void *p) {
+ InternalFree(p);
+}
+
+// Callback into Go.
+extern "C" int __tsan_symbolize(uptr pc, char **func, char **file,
+ int *line, int *off);
+
+ReportStack *SymbolizeCode(uptr addr) {
+ ReportStack *s = (ReportStack*)internal_alloc(MBlockReportStack,
+ sizeof(ReportStack));
+ internal_memset(s, 0, sizeof(*s));
+ s->pc = addr;
+ char *func = 0, *file = 0;
+ int line = 0, off = 0;
+ if (__tsan_symbolize(addr, &func, &file, &line, &off)) {
+ s->offset = off;
+ s->func = internal_strdup(func ? func : "??");
+ s->file = internal_strdup(file ? file : "-");
+ s->line = line;
+ s->col = 0;
+ free(func);
+ free(file);
+ }
+ return s;
+}
+
+extern "C" {
+
+static void AllocGoroutine(int tid) {
+ goroutines[tid] = (ThreadState*)internal_alloc(MBlockThreadContex,
+ sizeof(ThreadState));
+ internal_memset(goroutines[tid], 0, sizeof(ThreadState));
+}
+
+void __tsan_init() {
+ AllocGoroutine(0);
+ ThreadState *thr = goroutines[0];
+ thr->in_rtl++;
+ Initialize(thr);
+ thr->in_rtl--;
+}
+
+void __tsan_fini() {
+ // FIXME: Not necessary thread 0.
+ ThreadState *thr = goroutines[0];
+ thr->in_rtl++;
+ int res = Finalize(thr);
+ thr->in_rtl--;
+ exit(res);
+}
+
+void __tsan_read(int goid, void *addr, void *pc) {
+ ThreadState *thr = goroutines[goid];
+ MemoryAccess(thr, (uptr)pc, (uptr)addr, 0, false);
+}
+
+void __tsan_write(int goid, void *addr, void *pc) {
+ ThreadState *thr = goroutines[goid];
+ MemoryAccess(thr, (uptr)pc, (uptr)addr, 0, true);
+}
+
+void __tsan_func_enter(int goid, void *pc) {
+ ThreadState *thr = goroutines[goid];
+ FuncEntry(thr, (uptr)pc);
+}
+
+void __tsan_func_exit(int goid) {
+ ThreadState *thr = goroutines[goid];
+ FuncExit(thr);
+}
+
+void __tsan_malloc(int goid, void *p, uptr sz, void *pc) {
+ ThreadState *thr = goroutines[goid];
+ thr->in_rtl++;
+ MemoryResetRange(thr, (uptr)pc, (uptr)p, sz);
+ MemoryAccessRange(thr, (uptr)pc, (uptr)p, sz, true);
+ thr->in_rtl--;
+}
+
+void __tsan_free(void *p) {
+ (void)p;
+}
+
+void __tsan_go_start(int pgoid, int chgoid, void *pc) {
+ if (chgoid == 0)
+ return;
+ AllocGoroutine(chgoid);
+ ThreadState *thr = goroutines[chgoid];
+ ThreadState *parent = goroutines[pgoid];
+ thr->in_rtl++;
+ parent->in_rtl++;
+ int goid2 = ThreadCreate(parent, (uptr)pc, 0, true);
+ ThreadStart(thr, goid2);
+ parent->in_rtl--;
+ thr->in_rtl--;
+}
+
+void __tsan_go_end(int goid) {
+ ThreadState *thr = goroutines[goid];
+ thr->in_rtl++;
+ ThreadFinish(thr);
+ thr->in_rtl--;
+}
+
+void __tsan_acquire(int goid, void *addr) {
+ ThreadState *thr = goroutines[goid];
+ thr->in_rtl++;
+ Acquire(thr, 0, (uptr)addr);
+ thr->in_rtl--;
+ //internal_free(thr);
+}
+
+void __tsan_release(int goid, void *addr) {
+ ThreadState *thr = goroutines[goid];
+ thr->in_rtl++;
+ ReleaseStore(thr, 0, (uptr)addr);
+ thr->in_rtl--;
+}
+
+void __tsan_release_merge(int goid, void *addr) {
+ ThreadState *thr = goroutines[goid];
+ thr->in_rtl++;
+ Release(thr, 0, (uptr)addr);
+ thr->in_rtl--;
+}
+
+void __tsan_finalizer_goroutine(int goid) {
+ ThreadState *thr = goroutines[goid];
+ ThreadFinalizerGoroutine(thr);
+}
+
+} // extern "C"
+} // namespace __tsan
diff --git a/lib/tsan/output_tests/free_race.c b/lib/tsan/output_tests/free_race.c
new file mode 100644
index 000000000000..fb7fbac77b04
--- /dev/null
+++ b/lib/tsan/output_tests/free_race.c
@@ -0,0 +1,43 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+int *mem;
+pthread_mutex_t mtx;
+
+void *Thread1(void *x) {
+ pthread_mutex_lock(&mtx);
+ free(mem);
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ usleep(1000000);
+ pthread_mutex_lock(&mtx);
+ mem[0] = 42;
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+int main() {
+ mem = (int*)malloc(100);
+ pthread_mutex_init(&mtx, 0);
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Thread2(0);
+ pthread_join(t, NULL);
+ pthread_mutex_destroy(&mtx);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: heap-use-after-free
+// CHECK: Write of size 4 at {{.*}} by main thread:
+// CHECK: #0 Thread2
+// CHECK: #1 main
+// CHECK: Previous write of size 8 at {{.*}} by thread 1:
+// CHECK: #0 free
+// CHECK: #1 Thread1
+
diff --git a/lib/tsan/output_tests/free_race2.c b/lib/tsan/output_tests/free_race2.c
new file mode 100644
index 000000000000..7b2bdec039e1
--- /dev/null
+++ b/lib/tsan/output_tests/free_race2.c
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+
+void __attribute__((noinline)) foo(int *mem) {
+ free(mem);
+}
+
+void __attribute__((noinline)) bar(int *mem) {
+ mem[0] = 42;
+}
+
+int main() {
+ int *mem = (int*)malloc(100);
+ foo(mem);
+ bar(mem);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: heap-use-after-free
+// CHECK: Write of size 4 at {{.*}} by main thread:
+// CHECK: #0 bar
+// CHECK: #1 main
+// CHECK: Previous write of size 8 at {{.*}} by main thread:
+// CHECK: #0 free
+// CHECK: #1 foo
+// CHECK: #2 main
+
diff --git a/lib/tsan/output_tests/heap_race.cc b/lib/tsan/output_tests/heap_race.cc
new file mode 100644
index 000000000000..e92bb379737e
--- /dev/null
+++ b/lib/tsan/output_tests/heap_race.cc
@@ -0,0 +1,19 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+
+void *Thread(void *a) {
+ ((int*)a)[0]++;
+ return NULL;
+}
+
+int main() {
+ int *p = new int(42);
+ pthread_t t;
+ pthread_create(&t, NULL, Thread, p);
+ p[0]++;
+ pthread_join(t, NULL);
+ delete p;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/memcpy_race.cc b/lib/tsan/output_tests/memcpy_race.cc
new file mode 100644
index 000000000000..c6b79a709e48
--- /dev/null
+++ b/lib/tsan/output_tests/memcpy_race.cc
@@ -0,0 +1,40 @@
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+char *data = new char[10];
+char *data1 = new char[10];
+char *data2 = new char[10];
+
+void *Thread1(void *x) {
+ memcpy(data+5, data1, 1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ usleep(500*1000);
+ memcpy(data+3, data2, 4);
+ return NULL;
+}
+
+int main() {
+ fprintf(stderr, "addr=%p\n", &data[5]);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 1 at [[ADDR]] by thread 2:
+// CHECK: #0 memcpy
+// CHECK: #1 Thread2
+// CHECK: Previous write of size 1 at [[ADDR]] by thread 1:
+// CHECK: #0 memcpy
+// CHECK: #1 Thread1
+
diff --git a/lib/tsan/output_tests/mop_with_offset.cc b/lib/tsan/output_tests/mop_with_offset.cc
new file mode 100644
index 000000000000..fc497bfb4a74
--- /dev/null
+++ b/lib/tsan/output_tests/mop_with_offset.cc
@@ -0,0 +1,36 @@
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *Thread1(void *x) {
+ int *p = (int*)x;
+ p[0] = 1;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ usleep(500*1000);
+ char *p = (char*)x;
+ p[2] = 1;
+ return NULL;
+}
+
+int main() {
+ int *data = new int(42);
+ fprintf(stderr, "ptr1=%p\n", data);
+ fprintf(stderr, "ptr2=%p\n", (char*)data + 2);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, data);
+ pthread_create(&t[1], NULL, Thread2, data);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ delete data;
+}
+
+// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
+// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 1 at [[PTR2]] by thread 2:
+// CHECK: Previous write of size 4 at [[PTR1]] by thread 1:
+
diff --git a/lib/tsan/output_tests/mop_with_offset2.cc b/lib/tsan/output_tests/mop_with_offset2.cc
new file mode 100644
index 000000000000..bbeda554929f
--- /dev/null
+++ b/lib/tsan/output_tests/mop_with_offset2.cc
@@ -0,0 +1,36 @@
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *Thread1(void *x) {
+ usleep(500*1000);
+ int *p = (int*)x;
+ p[0] = 1;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ char *p = (char*)x;
+ p[2] = 1;
+ return NULL;
+}
+
+int main() {
+ int *data = new int(42);
+ fprintf(stderr, "ptr1=%p\n", data);
+ fprintf(stderr, "ptr2=%p\n", (char*)data + 2);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, data);
+ pthread_create(&t[1], NULL, Thread2, data);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ delete data;
+}
+
+// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
+// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at [[PTR1]] by thread 1:
+// CHECK: Previous write of size 1 at [[PTR2]] by thread 2:
+
diff --git a/lib/tsan/output_tests/race_on_barrier.c b/lib/tsan/output_tests/race_on_barrier.c
new file mode 100644
index 000000000000..98d7a1d847f9
--- /dev/null
+++ b/lib/tsan/output_tests/race_on_barrier.c
@@ -0,0 +1,31 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+pthread_barrier_t B;
+int Global;
+
+void *Thread1(void *x) {
+ pthread_barrier_init(&B, 0, 2);
+ pthread_barrier_wait(&B);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ usleep(1000000);
+ pthread_barrier_wait(&B);
+ return NULL;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Thread2(0);
+ pthread_join(t, NULL);
+ pthread_barrier_destroy(&B);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+
diff --git a/lib/tsan/output_tests/race_on_barrier2.c b/lib/tsan/output_tests/race_on_barrier2.c
new file mode 100644
index 000000000000..dbdb6b557004
--- /dev/null
+++ b/lib/tsan/output_tests/race_on_barrier2.c
@@ -0,0 +1,30 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+pthread_barrier_t B;
+int Global;
+
+void *Thread1(void *x) {
+ if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD)
+ pthread_barrier_destroy(&B);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD)
+ pthread_barrier_destroy(&B);
+ return NULL;
+}
+
+int main() {
+ pthread_barrier_init(&B, 0, 2);
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Thread2(0);
+ pthread_join(t, NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/race_on_mutex.c b/lib/tsan/output_tests/race_on_mutex.c
new file mode 100644
index 000000000000..45c75be782d1
--- /dev/null
+++ b/lib/tsan/output_tests/race_on_mutex.c
@@ -0,0 +1,41 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+pthread_mutex_t Mtx;
+int Global;
+
+void *Thread1(void *x) {
+ pthread_mutex_init(&Mtx, 0);
+ pthread_mutex_lock(&Mtx);
+ Global = 42;
+ pthread_mutex_unlock(&Mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ usleep(1000000);
+ pthread_mutex_lock(&Mtx);
+ Global = 43;
+ pthread_mutex_unlock(&Mtx);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&Mtx);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Read of size 1 at {{.*}} by thread 2:
+// CHECK-NEXT: #0 pthread_mutex_lock {{.*}} ({{.*}})
+// CHECK-NEXT: #1 Thread2 {{.*}}race_on_mutex.c:19{{(:3)?}} ({{.*}})
+// CHECK-NEXT: Previous write of size 1 at {{.*}} by thread 1:
+// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}})
+// CHECK-NEXT: #1 Thread1 {{.*}}race_on_mutex.c:10{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/output_tests/race_with_finished_thread.cc b/lib/tsan/output_tests/race_with_finished_thread.cc
new file mode 100644
index 000000000000..1f60f4ba349b
--- /dev/null
+++ b/lib/tsan/output_tests/race_with_finished_thread.cc
@@ -0,0 +1,43 @@
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+// Ensure that we can restore a stack of a finished thread.
+
+int g_data;
+
+void __attribute__((noinline)) foobar(int *p) {
+ *p = 42;
+}
+
+void *Thread1(void *x) {
+ foobar(&g_data);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ usleep(1000*1000);
+ g_data = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at {{.*}} by thread 2:
+// CHECK: Previous write of size 4 at {{.*}} by thread 1:
+// CHECK: #0 foobar
+// CHECK: #1 Thread1
+// CHECK: Thread 1 (finished) created at:
+// CHECK: #0 pthread_create
+// CHECK: #1 main
+
diff --git a/lib/tsan/output_tests/simple_race.c b/lib/tsan/output_tests/simple_race.c
new file mode 100644
index 000000000000..ed831fd8c5a3
--- /dev/null
+++ b/lib/tsan/output_tests/simple_race.c
@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <stdio.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ Global = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/simple_race.cc b/lib/tsan/output_tests/simple_race.cc
new file mode 100644
index 000000000000..8d2cabff772c
--- /dev/null
+++ b/lib/tsan/output_tests/simple_race.cc
@@ -0,0 +1,24 @@
+#include <pthread.h>
+#include <stdio.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ Global++;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/simple_stack.c b/lib/tsan/output_tests/simple_stack.c
new file mode 100644
index 000000000000..2e94f23f3c46
--- /dev/null
+++ b/lib/tsan/output_tests/simple_stack.c
@@ -0,0 +1,65 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void __attribute__((noinline)) foo1() {
+ Global = 42;
+}
+
+void __attribute__((noinline)) bar1() {
+ volatile int tmp = 42; (void)tmp;
+ foo1();
+}
+
+void __attribute__((noinline)) foo2() {
+ volatile int v = Global; (void)v;
+}
+
+void __attribute__((noinline)) bar2() {
+ volatile int tmp = 42; (void)tmp;
+ foo2();
+}
+
+void *Thread1(void *x) {
+ usleep(1000000);
+ bar1();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ bar2();
+ return NULL;
+}
+
+void StartThread(pthread_t *t, void *(*f)(void*)) {
+ pthread_create(t, NULL, f, NULL);
+}
+
+int main() {
+ pthread_t t[2];
+ StartThread(&t[0], Thread1);
+ StartThread(&t[1], Thread2);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1:
+// CHECK-NEXT: #0 foo1 {{.*}}simple_stack.c:8{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #1 bar1 {{.*}}simple_stack.c:13{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread1 {{.*}}simple_stack.c:27{{(:3)?}} ({{.*}})
+// CHECK-NEXT: Previous read of size 4 at {{.*}} by thread 2:
+// CHECK-NEXT: #0 foo2 {{.*}}simple_stack.c:17{{(:26)?}} ({{.*}})
+// CHECK-NEXT: #1 bar2 {{.*}}simple_stack.c:22{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread2 {{.*}}simple_stack.c:32{{(:3)?}} ({{.*}})
+// CHECK-NEXT: Thread 1 (running) created at:
+// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
+// CHECK-NEXT: #1 StartThread {{.*}}simple_stack.c:37{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main {{.*}}simple_stack.c:42{{(:3)?}} ({{.*}})
+// CHECK-NEXT: Thread 2 ({{.*}}) created at:
+// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
+// CHECK-NEXT: #1 StartThread {{.*}}simple_stack.c:37{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/output_tests/simple_stack2.cc b/lib/tsan/output_tests/simple_stack2.cc
new file mode 100644
index 000000000000..336cc9ff599d
--- /dev/null
+++ b/lib/tsan/output_tests/simple_stack2.cc
@@ -0,0 +1,46 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void __attribute__((noinline)) foo1() {
+ Global = 42;
+}
+
+void __attribute__((noinline)) bar1() {
+ volatile int tmp = 42; int tmp2 = tmp; (void)tmp2;
+ foo1();
+}
+
+void __attribute__((noinline)) foo2() {
+ volatile int tmp = Global; int tmp2 = tmp; (void)tmp2;
+}
+
+void __attribute__((noinline)) bar2() {
+ volatile int tmp = 42; int tmp2 = tmp; (void)tmp2;
+ foo2();
+}
+
+void *Thread1(void *x) {
+ usleep(1000000);
+ bar1();
+ return NULL;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ bar2();
+ pthread_join(t, NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1:
+// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:8{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:13{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:27{{(:3)?}} ({{.*}})
+// CHECK-NEXT: Previous read of size 4 at {{.*}} by main thread:
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:17{{(:28)?}} ({{.*}})
+// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:22{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/output_tests/static_init1.cc b/lib/tsan/output_tests/static_init1.cc
new file mode 100644
index 000000000000..75d281954e1a
--- /dev/null
+++ b/lib/tsan/output_tests/static_init1.cc
@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+struct P {
+ int x;
+ int y;
+};
+
+void *Thread(void *x) {
+ static P p = {rand(), rand()};
+ if (p.x > RAND_MAX || p.y > RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread, 0);
+ pthread_create(&t[1], 0, Thread, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/static_init2.cc b/lib/tsan/output_tests/static_init2.cc
new file mode 100644
index 000000000000..f6e95965521a
--- /dev/null
+++ b/lib/tsan/output_tests/static_init2.cc
@@ -0,0 +1,31 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+struct Cache {
+ int x;
+ Cache(int x)
+ : x(x) {
+ }
+};
+
+void foo(Cache *my) {
+ static Cache *c = my ? my : new Cache(rand());
+ if (c->x >= RAND_MAX)
+ exit(1);
+}
+
+void *Thread(void *x) {
+ foo(new Cache(rand()));
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread, 0);
+ pthread_create(&t[1], 0, Thread, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/static_init3.cc b/lib/tsan/output_tests/static_init3.cc
new file mode 100644
index 000000000000..718f811d0df4
--- /dev/null
+++ b/lib/tsan/output_tests/static_init3.cc
@@ -0,0 +1,46 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+};
+
+Cache g_cache;
+
+Cache *CreateCache() {
+ g_cache.x = rand();
+ return &g_cache;
+}
+
+_Atomic(Cache*) queue;
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ __c11_atomic_store(&queue, c, 0);
+ return 0;
+}
+
+void *Thread2(void *x) {
+ Cache *c = 0;
+ for (;;) {
+ c = __c11_atomic_load(&queue, 0);
+ if (c)
+ break;
+ sched_yield();
+ }
+ if (c->x >= RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread2, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/static_init4.cc b/lib/tsan/output_tests/static_init4.cc
new file mode 100644
index 000000000000..cdacbce80026
--- /dev/null
+++ b/lib/tsan/output_tests/static_init4.cc
@@ -0,0 +1,35 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+ Cache(int x)
+ : x(x) {
+ }
+};
+
+int g_other;
+
+Cache *CreateCache() {
+ g_other = rand();
+ return new Cache(rand());
+}
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ if (c->x == g_other)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread1, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/static_init5.cc b/lib/tsan/output_tests/static_init5.cc
new file mode 100644
index 000000000000..4b050c9fa69b
--- /dev/null
+++ b/lib/tsan/output_tests/static_init5.cc
@@ -0,0 +1,40 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+ Cache(int x)
+ : x(x) {
+ }
+};
+
+void *AsyncInit(void *p) {
+ return new Cache((int)(long)p);
+}
+
+Cache *CreateCache() {
+ pthread_t t;
+ pthread_create(&t, 0, AsyncInit, (void*)rand());
+ void *res;
+ pthread_join(t, &res);
+ return (Cache*)res;
+}
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ if (c->x >= RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread1, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/suppress_same_address.cc b/lib/tsan/output_tests/suppress_same_address.cc
new file mode 100644
index 000000000000..6e98970a16ef
--- /dev/null
+++ b/lib/tsan/output_tests/suppress_same_address.cc
@@ -0,0 +1,27 @@
+#include <pthread.h>
+
+int X;
+
+void *Thread1(void *x) {
+ X = 42;
+ X = 66;
+ X = 78;
+ return 0;
+}
+
+void *Thread2(void *x) {
+ X = 11;
+ X = 99;
+ X = 73;
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ Thread2(0);
+ pthread_join(t, 0);
+}
+
+// CHECK: ThreadSanitizer: reported 1 warnings
+
diff --git a/lib/tsan/output_tests/suppress_same_stacks.cc b/lib/tsan/output_tests/suppress_same_stacks.cc
new file mode 100644
index 000000000000..6046a4ea9f3a
--- /dev/null
+++ b/lib/tsan/output_tests/suppress_same_stacks.cc
@@ -0,0 +1,27 @@
+#include <pthread.h>
+
+volatile int N; // Prevent loop unrolling.
+int **data;
+
+void *Thread1(void *x) {
+ for (int i = 0; i < N; i++)
+ data[i][0] = 42;
+ return 0;
+}
+
+int main() {
+ N = 4;
+ data = new int*[N];
+ for (int i = 0; i < N; i++)
+ data[i] = new int;
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ Thread1(0);
+ pthread_join(t, 0);
+ for (int i = 0; i < N; i++)
+ delete data[i];
+ delete[] data;
+}
+
+// CHECK: ThreadSanitizer: reported 1 warnings
+
diff --git a/lib/tsan/output_tests/test_output.sh b/lib/tsan/output_tests/test_output.sh
new file mode 100755
index 000000000000..bd9cd9158763
--- /dev/null
+++ b/lib/tsan/output_tests/test_output.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+ulimit -s 8192
+set -e # fail on any error
+
+ROOTDIR=$(dirname $0)/..
+
+# Assuming clang is in path.
+CC=clang
+CXX=clang++
+
+# TODO: add testing for all of -O0...-O3
+CFLAGS="-fthread-sanitizer -fPIE -O1 -g -fno-builtin -Wall"
+LDFLAGS="-pie -lpthread -ldl $ROOTDIR/rtl/libtsan.a"
+
+test_file() {
+ SRC=$1
+ COMPILER=$2
+ echo ----- TESTING $(basename $1)
+ OBJ=$SRC.o
+ EXE=$SRC.exe
+ $COMPILER $SRC $CFLAGS -c -o $OBJ
+ $COMPILER $OBJ $LDFLAGS -o $EXE
+ RES=$(TSAN_OPTIONS="atexit_sleep_ms=0" $EXE 2>&1 || true)
+ if [ "$3" != "" ]; then
+ printf "%s\n" "$RES"
+ fi
+ printf "%s\n" "$RES" | FileCheck $SRC
+ if [ "$3" == "" ]; then
+ rm -f $EXE $OBJ
+ fi
+}
+
+if [ "$1" == "" ]; then
+ for c in $ROOTDIR/output_tests/*.{c,cc}; do
+ if [[ $c == */failing_* ]]; then
+ echo SKIPPING FAILING TEST $c
+ continue
+ fi
+ COMPILER=$CXX
+ case $c in
+ *.c) COMPILER=$CC
+ esac
+ test_file $c $COMPILER
+ done
+ wait
+else
+ test_file $ROOTDIR/output_tests/$1 $CXX "DUMP"
+fi
diff --git a/lib/tsan/output_tests/thread_leak.c b/lib/tsan/output_tests/thread_leak.c
new file mode 100644
index 000000000000..88a11be4e9c0
--- /dev/null
+++ b/lib/tsan/output_tests/thread_leak.c
@@ -0,0 +1,15 @@
+#include <pthread.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_join(t, 0);
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
+
diff --git a/lib/tsan/output_tests/thread_leak2.c b/lib/tsan/output_tests/thread_leak2.c
new file mode 100644
index 000000000000..71e9c50b8a2f
--- /dev/null
+++ b/lib/tsan/output_tests/thread_leak2.c
@@ -0,0 +1,15 @@
+#include <pthread.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_detach(t);
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
+
diff --git a/lib/tsan/output_tests/thread_leak3.c b/lib/tsan/output_tests/thread_leak3.c
new file mode 100644
index 000000000000..058b6e54d9da
--- /dev/null
+++ b/lib/tsan/output_tests/thread_leak3.c
@@ -0,0 +1,14 @@
+#include <pthread.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: thread leak
+
diff --git a/lib/tsan/output_tests/tiny_race.c b/lib/tsan/output_tests/tiny_race.c
new file mode 100644
index 000000000000..3a8d192a671a
--- /dev/null
+++ b/lib/tsan/output_tests/tiny_race.c
@@ -0,0 +1,14 @@
+#include <pthread.h>
+int Global;
+void *Thread1(void *x) {
+ Global = 42;
+ return x;
+}
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Global = 43;
+ pthread_join(t, NULL);
+ return Global;
+}
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/virtual_inheritance_compile_bug.cc b/lib/tsan/output_tests/virtual_inheritance_compile_bug.cc
new file mode 100644
index 000000000000..fd2febe895b4
--- /dev/null
+++ b/lib/tsan/output_tests/virtual_inheritance_compile_bug.cc
@@ -0,0 +1,13 @@
+// Regression test for http://code.google.com/p/thread-sanitizer/issues/detail?id=3.
+// The C++ variant is much more compact that the LLVM IR equivalent.
+#include <stdio.h>
+struct AAA { virtual long aaa () { return 0; } };
+struct BBB: virtual AAA { unsigned long bbb; };
+struct CCC: virtual AAA { };
+struct DDD: CCC, BBB { DDD (); };
+DDD::DDD() { }
+int main() {
+ DDD d;
+ printf("OK\n");
+}
+// CHECK: OK
diff --git a/lib/tsan/output_tests/vptr_benign_race.cc b/lib/tsan/output_tests/vptr_benign_race.cc
new file mode 100644
index 000000000000..fec4ffbb6bc0
--- /dev/null
+++ b/lib/tsan/output_tests/vptr_benign_race.cc
@@ -0,0 +1,50 @@
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+
+struct A {
+ A() {
+ sem_init(&sem_, 0, 0);
+ }
+ virtual void F() {
+ }
+ void Done() {
+ sem_post(&sem_);
+ }
+ virtual ~A() {
+ }
+ sem_t sem_;
+};
+
+struct B : A {
+ virtual void F() {
+ }
+ virtual ~B() {
+ sem_wait(&sem_);
+ sem_destroy(&sem_);
+ }
+};
+
+static A *obj = new B;
+
+void *Thread1(void *x) {
+ obj->F();
+ obj->Done();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ delete obj;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ fprintf(stderr, "PASS\n");
+}
+// CHECK: PASS
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/vptr_harmful_race.cc b/lib/tsan/output_tests/vptr_harmful_race.cc
new file mode 100644
index 000000000000..a19e6abc7bc3
--- /dev/null
+++ b/lib/tsan/output_tests/vptr_harmful_race.cc
@@ -0,0 +1,48 @@
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+
+struct A {
+ A() {
+ sem_init(&sem_, 0, 0);
+ }
+ virtual void F() {
+ }
+ void Done() {
+ sem_post(&sem_);
+ }
+ virtual ~A() {
+ sem_wait(&sem_);
+ sem_destroy(&sem_);
+ }
+ sem_t sem_;
+};
+
+struct B : A {
+ virtual void F() {
+ }
+ virtual ~B() { }
+};
+
+static A *obj = new B;
+
+void *Thread1(void *x) {
+ obj->F();
+ obj->Done();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ delete obj;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/rtl/Makefile.mk b/lib/tsan/rtl/Makefile.mk
new file mode 100644
index 000000000000..d5d6327b5435
--- /dev/null
+++ b/lib/tsan/rtl/Makefile.mk
@@ -0,0 +1,23 @@
+#===- lib/tsan/rtl/Makefile.mk -----------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := tsan
+SubDirs :=
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
+AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
+ObjNames := $(Sources:%.cc=%.o) $(AsmSources:%.S=%.o)
+
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
+
+# Define a convenience variable for all the tsan functions.
+TsanFunctions += $(Sources:%.cc=%) $(AsmSources:%.S=%)
diff --git a/lib/tsan/rtl/Makefile.old b/lib/tsan/rtl/Makefile.old
new file mode 100644
index 000000000000..9b79f576d4aa
--- /dev/null
+++ b/lib/tsan/rtl/Makefile.old
@@ -0,0 +1,59 @@
+CXXFLAGS = -fPIE -g -Wall -Werror -fno-builtin -DTSAN_DEBUG=$(DEBUG)
+ifeq ($(DEBUG), 0)
+ CXXFLAGS += -O3
+endif
+
+# For interception. FIXME: move interception one level higher.
+INTERCEPTION=../../interception
+COMMON=../../sanitizer_common
+INCLUDES= -I../..
+EXTRA_CXXFLAGS=-fno-exceptions
+NO_SYSROOT=--sysroot=.
+CXXFLAGS+=$(EXTRA_CXXFLAGS)
+CXXFLAGS+=$(CFLAGS)
+ifeq ($(DEBUG), 0)
+ CXXFLAGS+=-fomit-frame-pointer
+ifeq ($(CXX), g++)
+ CXXFLAGS+=-Wframe-larger-than=512
+endif # CXX=g++
+endif # DEBUG=0
+
+ifeq ($(CXX), clang++)
+ # Global constructors are banned.
+ CXXFLAGS+=-Wglobal-constructors
+endif
+
+
+
+all: libtsan.a
+
+LIBTSAN_HEADERS=$(wildcard *.h) \
+ $(wildcard $(INTERCEPTION)/*.h) \
+ $(wildcard $(COMMON)/*.h)
+LIBTSAN_SRC=$(wildcard *.cc)
+LIBTSAN_ASM_SRC=$(wildcard *.S)
+INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc)
+COMMON_SRC=$(wildcard $(COMMON)/*.cc)
+
+LIBTSAN_OBJ=$(patsubst %.cc,%.o,$(LIBTSAN_SRC)) \
+ $(patsubst %.S,%.o,$(LIBTSAN_ASM_SRC)) \
+ $(patsubst $(INTERCEPTION)/%.cc,%.o,$(INTERCEPTION_SRC)) \
+ $(patsubst $(COMMON)/%.cc,%.o,$(COMMON_SRC))
+
+%_linux.o: %_linux.cc Makefile.old $(LIBTSAN_HEADERS)
+ $(CXX) $(CXXFLAGS) $(INCLUDES) -c $<
+%.o: %.cc Makefile.old $(LIBTSAN_HEADERS)
+ $(CXX) $(CXXFLAGS) $(INCLUDES) $(NO_SYSROOT) -c $<
+%.o: $(INTERCEPTION)/%.cc Makefile.old $(LIBTSAN_HEADERS)
+ $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
+%.o: $(COMMON)/%.cc Makefile.old $(LIBTSAN_HEADERS)
+ $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
+
+libtsan.a: $(LIBTSAN_OBJ)
+ ar ru $@ $(LIBTSAN_OBJ)
+
+libtsan_dummy.a: tsan_dummy_rtl.o
+ ar ru $@ $<
+
+clean:
+ rm -f *.o *.a
diff --git a/lib/tsan/rtl/tsan_clock.cc b/lib/tsan/rtl/tsan_clock.cc
new file mode 100644
index 000000000000..32ed91dc2103
--- /dev/null
+++ b/lib/tsan/rtl/tsan_clock.cc
@@ -0,0 +1,118 @@
+//===-- tsan_clock.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_clock.h"
+#include "tsan_rtl.h"
+
+// It's possible to optimize clock operations for some important cases
+// so that they are O(1). The cases include singletons, once's, local mutexes.
+// First, SyncClock must be re-implemented to allow indexing by tid.
+// It must not necessarily be a full vector clock, though. For example it may
+// be a multi-level table.
+// Then, each slot in SyncClock must contain a dirty bit (it's united with
+// the clock value, so no space increase). The acquire algorithm looks
+// as follows:
+// void acquire(thr, tid, thr_clock, sync_clock) {
+// if (!sync_clock[tid].dirty)
+// return; // No new info to acquire.
+// // This handles constant reads of singleton pointers and
+// // stop-flags.
+// acquire_impl(thr_clock, sync_clock); // As usual, O(N).
+// sync_clock[tid].dirty = false;
+// sync_clock.dirty_count--;
+// }
+// The release operation looks as follows:
+// void release(thr, tid, thr_clock, sync_clock) {
+// // thr->sync_cache is a simple fixed-size hash-based cache that holds
+// // several previous sync_clock's.
+// if (thr->sync_cache[sync_clock] >= thr->last_acquire_epoch) {
+// // The thread did no acquire operations since last release on this clock.
+// // So update only the thread's slot (other slots can't possibly change).
+// sync_clock[tid].clock = thr->epoch;
+// if (sync_clock.dirty_count == sync_clock.cnt
+// || (sync_clock.dirty_count == sync_clock.cnt - 1
+// && sync_clock[tid].dirty == false))
+// // All dirty flags are set, bail out.
+// return;
+// set all dirty bits, but preserve the thread's bit. // O(N)
+// update sync_clock.dirty_count;
+// return;
+// }
+// release_impl(thr_clock, sync_clock); // As usual, O(N).
+// set all dirty bits, but preserve the thread's bit.
+// // The previous step is combined with release_impl(), so that
+// // we scan the arrays only once.
+// update sync_clock.dirty_count;
+// }
+
+namespace __tsan {
+
+ThreadClock::ThreadClock() {
+ nclk_ = 0;
+ for (uptr i = 0; i < (uptr)kMaxTidInClock; i++)
+ clk_[i] = 0;
+}
+
+void ThreadClock::acquire(const SyncClock *src) {
+ DCHECK(nclk_ <= kMaxTid);
+ DCHECK(src->clk_.Size() <= kMaxTid);
+
+ const uptr nclk = src->clk_.Size();
+ if (nclk == 0)
+ return;
+ nclk_ = max(nclk_, nclk);
+ for (uptr i = 0; i < nclk; i++) {
+ if (clk_[i] < src->clk_[i])
+ clk_[i] = src->clk_[i];
+ }
+}
+
+void ThreadClock::release(SyncClock *dst) const {
+ DCHECK(nclk_ <= kMaxTid);
+ DCHECK(dst->clk_.Size() <= kMaxTid);
+
+ if (dst->clk_.Size() < nclk_)
+ dst->clk_.Resize(nclk_);
+ for (uptr i = 0; i < nclk_; i++) {
+ if (dst->clk_[i] < clk_[i])
+ dst->clk_[i] = clk_[i];
+ }
+}
+
+void ThreadClock::ReleaseStore(SyncClock *dst) const {
+ DCHECK(nclk_ <= kMaxTid);
+ DCHECK(dst->clk_.Size() <= kMaxTid);
+
+ if (dst->clk_.Size() < nclk_)
+ dst->clk_.Resize(nclk_);
+ for (uptr i = 0; i < nclk_; i++)
+ dst->clk_[i] = clk_[i];
+ for (uptr i = nclk_; i < dst->clk_.Size(); i++)
+ dst->clk_[i] = 0;
+}
+
+void ThreadClock::acq_rel(SyncClock *dst) {
+ acquire(dst);
+ release(dst);
+}
+
+void ThreadClock::Disable(unsigned tid) {
+ u64 c0 = clk_[tid];
+ for (uptr i = 0; i < kMaxTidInClock; i++)
+ clk_[i] = (u64)-1;
+ clk_[tid] = c0;
+}
+
+SyncClock::SyncClock()
+ : clk_(MBlockClock) {
+}
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_clock.h b/lib/tsan/rtl/tsan_clock.h
new file mode 100644
index 000000000000..02ddb9abdb30
--- /dev/null
+++ b/lib/tsan/rtl/tsan_clock.h
@@ -0,0 +1,82 @@
+//===-- tsan_clock.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_CLOCK_H
+#define TSAN_CLOCK_H
+
+#include "tsan_defs.h"
+#include "tsan_vector.h"
+
+namespace __tsan {
+
+// The clock that lives in sync variables (mutexes, atomics, etc).
+class SyncClock {
+ public:
+ SyncClock();
+
+ uptr size() const {
+ return clk_.Size();
+ }
+
+ void Reset() {
+ clk_.Reset();
+ }
+
+ private:
+ Vector<u64> clk_;
+ friend struct ThreadClock;
+};
+
+// The clock that lives in threads.
+struct ThreadClock {
+ public:
+ ThreadClock();
+
+ u64 get(unsigned tid) const {
+ DCHECK_LT(tid, kMaxTidInClock);
+ return clk_[tid];
+ }
+
+ void set(unsigned tid, u64 v) {
+ DCHECK_LT(tid, kMaxTid);
+ DCHECK_GE(v, clk_[tid]);
+ clk_[tid] = v;
+ if (nclk_ <= tid)
+ nclk_ = tid + 1;
+ }
+
+ void tick(unsigned tid) {
+ DCHECK_LT(tid, kMaxTid);
+ clk_[tid]++;
+ if (nclk_ <= tid)
+ nclk_ = tid + 1;
+ }
+
+ void Disable(unsigned tid);
+
+ uptr size() const {
+ return nclk_;
+ }
+
+ void acquire(const SyncClock *src);
+ void release(SyncClock *dst) const;
+ void acq_rel(SyncClock *dst);
+ void ReleaseStore(SyncClock *dst) const;
+
+ private:
+ uptr nclk_;
+ u64 clk_[kMaxTidInClock];
+};
+
+} // namespace __tsan
+
+#endif // TSAN_CLOCK_H
diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h
new file mode 100644
index 000000000000..ca8f0aecc83a
--- /dev/null
+++ b/lib/tsan/rtl/tsan_defs.h
@@ -0,0 +1,139 @@
+//===-- tsan_defs.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TSAN_DEFS_H
+#define TSAN_DEFS_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_stat.h"
+
+#ifndef TSAN_DEBUG
+#define TSAN_DEBUG 0
+#endif // TSAN_DEBUG
+
+namespace __tsan {
+
+const int kTidBits = 13;
+const unsigned kMaxTid = 1 << kTidBits;
+const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
+const int kClkBits = 43;
+#ifndef TSAN_GO
+const int kShadowStackSize = 1024;
+#endif
+
+#ifdef TSAN_SHADOW_COUNT
+# if TSAN_SHADOW_COUNT == 2 \
+ || TSAN_SHADOW_COUNT == 4 || TSAN_SHADOW_COUNT == 8
+const unsigned kShadowCnt = TSAN_SHADOW_COUNT;
+# else
+# error "TSAN_SHADOW_COUNT must be one of 2,4,8"
+# endif
+#else
+// Count of shadow values in a shadow cell.
+const unsigned kShadowCnt = 8;
+#endif
+
+// That many user bytes are mapped onto a single shadow cell.
+const unsigned kShadowCell = 8;
+
+// Size of a single shadow value (u64).
+const unsigned kShadowSize = 8;
+
+#if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS
+const bool kCollectStats = true;
+#else
+const bool kCollectStats = false;
+#endif
+
+// The following "build consistency" machinery ensures that all source files
+// are built in the same configuration. Inconsistent builds lead to
+// hard to debug crashes.
+#if TSAN_DEBUG
+void build_consistency_debug();
+#else
+void build_consistency_release();
+#endif
+
+#if TSAN_COLLECT_STATS
+void build_consistency_stats();
+#else
+void build_consistency_nostats();
+#endif
+
+#if TSAN_SHADOW_COUNT == 1
+void build_consistency_shadow1();
+#elif TSAN_SHADOW_COUNT == 2
+void build_consistency_shadow2();
+#elif TSAN_SHADOW_COUNT == 4
+void build_consistency_shadow4();
+#else
+void build_consistency_shadow8();
+#endif
+
+static inline void USED build_consistency() {
+#if TSAN_DEBUG
+ build_consistency_debug();
+#else
+ build_consistency_release();
+#endif
+#if TSAN_COLLECT_STATS
+ build_consistency_stats();
+#else
+ build_consistency_nostats();
+#endif
+#if TSAN_SHADOW_COUNT == 1
+ build_consistency_shadow1();
+#elif TSAN_SHADOW_COUNT == 2
+ build_consistency_shadow2();
+#elif TSAN_SHADOW_COUNT == 4
+ build_consistency_shadow4();
+#else
+ build_consistency_shadow8();
+#endif
+}
+
+template<typename T>
+T min(T a, T b) {
+ return a < b ? a : b;
+}
+
+template<typename T>
+T max(T a, T b) {
+ return a > b ? a : b;
+}
+
+template<typename T>
+T RoundUp(T p, int align) {
+ DCHECK_EQ(align & (align - 1), 0);
+ return (T)(((u64)p + align - 1) & ~(align - 1));
+}
+
+struct MD5Hash {
+ u64 hash[2];
+ bool operator==(const MD5Hash &other) const;
+};
+
+MD5Hash md5_hash(const void *data, uptr size);
+
+struct ThreadState;
+struct ThreadContext;
+struct Context;
+struct ReportStack;
+class ReportDesc;
+class RegionAlloc;
+class StackTrace;
+
+} // namespace __tsan
+
+#endif // TSAN_DEFS_H
diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc
new file mode 100644
index 000000000000..8f91939db1de
--- /dev/null
+++ b/lib/tsan/rtl/tsan_flags.cc
@@ -0,0 +1,79 @@
+//===-- tsan_flags.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_flags.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+
+namespace __tsan {
+
+Flags *flags() {
+ return &CTX()->flags;
+}
+
+// Can be overriden in frontend.
+#ifdef TSAN_EXTERNAL_HOOKS
+void OverrideFlags(Flags *f);
+#else
+void WEAK OverrideFlags(Flags *f) {
+ (void)f;
+}
+#endif
+
+void InitializeFlags(Flags *f, const char *env) {
+ internal_memset(f, 0, sizeof(*f));
+
+ // Default values.
+ f->enable_annotations = true;
+ f->suppress_equal_stacks = true;
+ f->suppress_equal_addresses = true;
+ f->report_thread_leaks = true;
+ f->report_signal_unsafe = true;
+ f->force_seq_cst_atomics = false;
+ f->strip_path_prefix = "";
+ f->suppressions = "";
+ f->exitcode = 66;
+ f->log_fileno = 2;
+ f->atexit_sleep_ms = 1000;
+ f->verbosity = 0;
+ f->profile_memory = "";
+ f->flush_memory_ms = 0;
+ f->stop_on_start = false;
+ f->running_on_valgrind = false;
+ f->use_internal_symbolizer = false;
+
+ // Let a frontend override.
+ OverrideFlags(f);
+
+ // Override from command line.
+ ParseFlag(env, &f->enable_annotations, "enable_annotations");
+ ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks");
+ ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses");
+ ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks");
+ ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe");
+ ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics");
+ ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix");
+ ParseFlag(env, &f->suppressions, "suppressions");
+ ParseFlag(env, &f->exitcode, "exitcode");
+ ParseFlag(env, &f->log_fileno, "log_fileno");
+ ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms");
+ ParseFlag(env, &f->verbosity, "verbosity");
+ ParseFlag(env, &f->profile_memory, "profile_memory");
+ ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms");
+ ParseFlag(env, &f->stop_on_start, "stop_on_start");
+ ParseFlag(env, &f->use_internal_symbolizer, "use_internal_symbolizer");
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h
new file mode 100644
index 000000000000..c22132f2d32f
--- /dev/null
+++ b/lib/tsan/rtl/tsan_flags.h
@@ -0,0 +1,71 @@
+//===-- tsan_flags.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+// NOTE: This file may be included into user code.
+//===----------------------------------------------------------------------===//
+
+#ifndef TSAN_FLAGS_H
+#define TSAN_FLAGS_H
+
+// ----------- ATTENTION -------------
+// ThreadSanitizer user may provide its implementation of weak
+// symbol __tsan::OverrideFlags(__tsan::Flags). Therefore, this
+// header may be included in the user code, and shouldn't include
+// other headers from TSan or common sanitizer runtime.
+
+namespace __tsan {
+
+struct Flags {
+ // Enable dynamic annotations, otherwise they are no-ops.
+ bool enable_annotations;
+ // Supress a race report if we've already output another race report
+ // with the same stack.
+ bool suppress_equal_stacks;
+ // Supress a race report if we've already output another race report
+ // on the same address.
+ bool suppress_equal_addresses;
+ // Report thread leaks at exit?
+ bool report_thread_leaks;
+ // Report violations of async signal-safety
+ // (e.g. malloc() call from a signal handler).
+ bool report_signal_unsafe;
+ // If set, all atomics are effectively sequentially consistent (seq_cst),
+ // regardless of what user actually specified.
+ bool force_seq_cst_atomics;
+ // Strip that prefix from file paths in reports.
+ const char *strip_path_prefix;
+ // Suppressions filename.
+ const char *suppressions;
+ // Override exit status if something was reported.
+ int exitcode;
+ // Log fileno (1 - stdout, 2 - stderr).
+ int log_fileno;
+ // Sleep in main thread before exiting for that many ms
+ // (useful to catch "at exit" races).
+ int atexit_sleep_ms;
+ // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
+ int verbosity;
+ // If set, periodically write memory profile to that file.
+ const char *profile_memory;
+ // Flush shadow memory every X ms.
+ int flush_memory_ms;
+ // Stops on start until __tsan_resume() is called (for debugging).
+ bool stop_on_start;
+ // Controls whether RunningOnValgrind() returns true or false.
+ bool running_on_valgrind;
+ // If set, uses in-process symbolizer from common sanitizer runtime.
+ bool use_internal_symbolizer;
+};
+
+Flags *flags();
+void InitializeFlags(Flags *flags, const char *env);
+}
+
+#endif // TSAN_FLAGS_H
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
new file mode 100644
index 000000000000..a962250568b7
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -0,0 +1,1596 @@
+//===-- tsan_interceptors_linux.cc ----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_rtl.h"
+#include "tsan_interface.h"
+#include "tsan_platform.h"
+#include "tsan_mman.h"
+
+using namespace __tsan; // NOLINT
+
+const int kSigCount = 128;
+
+struct my_siginfo_t {
+ int opaque[128];
+};
+
+struct sigset_t {
+ u64 val[1024 / 8 / sizeof(u64)];
+};
+
+struct ucontext_t {
+ uptr opaque[117];
+};
+
+extern "C" int pthread_attr_init(void *attr);
+extern "C" int pthread_attr_destroy(void *attr);
+extern "C" int pthread_attr_getdetachstate(void *attr, int *v);
+extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize);
+extern "C" int pthread_attr_getstacksize(void *attr, uptr *stacksize);
+extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
+extern "C" int pthread_setspecific(unsigned key, const void *v);
+extern "C" int pthread_mutexattr_gettype(void *a, int *type);
+extern "C" int pthread_yield();
+extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
+extern "C" int sigfillset(sigset_t *set);
+extern "C" void *pthread_self();
+extern "C" void _exit(int status);
+extern "C" int __cxa_atexit(void (*func)(void *arg), void *arg, void *dso);
+extern "C" int *__errno_location();
+extern "C" int usleep(unsigned usec);
+const int PTHREAD_MUTEX_RECURSIVE = 1;
+const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
+const int kPthreadAttrSize = 56;
+const int EINVAL = 22;
+const int EBUSY = 16;
+const int EPOLL_CTL_ADD = 1;
+const int SIGILL = 4;
+const int SIGABRT = 6;
+const int SIGFPE = 8;
+const int SIGSEGV = 11;
+const int SIGPIPE = 13;
+const int SIGBUS = 7;
+void *const MAP_FAILED = (void*)-1;
+const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
+const int MAP_FIXED = 0x10;
+typedef long long_t; // NOLINT
+
+typedef void (*sighandler_t)(int sig);
+
+#define errno (*__errno_location())
+
+union pthread_attr_t {
+ char size[kPthreadAttrSize];
+ void *align;
+};
+
+struct sigaction_t {
+ union {
+ sighandler_t sa_handler;
+ void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx);
+ };
+ sigset_t sa_mask;
+ int sa_flags;
+ void (*sa_restorer)();
+};
+
+const sighandler_t SIG_DFL = (sighandler_t)0;
+const sighandler_t SIG_IGN = (sighandler_t)1;
+const sighandler_t SIG_ERR = (sighandler_t)-1;
+const int SA_SIGINFO = 4;
+const int SIG_SETMASK = 2;
+
+static sigaction_t sigactions[kSigCount];
+
+namespace __tsan {
+struct SignalDesc {
+ bool armed;
+ bool sigaction;
+ my_siginfo_t siginfo;
+ ucontext_t ctx;
+};
+
+struct SignalContext {
+ int int_signal_send;
+ int pending_signal_count;
+ SignalDesc pending_signals[kSigCount];
+};
+}
+
+static SignalContext *SigCtx(ThreadState *thr) {
+ SignalContext *ctx = (SignalContext*)thr->signal_ctx;
+ if (ctx == 0 && thr->is_alive) {
+ ScopedInRtl in_rtl;
+ ctx = (SignalContext*)internal_alloc(
+ MBlockSignal, sizeof(*ctx));
+ MemoryResetRange(thr, 0, (uptr)ctx, sizeof(*ctx));
+ internal_memset(ctx, 0, sizeof(*ctx));
+ thr->signal_ctx = ctx;
+ }
+ return ctx;
+}
+
+static unsigned g_thread_finalize_key;
+
+static void process_pending_signals(ThreadState *thr);
+
+class ScopedInterceptor {
+ public:
+ ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc)
+ : thr_(thr)
+ , in_rtl_(thr->in_rtl) {
+ if (thr_->in_rtl == 0) {
+ Initialize(thr);
+ FuncEntry(thr, pc);
+ thr_->in_rtl++;
+ DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
+ } else {
+ thr_->in_rtl++;
+ }
+ }
+
+ ~ScopedInterceptor() {
+ thr_->in_rtl--;
+ if (thr_->in_rtl == 0) {
+ FuncExit(thr_);
+ process_pending_signals(thr_);
+ }
+ CHECK_EQ(in_rtl_, thr_->in_rtl);
+ }
+
+ private:
+ ThreadState *const thr_;
+ const int in_rtl_;
+};
+
+#define SCOPED_INTERCEPTOR_RAW(func, ...) \
+ ThreadState *thr = cur_thread(); \
+ StatInc(thr, StatInterceptor); \
+ StatInc(thr, StatInt_##func); \
+ ScopedInterceptor si(thr, #func, \
+ (__sanitizer::uptr)__builtin_return_address(0)); \
+ const uptr pc = (uptr)&func; \
+ (void)pc; \
+/**/
+
+#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
+ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+ if (thr->in_rtl > 1) \
+ return REAL(func)(__VA_ARGS__); \
+/**/
+
+#define SCOPED_INTERCEPTOR_LIBC(func, ...) \
+ ThreadState *thr = cur_thread(); \
+ StatInc(thr, StatInterceptor); \
+ StatInc(thr, StatInt_##func); \
+ ScopedInterceptor si(thr, #func, callpc); \
+ const uptr pc = (uptr)&func; \
+ (void)pc; \
+ if (thr->in_rtl > 1) \
+ return REAL(func)(__VA_ARGS__); \
+/**/
+
+#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
+#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
+
+// May be overriden by front-end.
+extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+
+extern "C" void WEAK __tsan_free_hook(void *ptr) {
+ (void)ptr;
+}
+
+static void invoke_malloc_hook(void *ptr, uptr size) {
+ Context *ctx = CTX();
+ ThreadState *thr = cur_thread();
+ if (ctx == 0 || !ctx->initialized || thr->in_rtl)
+ return;
+ __tsan_malloc_hook(ptr, size);
+}
+
+static void invoke_free_hook(void *ptr) {
+ Context *ctx = CTX();
+ ThreadState *thr = cur_thread();
+ if (ctx == 0 || !ctx->initialized || thr->in_rtl)
+ return;
+ __tsan_free_hook(ptr);
+}
+
+class AtExitContext {
+ public:
+ AtExitContext()
+ : mtx_(MutexTypeAtExit, StatMtxAtExit)
+ , pos_() {
+ }
+
+ typedef void(*atexit_t)();
+
+ int atexit(ThreadState *thr, uptr pc, atexit_t f) {
+ Lock l(&mtx_);
+ if (pos_ == kMaxAtExit)
+ return 1;
+ Release(thr, pc, (uptr)this);
+ stack_[pos_] = f;
+ pos_++;
+ return 0;
+ }
+
+ void exit(ThreadState *thr, uptr pc) {
+ CHECK_EQ(thr->in_rtl, 0);
+ for (;;) {
+ atexit_t f = 0;
+ {
+ Lock l(&mtx_);
+ if (pos_) {
+ pos_--;
+ f = stack_[pos_];
+ ScopedInRtl in_rtl;
+ Acquire(thr, pc, (uptr)this);
+ }
+ }
+ if (f == 0)
+ break;
+ DPrintf("#%d: executing atexit func %p\n", thr->tid, f);
+ CHECK_EQ(thr->in_rtl, 0);
+ f();
+ }
+ }
+
+ private:
+ static const int kMaxAtExit = 128;
+ Mutex mtx_;
+ atexit_t stack_[kMaxAtExit];
+ int pos_;
+};
+
+static AtExitContext *atexit_ctx;
+
+static void finalize(void *arg) {
+ ThreadState * thr = cur_thread();
+ uptr pc = 0;
+ atexit_ctx->exit(thr, pc);
+ {
+ ScopedInRtl in_rtl;
+ DestroyAndFree(atexit_ctx);
+ usleep(flags()->atexit_sleep_ms * 1000);
+ }
+ int status = Finalize(cur_thread());
+ if (status)
+ _exit(status);
+}
+
+TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
+ SCOPED_TSAN_INTERCEPTOR(atexit, f);
+ return atexit_ctx->atexit(thr, pc, f);
+ return 0;
+}
+
+TSAN_INTERCEPTOR(void, longjmp, void *env, int val) {
+ SCOPED_TSAN_INTERCEPTOR(longjmp, env, val);
+ TsanPrintf("ThreadSanitizer: longjmp() is not supported\n");
+ Die();
+}
+
+TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) {
+ SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val);
+ TsanPrintf("ThreadSanitizer: siglongjmp() is not supported\n");
+ Die();
+}
+
+static uptr fd2addr(int fd) {
+ (void)fd;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+static uptr epollfd2addr(int fd) {
+ (void)fd;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+static uptr file2addr(char *path) {
+ (void)path;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+static uptr dir2addr(char *path) {
+ (void)path;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+TSAN_INTERCEPTOR(void*, malloc, uptr size) {
+ void *p = 0;
+ {
+ SCOPED_INTERCEPTOR_RAW(malloc, size);
+ p = user_alloc(thr, pc, size);
+ }
+ invoke_malloc_hook(p, size);
+ return p;
+}
+
+TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
+ void *p = 0;
+ {
+ SCOPED_INTERCEPTOR_RAW(calloc, size, n);
+ p = user_alloc(thr, pc, n * size);
+ internal_memset(p, 0, n * size);
+ }
+ invoke_malloc_hook(p, n * size);
+ return p;
+}
+
+TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
+ if (p)
+ invoke_free_hook(p);
+ {
+ SCOPED_INTERCEPTOR_RAW(realloc, p, size);
+ p = user_realloc(thr, pc, p, size);
+ }
+ invoke_malloc_hook(p, size);
+ return p;
+}
+
+TSAN_INTERCEPTOR(void, free, void *p) {
+ if (p == 0)
+ return;
+ invoke_free_hook(p);
+ SCOPED_INTERCEPTOR_RAW(free, p);
+ user_free(thr, pc, p);
+}
+
+TSAN_INTERCEPTOR(void, cfree, void *p) {
+ if (p == 0)
+ return;
+ invoke_free_hook(p);
+ SCOPED_INTERCEPTOR_RAW(cfree, p);
+ user_free(thr, pc, p);
+}
+
+TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
+ SCOPED_TSAN_INTERCEPTOR(strlen, s);
+ uptr len = internal_strlen(s);
+ MemoryAccessRange(thr, pc, (uptr)s, len + 1, false);
+ return len;
+}
+
+TSAN_INTERCEPTOR(void*, memset, void *dst, int v, uptr size) {
+ SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size);
+ MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ return internal_memset(dst, v, size);
+}
+
+TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) {
+ SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size);
+ MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ MemoryAccessRange(thr, pc, (uptr)src, size, false);
+ return internal_memcpy(dst, src, size);
+}
+
+TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) {
+ SCOPED_TSAN_INTERCEPTOR(memcmp, s1, s2, n);
+ int res = 0;
+ uptr len = 0;
+ for (; len < n; len++) {
+ if ((res = ((unsigned char*)s1)[len] - ((unsigned char*)s2)[len]))
+ break;
+ }
+ MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false);
+ MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
+ SCOPED_TSAN_INTERCEPTOR(strcmp, s1, s2);
+ uptr len = 0;
+ for (; s1[len] && s2[len]; len++) {
+ if (s1[len] != s2[len])
+ break;
+ }
+ MemoryAccessRange(thr, pc, (uptr)s1, len + 1, false);
+ MemoryAccessRange(thr, pc, (uptr)s2, len + 1, false);
+ return s1[len] - s2[len];
+}
+
+TSAN_INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr n) {
+ SCOPED_TSAN_INTERCEPTOR(strncmp, s1, s2, n);
+ uptr len = 0;
+ for (; len < n && s1[len] && s2[len]; len++) {
+ if (s1[len] != s2[len])
+ break;
+ }
+ MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false);
+ MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false);
+ return len == n ? 0 : s1[len] - s2[len];
+}
+
+TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) {
+ SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n);
+ void *res = REAL(memchr)(s, c, n);
+ uptr len = res ? (char*)res - (char*)s + 1 : n;
+ MemoryAccessRange(thr, pc, (uptr)s, len, false);
+ return res;
+}
+
+TSAN_INTERCEPTOR(void*, memrchr, char *s, int c, uptr n) {
+ SCOPED_TSAN_INTERCEPTOR(memrchr, s, c, n);
+ MemoryAccessRange(thr, pc, (uptr)s, n, false);
+ return REAL(memrchr)(s, c, n);
+}
+
+TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) {
+ SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n);
+ MemoryAccessRange(thr, pc, (uptr)dst, n, true);
+ MemoryAccessRange(thr, pc, (uptr)src, n, false);
+ return REAL(memmove)(dst, src, n);
+}
+
+TSAN_INTERCEPTOR(char*, strchr, char *s, int c) {
+ SCOPED_TSAN_INTERCEPTOR(strchr, s, c);
+ char *res = REAL(strchr)(s, c);
+ uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1;
+ MemoryAccessRange(thr, pc, (uptr)s, len, false);
+ return res;
+}
+
+TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
+ SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c);
+ char *res = REAL(strchrnul)(s, c);
+ uptr len = (char*)res - (char*)s + 1;
+ MemoryAccessRange(thr, pc, (uptr)s, len, false);
+ return res;
+}
+
+TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) {
+ SCOPED_TSAN_INTERCEPTOR(strrchr, s, c);
+ MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s) + 1, false);
+ return REAL(strrchr)(s, c);
+}
+
+TSAN_INTERCEPTOR(char*, strcpy, char *dst, const char *src) { // NOLINT
+ SCOPED_TSAN_INTERCEPTOR(strcpy, dst, src); // NOLINT
+ uptr srclen = internal_strlen(src);
+ MemoryAccessRange(thr, pc, (uptr)dst, srclen + 1, true);
+ MemoryAccessRange(thr, pc, (uptr)src, srclen + 1, false);
+ return REAL(strcpy)(dst, src); // NOLINT
+}
+
+TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) {
+ SCOPED_TSAN_INTERCEPTOR(strncpy, dst, src, n);
+ uptr srclen = internal_strnlen(src, n);
+ MemoryAccessRange(thr, pc, (uptr)dst, n, true);
+ MemoryAccessRange(thr, pc, (uptr)src, min(srclen + 1, n), false);
+ return REAL(strncpy)(dst, src, n);
+}
+
+TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) {
+ SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2);
+ const char *res = REAL(strstr)(s1, s2);
+ uptr len1 = internal_strlen(s1);
+ uptr len2 = internal_strlen(s2);
+ MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false);
+ MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false);
+ return res;
+}
+
+static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
+ if (*addr) {
+ if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) {
+ if (flags & MAP_FIXED) {
+ errno = EINVAL;
+ return false;
+ } else {
+ *addr = 0;
+ }
+ }
+ }
+ return true;
+}
+
+TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot,
+ int flags, int fd, unsigned off) {
+ SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off);
+ if (!fix_mmap_addr(&addr, sz, flags))
+ return MAP_FAILED;
+ void *res = REAL(mmap)(addr, sz, prot, flags, fd, off);
+ if (res != MAP_FAILED) {
+ MemoryResetRange(thr, pc, (uptr)res, sz);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
+ int flags, int fd, u64 off) {
+ SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off);
+ if (!fix_mmap_addr(&addr, sz, flags))
+ return MAP_FAILED;
+ void *res = REAL(mmap64)(addr, sz, prot, flags, fd, off);
+ if (res != MAP_FAILED) {
+ MemoryResetRange(thr, pc, (uptr)res, sz);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
+ SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
+ int res = REAL(munmap)(addr, sz);
+ return res;
+}
+
+#ifdef __LP64__
+
+// void *operator new(size_t)
+TSAN_INTERCEPTOR(void*, _Znwm, uptr sz) {
+ void *p = 0;
+ {
+ SCOPED_TSAN_INTERCEPTOR(_Znwm, sz);
+ p = user_alloc(thr, pc, sz);
+ }
+ invoke_malloc_hook(p, sz);
+ return p;
+}
+
+// void *operator new(size_t, nothrow_t)
+TSAN_INTERCEPTOR(void*, _ZnwmRKSt9nothrow_t, uptr sz) {
+ void *p = 0;
+ {
+ SCOPED_TSAN_INTERCEPTOR(_ZnwmRKSt9nothrow_t, sz);
+ p = user_alloc(thr, pc, sz);
+ }
+ invoke_malloc_hook(p, sz);
+ return p;
+}
+
+// void *operator new[](size_t)
+TSAN_INTERCEPTOR(void*, _Znam, uptr sz) {
+ void *p = 0;
+ {
+ SCOPED_TSAN_INTERCEPTOR(_Znam, sz);
+ p = user_alloc(thr, pc, sz);
+ }
+ invoke_malloc_hook(p, sz);
+ return p;
+}
+
+// void *operator new[](size_t, nothrow_t)
+TSAN_INTERCEPTOR(void*, _ZnamRKSt9nothrow_t, uptr sz) {
+ void *p = 0;
+ {
+ SCOPED_TSAN_INTERCEPTOR(_ZnamRKSt9nothrow_t, sz);
+ p = user_alloc(thr, pc, sz);
+ }
+ invoke_malloc_hook(p, sz);
+ return p;
+}
+
+#else
+#error "Not implemented"
+#endif
+
+// void operator delete(void*)
+TSAN_INTERCEPTOR(void, _ZdlPv, void *p) {
+ if (p == 0)
+ return;
+ invoke_free_hook(p);
+ SCOPED_TSAN_INTERCEPTOR(_ZdlPv, p);
+ user_free(thr, pc, p);
+}
+
+// void operator delete(void*, nothrow_t)
+TSAN_INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *p) {
+ if (p == 0)
+ return;
+ invoke_free_hook(p);
+ SCOPED_TSAN_INTERCEPTOR(_ZdlPvRKSt9nothrow_t, p);
+ user_free(thr, pc, p);
+}
+
+// void operator delete[](void*)
+TSAN_INTERCEPTOR(void, _ZdaPv, void *p) {
+ if (p == 0)
+ return;
+ invoke_free_hook(p);
+ SCOPED_TSAN_INTERCEPTOR(_ZdaPv, p);
+ user_free(thr, pc, p);
+}
+
+// void operator delete[](void*, nothrow_t)
+TSAN_INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *p) {
+ if (p == 0)
+ return;
+ invoke_free_hook(p);
+ SCOPED_TSAN_INTERCEPTOR(_ZdaPvRKSt9nothrow_t, p);
+ user_free(thr, pc, p);
+}
+
+TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
+ SCOPED_TSAN_INTERCEPTOR(memalign, align, sz);
+ return user_alloc_aligned(thr, pc, sz, align);
+}
+
+TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
+ SCOPED_TSAN_INTERCEPTOR(valloc, sz);
+ return user_alloc_aligned(thr, pc, sz, kPageSize);
+}
+
+TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
+ SCOPED_TSAN_INTERCEPTOR(pvalloc, sz);
+ sz = RoundUp(sz, kPageSize);
+ return user_alloc_aligned(thr, pc, sz, kPageSize);
+}
+
+TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
+ SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz);
+ *memptr = user_alloc_aligned(thr, pc, sz, align);
+ return 0;
+}
+
+// Used in thread-safe function static initialization.
+TSAN_INTERCEPTOR(int, __cxa_guard_acquire, char *m) {
+ SCOPED_TSAN_INTERCEPTOR(__cxa_guard_acquire, m);
+ int res = REAL(__cxa_guard_acquire)(m);
+ if (res) {
+ // This thread does the init.
+ } else {
+ Acquire(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(void, __cxa_guard_release, char *m) {
+ SCOPED_TSAN_INTERCEPTOR(__cxa_guard_release, m);
+ Release(thr, pc, (uptr)m);
+ REAL(__cxa_guard_release)(m);
+}
+
+static void thread_finalize(void *v) {
+ uptr iter = (uptr)v;
+ if (iter > 1) {
+ if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) {
+ TsanPrintf("ThreadSanitizer: failed to set thread key\n");
+ Die();
+ }
+ return;
+ }
+ {
+ ScopedInRtl in_rtl;
+ ThreadState *thr = cur_thread();
+ ThreadFinish(thr);
+ SignalContext *sctx = thr->signal_ctx;
+ if (sctx) {
+ thr->signal_ctx = 0;
+ internal_free(sctx);
+ }
+ }
+}
+
+
+struct ThreadParam {
+ void* (*callback)(void *arg);
+ void *param;
+ atomic_uintptr_t tid;
+};
+
+extern "C" void *__tsan_thread_start_func(void *arg) {
+ ThreadParam *p = (ThreadParam*)arg;
+ void* (*callback)(void *arg) = p->callback;
+ void *param = p->param;
+ int tid = 0;
+ {
+ ThreadState *thr = cur_thread();
+ ScopedInRtl in_rtl;
+ if (pthread_setspecific(g_thread_finalize_key, (void*)4)) {
+ TsanPrintf("ThreadSanitizer: failed to set thread key\n");
+ Die();
+ }
+ while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
+ pthread_yield();
+ atomic_store(&p->tid, 0, memory_order_release);
+ ThreadStart(thr, tid);
+ CHECK_EQ(thr->in_rtl, 1);
+ }
+ void *res = callback(param);
+ // Prevent the callback from being tail called,
+ // it mixes up stack traces.
+ volatile int foo = 42;
+ foo++;
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_create,
+ void *th, void *attr, void *(*callback)(void*), void * param) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param);
+ pthread_attr_t myattr;
+ if (attr == 0) {
+ pthread_attr_init(&myattr);
+ attr = &myattr;
+ }
+ int detached = 0;
+ pthread_attr_getdetachstate(attr, &detached);
+ uptr stacksize = 0;
+ pthread_attr_getstacksize(attr, &stacksize);
+ // We place the huge ThreadState object into TLS, account for that.
+ const uptr minstacksize = GetTlsSize() + 128*1024;
+ if (stacksize < minstacksize) {
+ DPrintf("ThreadSanitizer: stacksize %zu->%zu\n", stacksize, minstacksize);
+ pthread_attr_setstacksize(attr, minstacksize);
+ }
+ ThreadParam p;
+ p.callback = callback;
+ p.param = param;
+ atomic_store(&p.tid, 0, memory_order_relaxed);
+ int res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p);
+ if (res == 0) {
+ int tid = ThreadCreate(cur_thread(), pc, *(uptr*)th, detached);
+ CHECK_NE(tid, 0);
+ atomic_store(&p.tid, tid, memory_order_release);
+ while (atomic_load(&p.tid, memory_order_acquire) != 0)
+ pthread_yield();
+ }
+ if (attr == &myattr)
+ pthread_attr_destroy(&myattr);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ int res = REAL(pthread_join)(th, ret);
+ if (res == 0) {
+ ThreadJoin(cur_thread(), pc, tid);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_detach, th);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ int res = REAL(pthread_detach)(th);
+ if (res == 0) {
+ ThreadDetach(cur_thread(), pc, tid);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a);
+ int res = REAL(pthread_mutex_init)(m, a);
+ if (res == 0) {
+ bool recursive = false;
+ if (a) {
+ int type = 0;
+ if (pthread_mutexattr_gettype(a, &type) == 0)
+ recursive = (type == PTHREAD_MUTEX_RECURSIVE
+ || type == PTHREAD_MUTEX_RECURSIVE_NP);
+ }
+ MutexCreate(cur_thread(), pc, (uptr)m, false, recursive);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_destroy, m);
+ int res = REAL(pthread_mutex_destroy)(m);
+ if (res == 0 || res == EBUSY) {
+ MutexDestroy(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m);
+ int res = REAL(pthread_mutex_lock)(m);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m);
+ int res = REAL(pthread_mutex_trylock)(m);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
+ int res = REAL(pthread_mutex_timedlock)(m, abstime);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m);
+ MutexUnlock(cur_thread(), pc, (uptr)m);
+ int res = REAL(pthread_mutex_unlock)(m);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
+ int res = REAL(pthread_spin_init)(m, pshared);
+ if (res == 0) {
+ MutexCreate(cur_thread(), pc, (uptr)m, false, false);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_destroy, m);
+ int res = REAL(pthread_spin_destroy)(m);
+ if (res == 0) {
+ MutexDestroy(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m);
+ int res = REAL(pthread_spin_lock)(m);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
+ int res = REAL(pthread_spin_trylock)(m);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m);
+ MutexUnlock(cur_thread(), pc, (uptr)m);
+ int res = REAL(pthread_spin_unlock)(m);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
+ int res = REAL(pthread_rwlock_init)(m, a);
+ if (res == 0) {
+ MutexCreate(cur_thread(), pc, (uptr)m, true, false);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_destroy, m);
+ int res = REAL(pthread_rwlock_destroy)(m);
+ if (res == 0) {
+ MutexDestroy(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m);
+ int res = REAL(pthread_rwlock_rdlock)(m);
+ if (res == 0) {
+ MutexReadLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m);
+ int res = REAL(pthread_rwlock_tryrdlock)(m);
+ if (res == 0) {
+ MutexReadLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
+ int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
+ if (res == 0) {
+ MutexReadLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
+ int res = REAL(pthread_rwlock_wrlock)(m);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m);
+ int res = REAL(pthread_rwlock_trywrlock)(m);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
+ int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
+ if (res == 0) {
+ MutexLock(cur_thread(), pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
+ MutexReadOrWriteUnlock(cur_thread(), pc, (uptr)m);
+ int res = REAL(pthread_rwlock_unlock)(m);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a);
+ int res = REAL(pthread_cond_init)(c, a);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c);
+ int res = REAL(pthread_cond_destroy)(c);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_signal, void *c) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c);
+ int res = REAL(pthread_cond_signal)(c);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c);
+ int res = REAL(pthread_cond_broadcast)(c);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m);
+ MutexUnlock(cur_thread(), pc, (uptr)m);
+ int res = REAL(pthread_cond_wait)(c, m);
+ MutexLock(cur_thread(), pc, (uptr)m);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime);
+ MutexUnlock(cur_thread(), pc, (uptr)m);
+ int res = REAL(pthread_cond_timedwait)(c, m, abstime);
+ MutexLock(cur_thread(), pc, (uptr)m);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count);
+ MemoryWrite1Byte(thr, pc, (uptr)b);
+ int res = REAL(pthread_barrier_init)(b, a, count);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b);
+ MemoryWrite1Byte(thr, pc, (uptr)b);
+ int res = REAL(pthread_barrier_destroy)(b);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b);
+ Release(cur_thread(), pc, (uptr)b);
+ MemoryRead1Byte(thr, pc, (uptr)b);
+ int res = REAL(pthread_barrier_wait)(b);
+ MemoryRead1Byte(thr, pc, (uptr)b);
+ if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) {
+ Acquire(cur_thread(), pc, (uptr)b);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_once, o, f);
+ if (o == 0 || f == 0)
+ return EINVAL;
+ atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o);
+ u32 v = atomic_load(a, memory_order_acquire);
+ if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
+ memory_order_relaxed)) {
+ const int old_in_rtl = thr->in_rtl;
+ thr->in_rtl = 0;
+ (*f)();
+ CHECK_EQ(thr->in_rtl, 0);
+ thr->in_rtl = old_in_rtl;
+ Release(cur_thread(), pc, (uptr)o);
+ atomic_store(a, 2, memory_order_release);
+ } else {
+ while (v != 2) {
+ pthread_yield();
+ v = atomic_load(a, memory_order_acquire);
+ }
+ Acquire(cur_thread(), pc, (uptr)o);
+ }
+ return 0;
+}
+
+TSAN_INTERCEPTOR(int, sem_init, void *s, int pshared, unsigned value) {
+ SCOPED_TSAN_INTERCEPTOR(sem_init, s, pshared, value);
+ int res = REAL(sem_init)(s, pshared, value);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_destroy, void *s) {
+ SCOPED_TSAN_INTERCEPTOR(sem_destroy, s);
+ int res = REAL(sem_destroy)(s);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_wait, void *s) {
+ SCOPED_TSAN_INTERCEPTOR(sem_wait, s);
+ int res = REAL(sem_wait)(s);
+ if (res == 0) {
+ Acquire(cur_thread(), pc, (uptr)s);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_trywait, void *s) {
+ SCOPED_TSAN_INTERCEPTOR(sem_trywait, s);
+ int res = REAL(sem_trywait)(s);
+ if (res == 0) {
+ Acquire(cur_thread(), pc, (uptr)s);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_timedwait, void *s, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(sem_timedwait, s, abstime);
+ int res = REAL(sem_timedwait)(s, abstime);
+ if (res == 0) {
+ Acquire(cur_thread(), pc, (uptr)s);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_post, void *s) {
+ SCOPED_TSAN_INTERCEPTOR(sem_post, s);
+ Release(cur_thread(), pc, (uptr)s);
+ int res = REAL(sem_post)(s);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
+ SCOPED_TSAN_INTERCEPTOR(sem_getvalue, s, sval);
+ int res = REAL(sem_getvalue)(s, sval);
+ if (res == 0) {
+ Acquire(cur_thread(), pc, (uptr)s);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, read, int fd, void *buf, long_t sz) {
+ SCOPED_TSAN_INTERCEPTOR(read, fd, buf, sz);
+ int res = REAL(read)(fd, buf, sz);
+ if (res >= 0) {
+ Acquire(cur_thread(), pc, fd2addr(fd));
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pread, int fd, void *buf, long_t sz, unsigned off) {
+ SCOPED_TSAN_INTERCEPTOR(pread, fd, buf, sz, off);
+ int res = REAL(pread)(fd, buf, sz, off);
+ if (res >= 0) {
+ Acquire(cur_thread(), pc, fd2addr(fd));
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pread64, int fd, void *buf, long_t sz, u64 off) {
+ SCOPED_TSAN_INTERCEPTOR(pread64, fd, buf, sz, off);
+ int res = REAL(pread64)(fd, buf, sz, off);
+ if (res >= 0) {
+ Acquire(cur_thread(), pc, fd2addr(fd));
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) {
+ SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt);
+ int res = REAL(readv)(fd, vec, cnt);
+ if (res >= 0) {
+ Acquire(cur_thread(), pc, fd2addr(fd));
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) {
+ SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off);
+ int res = REAL(preadv64)(fd, vec, cnt, off);
+ if (res >= 0) {
+ Acquire(cur_thread(), pc, fd2addr(fd));
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, write, int fd, void *buf, long_t sz) {
+ SCOPED_TSAN_INTERCEPTOR(write, fd, buf, sz);
+ Release(cur_thread(), pc, fd2addr(fd));
+ int res = REAL(write)(fd, buf, sz);
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pwrite, int fd, void *buf, long_t sz, unsigned off) {
+ SCOPED_TSAN_INTERCEPTOR(pwrite, fd, buf, sz, off);
+ Release(cur_thread(), pc, fd2addr(fd));
+ int res = REAL(pwrite)(fd, buf, sz, off);
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pwrite64, int fd, void *buf, long_t sz, unsigned off) {
+ SCOPED_TSAN_INTERCEPTOR(pwrite64, fd, buf, sz, off);
+ Release(cur_thread(), pc, fd2addr(fd));
+ int res = REAL(pwrite64)(fd, buf, sz, off);
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) {
+ SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt);
+ Release(cur_thread(), pc, fd2addr(fd));
+ int res = REAL(writev)(fd, vec, cnt);
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) {
+ SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off);
+ Release(cur_thread(), pc, fd2addr(fd));
+ int res = REAL(pwritev64)(fd, vec, cnt, off);
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
+ Release(cur_thread(), pc, fd2addr(fd));
+ int res = REAL(send)(fd, buf, len, flags);
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags);
+ Release(cur_thread(), pc, fd2addr(fd));
+ int res = REAL(sendmsg)(fd, msg, flags);
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags);
+ int res = REAL(recv)(fd, buf, len, flags);
+ if (res >= 0) {
+ Acquire(cur_thread(), pc, fd2addr(fd));
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags);
+ int res = REAL(recvmsg)(fd, msg, flags);
+ if (res >= 0) {
+ Acquire(cur_thread(), pc, fd2addr(fd));
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, unlink, char *path) {
+ SCOPED_TSAN_INTERCEPTOR(unlink, path);
+ Release(cur_thread(), pc, file2addr(path));
+ int res = REAL(unlink)(path);
+ return res;
+}
+
+TSAN_INTERCEPTOR(void*, fopen, char *path, char *mode) {
+ SCOPED_TSAN_INTERCEPTOR(fopen, path, mode);
+ void *res = REAL(fopen)(path, mode);
+ Acquire(cur_thread(), pc, file2addr(path));
+ return res;
+}
+
+TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
+ SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f);
+ MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true);
+ return REAL(fread)(ptr, size, nmemb, f);
+}
+
+TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) {
+ SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f);
+ MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false);
+ return REAL(fwrite)(p, size, nmemb, f);
+}
+
+TSAN_INTERCEPTOR(int, puts, const char *s) {
+ SCOPED_TSAN_INTERCEPTOR(puts, s);
+ MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s), false);
+ return REAL(puts)(s);
+}
+
+TSAN_INTERCEPTOR(int, rmdir, char *path) {
+ SCOPED_TSAN_INTERCEPTOR(rmdir, path);
+ Release(cur_thread(), pc, dir2addr(path));
+ int res = REAL(rmdir)(path);
+ return res;
+}
+
+TSAN_INTERCEPTOR(void*, opendir, char *path) {
+ SCOPED_TSAN_INTERCEPTOR(opendir, path);
+ void *res = REAL(opendir)(path);
+ Acquire(cur_thread(), pc, dir2addr(path));
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
+ if (op == EPOLL_CTL_ADD) {
+ Release(cur_thread(), pc, epollfd2addr(epfd));
+ }
+ int res = REAL(epoll_ctl)(epfd, op, fd, ev);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
+ int res = REAL(epoll_wait)(epfd, ev, cnt, timeout);
+ if (res > 0) {
+ Acquire(cur_thread(), pc, epollfd2addr(epfd));
+ }
+ return res;
+}
+
+static void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
+ my_siginfo_t *info, void *ctx) {
+ ThreadState *thr = cur_thread();
+ SignalContext *sctx = SigCtx(thr);
+ // Don't mess with synchronous signals.
+ if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
+ sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE ||
+ (sctx && sig == sctx->int_signal_send)) {
+ CHECK(thr->in_rtl == 0 || thr->in_rtl == 1);
+ int in_rtl = thr->in_rtl;
+ thr->in_rtl = 0;
+ CHECK_EQ(thr->in_signal_handler, false);
+ thr->in_signal_handler = true;
+ if (sigact)
+ sigactions[sig].sa_sigaction(sig, info, ctx);
+ else
+ sigactions[sig].sa_handler(sig);
+ CHECK_EQ(thr->in_signal_handler, true);
+ thr->in_signal_handler = false;
+ thr->in_rtl = in_rtl;
+ return;
+ }
+
+ if (sctx == 0)
+ return;
+ SignalDesc *signal = &sctx->pending_signals[sig];
+ if (signal->armed == false) {
+ signal->armed = true;
+ signal->sigaction = sigact;
+ if (info)
+ internal_memcpy(&signal->siginfo, info, sizeof(*info));
+ if (ctx)
+ internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx));
+ sctx->pending_signal_count++;
+ }
+}
+
+static void rtl_sighandler(int sig) {
+ rtl_generic_sighandler(false, sig, 0, 0);
+}
+
+static void rtl_sigaction(int sig, my_siginfo_t *info, void *ctx) {
+ rtl_generic_sighandler(true, sig, info, ctx);
+}
+
+TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) {
+ SCOPED_TSAN_INTERCEPTOR(sigaction, sig, act, old);
+ if (old)
+ internal_memcpy(old, &sigactions[sig], sizeof(*old));
+ if (act == 0)
+ return 0;
+ internal_memcpy(&sigactions[sig], act, sizeof(*act));
+ sigaction_t newact;
+ internal_memcpy(&newact, act, sizeof(newact));
+ sigfillset(&newact.sa_mask);
+ if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) {
+ if (newact.sa_flags & SA_SIGINFO)
+ newact.sa_sigaction = rtl_sigaction;
+ else
+ newact.sa_handler = rtl_sighandler;
+ }
+ int res = REAL(sigaction)(sig, &newact, 0);
+ return res;
+}
+
+TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) {
+ sigaction_t act = {};
+ act.sa_handler = h;
+ REAL(memset)(&act.sa_mask, -1, sizeof(act.sa_mask));
+ act.sa_flags = 0;
+ sigaction_t old = {};
+ int res = sigaction(sig, &act, &old);
+ if (res)
+ return SIG_ERR;
+ return old.sa_handler;
+}
+
+TSAN_INTERCEPTOR(int, raise, int sig) {
+ SCOPED_TSAN_INTERCEPTOR(raise, sig);
+ SignalContext *sctx = SigCtx(thr);
+ CHECK_NE(sctx, 0);
+ int prev = sctx->int_signal_send;
+ sctx->int_signal_send = sig;
+ int res = REAL(raise)(sig);
+ CHECK_EQ(sctx->int_signal_send, sig);
+ sctx->int_signal_send = prev;
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
+ SCOPED_TSAN_INTERCEPTOR(kill, pid, sig);
+ SignalContext *sctx = SigCtx(thr);
+ CHECK_NE(sctx, 0);
+ int prev = sctx->int_signal_send;
+ if (pid == GetPid()) {
+ sctx->int_signal_send = sig;
+ }
+ int res = REAL(kill)(pid, sig);
+ if (pid == GetPid()) {
+ CHECK_EQ(sctx->int_signal_send, sig);
+ sctx->int_signal_send = prev;
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig);
+ SignalContext *sctx = SigCtx(thr);
+ CHECK_NE(sctx, 0);
+ int prev = sctx->int_signal_send;
+ if (tid == pthread_self()) {
+ sctx->int_signal_send = sig;
+ }
+ int res = REAL(pthread_kill)(tid, sig);
+ if (tid == pthread_self()) {
+ CHECK_EQ(sctx->int_signal_send, sig);
+ sctx->int_signal_send = prev;
+ }
+ return res;
+}
+
+static void process_pending_signals(ThreadState *thr) {
+ CHECK_EQ(thr->in_rtl, 0);
+ SignalContext *sctx = SigCtx(thr);
+ if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
+ return;
+ thr->in_signal_handler = true;
+ sctx->pending_signal_count = 0;
+ // These are too big for stack.
+ static THREADLOCAL sigset_t emptyset, oldset;
+ sigfillset(&emptyset);
+ pthread_sigmask(SIG_SETMASK, &emptyset, &oldset);
+ for (int sig = 0; sig < kSigCount; sig++) {
+ SignalDesc *signal = &sctx->pending_signals[sig];
+ if (signal->armed) {
+ signal->armed = false;
+ if (sigactions[sig].sa_handler != SIG_DFL
+ && sigactions[sig].sa_handler != SIG_IGN) {
+ // Insure that the handler does not spoil errno.
+ const int saved_errno = errno;
+ errno = 0;
+ if (signal->sigaction)
+ sigactions[sig].sa_sigaction(sig, &signal->siginfo, &signal->ctx);
+ else
+ sigactions[sig].sa_handler(sig);
+ if (errno != 0) {
+ ScopedInRtl in_rtl;
+ StackTrace stack;
+ uptr pc = signal->sigaction ?
+ (uptr)sigactions[sig].sa_sigaction :
+ (uptr)sigactions[sig].sa_handler;
+ stack.Init(&pc, 1);
+ ScopedReport rep(ReportTypeErrnoInSignal);
+ rep.AddStack(&stack);
+ OutputReport(rep, rep.GetReport()->stacks[0]);
+ }
+ errno = saved_errno;
+ }
+ }
+ }
+ pthread_sigmask(SIG_SETMASK, &oldset, 0);
+ CHECK_EQ(thr->in_signal_handler, true);
+ thr->in_signal_handler = false;
+}
+
+namespace __tsan {
+
+void InitializeInterceptors() {
+ CHECK_GT(cur_thread()->in_rtl, 0);
+
+ // We need to setup it early, because functions like dlsym() can call it.
+ REAL(memset) = internal_memset;
+ REAL(memcpy) = internal_memcpy;
+ REAL(memcmp) = internal_memcmp;
+
+ TSAN_INTERCEPT(longjmp);
+ TSAN_INTERCEPT(siglongjmp);
+
+ TSAN_INTERCEPT(malloc);
+ TSAN_INTERCEPT(calloc);
+ TSAN_INTERCEPT(realloc);
+ TSAN_INTERCEPT(free);
+ TSAN_INTERCEPT(cfree);
+ TSAN_INTERCEPT(mmap);
+ TSAN_INTERCEPT(mmap64);
+ TSAN_INTERCEPT(munmap);
+ TSAN_INTERCEPT(memalign);
+ TSAN_INTERCEPT(valloc);
+ TSAN_INTERCEPT(pvalloc);
+ TSAN_INTERCEPT(posix_memalign);
+
+ TSAN_INTERCEPT(_Znwm);
+ TSAN_INTERCEPT(_ZnwmRKSt9nothrow_t);
+ TSAN_INTERCEPT(_Znam);
+ TSAN_INTERCEPT(_ZnamRKSt9nothrow_t);
+ TSAN_INTERCEPT(_ZdlPv);
+ TSAN_INTERCEPT(_ZdlPvRKSt9nothrow_t);
+ TSAN_INTERCEPT(_ZdaPv);
+ TSAN_INTERCEPT(_ZdaPvRKSt9nothrow_t);
+
+ TSAN_INTERCEPT(strlen);
+ TSAN_INTERCEPT(memset);
+ TSAN_INTERCEPT(memcpy);
+ TSAN_INTERCEPT(strcmp);
+ TSAN_INTERCEPT(memchr);
+ TSAN_INTERCEPT(memrchr);
+ TSAN_INTERCEPT(memmove);
+ TSAN_INTERCEPT(memcmp);
+ TSAN_INTERCEPT(strchr);
+ TSAN_INTERCEPT(strchrnul);
+ TSAN_INTERCEPT(strrchr);
+ TSAN_INTERCEPT(strncmp);
+ TSAN_INTERCEPT(strcpy); // NOLINT
+ TSAN_INTERCEPT(strncpy);
+ TSAN_INTERCEPT(strstr);
+
+ TSAN_INTERCEPT(__cxa_guard_acquire);
+ TSAN_INTERCEPT(__cxa_guard_release);
+
+ TSAN_INTERCEPT(pthread_create);
+ TSAN_INTERCEPT(pthread_join);
+ TSAN_INTERCEPT(pthread_detach);
+
+ TSAN_INTERCEPT(pthread_mutex_init);
+ TSAN_INTERCEPT(pthread_mutex_destroy);
+ TSAN_INTERCEPT(pthread_mutex_lock);
+ TSAN_INTERCEPT(pthread_mutex_trylock);
+ TSAN_INTERCEPT(pthread_mutex_timedlock);
+ TSAN_INTERCEPT(pthread_mutex_unlock);
+
+ TSAN_INTERCEPT(pthread_spin_init);
+ TSAN_INTERCEPT(pthread_spin_destroy);
+ TSAN_INTERCEPT(pthread_spin_lock);
+ TSAN_INTERCEPT(pthread_spin_trylock);
+ TSAN_INTERCEPT(pthread_spin_unlock);
+
+ TSAN_INTERCEPT(pthread_rwlock_init);
+ TSAN_INTERCEPT(pthread_rwlock_destroy);
+ TSAN_INTERCEPT(pthread_rwlock_rdlock);
+ TSAN_INTERCEPT(pthread_rwlock_tryrdlock);
+ TSAN_INTERCEPT(pthread_rwlock_timedrdlock);
+ TSAN_INTERCEPT(pthread_rwlock_wrlock);
+ TSAN_INTERCEPT(pthread_rwlock_trywrlock);
+ TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
+ TSAN_INTERCEPT(pthread_rwlock_unlock);
+
+ TSAN_INTERCEPT(pthread_cond_init);
+ TSAN_INTERCEPT(pthread_cond_destroy);
+ TSAN_INTERCEPT(pthread_cond_signal);
+ TSAN_INTERCEPT(pthread_cond_broadcast);
+ TSAN_INTERCEPT(pthread_cond_wait);
+ TSAN_INTERCEPT(pthread_cond_timedwait);
+
+ TSAN_INTERCEPT(pthread_barrier_init);
+ TSAN_INTERCEPT(pthread_barrier_destroy);
+ TSAN_INTERCEPT(pthread_barrier_wait);
+
+ TSAN_INTERCEPT(pthread_once);
+
+ TSAN_INTERCEPT(sem_init);
+ TSAN_INTERCEPT(sem_destroy);
+ TSAN_INTERCEPT(sem_wait);
+ TSAN_INTERCEPT(sem_trywait);
+ TSAN_INTERCEPT(sem_timedwait);
+ TSAN_INTERCEPT(sem_post);
+ TSAN_INTERCEPT(sem_getvalue);
+
+ TSAN_INTERCEPT(read);
+ TSAN_INTERCEPT(pread);
+ TSAN_INTERCEPT(pread64);
+ TSAN_INTERCEPT(readv);
+ TSAN_INTERCEPT(preadv64);
+ TSAN_INTERCEPT(write);
+ TSAN_INTERCEPT(pwrite);
+ TSAN_INTERCEPT(pwrite64);
+ TSAN_INTERCEPT(writev);
+ TSAN_INTERCEPT(pwritev64);
+ TSAN_INTERCEPT(send);
+ TSAN_INTERCEPT(sendmsg);
+ TSAN_INTERCEPT(recv);
+ TSAN_INTERCEPT(recvmsg);
+
+ TSAN_INTERCEPT(unlink);
+ TSAN_INTERCEPT(fopen);
+ TSAN_INTERCEPT(fread);
+ TSAN_INTERCEPT(fwrite);
+ TSAN_INTERCEPT(puts);
+ TSAN_INTERCEPT(rmdir);
+ TSAN_INTERCEPT(opendir);
+
+ TSAN_INTERCEPT(epoll_ctl);
+ TSAN_INTERCEPT(epoll_wait);
+
+ TSAN_INTERCEPT(sigaction);
+ TSAN_INTERCEPT(signal);
+ TSAN_INTERCEPT(raise);
+ TSAN_INTERCEPT(kill);
+ TSAN_INTERCEPT(pthread_kill);
+
+ atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext)))
+ AtExitContext();
+
+ if (__cxa_atexit(&finalize, 0, 0)) {
+ TsanPrintf("ThreadSanitizer: failed to setup atexit callback\n");
+ Die();
+ }
+
+ if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) {
+ TsanPrintf("ThreadSanitizer: failed to create thread key\n");
+ Die();
+ }
+}
+
+void internal_start_thread(void(*func)(void *arg), void *arg) {
+ void *th;
+ REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg);
+ REAL(pthread_detach)(th);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_interface.cc b/lib/tsan/rtl/tsan_interface.cc
new file mode 100644
index 000000000000..6d0954602ff7
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface.cc
@@ -0,0 +1,42 @@
+//===-- tsan_interface.cc -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_interface.h"
+#include "tsan_interface_ann.h"
+#include "tsan_rtl.h"
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+using namespace __tsan; // NOLINT
+
+void __tsan_init() {
+ Initialize(cur_thread());
+}
+
+void __tsan_read16(void *addr) {
+ MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr);
+ MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr + 8);
+}
+
+void __tsan_write16(void *addr) {
+ MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr);
+ MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr + 8);
+}
+
+void __tsan_acquire(void *addr) {
+ Acquire(cur_thread(), CALLERPC, (uptr)addr);
+}
+
+void __tsan_release(void *addr) {
+ Release(cur_thread(), CALLERPC, (uptr)addr);
+}
diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h
new file mode 100644
index 000000000000..ed21ec60544e
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface.h
@@ -0,0 +1,51 @@
+//===-- tsan_interface.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// The functions declared in this header will be inserted by the instrumentation
+// module.
+// This header can be included by the instrumented program or by TSan tests.
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_INTERFACE_H
+#define TSAN_INTERFACE_H
+
+// This header should NOT include any other headers.
+// All functions in this header are extern "C" and start with __tsan_.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// This function should be called at the very beginning of the process,
+// before any instrumented code is executed and before any call to malloc.
+void __tsan_init();
+
+void __tsan_read1(void *addr);
+void __tsan_read2(void *addr);
+void __tsan_read4(void *addr);
+void __tsan_read8(void *addr);
+void __tsan_read16(void *addr);
+
+void __tsan_write1(void *addr);
+void __tsan_write2(void *addr);
+void __tsan_write4(void *addr);
+void __tsan_write8(void *addr);
+void __tsan_write16(void *addr);
+
+void __tsan_vptr_update(void **vptr_p, void *new_val);
+
+void __tsan_func_entry(void *call_pc);
+void __tsan_func_exit();
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // TSAN_INTERFACE_H
diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc
new file mode 100644
index 000000000000..a605b6c9d3f0
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface_ann.cc
@@ -0,0 +1,352 @@
+//===-- tsan_interface_ann.cc ---------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_interface_ann.h"
+#include "tsan_mutex.h"
+#include "tsan_report.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_flags.h"
+#include "tsan_platform.h"
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+using namespace __tsan; // NOLINT
+
+namespace __tsan {
+
+class ScopedAnnotation {
+ public:
+ ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l,
+ uptr pc)
+ : thr_(thr)
+ , in_rtl_(thr->in_rtl) {
+ CHECK_EQ(thr_->in_rtl, 0);
+ FuncEntry(thr_, pc);
+ thr_->in_rtl++;
+ DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l);
+ }
+
+ ~ScopedAnnotation() {
+ thr_->in_rtl--;
+ CHECK_EQ(in_rtl_, thr_->in_rtl);
+ FuncExit(thr_);
+ }
+ private:
+ ThreadState *const thr_;
+ const int in_rtl_;
+};
+
+#define SCOPED_ANNOTATION(typ) \
+ if (!flags()->enable_annotations) \
+ return; \
+ ThreadState *thr = cur_thread(); \
+ StatInc(thr, StatAnnotation); \
+ StatInc(thr, Stat##typ); \
+ ScopedAnnotation sa(thr, __FUNCTION__, f, l, \
+ (uptr)__builtin_return_address(0)); \
+ const uptr pc = (uptr)&__FUNCTION__; \
+ (void)pc; \
+/**/
+
+static const int kMaxDescLen = 128;
+
+struct ExpectRace {
+ ExpectRace *next;
+ ExpectRace *prev;
+ int hitcount;
+ uptr addr;
+ uptr size;
+ char *file;
+ int line;
+ char desc[kMaxDescLen];
+};
+
+struct DynamicAnnContext {
+ Mutex mtx;
+ ExpectRace expect;
+ ExpectRace benign;
+
+ DynamicAnnContext()
+ : mtx(MutexTypeAnnotations, StatMtxAnnotations) {
+ }
+};
+
+static DynamicAnnContext *dyn_ann_ctx;
+static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)] ALIGNED(64);
+
+static void AddExpectRace(ExpectRace *list,
+ char *f, int l, uptr addr, uptr size, char *desc) {
+ ExpectRace *race = list->next;
+ for (; race != list; race = race->next) {
+ if (race->addr == addr && race->size == size)
+ return;
+ }
+ race = (ExpectRace*)internal_alloc(MBlockExpectRace, sizeof(ExpectRace));
+ race->hitcount = 0;
+ race->addr = addr;
+ race->size = size;
+ race->file = f;
+ race->line = l;
+ race->desc[0] = 0;
+ if (desc) {
+ int i = 0;
+ for (; i < kMaxDescLen - 1 && desc[i]; i++)
+ race->desc[i] = desc[i];
+ race->desc[i] = 0;
+ }
+ race->prev = list;
+ race->next = list->next;
+ race->next->prev = race;
+ list->next = race;
+}
+
+static ExpectRace *FindRace(ExpectRace *list, uptr addr, uptr size) {
+ for (ExpectRace *race = list->next; race != list; race = race->next) {
+ uptr maxbegin = max(race->addr, addr);
+ uptr minend = min(race->addr + race->size, addr + size);
+ if (maxbegin < minend)
+ return race;
+ }
+ return 0;
+}
+
+static bool CheckContains(ExpectRace *list, uptr addr, uptr size) {
+ ExpectRace *race = FindRace(list, addr, size);
+ if (race == 0 && AlternativeAddress(addr))
+ race = FindRace(list, AlternativeAddress(addr), size);
+ if (race == 0)
+ return false;
+ DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n",
+ race->desc, race->addr, (int)race->size, race->file, race->line);
+ race->hitcount++;
+ return true;
+}
+
+static void InitList(ExpectRace *list) {
+ list->next = list;
+ list->prev = list;
+}
+
+void InitializeDynamicAnnotations() {
+ dyn_ann_ctx = new(dyn_ann_ctx_placeholder) DynamicAnnContext;
+ InitList(&dyn_ann_ctx->expect);
+ InitList(&dyn_ann_ctx->benign);
+}
+
+bool IsExpectedReport(uptr addr, uptr size) {
+ Lock lock(&dyn_ann_ctx->mtx);
+ if (CheckContains(&dyn_ann_ctx->expect, addr, size))
+ return true;
+ if (CheckContains(&dyn_ann_ctx->benign, addr, size))
+ return true;
+ return false;
+}
+
+} // namespace __tsan
+
+using namespace __tsan; // NOLINT
+
+extern "C" {
+void AnnotateHappensBefore(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensBefore);
+ Release(cur_thread(), CALLERPC, addr);
+}
+
+void AnnotateHappensAfter(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensAfter);
+ Acquire(cur_thread(), CALLERPC, addr);
+}
+
+void AnnotateCondVarSignal(char *f, int l, uptr cv) {
+ SCOPED_ANNOTATION(AnnotateCondVarSignal);
+}
+
+void AnnotateCondVarSignalAll(char *f, int l, uptr cv) {
+ SCOPED_ANNOTATION(AnnotateCondVarSignalAll);
+}
+
+void AnnotateMutexIsNotPHB(char *f, int l, uptr mu) {
+ SCOPED_ANNOTATION(AnnotateMutexIsNotPHB);
+}
+
+void AnnotateCondVarWait(char *f, int l, uptr cv, uptr lock) {
+ SCOPED_ANNOTATION(AnnotateCondVarWait);
+}
+
+void AnnotateRWLockCreate(char *f, int l, uptr lock) {
+ SCOPED_ANNOTATION(AnnotateRWLockCreate);
+}
+
+void AnnotateRWLockDestroy(char *f, int l, uptr lock) {
+ SCOPED_ANNOTATION(AnnotateRWLockDestroy);
+}
+
+void AnnotateRWLockAcquired(char *f, int l, uptr lock, uptr is_w) {
+ SCOPED_ANNOTATION(AnnotateRWLockAcquired);
+}
+
+void AnnotateRWLockReleased(char *f, int l, uptr lock, uptr is_w) {
+ SCOPED_ANNOTATION(AnnotateRWLockReleased);
+}
+
+void AnnotateTraceMemory(char *f, int l, uptr mem) {
+ SCOPED_ANNOTATION(AnnotateTraceMemory);
+}
+
+void AnnotateFlushState(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateFlushState);
+}
+
+void AnnotateNewMemory(char *f, int l, uptr mem, uptr size) {
+ SCOPED_ANNOTATION(AnnotateNewMemory);
+}
+
+void AnnotateNoOp(char *f, int l, uptr mem) {
+ SCOPED_ANNOTATION(AnnotateNoOp);
+}
+
+static void ReportMissedExpectedRace(ExpectRace *race) {
+ TsanPrintf("==================\n");
+ TsanPrintf("WARNING: ThreadSanitizer: missed expected data race\n");
+ TsanPrintf(" %s addr=%zx %s:%d\n",
+ race->desc, race->addr, race->file, race->line);
+ TsanPrintf("==================\n");
+}
+
+void AnnotateFlushExpectedRaces(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateFlushExpectedRaces);
+ Lock lock(&dyn_ann_ctx->mtx);
+ while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) {
+ ExpectRace *race = dyn_ann_ctx->expect.next;
+ if (race->hitcount == 0) {
+ CTX()->nmissed_expected++;
+ ReportMissedExpectedRace(race);
+ }
+ race->prev->next = race->next;
+ race->next->prev = race->prev;
+ internal_free(race);
+ }
+}
+
+void AnnotateEnableRaceDetection(char *f, int l, int enable) {
+ SCOPED_ANNOTATION(AnnotateEnableRaceDetection);
+ // FIXME: Reconsider this functionality later. It may be irrelevant.
+}
+
+void AnnotateMutexIsUsedAsCondVar(char *f, int l, uptr mu) {
+ SCOPED_ANNOTATION(AnnotateMutexIsUsedAsCondVar);
+}
+
+void AnnotatePCQGet(char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQGet);
+}
+
+void AnnotatePCQPut(char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQPut);
+}
+
+void AnnotatePCQDestroy(char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQDestroy);
+}
+
+void AnnotatePCQCreate(char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQCreate);
+}
+
+void AnnotateExpectRace(char *f, int l, uptr mem, char *desc) {
+ SCOPED_ANNOTATION(AnnotateExpectRace);
+ Lock lock(&dyn_ann_ctx->mtx);
+ AddExpectRace(&dyn_ann_ctx->expect,
+ f, l, mem, 1, desc);
+ DPrintf("Add expected race: %s addr=%zx %s:%d\n", desc, mem, f, l);
+}
+
+static void BenignRaceImpl(char *f, int l, uptr mem, uptr size, char *desc) {
+ Lock lock(&dyn_ann_ctx->mtx);
+ AddExpectRace(&dyn_ann_ctx->benign,
+ f, l, mem, size, desc);
+ DPrintf("Add benign race: %s addr=%zx %s:%d\n", desc, mem, f, l);
+}
+
+// FIXME: Turn it off later. WTF is benign race?1?? Go talk to Hans Boehm.
+void AnnotateBenignRaceSized(char *f, int l, uptr mem, uptr size, char *desc) {
+ SCOPED_ANNOTATION(AnnotateBenignRaceSized);
+ BenignRaceImpl(f, l, mem, size, desc);
+}
+
+void AnnotateBenignRace(char *f, int l, uptr mem, char *desc) {
+ SCOPED_ANNOTATION(AnnotateBenignRace);
+ BenignRaceImpl(f, l, mem, 1, desc);
+}
+
+void AnnotateIgnoreReadsBegin(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin);
+ IgnoreCtl(cur_thread(), false, true);
+}
+
+void AnnotateIgnoreReadsEnd(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd);
+ IgnoreCtl(cur_thread(), false, false);
+}
+
+void AnnotateIgnoreWritesBegin(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin);
+ IgnoreCtl(cur_thread(), true, true);
+}
+
+void AnnotateIgnoreWritesEnd(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd);
+ IgnoreCtl(cur_thread(), true, false);
+}
+
+void AnnotatePublishMemoryRange(char *f, int l, uptr addr, uptr size) {
+ SCOPED_ANNOTATION(AnnotatePublishMemoryRange);
+}
+
+void AnnotateUnpublishMemoryRange(char *f, int l, uptr addr, uptr size) {
+ SCOPED_ANNOTATION(AnnotateUnpublishMemoryRange);
+}
+
+void AnnotateThreadName(char *f, int l, char *name) {
+ SCOPED_ANNOTATION(AnnotateThreadName);
+}
+
+void WTFAnnotateHappensBefore(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensBefore);
+}
+
+void WTFAnnotateHappensAfter(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensAfter);
+}
+
+void WTFAnnotateBenignRaceSized(char *f, int l, uptr mem, uptr sz, char *desc) {
+ SCOPED_ANNOTATION(AnnotateBenignRaceSized);
+}
+
+int RunningOnValgrind() {
+ return flags()->running_on_valgrind;
+}
+
+double __attribute__((weak)) ValgrindSlowdown(void) {
+ return 10.0;
+}
+
+const char *ThreadSanitizerQuery(const char *query) {
+ if (internal_strcmp(query, "pure_happens_before") == 0)
+ return "1";
+ else
+ return "0";
+}
+} // extern "C"
diff --git a/lib/tsan/rtl/tsan_interface_ann.h b/lib/tsan/rtl/tsan_interface_ann.h
new file mode 100644
index 000000000000..09e807a0087d
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface_ann.h
@@ -0,0 +1,31 @@
+//===-- tsan_interface_ann.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Interface for dynamic annotations.
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_INTERFACE_ANN_H
+#define TSAN_INTERFACE_ANN_H
+
+// This header should NOT include any other headers.
+// All functions in this header are extern "C" and start with __tsan_.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __tsan_acquire(void *addr);
+void __tsan_release(void *addr);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // TSAN_INTERFACE_ANN_H
diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc
new file mode 100644
index 000000000000..a3982a161b9b
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface_atomic.cc
@@ -0,0 +1,321 @@
+//===-- tsan_interface_atomic.cc ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_interface_atomic.h"
+#include "tsan_flags.h"
+#include "tsan_rtl.h"
+
+using namespace __tsan; // NOLINT
+
+class ScopedAtomic {
+ public:
+ ScopedAtomic(ThreadState *thr, uptr pc, const char *func)
+ : thr_(thr) {
+ CHECK_EQ(thr_->in_rtl, 1); // 1 due to our own ScopedInRtl member.
+ DPrintf("#%d: %s\n", thr_->tid, func);
+ }
+ ~ScopedAtomic() {
+ CHECK_EQ(thr_->in_rtl, 1);
+ }
+ private:
+ ThreadState *thr_;
+ ScopedInRtl in_rtl_;
+};
+
+// Some shortcuts.
+typedef __tsan_memory_order morder;
+typedef __tsan_atomic8 a8;
+typedef __tsan_atomic16 a16;
+typedef __tsan_atomic32 a32;
+typedef __tsan_atomic64 a64;
+const int mo_relaxed = __tsan_memory_order_relaxed;
+const int mo_consume = __tsan_memory_order_consume;
+const int mo_acquire = __tsan_memory_order_acquire;
+const int mo_release = __tsan_memory_order_release;
+const int mo_acq_rel = __tsan_memory_order_acq_rel;
+const int mo_seq_cst = __tsan_memory_order_seq_cst;
+
+static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
+ StatInc(thr, StatAtomic);
+ StatInc(thr, t);
+ StatInc(thr, size == 1 ? StatAtomic1
+ : size == 2 ? StatAtomic2
+ : size == 4 ? StatAtomic4
+ : StatAtomic8);
+ StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
+ : mo == mo_consume ? StatAtomicConsume
+ : mo == mo_acquire ? StatAtomicAcquire
+ : mo == mo_release ? StatAtomicRelease
+ : mo == mo_acq_rel ? StatAtomicAcq_Rel
+ : StatAtomicSeq_Cst);
+}
+
+#define SCOPED_ATOMIC(func, ...) \
+ mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
+ ThreadState *const thr = cur_thread(); \
+ const uptr pc = (uptr)__builtin_return_address(0); \
+ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
+ ScopedAtomic sa(thr, pc, __FUNCTION__); \
+ return Atomic##func(thr, pc, __VA_ARGS__); \
+/**/
+
+template<typename T>
+static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
+ morder mo) {
+ CHECK(mo & (mo_relaxed | mo_consume | mo_acquire | mo_seq_cst));
+ T v = *a;
+ if (mo & (mo_consume | mo_acquire | mo_seq_cst))
+ Acquire(thr, pc, (uptr)a);
+ return v;
+}
+
+template<typename T>
+static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ CHECK(mo & (mo_relaxed | mo_release | mo_seq_cst));
+ if (mo & (mo_release | mo_seq_cst))
+ Release(thr, pc, (uptr)a);
+ *a = v;
+}
+
+template<typename T>
+static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
+ Release(thr, pc, (uptr)a);
+ v = __sync_lock_test_and_set(a, v);
+ if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
+ Acquire(thr, pc, (uptr)a);
+ return v;
+}
+
+template<typename T>
+static T AtomicFetchAdd(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
+ Release(thr, pc, (uptr)a);
+ v = __sync_fetch_and_add(a, v);
+ if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
+ Acquire(thr, pc, (uptr)a);
+ return v;
+}
+
+template<typename T>
+static T AtomicFetchAnd(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
+ Release(thr, pc, (uptr)a);
+ v = __sync_fetch_and_and(a, v);
+ if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
+ Acquire(thr, pc, (uptr)a);
+ return v;
+}
+
+template<typename T>
+static T AtomicFetchOr(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
+ Release(thr, pc, (uptr)a);
+ v = __sync_fetch_and_or(a, v);
+ if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
+ Acquire(thr, pc, (uptr)a);
+ return v;
+}
+
+template<typename T>
+static T AtomicFetchXor(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
+ Release(thr, pc, (uptr)a);
+ v = __sync_fetch_and_xor(a, v);
+ if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
+ Acquire(thr, pc, (uptr)a);
+ return v;
+}
+
+template<typename T>
+static bool AtomicCAS(ThreadState *thr, uptr pc,
+ volatile T *a, T *c, T v, morder mo) {
+ if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
+ Release(thr, pc, (uptr)a);
+ T cc = *c;
+ T pr = __sync_val_compare_and_swap(a, cc, v);
+ if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
+ Acquire(thr, pc, (uptr)a);
+ if (pr == cc)
+ return true;
+ *c = pr;
+ return false;
+}
+
+static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
+ __sync_synchronize();
+}
+
+a8 __tsan_atomic8_load(const volatile a8 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+a16 __tsan_atomic16_load(const volatile a16 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+a32 __tsan_atomic32_load(const volatile a32 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+a64 __tsan_atomic64_load(const volatile a64 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
+ morder mo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+void __tsan_atomic_thread_fence(morder mo) {
+ char* a;
+ SCOPED_ATOMIC(Fence, mo);
+}
diff --git a/lib/tsan/rtl/tsan_interface_atomic.h b/lib/tsan/rtl/tsan_interface_atomic.h
new file mode 100644
index 000000000000..dff32b1473f4
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface_atomic.h
@@ -0,0 +1,121 @@
+//===-- tsan_interface_atomic.h ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_INTERFACE_ATOMIC_H
+#define TSAN_INTERFACE_ATOMIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef char __tsan_atomic8;
+typedef short __tsan_atomic16; // NOLINT
+typedef int __tsan_atomic32;
+typedef long __tsan_atomic64; // NOLINT
+
+typedef enum {
+ __tsan_memory_order_relaxed = 1 << 0,
+ __tsan_memory_order_consume = 1 << 1,
+ __tsan_memory_order_acquire = 1 << 2,
+ __tsan_memory_order_release = 1 << 3,
+ __tsan_memory_order_acq_rel = 1 << 4,
+ __tsan_memory_order_seq_cst = 1 << 5,
+} __tsan_memory_order;
+
+__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a,
+ __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a,
+ __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a,
+ __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a,
+ __tsan_memory_order mo);
+
+void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v,
+ __tsan_memory_order mo);
+void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v,
+ __tsan_memory_order mo);
+void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v,
+ __tsan_memory_order mo);
+void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v,
+ __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+
+int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo);
+int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo);
+int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo);
+int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo);
+
+int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo);
+int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo);
+int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo);
+int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo);
+
+void __tsan_atomic_thread_fence(__tsan_memory_order mo);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // #ifndef TSAN_INTERFACE_ATOMIC_H
diff --git a/lib/tsan/rtl/tsan_interface_inl.h b/lib/tsan/rtl/tsan_interface_inl.h
new file mode 100644
index 000000000000..233f9028a63b
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface_inl.h
@@ -0,0 +1,65 @@
+//===-- tsan_interface_inl.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_interface.h"
+#include "tsan_rtl.h"
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+using namespace __tsan; // NOLINT
+
+void __tsan_read1(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 0);
+}
+
+void __tsan_read2(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 0);
+}
+
+void __tsan_read4(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 0);
+}
+
+void __tsan_read8(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 0);
+}
+
+void __tsan_write1(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 1);
+}
+
+void __tsan_write2(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 1);
+}
+
+void __tsan_write4(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 1);
+}
+
+void __tsan_write8(void *addr) {
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 1);
+}
+
+void __tsan_vptr_update(void **vptr_p, void *new_val) {
+ CHECK_EQ(sizeof(vptr_p), 8);
+ if (*vptr_p != new_val)
+ MemoryAccess(cur_thread(), CALLERPC, (uptr)vptr_p, 3, 1);
+}
+
+void __tsan_func_entry(void *pc) {
+ FuncEntry(cur_thread(), (uptr)pc);
+}
+
+void __tsan_func_exit() {
+ FuncExit(cur_thread());
+}
diff --git a/lib/tsan/rtl/tsan_md5.cc b/lib/tsan/rtl/tsan_md5.cc
new file mode 100644
index 000000000000..c9d671f5b599
--- /dev/null
+++ b/lib/tsan/rtl/tsan_md5.cc
@@ -0,0 +1,245 @@
+//===-- tsan_md5.cc -------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+#define SET(n) \
+ (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+
+typedef unsigned int MD5_u32plus;
+typedef unsigned long ulong_t; // NOLINT
+
+typedef struct {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+static void *body(MD5_CTX *ctx, void *data, ulong_t size) {
+ unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ ptr = (unsigned char*)data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+void MD5_Init(MD5_CTX *ctx) {
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+void MD5_Update(MD5_CTX *ctx, void *data, ulong_t size) {
+ MD5_u32plus saved_lo;
+ ulong_t used, free;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ free = 64 - used;
+
+ if (size < free) {
+ internal_memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ internal_memcpy(&ctx->buffer[used], data, free);
+ data = (unsigned char *)data + free;
+ size -= free;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = body(ctx, data, size & ~(ulong_t)0x3f);
+ size &= 0x3f;
+ }
+
+ internal_memcpy(ctx->buffer, data, size);
+}
+
+void MD5_Final(unsigned char *result, MD5_CTX *ctx) {
+ ulong_t used, free;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ free = 64 - used;
+
+ if (free < 8) {
+ internal_memset(&ctx->buffer[used], 0, free);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ free = 64;
+ }
+
+ internal_memset(&ctx->buffer[used], 0, free - 8);
+
+ ctx->lo <<= 3;
+ ctx->buffer[56] = ctx->lo;
+ ctx->buffer[57] = ctx->lo >> 8;
+ ctx->buffer[58] = ctx->lo >> 16;
+ ctx->buffer[59] = ctx->lo >> 24;
+ ctx->buffer[60] = ctx->hi;
+ ctx->buffer[61] = ctx->hi >> 8;
+ ctx->buffer[62] = ctx->hi >> 16;
+ ctx->buffer[63] = ctx->hi >> 24;
+
+ body(ctx, ctx->buffer, 64);
+
+ result[0] = ctx->a;
+ result[1] = ctx->a >> 8;
+ result[2] = ctx->a >> 16;
+ result[3] = ctx->a >> 24;
+ result[4] = ctx->b;
+ result[5] = ctx->b >> 8;
+ result[6] = ctx->b >> 16;
+ result[7] = ctx->b >> 24;
+ result[8] = ctx->c;
+ result[9] = ctx->c >> 8;
+ result[10] = ctx->c >> 16;
+ result[11] = ctx->c >> 24;
+ result[12] = ctx->d;
+ result[13] = ctx->d >> 8;
+ result[14] = ctx->d >> 16;
+ result[15] = ctx->d >> 24;
+
+ internal_memset(ctx, 0, sizeof(*ctx));
+}
+
+MD5Hash md5_hash(const void *data, uptr size) {
+ MD5Hash res;
+ MD5_CTX ctx;
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, (void*)data, size);
+ MD5_Final((unsigned char*)&res.hash[0], &ctx);
+ return res;
+}
+}
diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc
new file mode 100644
index 000000000000..7f956dfe26bf
--- /dev/null
+++ b/lib/tsan/rtl/tsan_mman.cc
@@ -0,0 +1,123 @@
+//===-- tsan_mman.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_common.h"
+#include "tsan_mman.h"
+#include "tsan_rtl.h"
+#include "tsan_report.h"
+#include "tsan_flags.h"
+
+namespace __tsan {
+
+static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
+ if (!thr->in_signal_handler || !flags()->report_signal_unsafe)
+ return;
+ StackTrace stack;
+ stack.ObtainCurrent(thr, pc);
+ ScopedReport rep(ReportTypeSignalUnsafe);
+ rep.AddStack(&stack);
+ OutputReport(rep, rep.GetReport()->stacks[0]);
+}
+
+void *user_alloc(ThreadState *thr, uptr pc, uptr sz) {
+ CHECK_GT(thr->in_rtl, 0);
+ if (sz + sizeof(MBlock) < sz)
+ return 0;
+ MBlock *b = (MBlock*)InternalAlloc(sz + sizeof(MBlock));
+ if (b == 0)
+ return 0;
+ b->size = sz;
+ void *p = b + 1;
+ if (CTX() && CTX()->initialized) {
+ MemoryResetRange(thr, pc, (uptr)p, sz);
+ }
+ DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
+ SignalUnsafeCall(thr, pc);
+ return p;
+}
+
+void user_free(ThreadState *thr, uptr pc, void *p) {
+ CHECK_GT(thr->in_rtl, 0);
+ CHECK_NE(p, (void*)0);
+ DPrintf("#%d: free(%p)\n", thr->tid, p);
+ MBlock *b = user_mblock(thr, p);
+ p = b + 1;
+ if (CTX() && CTX()->initialized && thr->in_rtl == 1) {
+ MemoryRangeFreed(thr, pc, (uptr)p, b->size);
+ }
+ InternalFree(b);
+ SignalUnsafeCall(thr, pc);
+}
+
+void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
+ CHECK_GT(thr->in_rtl, 0);
+ void *p2 = 0;
+ // FIXME: Handle "shrinking" more efficiently,
+ // it seems that some software actually does this.
+ if (sz) {
+ p2 = user_alloc(thr, pc, sz);
+ if (p2 == 0)
+ return 0;
+ if (p) {
+ MBlock *b = user_mblock(thr, p);
+ internal_memcpy(p2, p, min(b->size, sz));
+ }
+ }
+ if (p) {
+ user_free(thr, pc, p);
+ }
+ return p2;
+}
+
+void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align) {
+ CHECK_GT(thr->in_rtl, 0);
+ void *p = user_alloc(thr, pc, sz + align);
+ void *pa = RoundUp(p, align);
+ DCHECK_LE((uptr)pa + sz, (uptr)p + sz + align);
+ return pa;
+}
+
+MBlock *user_mblock(ThreadState *thr, void *p) {
+ CHECK_GT(thr->in_rtl, 0);
+ CHECK_NE(p, (void*)0);
+ MBlock *b = (MBlock*)InternalAllocBlock(p);
+ // FIXME: Output a warning, it's a user error.
+ if (p < (char*)(b + 1) || p > (char*)(b + 1) + b->size) {
+ TsanPrintf("user_mblock p=%p b=%p size=%zu beg=%p end=%p\n",
+ p, b, b->size, (char*)(b + 1), (char*)(b + 1) + b->size);
+ CHECK_GE(p, (char*)(b + 1));
+ CHECK_LE(p, (char*)(b + 1) + b->size);
+ }
+ return b;
+}
+
+void *internal_alloc(MBlockType typ, uptr sz) {
+ ThreadState *thr = cur_thread();
+ CHECK_GT(thr->in_rtl, 0);
+ if (thr->nomalloc) {
+ thr->nomalloc = 0; // CHECK calls internal_malloc().
+ CHECK(0);
+ }
+ return InternalAlloc(sz);
+}
+
+void internal_free(void *p) {
+ ThreadState *thr = cur_thread();
+ CHECK_GT(thr->in_rtl, 0);
+ if (thr->nomalloc) {
+ thr->nomalloc = 0; // CHECK calls internal_malloc().
+ CHECK(0);
+ }
+ InternalFree(p);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_mman.h b/lib/tsan/rtl/tsan_mman.h
new file mode 100644
index 000000000000..53f147e40cea
--- /dev/null
+++ b/lib/tsan/rtl/tsan_mman.h
@@ -0,0 +1,114 @@
+//===-- tsan_mman.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_MMAN_H
+#define TSAN_MMAN_H
+
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+// Descriptor of user's memory block.
+struct MBlock {
+ uptr size;
+};
+
+// For user allocations.
+void *user_alloc(ThreadState *thr, uptr pc, uptr sz);
+// Does not accept NULL.
+void user_free(ThreadState *thr, uptr pc, void *p);
+void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz);
+void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align);
+// Given the pointer p into a valid allocated block,
+// returns the descriptor of the block.
+MBlock *user_mblock(ThreadState *thr, void *p);
+
+enum MBlockType {
+ MBlockScopedBuf,
+ MBlockString,
+ MBlockStackTrace,
+ MBlockShadowStack,
+ MBlockSync,
+ MBlockClock,
+ MBlockThreadContex,
+ MBlockDeadInfo,
+ MBlockRacyStacks,
+ MBlockRacyAddresses,
+ MBlockAtExit,
+ MBlockFlag,
+ MBlockReport,
+ MBlockReportMop,
+ MBlockReportThread,
+ MBlockReportMutex,
+ MBlockReportLoc,
+ MBlockReportStack,
+ MBlockSuppression,
+ MBlockExpectRace,
+ MBlockSignal,
+
+ // This must be the last.
+ MBlockTypeCount,
+};
+
+// For internal data structures.
+void *internal_alloc(MBlockType typ, uptr sz);
+void internal_free(void *p);
+
+template<typename T>
+void DestroyAndFree(T *&p) {
+ p->~T();
+ internal_free(p);
+ p = 0;
+}
+
+template<typename T>
+class InternalScopedBuf {
+ public:
+ explicit InternalScopedBuf(uptr cnt) {
+ cnt_ = cnt;
+ ptr_ = (T*)internal_alloc(MBlockScopedBuf, cnt * sizeof(T));
+ }
+
+ ~InternalScopedBuf() {
+ internal_free(ptr_);
+ }
+
+ operator T *() {
+ return ptr_;
+ }
+
+ T &operator[](uptr i) {
+ return ptr_[i];
+ }
+
+ T *Ptr() {
+ return ptr_;
+ }
+
+ uptr Count() {
+ return cnt_;
+ }
+
+ uptr Size() {
+ return cnt_ * sizeof(T);
+ }
+
+ private:
+ T *ptr_;
+ uptr cnt_;
+
+ InternalScopedBuf(const InternalScopedBuf&);
+ void operator = (const InternalScopedBuf&);
+};
+
+} // namespace __tsan
+#endif // TSAN_MMAN_H
diff --git a/lib/tsan/rtl/tsan_mutex.cc b/lib/tsan/rtl/tsan_mutex.cc
new file mode 100644
index 000000000000..1a70f8fe4430
--- /dev/null
+++ b/lib/tsan/rtl/tsan_mutex.cc
@@ -0,0 +1,259 @@
+//===-- tsan_mutex.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_mutex.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+// Simple reader-writer spin-mutex. Optimized for not-so-contended case.
+// Readers have preference, can possibly starvate writers.
+
+// The table fixes what mutexes can be locked under what mutexes.
+// E.g. if the row for MutexTypeThreads contains MutexTypeReport,
+// then Report mutex can be locked while under Threads mutex.
+// The leaf mutexes can be locked under any other mutexes.
+// Recursive locking is not supported.
+const MutexType MutexTypeLeaf = (MutexType)-1;
+static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
+ /*0 MutexTypeInvalid*/ {},
+ /*1 MutexTypeTrace*/ {MutexTypeLeaf},
+ /*2 MutexTypeThreads*/ {MutexTypeReport},
+ /*3 MutexTypeReport*/ {},
+ /*4 MutexTypeSyncVar*/ {},
+ /*5 MutexTypeSyncTab*/ {MutexTypeSyncVar},
+ /*6 MutexTypeSlab*/ {MutexTypeLeaf},
+ /*7 MutexTypeAnnotations*/ {},
+ /*8 MutexTypeAtExit*/ {MutexTypeSyncTab},
+};
+
+static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
+
+void InitializeMutex() {
+ // Build the "can lock" adjacency matrix.
+ // If [i][j]==true, then one can lock mutex j while under mutex i.
+ const int N = MutexTypeCount;
+ int cnt[N] = {};
+ bool leaf[N] = {};
+ for (int i = 1; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ int z = CanLockTab[i][j];
+ if (z == MutexTypeInvalid)
+ continue;
+ if (z == MutexTypeLeaf) {
+ CHECK(!leaf[i]);
+ leaf[i] = true;
+ continue;
+ }
+ CHECK(!CanLockAdj[i][z]);
+ CanLockAdj[i][z] = true;
+ cnt[i]++;
+ }
+ }
+ for (int i = 0; i < N; i++) {
+ CHECK(!leaf[i] || cnt[i] == 0);
+ }
+ // Add leaf mutexes.
+ for (int i = 0; i < N; i++) {
+ if (!leaf[i])
+ continue;
+ for (int j = 0; j < N; j++) {
+ if (i == j || leaf[j] || j == MutexTypeInvalid)
+ continue;
+ CHECK(!CanLockAdj[j][i]);
+ CanLockAdj[j][i] = true;
+ }
+ }
+ // Build the transitive closure.
+ bool CanLockAdj2[MutexTypeCount][MutexTypeCount];
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ CanLockAdj2[i][j] = CanLockAdj[i][j];
+ }
+ }
+ for (int k = 0; k < N; k++) {
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) {
+ CanLockAdj2[i][j] = true;
+ }
+ }
+ }
+ }
+#if 0
+ TsanPrintf("Can lock graph:\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ TsanPrintf("%d ", CanLockAdj[i][j]);
+ }
+ TsanPrintf("\n");
+ }
+ TsanPrintf("Can lock graph closure:\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ TsanPrintf("%d ", CanLockAdj2[i][j]);
+ }
+ TsanPrintf("\n");
+ }
+#endif
+ // Verify that the graph is acyclic.
+ for (int i = 0; i < N; i++) {
+ if (CanLockAdj2[i][i]) {
+ TsanPrintf("Mutex %d participates in a cycle\n", i);
+ Die();
+ }
+ }
+}
+
+DeadlockDetector::DeadlockDetector() {
+ // Rely on zero initialization because some mutexes can be locked before ctor.
+}
+
+void DeadlockDetector::Lock(MutexType t) {
+ // TsanPrintf("LOCK %d @%zu\n", t, seq_ + 1);
+ u64 max_seq = 0;
+ u64 max_idx = MutexTypeInvalid;
+ for (int i = 0; i != MutexTypeCount; i++) {
+ if (locked_[i] == 0)
+ continue;
+ CHECK_NE(locked_[i], max_seq);
+ if (max_seq < locked_[i]) {
+ max_seq = locked_[i];
+ max_idx = i;
+ }
+ }
+ locked_[t] = ++seq_;
+ if (max_idx == MutexTypeInvalid)
+ return;
+ // TsanPrintf(" last %d @%zu\n", max_idx, max_seq);
+ if (!CanLockAdj[max_idx][t]) {
+ TsanPrintf("ThreadSanitizer: internal deadlock detected\n");
+ TsanPrintf("ThreadSanitizer: can't lock %d while under %zu\n",
+ t, (uptr)max_idx);
+ Die();
+ }
+}
+
+void DeadlockDetector::Unlock(MutexType t) {
+ // TsanPrintf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
+ CHECK(locked_[t]);
+ locked_[t] = 0;
+}
+
+const uptr kUnlocked = 0;
+const uptr kWriteLock = 1;
+const uptr kReadLock = 2;
+
+class Backoff {
+ public:
+ Backoff()
+ : iter_() {
+ }
+
+ bool Do() {
+ if (iter_++ < kActiveSpinIters)
+ proc_yield(kActiveSpinCnt);
+ else
+ internal_sched_yield();
+ return true;
+ }
+
+ u64 Contention() const {
+ u64 active = iter_ % kActiveSpinIters;
+ u64 passive = iter_ - active;
+ return active + 10 * passive;
+ }
+
+ private:
+ int iter_;
+ static const int kActiveSpinIters = 10;
+ static const int kActiveSpinCnt = 20;
+};
+
+Mutex::Mutex(MutexType type, StatType stat_type) {
+ CHECK_GT(type, MutexTypeInvalid);
+ CHECK_LT(type, MutexTypeCount);
+#if TSAN_DEBUG
+ type_ = type;
+#endif
+#if TSAN_COLLECT_STATS
+ stat_type_ = stat_type;
+#endif
+ atomic_store(&state_, kUnlocked, memory_order_relaxed);
+}
+
+Mutex::~Mutex() {
+ CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
+}
+
+void Mutex::Lock() {
+#if TSAN_DEBUG && !TSAN_GO
+ cur_thread()->deadlock_detector.Lock(type_);
+#endif
+ uptr cmp = kUnlocked;
+ if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
+ memory_order_acquire))
+ return;
+ for (Backoff backoff; backoff.Do();) {
+ if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) {
+ cmp = kUnlocked;
+ if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
+ memory_order_acquire)) {
+#if TSAN_COLLECT_STATS
+ StatInc(cur_thread(), stat_type_, backoff.Contention());
+#endif
+ return;
+ }
+ }
+ }
+}
+
+void Mutex::Unlock() {
+ uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
+ (void)prev;
+ DCHECK_NE(prev & kWriteLock, 0);
+#if TSAN_DEBUG && !TSAN_GO
+ cur_thread()->deadlock_detector.Unlock(type_);
+#endif
+}
+
+void Mutex::ReadLock() {
+#if TSAN_DEBUG && !TSAN_GO
+ cur_thread()->deadlock_detector.Lock(type_);
+#endif
+ uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
+ if ((prev & kWriteLock) == 0)
+ return;
+ for (Backoff backoff; backoff.Do();) {
+ prev = atomic_load(&state_, memory_order_acquire);
+ if ((prev & kWriteLock) == 0) {
+#if TSAN_COLLECT_STATS
+ StatInc(cur_thread(), stat_type_, backoff.Contention());
+#endif
+ return;
+ }
+ }
+}
+
+void Mutex::ReadUnlock() {
+ uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
+ (void)prev;
+ DCHECK_EQ(prev & kWriteLock, 0);
+ DCHECK_GT(prev & ~kWriteLock, 0);
+#if TSAN_DEBUG && !TSAN_GO
+ cur_thread()->deadlock_detector.Unlock(type_);
+#endif
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_mutex.h b/lib/tsan/rtl/tsan_mutex.h
new file mode 100644
index 000000000000..5b22a4145185
--- /dev/null
+++ b/lib/tsan/rtl/tsan_mutex.h
@@ -0,0 +1,78 @@
+//===-- tsan_mutex.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_MUTEX_H
+#define TSAN_MUTEX_H
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+enum MutexType {
+ MutexTypeInvalid,
+ MutexTypeTrace,
+ MutexTypeThreads,
+ MutexTypeReport,
+ MutexTypeSyncVar,
+ MutexTypeSyncTab,
+ MutexTypeSlab,
+ MutexTypeAnnotations,
+ MutexTypeAtExit,
+
+ // This must be the last.
+ MutexTypeCount,
+};
+
+class Mutex {
+ public:
+ explicit Mutex(MutexType type, StatType stat_type);
+ ~Mutex();
+
+ void Lock();
+ void Unlock();
+
+ void ReadLock();
+ void ReadUnlock();
+
+ private:
+ atomic_uintptr_t state_;
+#if TSAN_DEBUG
+ MutexType type_;
+#endif
+#if TSAN_COLLECT_STATS
+ StatType stat_type_;
+#endif
+
+ Mutex(const Mutex&);
+ void operator = (const Mutex&);
+};
+
+typedef GenericScopedLock<Mutex> Lock;
+typedef GenericScopedReadLock<Mutex> ReadLock;
+
+class DeadlockDetector {
+ public:
+ DeadlockDetector();
+ void Lock(MutexType t);
+ void Unlock(MutexType t);
+ private:
+ u64 seq_;
+ u64 locked_[MutexTypeCount];
+};
+
+void InitializeMutex();
+
+} // namespace __tsan
+
+#endif // TSAN_MUTEX_H
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
new file mode 100644
index 000000000000..b557fa1caec5
--- /dev/null
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -0,0 +1,101 @@
+//===-- tsan_platform.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Platform-specific code.
+//===----------------------------------------------------------------------===//
+
+#ifndef TSAN_PLATFORM_H
+#define TSAN_PLATFORM_H
+
+#include "tsan_rtl.h"
+
+#if __LP64__
+namespace __tsan {
+
+#if defined(TSAN_GO)
+static const uptr kLinuxAppMemBeg = 0x000000000000ULL;
+static const uptr kLinuxAppMemEnd = 0x00fcffffffffULL;
+static const uptr kLinuxShadowMsk = 0x100000000000ULL;
+// TSAN_COMPAT_SHADOW is intended for COMPAT virtual memory layout,
+// when memory addresses are of the 0x2axxxxxxxxxx form.
+// The option is enabled with 'setarch x86_64 -L'.
+#elif defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
+static const uptr kLinuxAppMemBeg = 0x2a0000000000ULL;
+static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
+#else
+static const uptr kLinuxAppMemBeg = 0x7ef000000000ULL;
+static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
+#endif
+
+static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL;
+
+// This has to be a macro to allow constant initialization of constants below.
+#ifndef TSAN_GO
+#define MemToShadow(addr) \
+ (((addr) & ~(kLinuxAppMemMsk | (kShadowCell - 1))) * kShadowCnt)
+#else
+#define MemToShadow(addr) \
+ ((((addr) & ~(kShadowCell - 1)) * kShadowCnt) | kLinuxShadowMsk)
+#endif
+
+static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg);
+static const uptr kLinuxShadowEnd =
+ MemToShadow(kLinuxAppMemEnd) | (kPageSize - 1);
+
+static inline bool IsAppMem(uptr mem) {
+ return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd;
+}
+
+static inline bool IsShadowMem(uptr mem) {
+ return mem >= kLinuxShadowBeg && mem <= kLinuxShadowEnd;
+}
+
+static inline uptr ShadowToMem(uptr shadow) {
+ CHECK(IsShadowMem(shadow));
+#ifdef TSAN_GO
+ return (shadow & ~kLinuxShadowMsk) / kShadowCnt;
+#elif defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
+ // COMPAT mapping is not quite one-to-one.
+ return (shadow / kShadowCnt) | 0x280000000000ULL;
+#else
+ return (shadow / kShadowCnt) | kLinuxAppMemMsk;
+#endif
+}
+
+// For COMPAT mapping returns an alternative address
+// that mapped to the same shadow address.
+static inline uptr AlternativeAddress(uptr addr) {
+#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
+ return addr | kLinuxAppMemMsk;
+#else
+ return 0;
+#endif
+}
+
+uptr GetShadowMemoryConsumption();
+void FlushShadowMemory();
+
+const char *InitializePlatform();
+void FinalizePlatform();
+
+void internal_start_thread(void(*func)(void*), void *arg);
+
+uptr GetTlsSize();
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size);
+
+} // namespace __tsan
+
+#else // __LP64__
+# error "Only 64-bit is supported"
+#endif
+
+#endif // TSAN_PLATFORM_H
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
new file mode 100644
index 000000000000..c791c96c14ac
--- /dev/null
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -0,0 +1,238 @@
+//===-- tsan_platform_linux.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Linux-specific code.
+//===----------------------------------------------------------------------===//
+
+#ifdef __linux__
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_flags.h"
+
+#include <asm/prctl.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sched.h>
+#include <dlfcn.h>
+
+extern "C" int arch_prctl(int code, __sanitizer::uptr *addr);
+
+namespace __sanitizer {
+
+void Die() {
+ _exit(1);
+}
+
+} // namespace __sanitizer
+
+namespace __tsan {
+
+#ifndef TSAN_GO
+ScopedInRtl::ScopedInRtl()
+ : thr_(cur_thread()) {
+ in_rtl_ = thr_->in_rtl;
+ thr_->in_rtl++;
+ errno_ = errno;
+}
+
+ScopedInRtl::~ScopedInRtl() {
+ thr_->in_rtl--;
+ errno = errno_;
+ CHECK_EQ(in_rtl_, thr_->in_rtl);
+}
+#else
+ScopedInRtl::ScopedInRtl() {
+}
+
+ScopedInRtl::~ScopedInRtl() {
+}
+#endif
+
+uptr GetShadowMemoryConsumption() {
+ return 0;
+}
+
+void FlushShadowMemory() {
+ madvise((void*)kLinuxShadowBeg,
+ kLinuxShadowEnd - kLinuxShadowBeg,
+ MADV_DONTNEED);
+}
+
+#ifndef TSAN_GO
+static void ProtectRange(uptr beg, uptr end) {
+ ScopedInRtl in_rtl;
+ CHECK_LE(beg, end);
+ if (beg == end)
+ return;
+ if (beg != (uptr)Mprotect(beg, end - beg)) {
+ TsanPrintf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
+ TsanPrintf("FATAL: Make sure you are not using unlimited stack\n");
+ Die();
+ }
+}
+#endif
+
+void InitializeShadowMemory() {
+ uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
+ kLinuxShadowEnd - kLinuxShadowBeg);
+ if (shadow != kLinuxShadowBeg) {
+ TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ TsanPrintf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie.\n");
+ Die();
+ }
+#ifndef TSAN_GO
+ const uptr kClosedLowBeg = 0x200000;
+ const uptr kClosedLowEnd = kLinuxShadowBeg - 1;
+ const uptr kClosedMidBeg = kLinuxShadowEnd + 1;
+ const uptr kClosedMidEnd = kLinuxAppMemBeg - 1;
+ ProtectRange(kClosedLowBeg, kClosedLowEnd);
+ ProtectRange(kClosedMidBeg, kClosedMidEnd);
+#endif
+#ifndef TSAN_GO
+ DPrintf("kClosedLow %zx-%zx (%zuGB)\n",
+ kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30);
+#endif
+ DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
+ kLinuxShadowBeg, kLinuxShadowEnd,
+ (kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
+#ifndef TSAN_GO
+ DPrintf("kClosedMid %zx-%zx (%zuGB)\n",
+ kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30);
+#endif
+ DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
+ kLinuxAppMemBeg, kLinuxAppMemEnd,
+ (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
+ DPrintf("stack %zx\n", (uptr)&shadow);
+}
+
+#ifndef TSAN_GO
+static void CheckPIE() {
+ // Ensure that the binary is indeed compiled with -pie.
+ ProcessMaps proc_maps;
+ uptr start, end;
+ if (proc_maps.Next(&start, &end,
+ /*offset*/0, /*filename*/0, /*filename_size*/0)) {
+ if ((u64)start < kLinuxAppMemBeg) {
+ TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
+ "something is mapped at 0x%zx < 0x%zx)\n",
+ start, kLinuxAppMemBeg);
+ TsanPrintf("FATAL: Make sure to compile with -fPIE"
+ " and to link with -pie.\n");
+ Die();
+ }
+ }
+}
+
+static uptr g_tls_size;
+
+#ifdef __i386__
+# define INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
+#else
+# define INTERNAL_FUNCTION
+#endif
+extern "C" void _dl_get_tls_static_info(size_t*, size_t*)
+ __attribute__((weak)) INTERNAL_FUNCTION;
+
+static int InitTlsSize() {
+ typedef void (*get_tls_func)(size_t*, size_t*) INTERNAL_FUNCTION;
+ get_tls_func get_tls = &_dl_get_tls_static_info;
+ if (get_tls == 0)
+ get_tls = (get_tls_func)dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
+ CHECK_NE(get_tls, 0);
+ size_t tls_size = 0;
+ size_t tls_align = 0;
+ get_tls(&tls_size, &tls_align);
+ return tls_size;
+}
+#endif // #ifndef TSAN_GO
+
+const char *InitializePlatform() {
+ void *p = 0;
+ if (sizeof(p) == 8) {
+ // Disable core dumps, dumping of 16TB usually takes a bit long.
+ // The following magic is to prevent clang from replacing it with memset.
+ volatile rlimit lim;
+ lim.rlim_cur = 0;
+ lim.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, (rlimit*)&lim);
+ }
+
+#ifndef TSAN_GO
+ CheckPIE();
+ g_tls_size = (uptr)InitTlsSize();
+#endif
+ return getenv("TSAN_OPTIONS");
+}
+
+void FinalizePlatform() {
+ fflush(0);
+}
+
+uptr GetTlsSize() {
+#ifndef TSAN_GO
+ return g_tls_size;
+#else
+ return 0;
+#endif
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size) {
+#ifndef TSAN_GO
+ arch_prctl(ARCH_GET_FS, tls_addr);
+ *tls_addr -= g_tls_size;
+ *tls_size = g_tls_size;
+
+ uptr stack_top, stack_bottom;
+ GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+ *stk_addr = stack_bottom;
+ *stk_size = stack_top - stack_bottom;
+
+ if (!main) {
+ // If stack and tls intersect, make them non-intersecting.
+ if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
+ CHECK_GT(*tls_addr + *tls_size, *stk_addr);
+ CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
+ *stk_size -= *tls_size;
+ *tls_addr = *stk_addr + *stk_size;
+ }
+ }
+#else
+ *stk_addr = 0;
+ *stk_size = 0;
+ *tls_addr = 0;
+ *tls_size = 0;
+#endif
+}
+
+
+} // namespace __tsan
+
+#endif // #ifdef __linux__
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
new file mode 100644
index 000000000000..7451492744f2
--- /dev/null
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -0,0 +1,112 @@
+//===-- tsan_platform_mac.cc ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific code.
+//===----------------------------------------------------------------------===//
+
+#ifdef __APPLE__
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_flags.h"
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sched.h>
+
+namespace __sanitizer {
+
+void Die() {
+ _exit(1);
+}
+
+} // namespace __sanitizer
+
+namespace __tsan {
+
+ScopedInRtl::ScopedInRtl() {
+}
+
+ScopedInRtl::~ScopedInRtl() {
+}
+
+uptr GetShadowMemoryConsumption() {
+ return 0;
+}
+
+void FlushShadowMemory() {
+}
+
+void InitializeShadowMemory() {
+ uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
+ kLinuxShadowEnd - kLinuxShadowBeg);
+ if (shadow != kLinuxShadowBeg) {
+ TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ TsanPrintf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie.\n");
+ Die();
+ }
+ DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
+ kLinuxShadowBeg, kLinuxShadowEnd,
+ (kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
+ DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
+ kLinuxAppMemBeg, kLinuxAppMemEnd,
+ (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
+}
+
+const char *InitializePlatform() {
+ void *p = 0;
+ if (sizeof(p) == 8) {
+ // Disable core dumps, dumping of 16TB usually takes a bit long.
+ // The following magic is to prevent clang from replacing it with memset.
+ volatile rlimit lim;
+ lim.rlim_cur = 0;
+ lim.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, (rlimit*)&lim);
+ }
+
+ return getenv("TSAN_OPTIONS");
+}
+
+void FinalizePlatform() {
+ fflush(0);
+}
+
+uptr GetTlsSize() {
+ return 0;
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size) {
+ *stk_addr = 0;
+ *stk_size = 0;
+ *tls_addr = 0;
+ *tls_size = 0;
+}
+
+} // namespace __tsan
+
+#endif // #ifdef __APPLE__
diff --git a/lib/tsan/rtl/tsan_printf.cc b/lib/tsan/rtl/tsan_printf.cc
new file mode 100644
index 000000000000..6f41440fb929
--- /dev/null
+++ b/lib/tsan/rtl/tsan_printf.cc
@@ -0,0 +1,39 @@
+//===-- tsan_printf.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_defs.h"
+#include "tsan_mman.h"
+#include "tsan_platform.h"
+
+#include <stdarg.h> // va_list
+
+namespace __sanitizer {
+int VSNPrintf(char *buff, int buff_length, const char *format, va_list args);
+} // namespace __sanitizer
+
+namespace __tsan {
+
+void TsanPrintf(const char *format, ...) {
+ ScopedInRtl in_rtl;
+ const uptr kMaxLen = 16 * 1024;
+ InternalScopedBuf<char> buffer(kMaxLen);
+ va_list args;
+ va_start(args, format);
+ uptr len = VSNPrintf(buffer, buffer.Size(), format, args);
+ va_end(args);
+ internal_write(CTX() ? flags()->log_fileno : 2,
+ buffer, len < buffer.Size() ? len : buffer.Size() - 1);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc
new file mode 100644
index 000000000000..c841a9827f4c
--- /dev/null
+++ b/lib/tsan/rtl/tsan_report.cc
@@ -0,0 +1,167 @@
+//===-- tsan_report.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_report.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+ReportDesc::ReportDesc()
+ : stacks(MBlockReportStack)
+ , mops(MBlockReportMop)
+ , locs(MBlockReportLoc)
+ , mutexes(MBlockReportMutex)
+ , threads(MBlockReportThread) {
+}
+
+ReportDesc::~ReportDesc() {
+}
+
+#ifndef TSAN_GO
+
+static void PrintHeader(ReportType typ) {
+ TsanPrintf("WARNING: ThreadSanitizer: ");
+
+ if (typ == ReportTypeRace)
+ TsanPrintf("data race");
+ else if (typ == ReportTypeUseAfterFree)
+ TsanPrintf("heap-use-after-free");
+ else if (typ == ReportTypeThreadLeak)
+ TsanPrintf("thread leak");
+ else if (typ == ReportTypeMutexDestroyLocked)
+ TsanPrintf("destroy of a locked mutex");
+ else if (typ == ReportTypeSignalUnsafe)
+ TsanPrintf("signal-unsafe call inside of a signal");
+ else if (typ == ReportTypeErrnoInSignal)
+ TsanPrintf("signal handler spoils errno");
+
+ TsanPrintf(" (pid=%d)\n", GetPid());
+}
+
+static void PrintStack(const ReportStack *ent) {
+ for (int i = 0; ent; ent = ent->next, i++) {
+ TsanPrintf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line);
+ if (ent->col)
+ TsanPrintf(":%d", ent->col);
+ if (ent->module && ent->offset)
+ TsanPrintf(" (%s+%p)\n", ent->module, (void*)ent->offset);
+ else
+ TsanPrintf(" (%p)\n", (void*)ent->pc);
+ }
+}
+
+static void PrintMop(const ReportMop *mop, bool first) {
+ TsanPrintf(" %s of size %d at %p",
+ (first ? (mop->write ? "Write" : "Read")
+ : (mop->write ? "Previous write" : "Previous read")),
+ mop->size, (void*)mop->addr);
+ if (mop->tid == 0)
+ TsanPrintf(" by main thread:\n");
+ else
+ TsanPrintf(" by thread %d:\n", mop->tid);
+ PrintStack(mop->stack);
+}
+
+static void PrintLocation(const ReportLocation *loc) {
+ if (loc->type == ReportLocationGlobal) {
+ TsanPrintf(" Location is global '%s' of size %zu at %zx %s:%d\n",
+ loc->name, loc->size, loc->addr, loc->file, loc->line);
+ } else if (loc->type == ReportLocationHeap) {
+ TsanPrintf(" Location is heap of size %zu at %zx allocated "
+ "by thread %d:\n", loc->size, loc->addr, loc->tid);
+ PrintStack(loc->stack);
+ } else if (loc->type == ReportLocationStack) {
+ TsanPrintf(" Location is stack of thread %d:\n", loc->tid);
+ }
+}
+
+static void PrintMutex(const ReportMutex *rm) {
+ if (rm->stack == 0)
+ return;
+ TsanPrintf(" Mutex %d created at:\n", rm->id);
+ PrintStack(rm->stack);
+}
+
+static void PrintThread(const ReportThread *rt) {
+ if (rt->id == 0) // Little sense in describing the main thread.
+ return;
+ TsanPrintf(" Thread %d", rt->id);
+ if (rt->name)
+ TsanPrintf(" '%s'", rt->name);
+ TsanPrintf(" (%s)", rt->running ? "running" : "finished");
+ if (rt->stack)
+ TsanPrintf(" created at:");
+ TsanPrintf("\n");
+ PrintStack(rt->stack);
+}
+
+void PrintReport(const ReportDesc *rep) {
+ TsanPrintf("==================\n");
+ PrintHeader(rep->typ);
+
+ for (uptr i = 0; i < rep->stacks.Size(); i++)
+ PrintStack(rep->stacks[i]);
+
+ for (uptr i = 0; i < rep->mops.Size(); i++)
+ PrintMop(rep->mops[i], i == 0);
+
+ for (uptr i = 0; i < rep->locs.Size(); i++)
+ PrintLocation(rep->locs[i]);
+
+ for (uptr i = 0; i < rep->mutexes.Size(); i++)
+ PrintMutex(rep->mutexes[i]);
+
+ for (uptr i = 0; i < rep->threads.Size(); i++)
+ PrintThread(rep->threads[i]);
+
+ TsanPrintf("==================\n");
+}
+
+#else
+
+static void PrintStack(const ReportStack *ent) {
+ for (int i = 0; ent; ent = ent->next, i++) {
+ TsanPrintf(" %s()\n %s:%d +0x%zx\n",
+ ent->func, ent->file, ent->line, (void*)ent->offset);
+ }
+}
+
+static void PrintMop(const ReportMop *mop, bool first) {
+ TsanPrintf("%s by goroutine %d:\n",
+ (first ? (mop->write ? "Write" : "Read")
+ : (mop->write ? "Previous write" : "Previous read")),
+ mop->tid);
+ PrintStack(mop->stack);
+}
+
+static void PrintThread(const ReportThread *rt) {
+ if (rt->id == 0) // Little sense in describing the main thread.
+ return;
+ TsanPrintf("Goroutine %d (%s) created at:\n",
+ rt->id, rt->running ? "running" : "finished");
+ PrintStack(rt->stack);
+}
+
+void PrintReport(const ReportDesc *rep) {
+ TsanPrintf("==================\n");
+ TsanPrintf("WARNING: DATA RACE at %p\n", (void*)rep->mops[0]->addr);
+ for (uptr i = 0; i < rep->mops.Size(); i++)
+ PrintMop(rep->mops[i], i == 0);
+ for (uptr i = 0; i < rep->threads.Size(); i++)
+ PrintThread(rep->threads[i]);
+ TsanPrintf("==================\n");
+}
+
+#endif
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_report.h b/lib/tsan/rtl/tsan_report.h
new file mode 100644
index 000000000000..d139296d54c0
--- /dev/null
+++ b/lib/tsan/rtl/tsan_report.h
@@ -0,0 +1,102 @@
+//===-- tsan_report.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_REPORT_H
+#define TSAN_REPORT_H
+
+#include "tsan_defs.h"
+#include "tsan_vector.h"
+
+namespace __tsan {
+
+enum ReportType {
+ ReportTypeRace,
+ ReportTypeUseAfterFree,
+ ReportTypeThreadLeak,
+ ReportTypeMutexDestroyLocked,
+ ReportTypeSignalUnsafe,
+ ReportTypeErrnoInSignal,
+};
+
+struct ReportStack {
+ ReportStack *next;
+ char *module;
+ uptr offset;
+ uptr pc;
+ char *func;
+ char *file;
+ int line;
+ int col;
+};
+
+struct ReportMop {
+ int tid;
+ uptr addr;
+ int size;
+ bool write;
+ int nmutex;
+ int *mutex;
+ ReportStack *stack;
+};
+
+enum ReportLocationType {
+ ReportLocationGlobal,
+ ReportLocationHeap,
+ ReportLocationStack,
+};
+
+struct ReportLocation {
+ ReportLocationType type;
+ uptr addr;
+ uptr size;
+ int tid;
+ char *name;
+ char *file;
+ int line;
+ ReportStack *stack;
+};
+
+struct ReportThread {
+ int id;
+ bool running;
+ char *name;
+ ReportStack *stack;
+};
+
+struct ReportMutex {
+ int id;
+ ReportStack *stack;
+};
+
+class ReportDesc {
+ public:
+ ReportType typ;
+ Vector<ReportStack*> stacks;
+ Vector<ReportMop*> mops;
+ Vector<ReportLocation*> locs;
+ Vector<ReportMutex*> mutexes;
+ Vector<ReportThread*> threads;
+
+ ReportDesc();
+ ~ReportDesc();
+
+ private:
+ ReportDesc(const ReportDesc&);
+ void operator = (const ReportDesc&);
+};
+
+// Format and output the report to the console/log. No additional logic.
+void PrintReport(const ReportDesc *rep);
+
+} // namespace __tsan
+
+#endif // TSAN_REPORT_H
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
new file mode 100644
index 000000000000..0ceb26c90e67
--- /dev/null
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -0,0 +1,534 @@
+//===-- tsan_rtl.cc -------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Main file (entry points) for the TSan run-time.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_defs.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_suppressions.h"
+
+volatile int __tsan_resumed = 0;
+
+extern "C" void __tsan_resume() {
+ __tsan_resumed = 1;
+}
+
+namespace __tsan {
+
+#ifndef TSAN_GO
+THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
+#endif
+static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
+
+static Context *ctx;
+Context *CTX() {
+ return ctx;
+}
+
+Context::Context()
+ : initialized()
+ , report_mtx(MutexTypeReport, StatMtxReport)
+ , nreported()
+ , nmissed_expected()
+ , thread_mtx(MutexTypeThreads, StatMtxThreads)
+ , racy_stacks(MBlockRacyStacks)
+ , racy_addresses(MBlockRacyAddresses) {
+}
+
+// The objects are allocated in TLS, so one may rely on zero-initialization.
+ThreadState::ThreadState(Context *ctx, int tid, u64 epoch,
+ uptr stk_addr, uptr stk_size,
+ uptr tls_addr, uptr tls_size)
+ : fast_state(tid, epoch)
+ // Do not touch these, rely on zero initialization,
+ // they may be accessed before the ctor.
+ // , fast_ignore_reads()
+ // , fast_ignore_writes()
+ // , in_rtl()
+ , shadow_stack_pos(&shadow_stack[0])
+ , tid(tid)
+ , stk_addr(stk_addr)
+ , stk_size(stk_size)
+ , tls_addr(tls_addr)
+ , tls_size(tls_size) {
+}
+
+ThreadContext::ThreadContext(int tid)
+ : tid(tid)
+ , unique_id()
+ , user_id()
+ , thr()
+ , status(ThreadStatusInvalid)
+ , detached()
+ , reuse_count()
+ , epoch0()
+ , epoch1()
+ , dead_info()
+ , dead_next() {
+}
+
+static void WriteMemoryProfile(char *buf, uptr buf_size, int num) {
+ uptr shadow = GetShadowMemoryConsumption();
+
+ int nthread = 0;
+ int nlivethread = 0;
+ uptr threadmem = 0;
+ {
+ Lock l(&ctx->thread_mtx);
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx == 0)
+ continue;
+ nthread += 1;
+ threadmem += sizeof(ThreadContext);
+ if (tctx->status != ThreadStatusRunning)
+ continue;
+ nlivethread += 1;
+ threadmem += sizeof(ThreadState);
+ }
+ }
+
+ uptr nsync = 0;
+ uptr syncmem = CTX()->synctab.GetMemoryConsumption(&nsync);
+
+ internal_snprintf(buf, buf_size, "%d: shadow=%zuMB"
+ " thread=%zuMB(total=%d/live=%d)"
+ " sync=%zuMB(cnt=%zu)\n",
+ num,
+ shadow >> 20,
+ threadmem >> 20, nthread, nlivethread,
+ syncmem >> 20, nsync);
+}
+
+static void MemoryProfileThread(void *arg) {
+ ScopedInRtl in_rtl;
+ fd_t fd = (fd_t)(uptr)arg;
+ for (int i = 0; ; i++) {
+ InternalScopedBuf<char> buf(4096);
+ WriteMemoryProfile(buf.Ptr(), buf.Size(), i);
+ internal_write(fd, buf.Ptr(), internal_strlen(buf.Ptr()));
+ SleepForSeconds(1);
+ }
+}
+
+static void InitializeMemoryProfile() {
+ if (flags()->profile_memory == 0 || flags()->profile_memory[0] == 0)
+ return;
+ InternalScopedBuf<char> filename(4096);
+ internal_snprintf(filename.Ptr(), filename.Size(), "%s.%d",
+ flags()->profile_memory, GetPid());
+ fd_t fd = internal_open(filename.Ptr(), true);
+ if (fd == kInvalidFd) {
+ TsanPrintf("Failed to open memory profile file '%s'\n", &filename[0]);
+ Die();
+ }
+ internal_start_thread(&MemoryProfileThread, (void*)(uptr)fd);
+}
+
+static void MemoryFlushThread(void *arg) {
+ ScopedInRtl in_rtl;
+ for (int i = 0; ; i++) {
+ SleepForMillis(flags()->flush_memory_ms);
+ FlushShadowMemory();
+ }
+}
+
+static void InitializeMemoryFlush() {
+ if (flags()->flush_memory_ms == 0)
+ return;
+ if (flags()->flush_memory_ms < 100)
+ flags()->flush_memory_ms = 100;
+ internal_start_thread(&MemoryFlushThread, 0);
+}
+
+void Initialize(ThreadState *thr) {
+ // Thread safe because done before all threads exist.
+ static bool is_initialized = false;
+ if (is_initialized)
+ return;
+ is_initialized = true;
+ ScopedInRtl in_rtl;
+ InitializeInterceptors();
+ const char *env = InitializePlatform();
+ InitializeMutex();
+ InitializeDynamicAnnotations();
+ ctx = new(ctx_placeholder) Context;
+ InitializeShadowMemory();
+ ctx->dead_list_size = 0;
+ ctx->dead_list_head = 0;
+ ctx->dead_list_tail = 0;
+ InitializeFlags(&ctx->flags, env);
+ InitializeSuppressions();
+ InitializeMemoryProfile();
+ InitializeMemoryFlush();
+
+ if (ctx->flags.verbosity)
+ TsanPrintf("***** Running under ThreadSanitizer v2 (pid %d) *****\n",
+ GetPid());
+
+ // Initialize thread 0.
+ ctx->thread_seq = 0;
+ int tid = ThreadCreate(thr, 0, 0, true);
+ CHECK_EQ(tid, 0);
+ ThreadStart(thr, tid);
+ CHECK_EQ(thr->in_rtl, 1);
+ ctx->initialized = true;
+
+ if (flags()->stop_on_start) {
+ TsanPrintf("ThreadSanitizer is suspended at startup (pid %d)."
+ " Call __tsan_resume().\n",
+ GetPid());
+ while (__tsan_resumed == 0);
+ }
+}
+
+int Finalize(ThreadState *thr) {
+ ScopedInRtl in_rtl;
+ Context *ctx = __tsan::ctx;
+ bool failed = false;
+
+ ThreadFinalize(thr);
+
+ if (ctx->nreported) {
+ failed = true;
+ TsanPrintf("ThreadSanitizer: reported %d warnings\n", ctx->nreported);
+ }
+
+ if (ctx->nmissed_expected) {
+ failed = true;
+ TsanPrintf("ThreadSanitizer: missed %d expected races\n",
+ ctx->nmissed_expected);
+ }
+
+ StatOutput(ctx->stat);
+ return failed ? flags()->exitcode : 0;
+}
+
+void TraceSwitch(ThreadState *thr) {
+ thr->nomalloc++;
+ ScopedInRtl in_rtl;
+ Lock l(&thr->trace.mtx);
+ unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % kTraceParts;
+ TraceHeader *hdr = &thr->trace.headers[trace];
+ hdr->epoch0 = thr->fast_state.epoch();
+ hdr->stack0.ObtainCurrent(thr, 0);
+ thr->nomalloc--;
+}
+
+#ifndef TSAN_GO
+extern "C" void __tsan_trace_switch() {
+ TraceSwitch(cur_thread());
+}
+
+extern "C" void __tsan_report_race() {
+ ReportRace(cur_thread());
+}
+#endif
+
+ALWAYS_INLINE
+static Shadow LoadShadow(u64 *p) {
+ u64 raw = atomic_load((atomic_uint64_t*)p, memory_order_relaxed);
+ return Shadow(raw);
+}
+
+ALWAYS_INLINE
+static void StoreShadow(u64 *sp, u64 s) {
+ atomic_store((atomic_uint64_t*)sp, s, memory_order_relaxed);
+}
+
+ALWAYS_INLINE
+static void StoreIfNotYetStored(u64 *sp, u64 *s) {
+ StoreShadow(sp, *s);
+ *s = 0;
+}
+
+static inline void HandleRace(ThreadState *thr, u64 *shadow_mem,
+ Shadow cur, Shadow old) {
+ thr->racy_state[0] = cur.raw();
+ thr->racy_state[1] = old.raw();
+ thr->racy_shadow_addr = shadow_mem;
+#ifndef TSAN_GO
+ HACKY_CALL(__tsan_report_race);
+#else
+ ReportRace(thr);
+#endif
+}
+
+static inline bool BothReads(Shadow s, int kAccessIsWrite) {
+ return !kAccessIsWrite && !s.is_write();
+}
+
+static inline bool OldIsRWStronger(Shadow old, int kAccessIsWrite) {
+ return old.is_write() || !kAccessIsWrite;
+}
+
+static inline bool OldIsRWWeaker(Shadow old, int kAccessIsWrite) {
+ return !old.is_write() || kAccessIsWrite;
+}
+
+static inline bool OldIsInSameSynchEpoch(Shadow old, ThreadState *thr) {
+ return old.epoch() >= thr->fast_synch_epoch;
+}
+
+static inline bool HappensBefore(Shadow old, ThreadState *thr) {
+ return thr->clock.get(old.tid()) >= old.epoch();
+}
+
+ALWAYS_INLINE
+void MemoryAccessImpl(ThreadState *thr, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite, FastState fast_state,
+ u64 *shadow_mem, Shadow cur) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+
+ // This potentially can live in an MMX/SSE scratch register.
+ // The required intrinsics are:
+ // __m128i _mm_move_epi64(__m128i*);
+ // _mm_storel_epi64(u64*, __m128i);
+ u64 store_word = cur.raw();
+
+ // scan all the shadow values and dispatch to 4 categories:
+ // same, replace, candidate and race (see comments below).
+ // we consider only 3 cases regarding access sizes:
+ // equal, intersect and not intersect. initially I considered
+ // larger and smaller as well, it allowed to replace some
+ // 'candidates' with 'same' or 'replace', but I think
+ // it's just not worth it (performance- and complexity-wise).
+
+ Shadow old(0);
+ if (kShadowCnt == 1) {
+ int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+ } else if (kShadowCnt == 2) {
+ int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 1;
+#include "tsan_update_shadow_word_inl.h"
+ } else if (kShadowCnt == 4) {
+ int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 1;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 2;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 3;
+#include "tsan_update_shadow_word_inl.h"
+ } else if (kShadowCnt == 8) {
+ int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 1;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 2;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 3;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 4;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 5;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 6;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 7;
+#include "tsan_update_shadow_word_inl.h"
+ } else {
+ CHECK(false);
+ }
+
+ // we did not find any races and had already stored
+ // the current access info, so we are done
+ if (LIKELY(store_word == 0))
+ return;
+ // choose a random candidate slot and replace it
+ StoreShadow(shadow_mem + (cur.epoch() % kShadowCnt), store_word);
+ StatInc(thr, StatShadowReplace);
+ return;
+ RACE:
+ HandleRace(thr, shadow_mem, cur, old);
+ return;
+}
+
+ALWAYS_INLINE
+void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite) {
+ u64 *shadow_mem = (u64*)MemToShadow(addr);
+ DPrintf2("#%d: tsan::OnMemoryAccess: @%p %p size=%d"
+ " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n",
+ (int)thr->fast_state.tid(), (void*)pc, (void*)addr,
+ (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem,
+ (uptr)shadow_mem[0], (uptr)shadow_mem[1],
+ (uptr)shadow_mem[2], (uptr)shadow_mem[3]);
+#if TSAN_DEBUG
+ if (!IsAppMem(addr)) {
+ TsanPrintf("Access to non app mem %zx\n", addr);
+ DCHECK(IsAppMem(addr));
+ }
+ if (!IsShadowMem((uptr)shadow_mem)) {
+ TsanPrintf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+ DCHECK(IsShadowMem((uptr)shadow_mem));
+ }
+#endif
+
+ FastState fast_state = thr->fast_state;
+ if (fast_state.GetIgnoreBit())
+ return;
+ fast_state.IncrementEpoch();
+ thr->fast_state = fast_state;
+ Shadow cur(fast_state);
+ cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog);
+ cur.SetWrite(kAccessIsWrite);
+
+ // We must not store to the trace if we do not store to the shadow.
+ // That is, this call must be moved somewhere below.
+ TraceAddEvent(thr, fast_state.epoch(), EventTypeMop, pc);
+
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, fast_state,
+ shadow_mem, cur);
+}
+
+static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size,
+ u64 val) {
+ if (size == 0)
+ return;
+ // FIXME: fix me.
+ uptr offset = addr % kShadowCell;
+ if (offset) {
+ offset = kShadowCell - offset;
+ if (size <= offset)
+ return;
+ addr += offset;
+ size -= offset;
+ }
+ CHECK_EQ(addr % 8, 0);
+ CHECK(IsAppMem(addr));
+ CHECK(IsAppMem(addr + size - 1));
+ (void)thr;
+ (void)pc;
+ // Some programs mmap like hundreds of GBs but actually used a small part.
+ // So, it's better to report a false positive on the memory
+ // then to hang here senselessly.
+ const uptr kMaxResetSize = 1024*1024*1024;
+ if (size > kMaxResetSize)
+ size = kMaxResetSize;
+ size = (size + 7) & ~7;
+ u64 *p = (u64*)MemToShadow(addr);
+ CHECK(IsShadowMem((uptr)p));
+ CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1)));
+ // FIXME: may overwrite a part outside the region
+ for (uptr i = 0; i < size * kShadowCnt / kShadowCell; i++)
+ p[i] = val;
+}
+
+void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+ MemoryRangeSet(thr, pc, addr, size, 0);
+}
+
+void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+ MemoryAccessRange(thr, pc, addr, size, true);
+ Shadow s(thr->fast_state);
+ s.MarkAsFreed();
+ s.SetWrite(true);
+ s.SetAddr0AndSizeLog(0, 3);
+ MemoryRangeSet(thr, pc, addr, size, s.raw());
+}
+
+void FuncEntry(ThreadState *thr, uptr pc) {
+ DCHECK_EQ(thr->in_rtl, 0);
+ StatInc(thr, StatFuncEnter);
+ DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeFuncEnter, pc);
+
+ // Shadow stack maintenance can be replaced with
+ // stack unwinding during trace switch (which presumably must be faster).
+ DCHECK_GE(thr->shadow_stack_pos, &thr->shadow_stack[0]);
+#ifndef TSAN_GO
+ DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]);
+#else
+ if (thr->shadow_stack_pos == thr->shadow_stack_end) {
+ const int sz = thr->shadow_stack_end - thr->shadow_stack;
+ const int newsz = 2 * sz;
+ uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack,
+ newsz * sizeof(uptr));
+ internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr));
+ internal_free(thr->shadow_stack);
+ thr->shadow_stack = newstack;
+ thr->shadow_stack_pos = newstack + sz;
+ thr->shadow_stack_end = newstack + newsz;
+ }
+#endif
+ thr->shadow_stack_pos[0] = pc;
+ thr->shadow_stack_pos++;
+}
+
+void FuncExit(ThreadState *thr) {
+ DCHECK_EQ(thr->in_rtl, 0);
+ StatInc(thr, StatFuncExit);
+ DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeFuncExit, 0);
+
+ DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]);
+#ifndef TSAN_GO
+ DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]);
+#endif
+ thr->shadow_stack_pos--;
+}
+
+void IgnoreCtl(ThreadState *thr, bool write, bool begin) {
+ DPrintf("#%d: IgnoreCtl(%d, %d)\n", thr->tid, write, begin);
+ thr->ignore_reads_and_writes += begin ? 1 : -1;
+ CHECK_GE(thr->ignore_reads_and_writes, 0);
+ if (thr->ignore_reads_and_writes)
+ thr->fast_state.SetIgnoreBit();
+ else
+ thr->fast_state.ClearIgnoreBit();
+}
+
+bool MD5Hash::operator==(const MD5Hash &other) const {
+ return hash[0] == other.hash[0] && hash[1] == other.hash[1];
+}
+
+#if TSAN_DEBUG
+void build_consistency_debug() {}
+#else
+void build_consistency_release() {}
+#endif
+
+#if TSAN_COLLECT_STATS
+void build_consistency_stats() {}
+#else
+void build_consistency_nostats() {}
+#endif
+
+#if TSAN_SHADOW_COUNT == 1
+void build_consistency_shadow1() {}
+#elif TSAN_SHADOW_COUNT == 2
+void build_consistency_shadow2() {}
+#elif TSAN_SHADOW_COUNT == 4
+void build_consistency_shadow4() {}
+#else
+void build_consistency_shadow8() {}
+#endif
+
+} // namespace __tsan
+
+#ifndef TSAN_GO
+// Must be included in this file to make sure everything is inlined.
+#include "tsan_interface_inl.h"
+#endif
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
new file mode 100644
index 000000000000..c559cb2f080c
--- /dev/null
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -0,0 +1,491 @@
+//===-- tsan_rtl.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Main internal TSan header file.
+//
+// Ground rules:
+// - C++ run-time should not be used (static CTORs, RTTI, exceptions, static
+// function-scope locals)
+// - All functions/classes/etc reside in namespace __tsan, except for those
+// declared in tsan_interface.h.
+// - Platform-specific files should be used instead of ifdefs (*).
+// - No system headers included in header files (*).
+// - Platform specific headres included only into platform-specific files (*).
+//
+// (*) Except when inlining is critical for performance.
+//===----------------------------------------------------------------------===//
+
+#ifndef TSAN_RTL_H
+#define TSAN_RTL_H
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "tsan_clock.h"
+#include "tsan_defs.h"
+#include "tsan_flags.h"
+#include "tsan_sync.h"
+#include "tsan_trace.h"
+#include "tsan_vector.h"
+#include "tsan_report.h"
+
+namespace __tsan {
+
+void TsanPrintf(const char *format, ...);
+
+// FastState (from most significant bit):
+// unused : 1
+// tid : kTidBits
+// epoch : kClkBits
+// unused : -
+// ignore_bit : 1
+class FastState {
+ public:
+ FastState(u64 tid, u64 epoch) {
+ x_ = tid << kTidShift;
+ x_ |= epoch << kClkShift;
+ DCHECK(tid == this->tid());
+ DCHECK(epoch == this->epoch());
+ }
+
+ explicit FastState(u64 x)
+ : x_(x) {
+ }
+
+ u64 tid() const {
+ u64 res = x_ >> kTidShift;
+ return res;
+ }
+
+ u64 epoch() const {
+ u64 res = (x_ << (kTidBits + 1)) >> (64 - kClkBits);
+ return res;
+ }
+
+ void IncrementEpoch() {
+ u64 old_epoch = epoch();
+ x_ += 1 << kClkShift;
+ DCHECK_EQ(old_epoch + 1, epoch());
+ (void)old_epoch;
+ }
+
+ void SetIgnoreBit() { x_ |= kIgnoreBit; }
+ void ClearIgnoreBit() { x_ &= ~kIgnoreBit; }
+ bool GetIgnoreBit() const { return x_ & kIgnoreBit; }
+
+ private:
+ friend class Shadow;
+ static const int kTidShift = 64 - kTidBits - 1;
+ static const int kClkShift = kTidShift - kClkBits;
+ static const u64 kIgnoreBit = 1ull;
+ static const u64 kFreedBit = 1ull << 63;
+ u64 x_;
+};
+
+// Shadow (from most significant bit):
+// freed : 1
+// tid : kTidBits
+// epoch : kClkBits
+// is_write : 1
+// size_log : 2
+// addr0 : 3
+class Shadow : public FastState {
+ public:
+ explicit Shadow(u64 x) : FastState(x) { }
+
+ explicit Shadow(const FastState &s) : FastState(s.x_) { }
+
+ void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) {
+ DCHECK_EQ(x_ & 31, 0);
+ DCHECK_LE(addr0, 7);
+ DCHECK_LE(kAccessSizeLog, 3);
+ x_ |= (kAccessSizeLog << 3) | addr0;
+ DCHECK_EQ(kAccessSizeLog, size_log());
+ DCHECK_EQ(addr0, this->addr0());
+ }
+
+ void SetWrite(unsigned kAccessIsWrite) {
+ DCHECK_EQ(x_ & 32, 0);
+ if (kAccessIsWrite)
+ x_ |= 32;
+ DCHECK_EQ(kAccessIsWrite, is_write());
+ }
+
+ bool IsZero() const { return x_ == 0; }
+ u64 raw() const { return x_; }
+
+ static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) {
+ u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift;
+ DCHECK_EQ(shifted_xor == 0, s1.tid() == s2.tid());
+ return shifted_xor == 0;
+ }
+
+ static inline bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) {
+ u64 masked_xor = (s1.x_ ^ s2.x_) & 31;
+ return masked_xor == 0;
+ }
+
+ static inline bool TwoRangesIntersect(Shadow s1, Shadow s2,
+ unsigned kS2AccessSize) {
+ bool res = false;
+ u64 diff = s1.addr0() - s2.addr0();
+ if ((s64)diff < 0) { // s1.addr0 < s2.addr0 // NOLINT
+ // if (s1.addr0() + size1) > s2.addr0()) return true;
+ if (s1.size() > -diff) res = true;
+ } else {
+ // if (s2.addr0() + kS2AccessSize > s1.addr0()) return true;
+ if (kS2AccessSize > diff) res = true;
+ }
+ DCHECK_EQ(res, TwoRangesIntersectSLOW(s1, s2));
+ DCHECK_EQ(res, TwoRangesIntersectSLOW(s2, s1));
+ return res;
+ }
+
+ // The idea behind the offset is as follows.
+ // Consider that we have 8 bool's contained within a single 8-byte block
+ // (mapped to a single shadow "cell"). Now consider that we write to the bools
+ // from a single thread (which we consider the common case).
+ // W/o offsetting each access will have to scan 4 shadow values at average
+ // to find the corresponding shadow value for the bool.
+ // With offsetting we start scanning shadow with the offset so that
+ // each access hits necessary shadow straight off (at least in an expected
+ // optimistic case).
+ // This logic works seamlessly for any layout of user data. For example,
+ // if user data is {int, short, char, char}, then accesses to the int are
+ // offsetted to 0, short - 4, 1st char - 6, 2nd char - 7. Hopefully, accesses
+ // from a single thread won't need to scan all 8 shadow values.
+ unsigned ComputeSearchOffset() {
+ return x_ & 7;
+ }
+ u64 addr0() const { return x_ & 7; }
+ u64 size() const { return 1ull << size_log(); }
+ bool is_write() const { return x_ & 32; }
+
+ // The idea behind the freed bit is as follows.
+ // When the memory is freed (or otherwise unaccessible) we write to the shadow
+ // values with tid/epoch related to the free and the freed bit set.
+ // During memory accesses processing the freed bit is considered
+ // as msb of tid. So any access races with shadow with freed bit set
+ // (it is as if write from a thread with which we never synchronized before).
+ // This allows us to detect accesses to freed memory w/o additional
+ // overheads in memory access processing and at the same time restore
+ // tid/epoch of free.
+ void MarkAsFreed() {
+ x_ |= kFreedBit;
+ }
+
+ bool GetFreedAndReset() {
+ bool res = x_ & kFreedBit;
+ x_ &= ~kFreedBit;
+ return res;
+ }
+
+ private:
+ u64 size_log() const { return (x_ >> 3) & 3; }
+
+ static bool TwoRangesIntersectSLOW(const Shadow s1, const Shadow s2) {
+ if (s1.addr0() == s2.addr0()) return true;
+ if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0())
+ return true;
+ if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0())
+ return true;
+ return false;
+ }
+};
+
+// Freed memory.
+// As if 8-byte write by thread 0xff..f at epoch 0xff..f, races with everything.
+const u64 kShadowFreed = 0xfffffffffffffff8ull;
+
+struct SignalContext;
+
+// This struct is stored in TLS.
+struct ThreadState {
+ FastState fast_state;
+ // Synch epoch represents the threads's epoch before the last synchronization
+ // action. It allows to reduce number of shadow state updates.
+ // For example, fast_synch_epoch=100, last write to addr X was at epoch=150,
+ // if we are processing write to X from the same thread at epoch=200,
+ // we do nothing, because both writes happen in the same 'synch epoch'.
+ // That is, if another memory access does not race with the former write,
+ // it does not race with the latter as well.
+ // QUESTION: can we can squeeze this into ThreadState::Fast?
+ // E.g. ThreadState::Fast is a 44-bit, 32 are taken by synch_epoch and 12 are
+ // taken by epoch between synchs.
+ // This way we can save one load from tls.
+ u64 fast_synch_epoch;
+ // This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read.
+ // We do not distinguish beteween ignoring reads and writes
+ // for better performance.
+ int ignore_reads_and_writes;
+ uptr *shadow_stack_pos;
+ u64 *racy_shadow_addr;
+ u64 racy_state[2];
+ Trace trace;
+#ifndef TSAN_GO
+ // C/C++ uses embed shadow stack of fixed size.
+ uptr shadow_stack[kShadowStackSize];
+#else
+ // Go uses satellite shadow stack with dynamic size.
+ uptr *shadow_stack;
+ uptr *shadow_stack_end;
+#endif
+ ThreadClock clock;
+ u64 stat[StatCnt];
+ const int tid;
+ int in_rtl;
+ bool is_alive;
+ const uptr stk_addr;
+ const uptr stk_size;
+ const uptr tls_addr;
+ const uptr tls_size;
+
+ DeadlockDetector deadlock_detector;
+
+ bool in_signal_handler;
+ SignalContext *signal_ctx;
+
+ // Set in regions of runtime that must be signal-safe and fork-safe.
+ // If set, malloc must not be called.
+ int nomalloc;
+
+ explicit ThreadState(Context *ctx, int tid, u64 epoch,
+ uptr stk_addr, uptr stk_size,
+ uptr tls_addr, uptr tls_size);
+};
+
+Context *CTX();
+
+#ifndef TSAN_GO
+extern THREADLOCAL char cur_thread_placeholder[];
+INLINE ThreadState *cur_thread() {
+ return reinterpret_cast<ThreadState *>(&cur_thread_placeholder);
+}
+#endif
+
+enum ThreadStatus {
+ ThreadStatusInvalid, // Non-existent thread, data is invalid.
+ ThreadStatusCreated, // Created but not yet running.
+ ThreadStatusRunning, // The thread is currently running.
+ ThreadStatusFinished, // Joinable thread is finished but not yet joined.
+ ThreadStatusDead, // Joined, but some info (trace) is still alive.
+};
+
+// An info about a thread that is hold for some time after its termination.
+struct ThreadDeadInfo {
+ Trace trace;
+};
+
+struct ThreadContext {
+ const int tid;
+ int unique_id; // Non-rolling thread id.
+ uptr user_id; // Some opaque user thread id (e.g. pthread_t).
+ ThreadState *thr;
+ ThreadStatus status;
+ bool detached;
+ int reuse_count;
+ SyncClock sync;
+ // Epoch at which the thread had started.
+ // If we see an event from the thread stamped by an older epoch,
+ // the event is from a dead thread that shared tid with this thread.
+ u64 epoch0;
+ u64 epoch1;
+ StackTrace creation_stack;
+ ThreadDeadInfo *dead_info;
+ ThreadContext *dead_next; // In dead thread list.
+
+ explicit ThreadContext(int tid);
+};
+
+struct RacyStacks {
+ MD5Hash hash[2];
+ bool operator==(const RacyStacks &other) const {
+ if (hash[0] == other.hash[0] && hash[1] == other.hash[1])
+ return true;
+ if (hash[0] == other.hash[1] && hash[1] == other.hash[0])
+ return true;
+ return false;
+ }
+};
+
+struct RacyAddress {
+ uptr addr_min;
+ uptr addr_max;
+};
+
+struct Context {
+ Context();
+
+ bool initialized;
+
+ SyncTab synctab;
+
+ Mutex report_mtx;
+ int nreported;
+ int nmissed_expected;
+
+ Mutex thread_mtx;
+ unsigned thread_seq;
+ unsigned unique_thread_seq;
+ int alive_threads;
+ int max_alive_threads;
+ ThreadContext *threads[kMaxTid];
+ int dead_list_size;
+ ThreadContext* dead_list_head;
+ ThreadContext* dead_list_tail;
+
+ Vector<RacyStacks> racy_stacks;
+ Vector<RacyAddress> racy_addresses;
+
+ Flags flags;
+
+ u64 stat[StatCnt];
+ u64 int_alloc_cnt[MBlockTypeCount];
+ u64 int_alloc_siz[MBlockTypeCount];
+};
+
+class ScopedInRtl {
+ public:
+ ScopedInRtl();
+ ~ScopedInRtl();
+ private:
+ ThreadState*thr_;
+ int in_rtl_;
+ int errno_;
+};
+
+class ScopedReport {
+ public:
+ explicit ScopedReport(ReportType typ);
+ ~ScopedReport();
+
+ void AddStack(const StackTrace *stack);
+ void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack);
+ void AddThread(const ThreadContext *tctx);
+ void AddMutex(const SyncVar *s);
+ void AddLocation(uptr addr, uptr size);
+
+ const ReportDesc *GetReport() const;
+
+ private:
+ Context *ctx_;
+ ReportDesc *rep_;
+
+ ScopedReport(const ScopedReport&);
+ void operator = (const ScopedReport&);
+};
+
+void StatAggregate(u64 *dst, u64 *src);
+void StatOutput(u64 *stat);
+void ALWAYS_INLINE INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) {
+ if (kCollectStats)
+ thr->stat[typ] += n;
+}
+
+void InitializeShadowMemory();
+void InitializeInterceptors();
+void InitializeDynamicAnnotations();
+
+void ReportRace(ThreadState *thr);
+bool OutputReport(const ScopedReport &srep,
+ const ReportStack *suppress_stack = 0);
+bool IsExpectedReport(uptr addr, uptr size);
+
+#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1
+# define DPrintf TsanPrintf
+#else
+# define DPrintf(...)
+#endif
+
+#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 2
+# define DPrintf2 TsanPrintf
+#else
+# define DPrintf2(...)
+#endif
+
+void Initialize(ThreadState *thr);
+int Finalize(ThreadState *thr);
+
+void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite);
+void MemoryAccessImpl(ThreadState *thr, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite, FastState fast_state,
+ u64 *shadow_mem, Shadow cur);
+void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
+ uptr size, bool is_write);
+void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size);
+void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size);
+void IgnoreCtl(ThreadState *thr, bool write, bool begin);
+
+void FuncEntry(ThreadState *thr, uptr pc);
+void FuncExit(ThreadState *thr);
+
+int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
+void ThreadStart(ThreadState *thr, int tid);
+void ThreadFinish(ThreadState *thr);
+int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
+void ThreadJoin(ThreadState *thr, uptr pc, int tid);
+void ThreadDetach(ThreadState *thr, uptr pc, int tid);
+void ThreadFinalize(ThreadState *thr);
+void ThreadFinalizerGoroutine(ThreadState *thr);
+
+void MutexCreate(ThreadState *thr, uptr pc, uptr addr, bool rw, bool recursive);
+void MutexDestroy(ThreadState *thr, uptr pc, uptr addr);
+void MutexLock(ThreadState *thr, uptr pc, uptr addr);
+void MutexUnlock(ThreadState *thr, uptr pc, uptr addr);
+void MutexReadLock(ThreadState *thr, uptr pc, uptr addr);
+void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr);
+void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
+
+void Acquire(ThreadState *thr, uptr pc, uptr addr);
+void Release(ThreadState *thr, uptr pc, uptr addr);
+void ReleaseStore(ThreadState *thr, uptr pc, uptr addr);
+
+// The hacky call uses custom calling convention and an assembly thunk.
+// It is considerably faster that a normal call for the caller
+// if it is not executed (it is intended for slow paths from hot functions).
+// The trick is that the call preserves all registers and the compiler
+// does not treat it as a call.
+// If it does not work for you, use normal call.
+#if TSAN_DEBUG == 0
+// The caller may not create the stack frame for itself at all,
+// so we create a reserve stack frame for it (1024b must be enough).
+#define HACKY_CALL(f) \
+ __asm__ __volatile__("sub $0x400, %%rsp;" \
+ "call " #f "_thunk;" \
+ "add $0x400, %%rsp;" ::: "memory");
+#else
+#define HACKY_CALL(f) f()
+#endif
+
+void TraceSwitch(ThreadState *thr);
+
+extern "C" void __tsan_trace_switch();
+void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, u64 epoch,
+ EventType typ, uptr addr) {
+ StatInc(thr, StatEvents);
+ if (UNLIKELY((epoch % kTracePartSize) == 0)) {
+#ifndef TSAN_GO
+ HACKY_CALL(__tsan_trace_switch);
+#else
+ TraceSwitch(thr);
+#endif
+ }
+ Event *evp = &thr->trace.events[epoch % kTraceSize];
+ Event ev = (u64)addr | ((u64)typ << 61);
+ *evp = ev;
+}
+
+} // namespace __tsan
+
+#endif // TSAN_RTL_H
diff --git a/lib/tsan/rtl/tsan_rtl_amd64.S b/lib/tsan/rtl/tsan_rtl_amd64.S
new file mode 100644
index 000000000000..2028ec508611
--- /dev/null
+++ b/lib/tsan/rtl/tsan_rtl_amd64.S
@@ -0,0 +1,71 @@
+.section .text
+
+.globl __tsan_trace_switch_thunk
+__tsan_trace_switch_thunk:
+ # Save scratch registers.
+ push %rax
+ push %rcx
+ push %rdx
+ push %rsi
+ push %rdi
+ push %r8
+ push %r9
+ push %r10
+ push %r11
+ # Align stack frame.
+ push %rbx # non-scratch
+ mov %rsp, %rbx # save current rsp
+ shr $4, %rsp # clear 4 lsb, align to 16
+ shl $4, %rsp
+
+ call __tsan_trace_switch
+
+ # Unalign stack frame back.
+ mov %rbx, %rsp # restore the original rsp
+ pop %rbx
+ # Restore scratch registers.
+ pop %r11
+ pop %r10
+ pop %r9
+ pop %r8
+ pop %rdi
+ pop %rsi
+ pop %rdx
+ pop %rcx
+ pop %rax
+ ret
+
+.globl __tsan_report_race_thunk
+__tsan_report_race_thunk:
+ # Save scratch registers.
+ push %rax
+ push %rcx
+ push %rdx
+ push %rsi
+ push %rdi
+ push %r8
+ push %r9
+ push %r10
+ push %r11
+ # Align stack frame.
+ push %rbx # non-scratch
+ mov %rsp, %rbx # save current rsp
+ shr $4, %rsp # clear 4 lsb, align to 16
+ shl $4, %rsp
+
+ call __tsan_report_race
+
+ # Unalign stack frame back.
+ mov %rbx, %rsp # restore the original rsp
+ pop %rbx
+ # Restore scratch registers.
+ pop %r11
+ pop %r10
+ pop %r9
+ pop %r8
+ pop %rdi
+ pop %rsi
+ pop %rdx
+ pop %rcx
+ pop %rax
+ ret
diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc
new file mode 100644
index 000000000000..882def835658
--- /dev/null
+++ b/lib/tsan/rtl/tsan_rtl_mutex.cc
@@ -0,0 +1,220 @@
+//===-- tsan_rtl_mutex.cc -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_rtl.h"
+#include "tsan_sync.h"
+#include "tsan_report.h"
+#include "tsan_symbolize.h"
+
+namespace __tsan {
+
+void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
+ bool rw, bool recursive) {
+ Context *ctx = CTX();
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
+ StatInc(thr, StatMutexCreate);
+ MemoryWrite1Byte(thr, pc, addr);
+ SyncVar *s = ctx->synctab.GetAndLock(thr, pc, addr, true);
+ s->is_rw = rw;
+ s->is_recursive = recursive;
+ s->mtx.Unlock();
+}
+
+void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
+ Context *ctx = CTX();
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
+ StatInc(thr, StatMutexDestroy);
+ MemoryWrite1Byte(thr, pc, addr);
+ SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr);
+ if (s == 0)
+ return;
+ if (s->owner_tid != SyncVar::kInvalidTid && !s->is_broken) {
+ s->is_broken = true;
+ ScopedReport rep(ReportTypeMutexDestroyLocked);
+ rep.AddMutex(s);
+ rep.AddLocation(s->addr, 1);
+ OutputReport(rep);
+ }
+ DestroyAndFree(s);
+}
+
+void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
+ MemoryRead1Byte(thr, pc, addr);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeLock, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ if (s->owner_tid == SyncVar::kInvalidTid) {
+ CHECK_EQ(s->recursion, 0);
+ s->owner_tid = thr->tid;
+ } else if (s->owner_tid == thr->tid) {
+ CHECK_GT(s->recursion, 0);
+ } else {
+ TsanPrintf("ThreadSanitizer WARNING: double lock\n");
+ }
+ if (s->recursion == 0) {
+ StatInc(thr, StatMutexLock);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.acquire(&s->clock);
+ StatInc(thr, StatSyncAcquire);
+ thr->clock.acquire(&s->read_clock);
+ StatInc(thr, StatSyncAcquire);
+ } else if (!s->is_recursive) {
+ StatInc(thr, StatMutexRecLock);
+ }
+ s->recursion++;
+ s->mtx.Unlock();
+}
+
+void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
+ MemoryRead1Byte(thr, pc, addr);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeUnlock, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ if (s->recursion == 0) {
+ if (!s->is_broken) {
+ s->is_broken = true;
+ TsanPrintf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
+ }
+ } else if (s->owner_tid != thr->tid) {
+ if (!s->is_broken) {
+ s->is_broken = true;
+ TsanPrintf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
+ }
+ } else {
+ s->recursion--;
+ if (s->recursion == 0) {
+ StatInc(thr, StatMutexUnlock);
+ s->owner_tid = SyncVar::kInvalidTid;
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(&s->clock);
+ StatInc(thr, StatSyncRelease);
+ } else {
+ StatInc(thr, StatMutexRecUnlock);
+ }
+ }
+ s->mtx.Unlock();
+}
+
+void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
+ StatInc(thr, StatMutexReadLock);
+ MemoryRead1Byte(thr, pc, addr);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRLock, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
+ if (s->owner_tid != SyncVar::kInvalidTid)
+ TsanPrintf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.acquire(&s->clock);
+ StatInc(thr, StatSyncAcquire);
+ s->mtx.ReadUnlock();
+}
+
+void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
+ StatInc(thr, StatMutexReadUnlock);
+ MemoryRead1Byte(thr, pc, addr);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRUnlock, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ if (s->owner_tid != SyncVar::kInvalidTid)
+ TsanPrintf("ThreadSanitizer WARNING: read unlock of a write "
+ "locked mutex\n");
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(&s->read_clock);
+ StatInc(thr, StatSyncRelease);
+ s->mtx.Unlock();
+}
+
+void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
+ MemoryRead1Byte(thr, pc, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ if (s->owner_tid == SyncVar::kInvalidTid) {
+ // Seems to be read unlock.
+ StatInc(thr, StatMutexReadUnlock);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRUnlock, addr);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(&s->read_clock);
+ StatInc(thr, StatSyncRelease);
+ } else if (s->owner_tid == thr->tid) {
+ // Seems to be write unlock.
+ CHECK_GT(s->recursion, 0);
+ s->recursion--;
+ if (s->recursion == 0) {
+ StatInc(thr, StatMutexUnlock);
+ s->owner_tid = SyncVar::kInvalidTid;
+ // FIXME: Refactor me, plz.
+ // The sequence of events is quite tricky and doubled in several places.
+ // First, it's a bug to increment the epoch w/o writing to the trace.
+ // Then, the acquire/release logic can be factored out as well.
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeUnlock, addr);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(&s->clock);
+ StatInc(thr, StatSyncRelease);
+ } else {
+ StatInc(thr, StatMutexRecUnlock);
+ }
+ } else if (!s->is_broken) {
+ s->is_broken = true;
+ TsanPrintf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
+ }
+ s->mtx.Unlock();
+}
+
+void Acquire(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.acquire(&s->clock);
+ StatInc(thr, StatSyncAcquire);
+ s->mtx.ReadUnlock();
+}
+
+void Release(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: Release %zx\n", thr->tid, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.release(&s->clock);
+ StatInc(thr, StatSyncRelease);
+ s->mtx.Unlock();
+}
+
+void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
+ SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.ReleaseStore(&s->clock);
+ StatInc(thr, StatSyncRelease);
+ s->mtx.Unlock();
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc
new file mode 100644
index 000000000000..f66e17e4815c
--- /dev/null
+++ b/lib/tsan/rtl/tsan_rtl_report.cc
@@ -0,0 +1,372 @@
+//===-- tsan_rtl_report.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_suppressions.h"
+#include "tsan_symbolize.h"
+#include "tsan_report.h"
+#include "tsan_sync.h"
+#include "tsan_mman.h"
+#include "tsan_flags.h"
+
+namespace __sanitizer {
+using namespace __tsan;
+
+void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) {
+ ScopedInRtl in_rtl;
+ TsanPrintf("FATAL: ThreadSanitizer CHECK failed: "
+ "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
+ file, line, cond, (uptr)v1, (uptr)v2);
+ Die();
+}
+
+} // namespace __sanitizer
+
+namespace __tsan {
+
+// Can be overriden by an application/test to intercept reports.
+#ifdef TSAN_EXTERNAL_HOOKS
+bool OnReport(const ReportDesc *rep, bool suppressed);
+#else
+bool WEAK OnReport(const ReportDesc *rep, bool suppressed) {
+ (void)rep;
+ return suppressed;
+}
+#endif
+
+static void StackStripMain(ReportStack *stack) {
+ ReportStack *last_frame = 0;
+ ReportStack *last_frame2 = 0;
+ const char *prefix = "__interceptor_";
+ uptr prefix_len = internal_strlen(prefix);
+ const char *path_prefix = flags()->strip_path_prefix;
+ uptr path_prefix_len = internal_strlen(path_prefix);
+ char *pos;
+ for (ReportStack *ent = stack; ent; ent = ent->next) {
+ if (ent->func && 0 == internal_strncmp(ent->func, prefix, prefix_len))
+ ent->func += prefix_len;
+ if (ent->file && (pos = internal_strstr(ent->file, path_prefix)))
+ ent->file = pos + path_prefix_len;
+ if (ent->file && ent->file[0] == '.' && ent->file[1] == '/')
+ ent->file += 2;
+ last_frame2 = last_frame;
+ last_frame = ent;
+ }
+
+ if (last_frame2 == 0)
+ return;
+ const char *last = last_frame->func;
+#ifndef TSAN_GO
+ const char *last2 = last_frame2->func;
+ // Strip frame above 'main'
+ if (last2 && 0 == internal_strcmp(last2, "main")) {
+ last_frame2->next = 0;
+ // Strip our internal thread start routine.
+ } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) {
+ last_frame2->next = 0;
+ // Strip global ctors init.
+ } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) {
+ last_frame2->next = 0;
+ // If both are 0, then we probably just failed to symbolize.
+ } else if (last || last2) {
+ // Ensure that we recovered stack completely. Trimmed stack
+ // can actually happen if we do not instrument some code,
+ // so it's only a DCHECK. However we must try hard to not miss it
+ // due to our fault.
+ TsanPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc);
+ }
+#else
+ if (last && 0 == internal_strcmp(last, "schedunlock"))
+ last_frame2->next = 0;
+#endif
+}
+
+static ReportStack *SymbolizeStack(const StackTrace& trace) {
+ if (trace.IsEmpty())
+ return 0;
+ ReportStack *stack = 0;
+ for (uptr si = 0; si < trace.Size(); si++) {
+ // We obtain the return address, that is, address of the next instruction,
+ // so offset it by 1 byte.
+ bool is_last = (si == trace.Size() - 1);
+ ReportStack *ent = SymbolizeCode(trace.Get(si) - !is_last);
+ CHECK_NE(ent, 0);
+ ReportStack *last = ent;
+ while (last->next) {
+ last->pc += !is_last;
+ last = last->next;
+ }
+ last->pc += !is_last;
+ last->next = stack;
+ stack = ent;
+ }
+ StackStripMain(stack);
+ return stack;
+}
+
+ScopedReport::ScopedReport(ReportType typ) {
+ ctx_ = CTX();
+ void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
+ rep_ = new(mem) ReportDesc;
+ rep_->typ = typ;
+ ctx_->report_mtx.Lock();
+}
+
+ScopedReport::~ScopedReport() {
+ ctx_->report_mtx.Unlock();
+ rep_->~ReportDesc();
+ internal_free(rep_);
+}
+
+void ScopedReport::AddStack(const StackTrace *stack) {
+ ReportStack **rs = rep_->stacks.PushBack();
+ *rs = SymbolizeStack(*stack);
+}
+
+void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
+ const StackTrace *stack) {
+ void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
+ ReportMop *mop = new(mem) ReportMop;
+ rep_->mops.PushBack(mop);
+ mop->tid = s.tid();
+ mop->addr = addr + s.addr0();
+ mop->size = s.size();
+ mop->write = s.is_write();
+ mop->nmutex = 0;
+ mop->stack = SymbolizeStack(*stack);
+}
+
+void ScopedReport::AddThread(const ThreadContext *tctx) {
+ void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread));
+ ReportThread *rt = new(mem) ReportThread();
+ rep_->threads.PushBack(rt);
+ rt->id = tctx->tid;
+ rt->running = (tctx->status == ThreadStatusRunning);
+ rt->stack = SymbolizeStack(tctx->creation_stack);
+}
+
+void ScopedReport::AddMutex(const SyncVar *s) {
+ void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
+ ReportMutex *rm = new(mem) ReportMutex();
+ rep_->mutexes.PushBack(rm);
+ rm->id = 42;
+ rm->stack = SymbolizeStack(s->creation_stack);
+}
+
+void ScopedReport::AddLocation(uptr addr, uptr size) {
+ ReportStack *symb = SymbolizeData(addr);
+ if (symb) {
+ void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
+ ReportLocation *loc = new(mem) ReportLocation();
+ rep_->locs.PushBack(loc);
+ loc->type = ReportLocationGlobal;
+ loc->addr = addr;
+ loc->size = size;
+ loc->tid = 0;
+ loc->name = symb->func;
+ loc->file = symb->file;
+ loc->line = symb->line;
+ loc->stack = 0;
+ internal_free(symb);
+ }
+}
+
+const ReportDesc *ScopedReport::GetReport() const {
+ return rep_;
+}
+
+static void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
+ ThreadContext *tctx = CTX()->threads[tid];
+ if (tctx == 0)
+ return;
+ Trace* trace = 0;
+ if (tctx->status == ThreadStatusRunning) {
+ CHECK(tctx->thr);
+ trace = &tctx->thr->trace;
+ } else if (tctx->status == ThreadStatusFinished
+ || tctx->status == ThreadStatusDead) {
+ if (tctx->dead_info == 0)
+ return;
+ trace = &tctx->dead_info->trace;
+ } else {
+ return;
+ }
+ Lock l(&trace->mtx);
+ const int partidx = (epoch / (kTraceSize / kTraceParts)) % kTraceParts;
+ TraceHeader* hdr = &trace->headers[partidx];
+ if (epoch < hdr->epoch0)
+ return;
+ const u64 eend = epoch % kTraceSize;
+ const u64 ebegin = eend / kTracePartSize * kTracePartSize;
+ DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
+ tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
+ InternalScopedBuf<uptr> stack(1024); // FIXME: de-hardcode 1024
+ for (uptr i = 0; i < hdr->stack0.Size(); i++) {
+ stack[i] = hdr->stack0.Get(i);
+ DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
+ }
+ uptr pos = hdr->stack0.Size();
+ for (uptr i = ebegin; i <= eend; i++) {
+ Event ev = trace->events[i];
+ EventType typ = (EventType)(ev >> 61);
+ uptr pc = (uptr)(ev & 0xffffffffffffull);
+ DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc);
+ if (typ == EventTypeMop) {
+ stack[pos] = pc;
+ } else if (typ == EventTypeFuncEnter) {
+ stack[pos++] = pc;
+ } else if (typ == EventTypeFuncExit) {
+ // Since we have full stacks, this should never happen.
+ DCHECK_GT(pos, 0);
+ if (pos > 0)
+ pos--;
+ }
+ for (uptr j = 0; j <= pos; j++)
+ DPrintf2(" #%zu: %zx\n", j, stack[j]);
+ }
+ if (pos == 0 && stack[0] == 0)
+ return;
+ pos++;
+ stk->Init(stack, pos);
+}
+
+static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
+ uptr addr_min, uptr addr_max) {
+ Context *ctx = CTX();
+ bool equal_stack = false;
+ RacyStacks hash = {};
+ if (flags()->suppress_equal_stacks) {
+ hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
+ for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
+ if (hash == ctx->racy_stacks[i]) {
+ DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n");
+ equal_stack = true;
+ break;
+ }
+ }
+ }
+ bool equal_address = false;
+ RacyAddress ra0 = {addr_min, addr_max};
+ if (flags()->suppress_equal_addresses) {
+ for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
+ RacyAddress ra2 = ctx->racy_addresses[i];
+ uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
+ uptr minend = min(ra0.addr_max, ra2.addr_max);
+ if (maxbeg < minend) {
+ DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n");
+ equal_address = true;
+ break;
+ }
+ }
+ }
+ if (equal_stack || equal_address) {
+ if (!equal_stack)
+ ctx->racy_stacks.PushBack(hash);
+ if (!equal_address)
+ ctx->racy_addresses.PushBack(ra0);
+ return true;
+ }
+ return false;
+}
+
+static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
+ uptr addr_min, uptr addr_max) {
+ Context *ctx = CTX();
+ if (flags()->suppress_equal_stacks) {
+ RacyStacks hash;
+ hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
+ ctx->racy_stacks.PushBack(hash);
+ }
+ if (flags()->suppress_equal_addresses) {
+ RacyAddress ra0 = {addr_min, addr_max};
+ ctx->racy_addresses.PushBack(ra0);
+ }
+}
+
+bool OutputReport(const ScopedReport &srep, const ReportStack *suppress_stack) {
+ const ReportDesc *rep = srep.GetReport();
+ bool suppressed = IsSuppressed(rep->typ, suppress_stack);
+ suppressed = OnReport(rep, suppressed);
+ if (suppressed)
+ return false;
+ PrintReport(rep);
+ CTX()->nreported++;
+ return true;
+}
+
+void ReportRace(ThreadState *thr) {
+ ScopedInRtl in_rtl;
+
+ bool freed = false;
+ {
+ Shadow s(thr->racy_state[1]);
+ freed = s.GetFreedAndReset();
+ thr->racy_state[1] = s.raw();
+ }
+
+ uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr);
+ uptr addr_min = 0;
+ uptr addr_max = 0;
+ {
+ uptr a0 = addr + Shadow(thr->racy_state[0]).addr0();
+ uptr a1 = addr + Shadow(thr->racy_state[1]).addr0();
+ uptr e0 = a0 + Shadow(thr->racy_state[0]).size();
+ uptr e1 = a1 + Shadow(thr->racy_state[1]).size();
+ addr_min = min(a0, a1);
+ addr_max = max(e0, e1);
+ if (IsExpectedReport(addr_min, addr_max - addr_min))
+ return;
+ }
+
+ Context *ctx = CTX();
+ Lock l0(&ctx->thread_mtx);
+
+ ScopedReport rep(freed ? ReportTypeUseAfterFree : ReportTypeRace);
+ const uptr kMop = 2;
+ StackTrace traces[kMop];
+ for (uptr i = 0; i < kMop; i++) {
+ Shadow s(thr->racy_state[i]);
+ RestoreStack(s.tid(), s.epoch(), &traces[i]);
+ }
+
+ if (HandleRacyStacks(thr, traces, addr_min, addr_max))
+ return;
+
+ for (uptr i = 0; i < kMop; i++) {
+ Shadow s(thr->racy_state[i]);
+ rep.AddMemoryAccess(addr, s, &traces[i]);
+ }
+
+ // Ensure that we have at least something for the current thread.
+ CHECK_EQ(traces[0].IsEmpty(), false);
+
+ for (uptr i = 0; i < kMop; i++) {
+ FastState s(thr->racy_state[i]);
+ ThreadContext *tctx = ctx->threads[s.tid()];
+ if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1)
+ continue;
+ rep.AddThread(tctx);
+ }
+
+ if (!OutputReport(rep, rep.GetReport()->mops[0]->stack))
+ return;
+
+ AddRacyStacks(thr, traces, addr_min, addr_max);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
new file mode 100644
index 000000000000..f7d5f13dca78
--- /dev/null
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -0,0 +1,394 @@
+//===-- tsan_rtl_thread.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_platform.h"
+#include "tsan_report.h"
+#include "tsan_sync.h"
+
+namespace __tsan {
+
+#ifndef TSAN_GO
+const int kThreadQuarantineSize = 16;
+#else
+const int kThreadQuarantineSize = 64;
+#endif
+
+static void MaybeReportThreadLeak(ThreadContext *tctx) {
+ if (tctx->detached)
+ return;
+ if (tctx->status != ThreadStatusCreated
+ && tctx->status != ThreadStatusRunning
+ && tctx->status != ThreadStatusFinished)
+ return;
+ ScopedReport rep(ReportTypeThreadLeak);
+ rep.AddThread(tctx);
+ OutputReport(rep);
+}
+
+void ThreadFinalize(ThreadState *thr) {
+ CHECK_GT(thr->in_rtl, 0);
+ if (!flags()->report_thread_leaks)
+ return;
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx == 0)
+ continue;
+ MaybeReportThreadLeak(tctx);
+ }
+}
+
+static void ThreadDead(ThreadState *thr, ThreadContext *tctx) {
+ Context *ctx = CTX();
+ CHECK_GT(thr->in_rtl, 0);
+ CHECK(tctx->status == ThreadStatusRunning
+ || tctx->status == ThreadStatusFinished);
+ DPrintf("#%d: ThreadDead uid=%zu\n", thr->tid, tctx->user_id);
+ tctx->status = ThreadStatusDead;
+ tctx->user_id = 0;
+ tctx->sync.Reset();
+
+ // Put to dead list.
+ tctx->dead_next = 0;
+ if (ctx->dead_list_size == 0)
+ ctx->dead_list_head = tctx;
+ else
+ ctx->dead_list_tail->dead_next = tctx;
+ ctx->dead_list_tail = tctx;
+ ctx->dead_list_size++;
+}
+
+int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
+ CHECK_GT(thr->in_rtl, 0);
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ StatInc(thr, StatThreadCreate);
+ int tid = -1;
+ ThreadContext *tctx = 0;
+ if (ctx->dead_list_size > kThreadQuarantineSize
+ || ctx->thread_seq >= kMaxTid) {
+ if (ctx->dead_list_size == 0) {
+ TsanPrintf("ThreadSanitizer: %d thread limit exceeded. Dying.\n",
+ kMaxTid);
+ Die();
+ }
+ StatInc(thr, StatThreadReuse);
+ tctx = ctx->dead_list_head;
+ ctx->dead_list_head = tctx->dead_next;
+ ctx->dead_list_size--;
+ if (ctx->dead_list_size == 0) {
+ CHECK_EQ(tctx->dead_next, 0);
+ ctx->dead_list_head = 0;
+ }
+ CHECK_EQ(tctx->status, ThreadStatusDead);
+ tctx->status = ThreadStatusInvalid;
+ tctx->reuse_count++;
+ tctx->sync.Reset();
+ tid = tctx->tid;
+ DestroyAndFree(tctx->dead_info);
+ } else {
+ StatInc(thr, StatThreadMaxTid);
+ tid = ctx->thread_seq++;
+ void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext));
+ tctx = new(mem) ThreadContext(tid);
+ ctx->threads[tid] = tctx;
+ }
+ CHECK_NE(tctx, 0);
+ CHECK_GE(tid, 0);
+ CHECK_LT(tid, kMaxTid);
+ DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid);
+ CHECK_EQ(tctx->status, ThreadStatusInvalid);
+ ctx->alive_threads++;
+ if (ctx->max_alive_threads < ctx->alive_threads) {
+ ctx->max_alive_threads++;
+ CHECK_EQ(ctx->max_alive_threads, ctx->alive_threads);
+ StatInc(thr, StatThreadMaxAlive);
+ }
+ tctx->status = ThreadStatusCreated;
+ tctx->thr = 0;
+ tctx->user_id = uid;
+ tctx->unique_id = ctx->unique_thread_seq++;
+ tctx->detached = detached;
+ if (tid) {
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeMop, 0);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(&tctx->sync);
+ StatInc(thr, StatSyncRelease);
+
+ tctx->creation_stack.ObtainCurrent(thr, pc);
+ }
+ return tid;
+}
+
+void ThreadStart(ThreadState *thr, int tid) {
+ CHECK_GT(thr->in_rtl, 0);
+ uptr stk_addr = 0;
+ uptr stk_size = 0;
+ uptr tls_addr = 0;
+ uptr tls_size = 0;
+ GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
+
+ if (tid) {
+ if (stk_addr && stk_size) {
+ MemoryResetRange(thr, /*pc=*/ 1, stk_addr, stk_size);
+ }
+
+ if (tls_addr && tls_size) {
+ // Check that the thr object is in tls;
+ const uptr thr_beg = (uptr)thr;
+ const uptr thr_end = (uptr)thr + sizeof(*thr);
+ CHECK_GE(thr_beg, tls_addr);
+ CHECK_LE(thr_beg, tls_addr + tls_size);
+ CHECK_GE(thr_end, tls_addr);
+ CHECK_LE(thr_end, tls_addr + tls_size);
+ // Since the thr object is huge, skip it.
+ MemoryResetRange(thr, /*pc=*/ 2, tls_addr, thr_beg - tls_addr);
+ MemoryResetRange(thr, /*pc=*/ 2, thr_end, tls_addr + tls_size - thr_end);
+ }
+ }
+
+ Lock l(&CTX()->thread_mtx);
+ ThreadContext *tctx = CTX()->threads[tid];
+ CHECK_NE(tctx, 0);
+ CHECK_EQ(tctx->status, ThreadStatusCreated);
+ tctx->status = ThreadStatusRunning;
+ tctx->epoch0 = tctx->epoch1 + 1;
+ tctx->epoch1 = (u64)-1;
+ new(thr) ThreadState(CTX(), tid, tctx->epoch0, stk_addr, stk_size,
+ tls_addr, tls_size);
+#ifdef TSAN_GO
+ // Setup dynamic shadow stack.
+ const int kInitStackSize = 8;
+ thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack,
+ kInitStackSize * sizeof(uptr));
+ thr->shadow_stack_pos = thr->shadow_stack;
+ thr->shadow_stack_end = thr->shadow_stack + kInitStackSize;
+#endif
+ tctx->thr = thr;
+ thr->fast_synch_epoch = tctx->epoch0;
+ thr->clock.set(tid, tctx->epoch0);
+ thr->clock.acquire(&tctx->sync);
+ StatInc(thr, StatSyncAcquire);
+ DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
+ "tls_addr=%zx tls_size=%zx\n",
+ tid, (uptr)tctx->epoch0, stk_addr, stk_size, tls_addr, tls_size);
+ thr->is_alive = true;
+}
+
+void ThreadFinish(ThreadState *thr) {
+ CHECK_GT(thr->in_rtl, 0);
+ StatInc(thr, StatThreadFinish);
+ // FIXME: Treat it as write.
+ if (thr->stk_addr && thr->stk_size)
+ MemoryResetRange(thr, /*pc=*/ 3, thr->stk_addr, thr->stk_size);
+ if (thr->tls_addr && thr->tls_size) {
+ const uptr thr_beg = (uptr)thr;
+ const uptr thr_end = (uptr)thr + sizeof(*thr);
+ // Since the thr object is huge, skip it.
+ MemoryResetRange(thr, /*pc=*/ 4, thr->tls_addr, thr_beg - thr->tls_addr);
+ MemoryResetRange(thr, /*pc=*/ 5,
+ thr_end, thr->tls_addr + thr->tls_size - thr_end);
+ }
+ thr->is_alive = false;
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ ThreadContext *tctx = ctx->threads[thr->tid];
+ CHECK_NE(tctx, 0);
+ CHECK_EQ(tctx->status, ThreadStatusRunning);
+ CHECK_GT(ctx->alive_threads, 0);
+ ctx->alive_threads--;
+ if (tctx->detached) {
+ ThreadDead(thr, tctx);
+ } else {
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeMop, 0);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(&tctx->sync);
+ StatInc(thr, StatSyncRelease);
+ tctx->status = ThreadStatusFinished;
+ }
+
+ // Save from info about the thread.
+ tctx->dead_info = new(internal_alloc(MBlockDeadInfo, sizeof(ThreadDeadInfo)))
+ ThreadDeadInfo();
+ internal_memcpy(&tctx->dead_info->trace.events[0],
+ &thr->trace.events[0], sizeof(thr->trace.events));
+ for (int i = 0; i < kTraceParts; i++) {
+ tctx->dead_info->trace.headers[i].stack0.CopyFrom(
+ thr->trace.headers[i].stack0);
+ }
+ tctx->epoch1 = thr->fast_state.epoch();
+
+ thr->~ThreadState();
+ StatAggregate(ctx->stat, thr->stat);
+ tctx->thr = 0;
+}
+
+int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
+ CHECK_GT(thr->in_rtl, 0);
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ int res = -1;
+ for (unsigned tid = 0; tid < kMaxTid; tid++) {
+ ThreadContext *tctx = ctx->threads[tid];
+ if (tctx != 0 && tctx->user_id == uid
+ && tctx->status != ThreadStatusInvalid) {
+ tctx->user_id = 0;
+ res = tid;
+ break;
+ }
+ }
+ DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
+ return res;
+}
+
+void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
+ CHECK_GT(thr->in_rtl, 0);
+ CHECK_GT(tid, 0);
+ CHECK_LT(tid, kMaxTid);
+ DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid);
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ ThreadContext *tctx = ctx->threads[tid];
+ if (tctx->status == ThreadStatusInvalid) {
+ TsanPrintf("ThreadSanitizer: join of non-existent thread\n");
+ return;
+ }
+ CHECK_EQ(tctx->detached, false);
+ CHECK_EQ(tctx->status, ThreadStatusFinished);
+ thr->clock.acquire(&tctx->sync);
+ StatInc(thr, StatSyncAcquire);
+ ThreadDead(thr, tctx);
+}
+
+void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
+ CHECK_GT(thr->in_rtl, 0);
+ CHECK_GT(tid, 0);
+ CHECK_LT(tid, kMaxTid);
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ ThreadContext *tctx = ctx->threads[tid];
+ if (tctx->status == ThreadStatusInvalid) {
+ TsanPrintf("ThreadSanitizer: detach of non-existent thread\n");
+ return;
+ }
+ if (tctx->status == ThreadStatusFinished) {
+ ThreadDead(thr, tctx);
+ } else {
+ tctx->detached = true;
+ }
+}
+
+void ThreadFinalizerGoroutine(ThreadState *thr) {
+ thr->clock.Disable(thr->tid);
+}
+
+void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
+ uptr size, bool is_write) {
+ if (size == 0)
+ return;
+
+ u64 *shadow_mem = (u64*)MemToShadow(addr);
+ DPrintf2("#%d: MemoryAccessRange: @%p %p size=%d is_write=%d\n",
+ thr->tid, (void*)pc, (void*)addr,
+ (int)size, is_write);
+
+#if TSAN_DEBUG
+ if (!IsAppMem(addr)) {
+ TsanPrintf("Access to non app mem %zx\n", addr);
+ DCHECK(IsAppMem(addr));
+ }
+ if (!IsAppMem(addr + size - 1)) {
+ TsanPrintf("Access to non app mem %zx\n", addr + size - 1);
+ DCHECK(IsAppMem(addr + size - 1));
+ }
+ if (!IsShadowMem((uptr)shadow_mem)) {
+ TsanPrintf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+ DCHECK(IsShadowMem((uptr)shadow_mem));
+ }
+ if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) {
+ TsanPrintf("Bad shadow addr %p (%zx)\n",
+ shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1);
+ DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1)));
+ }
+#endif
+
+ StatInc(thr, StatMopRange);
+
+ FastState fast_state = thr->fast_state;
+ if (fast_state.GetIgnoreBit())
+ return;
+
+ fast_state.IncrementEpoch();
+ thr->fast_state = fast_state;
+ TraceAddEvent(thr, fast_state.epoch(), EventTypeMop, pc);
+
+ bool unaligned = (addr % kShadowCell) != 0;
+
+ // Handle unaligned beginning, if any.
+ for (; addr % kShadowCell && size; addr++, size--) {
+ int const kAccessSizeLog = 0;
+ Shadow cur(fast_state);
+ cur.SetWrite(is_write);
+ cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+ shadow_mem, cur);
+ }
+ if (unaligned)
+ shadow_mem += kShadowCnt;
+ // Handle middle part, if any.
+ for (; size >= kShadowCell; addr += kShadowCell, size -= kShadowCell) {
+ int const kAccessSizeLog = 3;
+ Shadow cur(fast_state);
+ cur.SetWrite(is_write);
+ cur.SetAddr0AndSizeLog(0, kAccessSizeLog);
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+ shadow_mem, cur);
+ shadow_mem += kShadowCnt;
+ }
+ // Handle ending, if any.
+ for (; size; addr++, size--) {
+ int const kAccessSizeLog = 0;
+ Shadow cur(fast_state);
+ cur.SetWrite(is_write);
+ cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+ shadow_mem, cur);
+ }
+}
+
+void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr) {
+ MemoryAccess(thr, pc, addr, 0, 0);
+}
+
+void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr) {
+ MemoryAccess(thr, pc, addr, 0, 1);
+}
+
+void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr) {
+ MemoryAccess(thr, pc, addr, 3, 0);
+}
+
+void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr) {
+ MemoryAccess(thr, pc, addr, 3, 1);
+}
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc
new file mode 100644
index 000000000000..a7c33a5de760
--- /dev/null
+++ b/lib/tsan/rtl/tsan_stat.cc
@@ -0,0 +1,249 @@
+//===-- tsan_stat.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_stat.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+void StatAggregate(u64 *dst, u64 *src) {
+ if (!kCollectStats)
+ return;
+ for (int i = 0; i < StatCnt; i++)
+ dst[i] += src[i];
+}
+
+void StatOutput(u64 *stat) {
+ if (!kCollectStats)
+ return;
+
+ stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero];
+
+ static const char *name[StatCnt] = {};
+ name[StatMop] = "Memory accesses ";
+ name[StatMopRead] = " Including reads ";
+ name[StatMopWrite] = " writes ";
+ name[StatMop1] = " Including size 1 ";
+ name[StatMop2] = " size 2 ";
+ name[StatMop4] = " size 4 ";
+ name[StatMop8] = " size 8 ";
+ name[StatMopSame] = " Including same ";
+ name[StatMopRange] = " Including range ";
+ name[StatShadowProcessed] = "Shadow processed ";
+ name[StatShadowZero] = " Including empty ";
+ name[StatShadowNonZero] = " Including non empty ";
+ name[StatShadowSameSize] = " Including same size ";
+ name[StatShadowIntersect] = " intersect ";
+ name[StatShadowNotIntersect] = " not intersect ";
+ name[StatShadowSameThread] = " Including same thread ";
+ name[StatShadowAnotherThread] = " another thread ";
+ name[StatShadowReplace] = " Including evicted ";
+
+ name[StatFuncEnter] = "Function entries ";
+ name[StatFuncExit] = "Function exits ";
+ name[StatEvents] = "Events collected ";
+
+ name[StatThreadCreate] = "Total threads created ";
+ name[StatThreadFinish] = " threads finished ";
+ name[StatThreadReuse] = " threads reused ";
+ name[StatThreadMaxTid] = " max tid ";
+ name[StatThreadMaxAlive] = " max alive threads ";
+
+ name[StatMutexCreate] = "Mutexes created ";
+ name[StatMutexDestroy] = " destroyed ";
+ name[StatMutexLock] = " lock ";
+ name[StatMutexUnlock] = " unlock ";
+ name[StatMutexRecLock] = " recursive lock ";
+ name[StatMutexRecUnlock] = " recursive unlock ";
+ name[StatMutexReadLock] = " read lock ";
+ name[StatMutexReadUnlock] = " read unlock ";
+
+ name[StatSyncCreated] = "Sync objects created ";
+ name[StatSyncDestroyed] = " destroyed ";
+ name[StatSyncAcquire] = " acquired ";
+ name[StatSyncRelease] = " released ";
+
+ name[StatAtomic] = "Atomic operations ";
+ name[StatAtomicLoad] = " Including load ";
+ name[StatAtomicStore] = " store ";
+ name[StatAtomicExchange] = " exchange ";
+ name[StatAtomicFetchAdd] = " fetch_add ";
+ name[StatAtomicCAS] = " compare_exchange ";
+ name[StatAtomicFence] = " fence ";
+ name[StatAtomicRelaxed] = " Including relaxed ";
+ name[StatAtomicConsume] = " consume ";
+ name[StatAtomicAcquire] = " acquire ";
+ name[StatAtomicRelease] = " release ";
+ name[StatAtomicAcq_Rel] = " acq_rel ";
+ name[StatAtomicSeq_Cst] = " seq_cst ";
+ name[StatAtomic1] = " Including size 1 ";
+ name[StatAtomic2] = " size 2 ";
+ name[StatAtomic4] = " size 4 ";
+ name[StatAtomic8] = " size 8 ";
+
+ name[StatInterceptor] = "Interceptors ";
+ name[StatInt_longjmp] = " longjmp ";
+ name[StatInt_siglongjmp] = " siglongjmp ";
+ name[StatInt_malloc] = " malloc ";
+ name[StatInt_calloc] = " calloc ";
+ name[StatInt_realloc] = " realloc ";
+ name[StatInt_free] = " free ";
+ name[StatInt_cfree] = " cfree ";
+ name[StatInt_mmap] = " mmap ";
+ name[StatInt_mmap64] = " mmap64 ";
+ name[StatInt_munmap] = " munmap ";
+ name[StatInt_memalign] = " memalign ";
+ name[StatInt_valloc] = " valloc ";
+ name[StatInt_pvalloc] = " pvalloc ";
+ name[StatInt_posix_memalign] = " posix_memalign ";
+ name[StatInt__Znwm] = " _Znwm ";
+ name[StatInt__ZnwmRKSt9nothrow_t] = " _ZnwmRKSt9nothrow_t ";
+ name[StatInt__Znam] = " _Znam ";
+ name[StatInt__ZnamRKSt9nothrow_t] = " _ZnamRKSt9nothrow_t ";
+ name[StatInt__ZdlPv] = " _ZdlPv ";
+ name[StatInt__ZdlPvRKSt9nothrow_t] = " _ZdlPvRKSt9nothrow_t ";
+ name[StatInt__ZdaPv] = " _ZdaPv ";
+ name[StatInt__ZdaPvRKSt9nothrow_t] = " _ZdaPvRKSt9nothrow_t ";
+ name[StatInt_strlen] = " strlen ";
+ name[StatInt_memset] = " memset ";
+ name[StatInt_memcpy] = " memcpy ";
+ name[StatInt_strcmp] = " strcmp ";
+ name[StatInt_memchr] = " memchr ";
+ name[StatInt_memrchr] = " memrchr ";
+ name[StatInt_memmove] = " memmove ";
+ name[StatInt_memcmp] = " memcmp ";
+ name[StatInt_strchr] = " strchr ";
+ name[StatInt_strchrnul] = " strchrnul ";
+ name[StatInt_strrchr] = " strrchr ";
+ name[StatInt_strncmp] = " strncmp ";
+ name[StatInt_strcpy] = " strcpy ";
+ name[StatInt_strncpy] = " strncpy ";
+ name[StatInt_strstr] = " strstr ";
+ name[StatInt_atexit] = " atexit ";
+ name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire ";
+ name[StatInt___cxa_guard_release] = " __cxa_guard_release ";
+ name[StatInt_pthread_create] = " pthread_create ";
+ name[StatInt_pthread_join] = " pthread_join ";
+ name[StatInt_pthread_detach] = " pthread_detach ";
+ name[StatInt_pthread_mutex_init] = " pthread_mutex_init ";
+ name[StatInt_pthread_mutex_destroy] = " pthread_mutex_destroy ";
+ name[StatInt_pthread_mutex_lock] = " pthread_mutex_lock ";
+ name[StatInt_pthread_mutex_trylock] = " pthread_mutex_trylock ";
+ name[StatInt_pthread_mutex_timedlock] = " pthread_mutex_timedlock ";
+ name[StatInt_pthread_mutex_unlock] = " pthread_mutex_unlock ";
+ name[StatInt_pthread_spin_init] = " pthread_spin_init ";
+ name[StatInt_pthread_spin_destroy] = " pthread_spin_destroy ";
+ name[StatInt_pthread_spin_lock] = " pthread_spin_lock ";
+ name[StatInt_pthread_spin_trylock] = " pthread_spin_trylock ";
+ name[StatInt_pthread_spin_unlock] = " pthread_spin_unlock ";
+ name[StatInt_pthread_rwlock_init] = " pthread_rwlock_init ";
+ name[StatInt_pthread_rwlock_destroy] = " pthread_rwlock_destroy ";
+ name[StatInt_pthread_rwlock_rdlock] = " pthread_rwlock_rdlock ";
+ name[StatInt_pthread_rwlock_tryrdlock] = " pthread_rwlock_tryrdlock ";
+ name[StatInt_pthread_rwlock_timedrdlock]
+ = " pthread_rwlock_timedrdlock ";
+ name[StatInt_pthread_rwlock_wrlock] = " pthread_rwlock_wrlock ";
+ name[StatInt_pthread_rwlock_trywrlock] = " pthread_rwlock_trywrlock ";
+ name[StatInt_pthread_rwlock_timedwrlock]
+ = " pthread_rwlock_timedwrlock ";
+ name[StatInt_pthread_rwlock_unlock] = " pthread_rwlock_unlock ";
+ name[StatInt_pthread_cond_init] = " pthread_cond_init ";
+ name[StatInt_pthread_cond_destroy] = " pthread_cond_destroy ";
+ name[StatInt_pthread_cond_signal] = " pthread_cond_signal ";
+ name[StatInt_pthread_cond_broadcast] = " pthread_cond_broadcast ";
+ name[StatInt_pthread_cond_wait] = " pthread_cond_wait ";
+ name[StatInt_pthread_cond_timedwait] = " pthread_cond_timedwait ";
+ name[StatInt_pthread_barrier_init] = " pthread_barrier_init ";
+ name[StatInt_pthread_barrier_destroy] = " pthread_barrier_destroy ";
+ name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait ";
+ name[StatInt_pthread_once] = " pthread_once ";
+ name[StatInt_sem_init] = " sem_init ";
+ name[StatInt_sem_destroy] = " sem_destroy ";
+ name[StatInt_sem_wait] = " sem_wait ";
+ name[StatInt_sem_trywait] = " sem_trywait ";
+ name[StatInt_sem_timedwait] = " sem_timedwait ";
+ name[StatInt_sem_post] = " sem_post ";
+ name[StatInt_sem_getvalue] = " sem_getvalue ";
+ name[StatInt_read] = " read ";
+ name[StatInt_pread] = " pread ";
+ name[StatInt_pread64] = " pread64 ";
+ name[StatInt_readv] = " readv ";
+ name[StatInt_preadv64] = " preadv64 ";
+ name[StatInt_write] = " write ";
+ name[StatInt_pwrite] = " pwrite ";
+ name[StatInt_pwrite64] = " pwrite64 ";
+ name[StatInt_writev] = " writev ";
+ name[StatInt_pwritev64] = " pwritev64 ";
+ name[StatInt_send] = " send ";
+ name[StatInt_sendmsg] = " sendmsg ";
+ name[StatInt_recv] = " recv ";
+ name[StatInt_recvmsg] = " recvmsg ";
+ name[StatInt_unlink] = " unlink ";
+ name[StatInt_fopen] = " fopen ";
+ name[StatInt_fread] = " fread ";
+ name[StatInt_fwrite] = " fwrite ";
+ name[StatInt_puts] = " puts ";
+ name[StatInt_rmdir] = " rmdir ";
+ name[StatInt_opendir] = " opendir ";
+ name[StatInt_epoll_ctl] = " epoll_ctl ";
+ name[StatInt_epoll_wait] = " epoll_wait ";
+ name[StatInt_sigaction] = " sigaction ";
+
+ name[StatAnnotation] = "Dynamic annotations ";
+ name[StatAnnotateHappensBefore] = " HappensBefore ";
+ name[StatAnnotateHappensAfter] = " HappensAfter ";
+ name[StatAnnotateCondVarSignal] = " CondVarSignal ";
+ name[StatAnnotateCondVarSignalAll] = " CondVarSignalAll ";
+ name[StatAnnotateMutexIsNotPHB] = " MutexIsNotPHB ";
+ name[StatAnnotateCondVarWait] = " CondVarWait ";
+ name[StatAnnotateRWLockCreate] = " RWLockCreate ";
+ name[StatAnnotateRWLockDestroy] = " RWLockDestroy ";
+ name[StatAnnotateRWLockAcquired] = " RWLockAcquired ";
+ name[StatAnnotateRWLockReleased] = " RWLockReleased ";
+ name[StatAnnotateTraceMemory] = " TraceMemory ";
+ name[StatAnnotateFlushState] = " FlushState ";
+ name[StatAnnotateNewMemory] = " NewMemory ";
+ name[StatAnnotateNoOp] = " NoOp ";
+ name[StatAnnotateFlushExpectedRaces] = " FlushExpectedRaces ";
+ name[StatAnnotateEnableRaceDetection] = " EnableRaceDetection ";
+ name[StatAnnotateMutexIsUsedAsCondVar] = " MutexIsUsedAsCondVar ";
+ name[StatAnnotatePCQGet] = " PCQGet ";
+ name[StatAnnotatePCQPut] = " PCQPut ";
+ name[StatAnnotatePCQDestroy] = " PCQDestroy ";
+ name[StatAnnotatePCQCreate] = " PCQCreate ";
+ name[StatAnnotateExpectRace] = " ExpectRace ";
+ name[StatAnnotateBenignRaceSized] = " BenignRaceSized ";
+ name[StatAnnotateBenignRace] = " BenignRace ";
+ name[StatAnnotateIgnoreReadsBegin] = " IgnoreReadsBegin ";
+ name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd ";
+ name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin ";
+ name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd ";
+ name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange ";
+ name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange ";
+ name[StatAnnotateThreadName] = " ThreadName ";
+
+ name[StatMtxTotal] = "Contentionz ";
+ name[StatMtxTrace] = " Trace ";
+ name[StatMtxThreads] = " Threads ";
+ name[StatMtxReport] = " Report ";
+ name[StatMtxSyncVar] = " SyncVar ";
+ name[StatMtxSyncTab] = " SyncTab ";
+ name[StatMtxSlab] = " Slab ";
+ name[StatMtxAtExit] = " Atexit ";
+ name[StatMtxAnnotations] = " Annotations ";
+
+ TsanPrintf("Statistics:\n");
+ for (int i = 0; i < StatCnt; i++)
+ TsanPrintf("%s: %zu\n", name[i], (uptr)stat[i]);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h
new file mode 100644
index 000000000000..71b1b13c0876
--- /dev/null
+++ b/lib/tsan/rtl/tsan_stat.h
@@ -0,0 +1,254 @@
+//===-- tsan_stat.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TSAN_STAT_H
+#define TSAN_STAT_H
+
+namespace __tsan {
+
+enum StatType {
+ // Memory access processing related stuff.
+ StatMop,
+ StatMopRead,
+ StatMopWrite,
+ StatMop1, // These must be consequtive.
+ StatMop2,
+ StatMop4,
+ StatMop8,
+ StatMopSame,
+ StatMopRange,
+ StatShadowProcessed,
+ StatShadowZero,
+ StatShadowNonZero, // Derived.
+ StatShadowSameSize,
+ StatShadowIntersect,
+ StatShadowNotIntersect,
+ StatShadowSameThread,
+ StatShadowAnotherThread,
+ StatShadowReplace,
+
+ // Func processing.
+ StatFuncEnter,
+ StatFuncExit,
+
+ // Trace processing.
+ StatEvents,
+
+ // Threads.
+ StatThreadCreate,
+ StatThreadFinish,
+ StatThreadReuse,
+ StatThreadMaxTid,
+ StatThreadMaxAlive,
+
+ // Mutexes.
+ StatMutexCreate,
+ StatMutexDestroy,
+ StatMutexLock,
+ StatMutexUnlock,
+ StatMutexRecLock,
+ StatMutexRecUnlock,
+ StatMutexReadLock,
+ StatMutexReadUnlock,
+
+ // Synchronization.
+ StatSyncCreated,
+ StatSyncDestroyed,
+ StatSyncAcquire,
+ StatSyncRelease,
+
+ // Atomics.
+ StatAtomic,
+ StatAtomicLoad,
+ StatAtomicStore,
+ StatAtomicExchange,
+ StatAtomicFetchAdd,
+ StatAtomicFetchAnd,
+ StatAtomicFetchOr,
+ StatAtomicFetchXor,
+ StatAtomicCAS,
+ StatAtomicFence,
+ StatAtomicRelaxed,
+ StatAtomicConsume,
+ StatAtomicAcquire,
+ StatAtomicRelease,
+ StatAtomicAcq_Rel,
+ StatAtomicSeq_Cst,
+ StatAtomic1,
+ StatAtomic2,
+ StatAtomic4,
+ StatAtomic8,
+
+ // Interceptors.
+ StatInterceptor,
+ StatInt_longjmp,
+ StatInt_siglongjmp,
+ StatInt_malloc,
+ StatInt_calloc,
+ StatInt_realloc,
+ StatInt_free,
+ StatInt_cfree,
+ StatInt_mmap,
+ StatInt_mmap64,
+ StatInt_munmap,
+ StatInt_memalign,
+ StatInt_valloc,
+ StatInt_pvalloc,
+ StatInt_posix_memalign,
+ StatInt__Znwm,
+ StatInt__ZnwmRKSt9nothrow_t,
+ StatInt__Znam,
+ StatInt__ZnamRKSt9nothrow_t,
+ StatInt__ZdlPv,
+ StatInt__ZdlPvRKSt9nothrow_t,
+ StatInt__ZdaPv,
+ StatInt__ZdaPvRKSt9nothrow_t,
+ StatInt_strlen,
+ StatInt_memset,
+ StatInt_memcpy,
+ StatInt_strcmp,
+ StatInt_memchr,
+ StatInt_memrchr,
+ StatInt_memmove,
+ StatInt_memcmp,
+ StatInt_strchr,
+ StatInt_strchrnul,
+ StatInt_strrchr,
+ StatInt_strncmp,
+ StatInt_strcpy,
+ StatInt_strncpy,
+ StatInt_strstr,
+ StatInt_atexit,
+ StatInt___cxa_guard_acquire,
+ StatInt___cxa_guard_release,
+ StatInt_pthread_create,
+ StatInt_pthread_join,
+ StatInt_pthread_detach,
+ StatInt_pthread_mutex_init,
+ StatInt_pthread_mutex_destroy,
+ StatInt_pthread_mutex_lock,
+ StatInt_pthread_mutex_trylock,
+ StatInt_pthread_mutex_timedlock,
+ StatInt_pthread_mutex_unlock,
+ StatInt_pthread_spin_init,
+ StatInt_pthread_spin_destroy,
+ StatInt_pthread_spin_lock,
+ StatInt_pthread_spin_trylock,
+ StatInt_pthread_spin_unlock,
+ StatInt_pthread_rwlock_init,
+ StatInt_pthread_rwlock_destroy,
+ StatInt_pthread_rwlock_rdlock,
+ StatInt_pthread_rwlock_tryrdlock,
+ StatInt_pthread_rwlock_timedrdlock,
+ StatInt_pthread_rwlock_wrlock,
+ StatInt_pthread_rwlock_trywrlock,
+ StatInt_pthread_rwlock_timedwrlock,
+ StatInt_pthread_rwlock_unlock,
+ StatInt_pthread_cond_init,
+ StatInt_pthread_cond_destroy,
+ StatInt_pthread_cond_signal,
+ StatInt_pthread_cond_broadcast,
+ StatInt_pthread_cond_wait,
+ StatInt_pthread_cond_timedwait,
+ StatInt_pthread_barrier_init,
+ StatInt_pthread_barrier_destroy,
+ StatInt_pthread_barrier_wait,
+ StatInt_pthread_once,
+ StatInt_sem_init,
+ StatInt_sem_destroy,
+ StatInt_sem_wait,
+ StatInt_sem_trywait,
+ StatInt_sem_timedwait,
+ StatInt_sem_post,
+ StatInt_sem_getvalue,
+ StatInt_read,
+ StatInt_pread,
+ StatInt_pread64,
+ StatInt_readv,
+ StatInt_preadv64,
+ StatInt_write,
+ StatInt_pwrite,
+ StatInt_pwrite64,
+ StatInt_writev,
+ StatInt_pwritev64,
+ StatInt_send,
+ StatInt_sendmsg,
+ StatInt_recv,
+ StatInt_recvmsg,
+ StatInt_unlink,
+ StatInt_fopen,
+ StatInt_fread,
+ StatInt_fwrite,
+ StatInt_puts,
+ StatInt_rmdir,
+ StatInt_opendir,
+ StatInt_epoll_ctl,
+ StatInt_epoll_wait,
+ StatInt_sigaction,
+ StatInt_signal,
+ StatInt_raise,
+ StatInt_kill,
+ StatInt_pthread_kill,
+
+ // Dynamic annotations.
+ StatAnnotation,
+ StatAnnotateHappensBefore,
+ StatAnnotateHappensAfter,
+ StatAnnotateCondVarSignal,
+ StatAnnotateCondVarSignalAll,
+ StatAnnotateMutexIsNotPHB,
+ StatAnnotateCondVarWait,
+ StatAnnotateRWLockCreate,
+ StatAnnotateRWLockDestroy,
+ StatAnnotateRWLockAcquired,
+ StatAnnotateRWLockReleased,
+ StatAnnotateTraceMemory,
+ StatAnnotateFlushState,
+ StatAnnotateNewMemory,
+ StatAnnotateNoOp,
+ StatAnnotateFlushExpectedRaces,
+ StatAnnotateEnableRaceDetection,
+ StatAnnotateMutexIsUsedAsCondVar,
+ StatAnnotatePCQGet,
+ StatAnnotatePCQPut,
+ StatAnnotatePCQDestroy,
+ StatAnnotatePCQCreate,
+ StatAnnotateExpectRace,
+ StatAnnotateBenignRaceSized,
+ StatAnnotateBenignRace,
+ StatAnnotateIgnoreReadsBegin,
+ StatAnnotateIgnoreReadsEnd,
+ StatAnnotateIgnoreWritesBegin,
+ StatAnnotateIgnoreWritesEnd,
+ StatAnnotatePublishMemoryRange,
+ StatAnnotateUnpublishMemoryRange,
+ StatAnnotateThreadName,
+
+ // Internal mutex contentionz.
+ StatMtxTotal,
+ StatMtxTrace,
+ StatMtxThreads,
+ StatMtxReport,
+ StatMtxSyncVar,
+ StatMtxSyncTab,
+ StatMtxSlab,
+ StatMtxAnnotations,
+ StatMtxAtExit,
+
+ // This must be the last.
+ StatCnt,
+};
+
+} // namespace __tsan
+
+#endif // TSAN_STAT_H
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
new file mode 100644
index 000000000000..7549a4f8ba83
--- /dev/null
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -0,0 +1,163 @@
+//===-- tsan_suppressions.cc ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_suppressions.h"
+#include "tsan_rtl.h"
+#include "tsan_flags.h"
+#include "tsan_mman.h"
+#include "tsan_platform.h"
+
+namespace __tsan {
+
+static Suppression *g_suppressions;
+
+static char *ReadFile(const char *filename) {
+ if (filename == 0 || filename[0] == 0)
+ return 0;
+ InternalScopedBuf<char> tmp(4*1024);
+ if (filename[0] == '/')
+ internal_snprintf(tmp, tmp.Size(), "%s", filename);
+ else
+ internal_snprintf(tmp, tmp.Size(), "%s/%s", GetPwd(), filename);
+ fd_t fd = internal_open(tmp, false);
+ if (fd == kInvalidFd) {
+ TsanPrintf("ThreadSanitizer: failed to open suppressions file '%s'\n",
+ tmp.Ptr());
+ Die();
+ }
+ const uptr fsize = internal_filesize(fd);
+ if (fsize == (uptr)-1) {
+ TsanPrintf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
+ tmp.Ptr());
+ Die();
+ }
+ char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
+ if (fsize != internal_read(fd, buf, fsize)) {
+ TsanPrintf("ThreadSanitizer: failed to read suppressions file '%s'\n",
+ tmp.Ptr());
+ Die();
+ }
+ internal_close(fd);
+ buf[fsize] = 0;
+ return buf;
+}
+
+bool SuppressionMatch(char *templ, const char *str) {
+ if (str == 0 || str[0] == 0)
+ return false;
+ char *tpos;
+ const char *spos;
+ while (templ && templ[0]) {
+ if (templ[0] == '*') {
+ templ++;
+ continue;
+ }
+ if (str[0] == 0)
+ return false;
+ tpos = (char*)internal_strchr(templ, '*');
+ if (tpos != 0)
+ tpos[0] = 0;
+ spos = internal_strstr(str, templ);
+ str = spos + internal_strlen(templ);
+ templ = tpos;
+ if (tpos)
+ tpos[0] = '*';
+ if (spos == 0)
+ return false;
+ }
+ return true;
+}
+
+Suppression *SuppressionParse(const char* supp) {
+ Suppression *head = 0;
+ const char *line = supp;
+ while (line) {
+ while (line[0] == ' ' || line[0] == '\t')
+ line++;
+ const char *end = internal_strchr(line, '\n');
+ if (end == 0)
+ end = line + internal_strlen(line);
+ if (line != end && line[0] != '#') {
+ const char *end2 = end;
+ while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
+ end2--;
+ SuppressionType stype;
+ if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) {
+ stype = SuppressionRace;
+ line += sizeof("race:") - 1;
+ } else if (0 == internal_strncmp(line, "thread:",
+ sizeof("thread:") - 1)) {
+ stype = SuppressionThread;
+ line += sizeof("thread:") - 1;
+ } else if (0 == internal_strncmp(line, "mutex:",
+ sizeof("mutex:") - 1)) {
+ stype = SuppressionMutex;
+ line += sizeof("mutex:") - 1;
+ } else if (0 == internal_strncmp(line, "signal:",
+ sizeof("signal:") - 1)) {
+ stype = SuppressionSignal;
+ line += sizeof("signal:") - 1;
+ } else {
+ TsanPrintf("ThreadSanitizer: failed to parse suppressions file\n");
+ Die();
+ }
+ Suppression *s = (Suppression*)internal_alloc(MBlockSuppression,
+ sizeof(Suppression));
+ s->next = head;
+ head = s;
+ s->type = stype;
+ s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1);
+ internal_memcpy(s->templ, line, end2 - line);
+ s->templ[end2 - line] = 0;
+ }
+ if (end[0] == 0)
+ break;
+ line = end + 1;
+ }
+ return head;
+}
+
+void InitializeSuppressions() {
+ char *supp = ReadFile(flags()->suppressions);
+ g_suppressions = SuppressionParse(supp);
+}
+
+bool IsSuppressed(ReportType typ, const ReportStack *stack) {
+ if (g_suppressions == 0 || stack == 0)
+ return false;
+ SuppressionType stype;
+ if (typ == ReportTypeRace)
+ stype = SuppressionRace;
+ else if (typ == ReportTypeThreadLeak)
+ stype = SuppressionThread;
+ else if (typ == ReportTypeMutexDestroyLocked)
+ stype = SuppressionMutex;
+ else if (typ == ReportTypeSignalUnsafe)
+ stype = SuppressionSignal;
+ else
+ return false;
+ for (const ReportStack *frame = stack; frame; frame = frame->next) {
+ for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
+ if (stype == supp->type &&
+ (SuppressionMatch(supp->templ, frame->func) ||
+ SuppressionMatch(supp->templ, frame->file))) {
+ DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h
new file mode 100644
index 000000000000..29311c123d6d
--- /dev/null
+++ b/lib/tsan/rtl/tsan_suppressions.h
@@ -0,0 +1,43 @@
+//===-- tsan_suppressions.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_SUPPRESSIONS_H
+#define TSAN_SUPPRESSIONS_H
+
+#include "tsan_report.h"
+
+namespace __tsan {
+
+void InitializeSuppressions();
+void FinalizeSuppressions();
+bool IsSuppressed(ReportType typ, const ReportStack *stack);
+
+// Exposed for testing.
+enum SuppressionType {
+ SuppressionRace,
+ SuppressionMutex,
+ SuppressionThread,
+ SuppressionSignal,
+};
+
+struct Suppression {
+ Suppression *next;
+ SuppressionType type;
+ char *templ;
+};
+
+Suppression *SuppressionParse(const char* supp);
+bool SuppressionMatch(char *templ, const char *str);
+
+} // namespace __tsan
+
+#endif // TSAN_SUPPRESSIONS_H
diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc
new file mode 100644
index 000000000000..f757d070e169
--- /dev/null
+++ b/lib/tsan/rtl/tsan_symbolize.cc
@@ -0,0 +1,78 @@
+//===-- tsan_symbolize.cc -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_symbolize.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "tsan_flags.h"
+#include "tsan_report.h"
+
+namespace __tsan {
+
+ReportStack *NewReportStackEntry(uptr addr) {
+ ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
+ sizeof(ReportStack));
+ internal_memset(ent, 0, sizeof(*ent));
+ ent->pc = addr;
+ return ent;
+}
+
+static ReportStack *NewReportStackEntry(const AddressInfo &info) {
+ ReportStack *ent = NewReportStackEntry(info.address);
+ if (info.module)
+ ent->module = internal_strdup(info.module);
+ ent->offset = info.module_offset;
+ if (info.function) {
+ ent->func = internal_strdup(info.function);
+ }
+ if (info.file)
+ ent->file = internal_strdup(info.file);
+ ent->line = info.line;
+ ent->col = info.column;
+ return ent;
+}
+
+ReportStack *SymbolizeCode(uptr addr) {
+ if (flags()->use_internal_symbolizer) {
+ static const uptr kMaxAddrFrames = 16;
+ InternalScopedBuf<AddressInfo> addr_frames(kMaxAddrFrames);
+ for (uptr i = 0; i < kMaxAddrFrames; i++)
+ new(&addr_frames[i]) AddressInfo();
+ uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames,
+ kMaxAddrFrames);
+ if (addr_frames_num == 0)
+ return NewReportStackEntry(addr);
+ ReportStack *top = 0;
+ ReportStack *bottom = 0;
+ for (uptr i = 0; i < addr_frames_num; i++) {
+ ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]);
+ CHECK(cur_entry);
+ addr_frames[i].Clear();
+ if (i == 0)
+ top = cur_entry;
+ else
+ bottom->next = cur_entry;
+ bottom = cur_entry;
+ }
+ return top;
+ }
+ return SymbolizeCodeAddr2Line(addr);
+}
+
+ReportStack *SymbolizeData(uptr addr) {
+ return SymbolizeDataAddr2Line(addr);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_symbolize.h b/lib/tsan/rtl/tsan_symbolize.h
new file mode 100644
index 000000000000..115339be38a9
--- /dev/null
+++ b/lib/tsan/rtl/tsan_symbolize.h
@@ -0,0 +1,31 @@
+//===-- tsan_symbolize.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_SYMBOLIZE_H
+#define TSAN_SYMBOLIZE_H
+
+#include "tsan_defs.h"
+#include "tsan_report.h"
+
+namespace __tsan {
+
+ReportStack *SymbolizeCode(uptr addr);
+ReportStack *SymbolizeData(uptr addr);
+
+ReportStack *SymbolizeCodeAddr2Line(uptr addr);
+ReportStack *SymbolizeDataAddr2Line(uptr addr);
+
+ReportStack *NewReportStackEntry(uptr addr);
+
+} // namespace __tsan
+
+#endif // TSAN_SYMBOLIZE_H
diff --git a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc
new file mode 100644
index 000000000000..5eed977ec278
--- /dev/null
+++ b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc
@@ -0,0 +1,193 @@
+//===-- tsan_symbolize_addr2line.cc ---------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_symbolize.h"
+#include "tsan_mman.h"
+#include "tsan_rtl.h"
+#include "tsan_platform.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <link.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+
+namespace __tsan {
+
+struct ModuleDesc {
+ const char *fullname;
+ const char *name;
+ uptr base;
+ int inp_fd;
+ int out_fd;
+};
+
+struct SectionDesc {
+ SectionDesc *next;
+ ModuleDesc *module;
+ uptr base;
+ uptr end;
+};
+
+struct DlIteratePhdrCtx {
+ SectionDesc *sections;
+ bool is_first;
+};
+
+static void NOINLINE InitModule(ModuleDesc *m) {
+ int outfd[2] = {};
+ if (pipe(&outfd[0])) {
+ TsanPrintf("ThreadSanitizer: outfd pipe() failed (%d)\n", errno);
+ Die();
+ }
+ int infd[2] = {};
+ if (pipe(&infd[0])) {
+ TsanPrintf("ThreadSanitizer: infd pipe() failed (%d)\n", errno);
+ Die();
+ }
+ int pid = fork();
+ if (pid == 0) {
+ flags()->log_fileno = STDERR_FILENO;
+ internal_close(STDOUT_FILENO);
+ internal_close(STDIN_FILENO);
+ internal_dup2(outfd[0], STDIN_FILENO);
+ internal_dup2(infd[1], STDOUT_FILENO);
+ internal_close(outfd[0]);
+ internal_close(outfd[1]);
+ internal_close(infd[0]);
+ internal_close(infd[1]);
+ for (int fd = getdtablesize(); fd > 2; fd--)
+ internal_close(fd);
+ execl("/usr/bin/addr2line", "/usr/bin/addr2line", "-Cfe", m->fullname, 0);
+ _exit(0);
+ } else if (pid < 0) {
+ TsanPrintf("ThreadSanitizer: failed to fork symbolizer\n");
+ Die();
+ }
+ internal_close(outfd[0]);
+ internal_close(infd[1]);
+ m->inp_fd = infd[0];
+ m->out_fd = outfd[1];
+}
+
+static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
+ DlIteratePhdrCtx *ctx = (DlIteratePhdrCtx*)arg;
+ InternalScopedBuf<char> tmp(128);
+ if (ctx->is_first) {
+ internal_snprintf(tmp.Ptr(), tmp.Size(), "/proc/%d/exe", GetPid());
+ info->dlpi_name = tmp.Ptr();
+ }
+ ctx->is_first = false;
+ if (info->dlpi_name == 0 || info->dlpi_name[0] == 0)
+ return 0;
+ ModuleDesc *m = (ModuleDesc*)internal_alloc(MBlockReportStack,
+ sizeof(ModuleDesc));
+ m->fullname = internal_strdup(info->dlpi_name);
+ m->name = internal_strrchr(m->fullname, '/');
+ if (m->name)
+ m->name += 1;
+ else
+ m->name = m->fullname;
+ m->base = (uptr)info->dlpi_addr;
+ m->inp_fd = -1;
+ m->out_fd = -1;
+ DPrintf("Module %s %zx\n", m->name, m->base);
+ for (int i = 0; i < info->dlpi_phnum; i++) {
+ const Elf64_Phdr *s = &info->dlpi_phdr[i];
+ DPrintf(" Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx"
+ " p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n",
+ (uptr)s->p_type, (uptr)s->p_offset, (uptr)s->p_vaddr,
+ (uptr)s->p_paddr, (uptr)s->p_filesz, (uptr)s->p_memsz,
+ (uptr)s->p_flags, (uptr)s->p_align);
+ if (s->p_type != PT_LOAD)
+ continue;
+ SectionDesc *sec = (SectionDesc*)internal_alloc(MBlockReportStack,
+ sizeof(SectionDesc));
+ sec->module = m;
+ sec->base = info->dlpi_addr + s->p_vaddr;
+ sec->end = sec->base + s->p_memsz;
+ sec->next = ctx->sections;
+ ctx->sections = sec;
+ DPrintf(" Section %zx-%zx\n", sec->base, sec->end);
+ }
+ return 0;
+}
+
+static SectionDesc *InitSections() {
+ DlIteratePhdrCtx ctx = {0, true};
+ dl_iterate_phdr(dl_iterate_phdr_cb, &ctx);
+ return ctx.sections;
+}
+
+static SectionDesc *GetSectionDesc(uptr addr) {
+ static SectionDesc *sections = 0;
+ if (sections == 0)
+ sections = InitSections();
+ for (SectionDesc *s = sections; s; s = s->next) {
+ if (addr >= s->base && addr < s->end) {
+ if (s->module->inp_fd == -1)
+ InitModule(s->module);
+ return s;
+ }
+ }
+ return 0;
+}
+
+ReportStack *SymbolizeCodeAddr2Line(uptr addr) {
+ SectionDesc *s = GetSectionDesc(addr);
+ if (s == 0)
+ return NewReportStackEntry(addr);
+ ModuleDesc *m = s->module;
+ uptr offset = addr - m->base;
+ char addrstr[32];
+ internal_snprintf(addrstr, sizeof(addrstr), "%p\n", (void*)offset);
+ if (0 >= internal_write(m->out_fd, addrstr, internal_strlen(addrstr))) {
+ TsanPrintf("ThreadSanitizer: can't write from symbolizer (%d, %d)\n",
+ m->out_fd, errno);
+ Die();
+ }
+ InternalScopedBuf<char> func(1024);
+ ssize_t len = internal_read(m->inp_fd, func, func.Size() - 1);
+ if (len <= 0) {
+ TsanPrintf("ThreadSanitizer: can't read from symbolizer (%d, %d)\n",
+ m->inp_fd, errno);
+ Die();
+ }
+ func.Ptr()[len] = 0;
+ ReportStack *res = NewReportStackEntry(addr);
+ res->module = internal_strdup(m->name);
+ res->offset = offset;
+ char *pos = (char*)internal_strchr(func, '\n');
+ if (pos && func[0] != '?') {
+ res->func = (char*)internal_alloc(MBlockReportStack, pos - func + 1);
+ internal_memcpy(res->func, func, pos - func);
+ res->func[pos - func] = 0;
+ char *pos2 = (char*)internal_strchr(pos, ':');
+ if (pos2) {
+ res->file = (char*)internal_alloc(MBlockReportStack, pos2 - pos - 1 + 1);
+ internal_memcpy(res->file, pos + 1, pos2 - pos - 1);
+ res->file[pos2 - pos - 1] = 0;
+ res->line = atoi(pos2 + 1);
+ }
+ }
+ return res;
+}
+
+ReportStack *SymbolizeDataAddr2Line(uptr addr) {
+ return 0;
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc
new file mode 100644
index 000000000000..abb5a2ad298f
--- /dev/null
+++ b/lib/tsan/rtl/tsan_sync.cc
@@ -0,0 +1,219 @@
+//===-- tsan_sync.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_sync.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+
+namespace __tsan {
+
+SyncVar::SyncVar(uptr addr)
+ : mtx(MutexTypeSyncVar, StatMtxSyncVar)
+ , addr(addr)
+ , owner_tid(kInvalidTid)
+ , recursion()
+ , is_rw()
+ , is_recursive()
+ , is_broken() {
+}
+
+SyncTab::Part::Part()
+ : mtx(MutexTypeSyncTab, StatMtxSyncTab)
+ , val() {
+}
+
+SyncTab::SyncTab() {
+}
+
+SyncTab::~SyncTab() {
+ for (int i = 0; i < kPartCount; i++) {
+ while (tab_[i].val) {
+ SyncVar *tmp = tab_[i].val;
+ tab_[i].val = tmp->next;
+ DestroyAndFree(tmp);
+ }
+ }
+}
+
+SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock) {
+ Part *p = &tab_[PartIdx(addr)];
+ {
+ ReadLock l(&p->mtx);
+ for (SyncVar *res = p->val; res; res = res->next) {
+ if (res->addr == addr) {
+ if (write_lock)
+ res->mtx.Lock();
+ else
+ res->mtx.ReadLock();
+ return res;
+ }
+ }
+ }
+ {
+ Lock l(&p->mtx);
+ SyncVar *res = p->val;
+ for (; res; res = res->next) {
+ if (res->addr == addr)
+ break;
+ }
+ if (res == 0) {
+ StatInc(thr, StatSyncCreated);
+ void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
+ res = new(mem) SyncVar(addr);
+#ifndef TSAN_GO
+ res->creation_stack.ObtainCurrent(thr, pc);
+#endif
+ res->next = p->val;
+ p->val = res;
+ }
+ if (write_lock)
+ res->mtx.Lock();
+ else
+ res->mtx.ReadLock();
+ return res;
+ }
+}
+
+SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) {
+ Part *p = &tab_[PartIdx(addr)];
+ SyncVar *res = 0;
+ {
+ Lock l(&p->mtx);
+ SyncVar **prev = &p->val;
+ res = *prev;
+ while (res) {
+ if (res->addr == addr) {
+ *prev = res->next;
+ break;
+ }
+ prev = &res->next;
+ res = *prev;
+ }
+ }
+ if (res) {
+ StatInc(thr, StatSyncDestroyed);
+ res->mtx.Lock();
+ res->mtx.Unlock();
+ }
+ return res;
+}
+
+uptr SyncVar::GetMemoryConsumption() {
+ return sizeof(*this)
+ + clock.size() * sizeof(u64)
+ + read_clock.size() * sizeof(u64)
+ + creation_stack.Size() * sizeof(uptr);
+}
+
+uptr SyncTab::GetMemoryConsumption(uptr *nsync) {
+ uptr mem = 0;
+ for (int i = 0; i < kPartCount; i++) {
+ Part *p = &tab_[i];
+ Lock l(&p->mtx);
+ for (SyncVar *s = p->val; s; s = s->next) {
+ *nsync += 1;
+ mem += s->GetMemoryConsumption();
+ }
+ }
+ return mem;
+}
+
+int SyncTab::PartIdx(uptr addr) {
+ return (addr >> 3) % kPartCount;
+}
+
+StackTrace::StackTrace()
+ : n_()
+ , s_()
+ , c_() {
+}
+
+StackTrace::StackTrace(uptr *buf, uptr cnt)
+ : n_()
+ , s_(buf)
+ , c_(cnt) {
+ CHECK_NE(buf, 0);
+ CHECK_NE(cnt, 0);
+}
+
+StackTrace::~StackTrace() {
+ Reset();
+}
+
+void StackTrace::Reset() {
+ if (s_ && !c_) {
+ CHECK_NE(n_, 0);
+ internal_free(s_);
+ s_ = 0;
+ }
+ n_ = 0;
+}
+
+void StackTrace::Init(const uptr *pcs, uptr cnt) {
+ Reset();
+ if (cnt == 0)
+ return;
+ if (c_) {
+ CHECK_NE(s_, 0);
+ CHECK_LE(cnt, c_);
+ } else {
+ s_ = (uptr*)internal_alloc(MBlockStackTrace, cnt * sizeof(s_[0]));
+ }
+ n_ = cnt;
+ internal_memcpy(s_, pcs, cnt * sizeof(s_[0]));
+}
+
+void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) {
+ Reset();
+ n_ = thr->shadow_stack_pos - thr->shadow_stack;
+ if (n_ + !!toppc == 0)
+ return;
+ if (c_) {
+ CHECK_NE(s_, 0);
+ CHECK_LE(n_ + !!toppc, c_);
+ } else {
+ s_ = (uptr*)internal_alloc(MBlockStackTrace,
+ (n_ + !!toppc) * sizeof(s_[0]));
+ }
+ for (uptr i = 0; i < n_; i++)
+ s_[i] = thr->shadow_stack[i];
+ if (toppc) {
+ s_[n_] = toppc;
+ n_++;
+ }
+}
+
+void StackTrace::CopyFrom(const StackTrace& other) {
+ Reset();
+ Init(other.Begin(), other.Size());
+}
+
+bool StackTrace::IsEmpty() const {
+ return n_ == 0;
+}
+
+uptr StackTrace::Size() const {
+ return n_;
+}
+
+uptr StackTrace::Get(uptr i) const {
+ CHECK_LT(i, n_);
+ return s_[i];
+}
+
+const uptr *StackTrace::Begin() const {
+ return s_;
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_sync.h b/lib/tsan/rtl/tsan_sync.h
new file mode 100644
index 000000000000..34d3e0b2132f
--- /dev/null
+++ b/lib/tsan/rtl/tsan_sync.h
@@ -0,0 +1,106 @@
+//===-- tsan_sync.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_SYNC_H
+#define TSAN_SYNC_H
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "tsan_clock.h"
+#include "tsan_defs.h"
+#include "tsan_mutex.h"
+
+namespace __tsan {
+
+class SlabCache;
+
+class StackTrace {
+ public:
+ StackTrace();
+ // Initialized the object in "static mode",
+ // in this mode it never calls malloc/free but uses the provided buffer.
+ StackTrace(uptr *buf, uptr cnt);
+ ~StackTrace();
+ void Reset();
+
+ void Init(const uptr *pcs, uptr cnt);
+ void ObtainCurrent(ThreadState *thr, uptr toppc);
+ bool IsEmpty() const;
+ uptr Size() const;
+ uptr Get(uptr i) const;
+ const uptr *Begin() const;
+ void CopyFrom(const StackTrace& other);
+
+ private:
+ uptr n_;
+ uptr *s_;
+ const uptr c_;
+
+ StackTrace(const StackTrace&);
+ void operator = (const StackTrace&);
+};
+
+struct SyncVar {
+ explicit SyncVar(uptr addr);
+
+ static const int kInvalidTid = -1;
+
+ Mutex mtx;
+ const uptr addr;
+ SyncClock clock;
+ SyncClock read_clock; // Used for rw mutexes only.
+ StackTrace creation_stack;
+ int owner_tid; // Set only by exclusive owners.
+ int recursion;
+ bool is_rw;
+ bool is_recursive;
+ bool is_broken;
+ SyncVar *next; // In SyncTab hashtable.
+
+ uptr GetMemoryConsumption();
+};
+
+class SyncTab {
+ public:
+ SyncTab();
+ ~SyncTab();
+
+ // If the SyncVar does not exist yet, it is created.
+ SyncVar* GetAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock);
+
+ // If the SyncVar does not exist, returns 0.
+ SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr);
+
+ uptr GetMemoryConsumption(uptr *nsync);
+
+ private:
+ struct Part {
+ Mutex mtx;
+ SyncVar *val;
+ char pad[kCacheLineSize - sizeof(Mutex) - sizeof(SyncVar*)]; // NOLINT
+ Part();
+ };
+
+ // FIXME: Implement something more sane.
+ static const int kPartCount = 1009;
+ Part tab_[kPartCount];
+
+ int PartIdx(uptr addr);
+
+ SyncTab(const SyncTab&); // Not implemented.
+ void operator = (const SyncTab&); // Not implemented.
+};
+
+} // namespace __tsan
+
+#endif // TSAN_SYNC_H
diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h
new file mode 100644
index 000000000000..bf15bf5cc239
--- /dev/null
+++ b/lib/tsan/rtl/tsan_trace.h
@@ -0,0 +1,71 @@
+//===-- tsan_trace.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_TRACE_H
+#define TSAN_TRACE_H
+
+#include "tsan_defs.h"
+#include "tsan_mutex.h"
+#include "tsan_sync.h"
+
+namespace __tsan {
+
+const int kTraceParts = 8;
+const int kTraceSize = 128*1024;
+const int kTracePartSize = kTraceSize / kTraceParts;
+
+// Must fit into 3 bits.
+enum EventType {
+ EventTypeMop,
+ EventTypeFuncEnter,
+ EventTypeFuncExit,
+ EventTypeLock,
+ EventTypeUnlock,
+ EventTypeRLock,
+ EventTypeRUnlock,
+};
+
+// Represents a thread event (from most significant bit):
+// u64 typ : 3; // EventType.
+// u64 addr : 61; // Associated pc.
+typedef u64 Event;
+
+struct TraceHeader {
+ StackTrace stack0; // Start stack for the trace.
+ u64 epoch0; // Start epoch for the trace.
+#ifndef TSAN_GO
+ uptr stack0buf[kShadowStackSize];
+#endif
+
+ TraceHeader()
+#ifndef TSAN_GO
+ : stack0(stack0buf, kShadowStackSize)
+#else
+ : stack0()
+#endif
+ , epoch0() {
+ }
+};
+
+struct Trace {
+ Event events[kTraceSize];
+ TraceHeader headers[kTraceParts];
+ Mutex mtx;
+
+ Trace()
+ : mtx(MutexTypeTrace, StatMtxTrace) {
+ }
+};
+
+} // namespace __tsan
+
+#endif // TSAN_TRACE_H
diff --git a/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/lib/tsan/rtl/tsan_update_shadow_word_inl.h
new file mode 100644
index 000000000000..c7864ce00af3
--- /dev/null
+++ b/lib/tsan/rtl/tsan_update_shadow_word_inl.h
@@ -0,0 +1,79 @@
+//===-- tsan_update_shadow_word_inl.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Body of the hottest inner loop.
+// If we wrap this body into a function, compilers (both gcc and clang)
+// produce sligtly less efficient code.
+//===----------------------------------------------------------------------===//
+do {
+ StatInc(thr, StatShadowProcessed);
+ const unsigned kAccessSize = 1 << kAccessSizeLog;
+ unsigned off = cur.ComputeSearchOffset();
+ u64 *sp = &shadow_mem[(idx + off) % kShadowCnt];
+ old = LoadShadow(sp);
+ if (old.IsZero()) {
+ StatInc(thr, StatShadowZero);
+ if (store_word)
+ StoreIfNotYetStored(sp, &store_word);
+ // The above StoreIfNotYetStored could be done unconditionally
+ // and it even shows 4% gain on synthetic benchmarks (r4307).
+ break;
+ }
+ // is the memory access equal to the previous?
+ if (Shadow::Addr0AndSizeAreEqual(cur, old)) {
+ StatInc(thr, StatShadowSameSize);
+ // same thread?
+ if (Shadow::TidsAreEqual(old, cur)) {
+ StatInc(thr, StatShadowSameThread);
+ if (OldIsInSameSynchEpoch(old, thr)) {
+ if (OldIsRWStronger(old, kAccessIsWrite)) {
+ // found a slot that holds effectively the same info
+ // (that is, same tid, same sync epoch and same size)
+ StatInc(thr, StatMopSame);
+ return;
+ }
+ StoreIfNotYetStored(sp, &store_word);
+ break;
+ }
+ if (OldIsRWWeaker(old, kAccessIsWrite))
+ StoreIfNotYetStored(sp, &store_word);
+ break;
+ }
+ StatInc(thr, StatShadowAnotherThread);
+ if (HappensBefore(old, thr)) {
+ StoreIfNotYetStored(sp, &store_word);
+ break;
+ }
+ if (BothReads(old, kAccessIsWrite))
+ break;
+ goto RACE;
+ }
+
+ // Do the memory access intersect?
+ if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) {
+ StatInc(thr, StatShadowIntersect);
+ if (Shadow::TidsAreEqual(old, cur)) {
+ StatInc(thr, StatShadowSameThread);
+ break;
+ }
+ StatInc(thr, StatShadowAnotherThread);
+ if (HappensBefore(old, thr))
+ break;
+
+ if (BothReads(old, kAccessIsWrite))
+ break;
+
+ goto RACE;
+ }
+ // The accesses do not intersect.
+ StatInc(thr, StatShadowNotIntersect);
+ break;
+} while (0);
diff --git a/lib/tsan/rtl/tsan_vector.h b/lib/tsan/rtl/tsan_vector.h
new file mode 100644
index 000000000000..d41063df3de5
--- /dev/null
+++ b/lib/tsan/rtl/tsan_vector.h
@@ -0,0 +1,110 @@
+//===-- tsan_vector.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+// Low-fat STL-like vector container.
+
+#ifndef TSAN_VECTOR_H
+#define TSAN_VECTOR_H
+
+#include "tsan_defs.h"
+#include "tsan_mman.h"
+
+namespace __tsan {
+
+template<typename T>
+class Vector {
+ public:
+ explicit Vector(MBlockType typ)
+ : typ_(typ)
+ , begin_()
+ , end_()
+ , last_() {
+ }
+
+ ~Vector() {
+ if (begin_)
+ internal_free(begin_);
+ }
+
+ void Reset() {
+ if (begin_)
+ internal_free(begin_);
+ begin_ = 0;
+ end_ = 0;
+ last_ = 0;
+ }
+
+ uptr Size() const {
+ return end_ - begin_;
+ }
+
+ T &operator[](uptr i) {
+ DCHECK_LT(i, end_ - begin_);
+ return begin_[i];
+ }
+
+ const T &operator[](uptr i) const {
+ DCHECK_LT(i, end_ - begin_);
+ return begin_[i];
+ }
+
+ T *PushBack(T v = T()) {
+ EnsureSize(Size() + 1);
+ end_[-1] = v;
+ return &end_[-1];
+ }
+
+ void Resize(uptr size) {
+ uptr old_size = Size();
+ EnsureSize(size);
+ if (old_size < size) {
+ for (uptr i = old_size; i < size; i++)
+ begin_[i] = T();
+ }
+ }
+
+ private:
+ const MBlockType typ_;
+ T *begin_;
+ T *end_;
+ T *last_;
+
+ void EnsureSize(uptr size) {
+ if (size <= Size())
+ return;
+ if (size <= (uptr)(last_ - begin_)) {
+ end_ = begin_ + size;
+ return;
+ }
+ uptr cap0 = last_ - begin_;
+ uptr cap = 2 * cap0;
+ if (cap == 0)
+ cap = 16;
+ if (cap < size)
+ cap = size;
+ T *p = (T*)internal_alloc(typ_, cap * sizeof(T));
+ if (cap0) {
+ internal_memcpy(p, begin_, cap0 * sizeof(T));
+ internal_free(begin_);
+ }
+ begin_ = p;
+ end_ = begin_ + size;
+ last_ = begin_ + cap;
+ }
+
+ Vector(const Vector&);
+ void operator=(const Vector&);
+};
+}
+
+#endif // #ifndef TSAN_VECTOR_H
diff --git a/lib/tsan/rtl_tests/tsan_bench.cc b/lib/tsan/rtl_tests/tsan_bench.cc
new file mode 100644
index 000000000000..a3cf22f2c626
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_bench.cc
@@ -0,0 +1,105 @@
+//===-- tsan_bench.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_test_util.h"
+#include "tsan_interface.h"
+#include "tsan_defs.h"
+#include "gtest/gtest.h"
+#include <stdint.h>
+
+const int kSize = 128;
+const int kRepeat = 2*1024*1024;
+
+void noinstr(void *p) {}
+
+template<typename T, void(*__tsan_mop)(void *p)>
+static void Benchmark() {
+ volatile T data[kSize];
+ for (int i = 0; i < kRepeat; i++) {
+ for (int j = 0; j < kSize; j++) {
+ __tsan_mop((void*)&data[j]);
+ data[j]++;
+ }
+ }
+}
+
+TEST(DISABLED_BENCH, Mop1) {
+ Benchmark<uint8_t, noinstr>();
+}
+
+TEST(DISABLED_BENCH, Mop1Read) {
+ Benchmark<uint8_t, __tsan_read1>();
+}
+
+TEST(DISABLED_BENCH, Mop1Write) {
+ Benchmark<uint8_t, __tsan_write1>();
+}
+
+TEST(DISABLED_BENCH, Mop2) {
+ Benchmark<uint16_t, noinstr>();
+}
+
+TEST(DISABLED_BENCH, Mop2Read) {
+ Benchmark<uint16_t, __tsan_read2>();
+}
+
+TEST(DISABLED_BENCH, Mop2Write) {
+ Benchmark<uint16_t, __tsan_write2>();
+}
+
+TEST(DISABLED_BENCH, Mop4) {
+ Benchmark<uint32_t, noinstr>();
+}
+
+TEST(DISABLED_BENCH, Mop4Read) {
+ Benchmark<uint32_t, __tsan_read4>();
+}
+
+TEST(DISABLED_BENCH, Mop4Write) {
+ Benchmark<uint32_t, __tsan_write4>();
+}
+
+TEST(DISABLED_BENCH, Mop8) {
+ Benchmark<uint8_t, noinstr>();
+}
+
+TEST(DISABLED_BENCH, Mop8Read) {
+ Benchmark<uint64_t, __tsan_read8>();
+}
+
+TEST(DISABLED_BENCH, Mop8Write) {
+ Benchmark<uint64_t, __tsan_write8>();
+}
+
+TEST(DISABLED_BENCH, FuncCall) {
+ for (int i = 0; i < kRepeat; i++) {
+ for (int j = 0; j < kSize; j++)
+ __tsan_func_entry((void*)(uintptr_t)j);
+ for (int j = 0; j < kSize; j++)
+ __tsan_func_exit();
+ }
+}
+
+TEST(DISABLED_BENCH, MutexLocal) {
+ Mutex m;
+ ScopedThread().Create(m);
+ for (int i = 0; i < 50; i++) {
+ ScopedThread t;
+ t.Lock(m);
+ t.Unlock(m);
+ }
+ for (int i = 0; i < 16*1024*1024; i++) {
+ m.Lock();
+ m.Unlock();
+ }
+ ScopedThread().Destroy(m);
+}
diff --git a/lib/tsan/rtl_tests/tsan_mop.cc b/lib/tsan/rtl_tests/tsan_mop.cc
new file mode 100644
index 000000000000..f21742825050
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_mop.cc
@@ -0,0 +1,233 @@
+//===-- tsan_mop.cc -------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_interface.h"
+#include "tsan_test_util.h"
+#include "gtest/gtest.h"
+#include <stddef.h>
+#include <stdint.h>
+
+TEST(ThreadSanitizer, SimpleWrite) {
+ ScopedThread t;
+ MemLoc l;
+ t.Write1(l);
+}
+
+TEST(ThreadSanitizer, SimpleWriteWrite) {
+ ScopedThread t1, t2;
+ MemLoc l1, l2;
+ t1.Write1(l1);
+ t2.Write1(l2);
+}
+
+TEST(ThreadSanitizer, WriteWriteRace) {
+ ScopedThread t1, t2;
+ MemLoc l;
+ t1.Write1(l);
+ t2.Write1(l, true);
+}
+
+TEST(ThreadSanitizer, ReadWriteRace) {
+ ScopedThread t1, t2;
+ MemLoc l;
+ t1.Read1(l);
+ t2.Write1(l, true);
+}
+
+TEST(ThreadSanitizer, WriteReadRace) {
+ ScopedThread t1, t2;
+ MemLoc l;
+ t1.Write1(l);
+ t2.Read1(l, true);
+}
+
+TEST(ThreadSanitizer, ReadReadNoRace) {
+ ScopedThread t1, t2;
+ MemLoc l;
+ t1.Read1(l);
+ t2.Read1(l);
+}
+
+TEST(ThreadSanitizer, WriteThenRead) {
+ MemLoc l;
+ ScopedThread t1, t2;
+ t1.Write1(l);
+ t1.Read1(l);
+ t2.Read1(l, true);
+}
+
+TEST(ThreadSanitizer, WriteThenLockedRead) {
+ Mutex m(Mutex::RW);
+ MainThread t0;
+ t0.Create(m);
+ MemLoc l;
+ {
+ ScopedThread t1, t2;
+
+ t1.Write8(l);
+
+ t1.Lock(m);
+ t1.Read8(l);
+ t1.Unlock(m);
+
+ t2.Read8(l, true);
+ }
+ t0.Destroy(m);
+}
+
+TEST(ThreadSanitizer, LockedWriteThenRead) {
+ Mutex m(Mutex::RW);
+ MainThread t0;
+ t0.Create(m);
+ MemLoc l;
+ {
+ ScopedThread t1, t2;
+
+ t1.Lock(m);
+ t1.Write8(l);
+ t1.Unlock(m);
+
+ t1.Read8(l);
+
+ t2.Read8(l, true);
+ }
+ t0.Destroy(m);
+}
+
+
+TEST(ThreadSanitizer, RaceWithOffset) {
+ ScopedThread t1, t2;
+ {
+ MemLoc l;
+ t1.Access(l.loc(), true, 8, false);
+ t2.Access((char*)l.loc() + 4, true, 4, true);
+ }
+ {
+ MemLoc l;
+ t1.Access(l.loc(), true, 8, false);
+ t2.Access((char*)l.loc() + 7, true, 1, true);
+ }
+ {
+ MemLoc l;
+ t1.Access((char*)l.loc() + 4, true, 4, false);
+ t2.Access((char*)l.loc() + 4, true, 2, true);
+ }
+ {
+ MemLoc l;
+ t1.Access((char*)l.loc() + 4, true, 4, false);
+ t2.Access((char*)l.loc() + 6, true, 2, true);
+ }
+ {
+ MemLoc l;
+ t1.Access((char*)l.loc() + 3, true, 2, false);
+ t2.Access((char*)l.loc() + 4, true, 1, true);
+ }
+ {
+ MemLoc l;
+ t1.Access((char*)l.loc() + 1, true, 8, false);
+ t2.Access((char*)l.loc() + 3, true, 1, true);
+ }
+}
+
+TEST(ThreadSanitizer, RaceWithOffset2) {
+ ScopedThread t1, t2;
+ {
+ MemLoc l;
+ t1.Access((char*)l.loc(), true, 4, false);
+ t2.Access((char*)l.loc() + 2, true, 1, true);
+ }
+ {
+ MemLoc l;
+ t1.Access((char*)l.loc() + 2, true, 1, false);
+ t2.Access((char*)l.loc(), true, 4, true);
+ }
+}
+
+TEST(ThreadSanitizer, NoRaceWithOffset) {
+ ScopedThread t1, t2;
+ {
+ MemLoc l;
+ t1.Access(l.loc(), true, 4, false);
+ t2.Access((char*)l.loc() + 4, true, 4, false);
+ }
+ {
+ MemLoc l;
+ t1.Access((char*)l.loc() + 3, true, 2, false);
+ t2.Access((char*)l.loc() + 1, true, 2, false);
+ t2.Access((char*)l.loc() + 5, true, 2, false);
+ }
+}
+
+TEST(ThreadSanitizer, RaceWithDeadThread) {
+ MemLoc l;
+ ScopedThread t;
+ ScopedThread().Write1(l);
+ t.Write1(l, true);
+}
+
+TEST(ThreadSanitizer, BenignRaceOnVptr) {
+ void *vptr_storage;
+ MemLoc vptr(&vptr_storage), val;
+ vptr_storage = val.loc();
+ ScopedThread t1, t2;
+ t1.VptrUpdate(vptr, val);
+ t2.Read8(vptr);
+}
+
+TEST(ThreadSanitizer, HarmfulRaceOnVptr) {
+ void *vptr_storage;
+ MemLoc vptr(&vptr_storage), val1, val2;
+ vptr_storage = val1.loc();
+ ScopedThread t1, t2;
+ t1.VptrUpdate(vptr, val2);
+ t2.Read8(vptr, true);
+}
+
+static void foo() {
+ volatile int x = 42;
+ int x2 = x;
+ (void)x2;
+}
+
+static void bar() {
+ volatile int x = 43;
+ int x2 = x;
+ (void)x2;
+}
+
+TEST(ThreadSanitizer, ReportDeadThread) {
+ MemLoc l;
+ ScopedThread t1;
+ {
+ ScopedThread t2;
+ t2.Call(&foo);
+ t2.Call(&bar);
+ t2.Write1(l);
+ }
+ t1.Write1(l, true);
+}
+
+struct ClassWithStatic {
+ static int Data[4];
+};
+
+int ClassWithStatic::Data[4];
+
+static void foobarbaz() {}
+
+TEST(ThreadSanitizer, ReportRace) {
+ ScopedThread t1;
+ MainThread().Access(&ClassWithStatic::Data, true, 4, false);
+ t1.Call(&foobarbaz);
+ t1.Access(&ClassWithStatic::Data, true, 2, true);
+ t1.Return();
+}
diff --git a/lib/tsan/rtl_tests/tsan_mutex.cc b/lib/tsan/rtl_tests/tsan_mutex.cc
new file mode 100644
index 000000000000..4d9c77961818
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_mutex.cc
@@ -0,0 +1,221 @@
+//===-- tsan_mutex.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "tsan_interface.h"
+#include "tsan_interface_ann.h"
+#include "tsan_test_util.h"
+#include "gtest/gtest.h"
+#include <stdint.h>
+
+namespace __tsan {
+
+TEST(ThreadSanitizer, BasicMutex) {
+ ScopedThread t;
+ Mutex m;
+ t.Create(m);
+
+ t.Lock(m);
+ t.Unlock(m);
+
+ CHECK(t.TryLock(m));
+ t.Unlock(m);
+
+ t.Lock(m);
+ CHECK(!t.TryLock(m));
+ t.Unlock(m);
+
+ t.Destroy(m);
+}
+
+TEST(ThreadSanitizer, BasicSpinMutex) {
+ ScopedThread t;
+ Mutex m(Mutex::Spin);
+ t.Create(m);
+
+ t.Lock(m);
+ t.Unlock(m);
+
+ CHECK(t.TryLock(m));
+ t.Unlock(m);
+
+ t.Lock(m);
+ CHECK(!t.TryLock(m));
+ t.Unlock(m);
+
+ t.Destroy(m);
+}
+
+TEST(ThreadSanitizer, BasicRwMutex) {
+ ScopedThread t;
+ Mutex m(Mutex::RW);
+ t.Create(m);
+
+ t.Lock(m);
+ t.Unlock(m);
+
+ CHECK(t.TryLock(m));
+ t.Unlock(m);
+
+ t.Lock(m);
+ CHECK(!t.TryLock(m));
+ t.Unlock(m);
+
+ t.ReadLock(m);
+ t.ReadUnlock(m);
+
+ CHECK(t.TryReadLock(m));
+ t.ReadUnlock(m);
+
+ t.Lock(m);
+ CHECK(!t.TryReadLock(m));
+ t.Unlock(m);
+
+ t.ReadLock(m);
+ CHECK(!t.TryLock(m));
+ t.ReadUnlock(m);
+
+ t.ReadLock(m);
+ CHECK(t.TryReadLock(m));
+ t.ReadUnlock(m);
+ t.ReadUnlock(m);
+
+ t.Destroy(m);
+}
+
+TEST(ThreadSanitizer, Mutex) {
+ Mutex m;
+ MainThread t0;
+ t0.Create(m);
+
+ ScopedThread t1, t2;
+ MemLoc l;
+ t1.Lock(m);
+ t1.Write1(l);
+ t1.Unlock(m);
+ t2.Lock(m);
+ t2.Write1(l);
+ t2.Unlock(m);
+ t2.Destroy(m);
+}
+
+TEST(ThreadSanitizer, SpinMutex) {
+ Mutex m(Mutex::Spin);
+ MainThread t0;
+ t0.Create(m);
+
+ ScopedThread t1, t2;
+ MemLoc l;
+ t1.Lock(m);
+ t1.Write1(l);
+ t1.Unlock(m);
+ t2.Lock(m);
+ t2.Write1(l);
+ t2.Unlock(m);
+ t2.Destroy(m);
+}
+
+TEST(ThreadSanitizer, RwMutex) {
+ Mutex m(Mutex::RW);
+ MainThread t0;
+ t0.Create(m);
+
+ ScopedThread t1, t2, t3;
+ MemLoc l;
+ t1.Lock(m);
+ t1.Write1(l);
+ t1.Unlock(m);
+ t2.Lock(m);
+ t2.Write1(l);
+ t2.Unlock(m);
+ t1.ReadLock(m);
+ t3.ReadLock(m);
+ t1.Read1(l);
+ t3.Read1(l);
+ t1.ReadUnlock(m);
+ t3.ReadUnlock(m);
+ t2.Lock(m);
+ t2.Write1(l);
+ t2.Unlock(m);
+ t2.Destroy(m);
+}
+
+TEST(ThreadSanitizer, StaticMutex) {
+ // Emulates statically initialized mutex.
+ Mutex m;
+ m.StaticInit();
+ {
+ ScopedThread t1, t2;
+ t1.Lock(m);
+ t1.Unlock(m);
+ t2.Lock(m);
+ t2.Unlock(m);
+ }
+ MainThread().Destroy(m);
+}
+
+static void *singleton_thread(void *param) {
+ atomic_uintptr_t *singleton = (atomic_uintptr_t *)param;
+ for (int i = 0; i < 4*1024*1024; i++) {
+ int *val = (int *)atomic_load(singleton, memory_order_acquire);
+ __tsan_acquire(singleton);
+ __tsan_read4(val);
+ CHECK_EQ(*val, 42);
+ }
+ return 0;
+}
+
+TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) {
+ const int kClockSize = 100;
+ const int kThreadCount = 8;
+
+ // Puff off thread's clock.
+ for (int i = 0; i < kClockSize; i++) {
+ ScopedThread t1;
+ (void)t1;
+ }
+ // Create the singleton.
+ int val = 42;
+ __tsan_write4(&val);
+ atomic_uintptr_t singleton;
+ __tsan_release(&singleton);
+ atomic_store(&singleton, (uintptr_t)&val, memory_order_release);
+ // Create reader threads.
+ pthread_t threads[kThreadCount];
+ for (int t = 0; t < kThreadCount; t++)
+ pthread_create(&threads[t], 0, singleton_thread, &singleton);
+ for (int t = 0; t < kThreadCount; t++)
+ pthread_join(threads[t], 0);
+}
+
+TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) {
+ const int kClockSize = 100;
+ const int kIters = 16*1024*1024;
+
+ // Puff off thread's clock.
+ for (int i = 0; i < kClockSize; i++) {
+ ScopedThread t1;
+ (void)t1;
+ }
+ // Create the stop flag.
+ atomic_uintptr_t flag;
+ __tsan_release(&flag);
+ atomic_store(&flag, 0, memory_order_release);
+ // Read it a lot.
+ for (int i = 0; i < kIters; i++) {
+ uptr v = atomic_load(&flag, memory_order_acquire);
+ __tsan_acquire(&flag);
+ CHECK_EQ(v, 0);
+ }
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl_tests/tsan_posix.cc b/lib/tsan/rtl_tests/tsan_posix.cc
new file mode 100644
index 000000000000..0caedd7207e6
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_posix.cc
@@ -0,0 +1,146 @@
+//===-- tsan_posix.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_interface.h"
+#include "tsan_test_util.h"
+#include "gtest/gtest.h"
+#include <pthread.h>
+
+struct thread_key {
+ pthread_key_t key;
+ pthread_mutex_t *mtx;
+ int val;
+ int *cnt;
+ thread_key(pthread_key_t key, pthread_mutex_t *mtx, int val, int *cnt)
+ : key(key)
+ , mtx(mtx)
+ , val(val)
+ , cnt(cnt) {
+ }
+};
+
+static void thread_secific_dtor(void *v) {
+ thread_key *k = (thread_key *)v;
+ EXPECT_EQ(pthread_mutex_lock(k->mtx), 0);
+ (*k->cnt)++;
+ __tsan_write4(&k->cnt);
+ EXPECT_EQ(pthread_mutex_unlock(k->mtx), 0);
+ if (k->val == 42) {
+ delete k;
+ } else if (k->val == 43 || k->val == 44) {
+ k->val--;
+ EXPECT_EQ(pthread_setspecific(k->key, k), 0);
+ } else {
+ ASSERT_TRUE(false);
+ }
+}
+
+static void *dtors_thread(void *p) {
+ thread_key *k = (thread_key *)p;
+ EXPECT_EQ(pthread_setspecific(k->key, k), 0);
+ return 0;
+}
+
+TEST(Posix, ThreadSpecificDtors) {
+ int cnt = 0;
+ pthread_key_t key;
+ EXPECT_EQ(pthread_key_create(&key, thread_secific_dtor), 0);
+ pthread_mutex_t mtx;
+ EXPECT_EQ(pthread_mutex_init(&mtx, 0), 0);
+ pthread_t th[3];
+ thread_key *k[3];
+ k[0] = new thread_key(key, &mtx, 42, &cnt);
+ k[1] = new thread_key(key, &mtx, 43, &cnt);
+ k[2] = new thread_key(key, &mtx, 44, &cnt);
+ EXPECT_EQ(pthread_create(&th[0], 0, dtors_thread, k[0]), 0);
+ EXPECT_EQ(pthread_create(&th[1], 0, dtors_thread, k[1]), 0);
+ EXPECT_EQ(pthread_join(th[0], 0), 0);
+ EXPECT_EQ(pthread_create(&th[2], 0, dtors_thread, k[2]), 0);
+ EXPECT_EQ(pthread_join(th[1], 0), 0);
+ EXPECT_EQ(pthread_join(th[2], 0), 0);
+ EXPECT_EQ(pthread_key_delete(key), 0);
+ EXPECT_EQ(6, cnt);
+}
+
+static __thread int local_var;
+
+static void *local_thread(void *p) {
+ __tsan_write1(&local_var);
+ __tsan_write1(&p);
+ if (p == 0)
+ return 0;
+ const int kThreads = 4;
+ pthread_t th[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ EXPECT_EQ(pthread_create(&th[i], 0, local_thread,
+ (void*)((long)p - 1)), 0); // NOLINT
+ for (int i = 0; i < kThreads; i++)
+ EXPECT_EQ(pthread_join(th[i], 0), 0);
+ return 0;
+}
+
+TEST(Posix, ThreadLocalAccesses) {
+ local_thread((void*)2);
+}
+
+struct CondContext {
+ pthread_mutex_t m;
+ pthread_cond_t c;
+ int data;
+};
+
+static void *cond_thread(void *p) {
+ CondContext &ctx = *static_cast<CondContext*>(p);
+
+ EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0);
+ EXPECT_EQ(ctx.data, 0);
+ ctx.data = 1;
+ EXPECT_EQ(pthread_cond_signal(&ctx.c), 0);
+ EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0);
+
+ EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0);
+ while (ctx.data != 2)
+ EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0);
+ EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0);
+
+ EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0);
+ ctx.data = 3;
+ EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0);
+ EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0);
+
+ return 0;
+}
+
+TEST(Posix, CondBasic) {
+ CondContext ctx;
+ EXPECT_EQ(pthread_mutex_init(&ctx.m, 0), 0);
+ EXPECT_EQ(pthread_cond_init(&ctx.c, 0), 0);
+ ctx.data = 0;
+ pthread_t th;
+ EXPECT_EQ(pthread_create(&th, 0, cond_thread, &ctx), 0);
+
+ EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0);
+ while (ctx.data != 1)
+ EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0);
+ ctx.data = 2;
+ EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0);
+ EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0);
+
+ EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0);
+ while (ctx.data != 3)
+ EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0);
+ EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0);
+
+ EXPECT_EQ(pthread_join(th, 0), 0);
+ EXPECT_EQ(pthread_cond_destroy(&ctx.c), 0);
+ EXPECT_EQ(pthread_mutex_destroy(&ctx.m), 0);
+}
diff --git a/lib/tsan/rtl_tests/tsan_string.cc b/lib/tsan/rtl_tests/tsan_string.cc
new file mode 100644
index 000000000000..75adc6c85ee9
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_string.cc
@@ -0,0 +1,82 @@
+//===-- tsan_string.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_test_util.h"
+#include "gtest/gtest.h"
+#include <string.h>
+
+namespace __tsan {
+
+TEST(ThreadSanitizer, Memcpy) {
+ char data0[7] = {1, 2, 3, 4, 5, 6, 7};
+ char data[7] = {42, 42, 42, 42, 42, 42, 42};
+ MainThread().Memcpy(data+1, data0+1, 5);
+ EXPECT_EQ(data[0], 42);
+ EXPECT_EQ(data[1], 2);
+ EXPECT_EQ(data[2], 3);
+ EXPECT_EQ(data[3], 4);
+ EXPECT_EQ(data[4], 5);
+ EXPECT_EQ(data[5], 6);
+ EXPECT_EQ(data[6], 42);
+ MainThread().Memset(data+1, 13, 5);
+ EXPECT_EQ(data[0], 42);
+ EXPECT_EQ(data[1], 13);
+ EXPECT_EQ(data[2], 13);
+ EXPECT_EQ(data[3], 13);
+ EXPECT_EQ(data[4], 13);
+ EXPECT_EQ(data[5], 13);
+ EXPECT_EQ(data[6], 42);
+}
+
+TEST(ThreadSanitizer, MemcpyRace1) {
+ char *data = new char[10];
+ char *data1 = new char[10];
+ char *data2 = new char[10];
+ ScopedThread t1, t2;
+ t1.Memcpy(data, data1, 10);
+ t2.Memcpy(data, data2, 10, true);
+}
+
+TEST(ThreadSanitizer, MemcpyRace2) {
+ char *data = new char[10];
+ char *data1 = new char[10];
+ char *data2 = new char[10];
+ ScopedThread t1, t2;
+ t1.Memcpy(data+5, data1, 1);
+ t2.Memcpy(data+3, data2, 4, true);
+}
+
+TEST(ThreadSanitizer, MemcpyRace3) {
+ char *data = new char[10];
+ char *data1 = new char[10];
+ char *data2 = new char[10];
+ ScopedThread t1, t2;
+ t1.Memcpy(data, data1, 10);
+ t2.Memcpy(data1, data2, 10, true);
+}
+
+TEST(ThreadSanitizer, MemcpyStack) {
+ char *data = new char[10];
+ char *data1 = new char[10];
+ ScopedThread t1, t2;
+ t1.Memcpy(data, data1, 10);
+ t2.Memcpy(data, data1, 10, true);
+}
+
+TEST(ThreadSanitizer, MemsetRace1) {
+ char *data = new char[10];
+ ScopedThread t1, t2;
+ t1.Memset(data, 1, 10);
+ t2.Memset(data, 2, 10, true);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl_tests/tsan_test.cc b/lib/tsan/rtl_tests/tsan_test.cc
new file mode 100644
index 000000000000..71641400efee
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_test.cc
@@ -0,0 +1,44 @@
+//===-- tsan_test.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_interface.h"
+#include "tsan_test_util.h"
+#include "gtest/gtest.h"
+
+static void foo() {}
+static void bar() {}
+
+TEST(ThreadSanitizer, FuncCall) {
+ ScopedThread t1, t2;
+ MemLoc l;
+ t1.Write1(l);
+ t2.Call(foo);
+ t2.Call(bar);
+ t2.Write1(l, true);
+ t2.Return();
+ t2.Return();
+}
+
+int main(int argc, char **argv) {
+ TestMutexBeforeInit(); // Mutexes must be usable before __tsan_init();
+ __tsan_init();
+ __tsan_func_entry(__builtin_return_address(0));
+ __tsan_func_entry((char*)&main + 1);
+
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ testing::InitGoogleTest(&argc, argv);
+ int res = RUN_ALL_TESTS();
+
+ __tsan_func_exit();
+ __tsan_func_exit();
+ return res;
+}
diff --git a/lib/tsan/rtl_tests/tsan_test_util.h b/lib/tsan/rtl_tests/tsan_test_util.h
new file mode 100644
index 000000000000..483a564c8475
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_test_util.h
@@ -0,0 +1,122 @@
+//===-- tsan_test_util.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Test utils.
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_TEST_UTIL_H
+#define TSAN_TEST_UTIL_H
+
+void TestMutexBeforeInit();
+
+// A location of memory on which a race may be detected.
+class MemLoc {
+ public:
+ explicit MemLoc(int offset_from_aligned = 0);
+ explicit MemLoc(void *const real_addr) : loc_(real_addr) { }
+ ~MemLoc();
+ void *loc() const { return loc_; }
+ private:
+ void *const loc_;
+ MemLoc(const MemLoc&);
+ void operator = (const MemLoc&);
+};
+
+class Mutex {
+ public:
+ enum Type { Normal, Spin, RW };
+
+ explicit Mutex(Type type = Normal);
+ ~Mutex();
+
+ void Init();
+ void StaticInit(); // Emulates static initalization (tsan invisible).
+ void Destroy();
+ void Lock();
+ bool TryLock();
+ void Unlock();
+ void ReadLock();
+ bool TryReadLock();
+ void ReadUnlock();
+
+ private:
+ // Placeholder for pthread_mutex_t, CRITICAL_SECTION or whatever.
+ void *mtx_[128];
+ bool alive_;
+ const Type type_;
+
+ Mutex(const Mutex&);
+ void operator = (const Mutex&);
+};
+
+// A thread is started in CTOR and joined in DTOR.
+class ScopedThread {
+ public:
+ explicit ScopedThread(bool detached = false, bool main = false);
+ ~ScopedThread();
+ void Detach();
+
+ void Access(void *addr, bool is_write, int size, bool expect_race);
+ void Read(const MemLoc &ml, int size, bool expect_race = false) {
+ Access(ml.loc(), false, size, expect_race);
+ }
+ void Write(const MemLoc &ml, int size, bool expect_race = false) {
+ Access(ml.loc(), true, size, expect_race);
+ }
+ void Read1(const MemLoc &ml, bool expect_race = false) {
+ Read(ml, 1, expect_race); }
+ void Read2(const MemLoc &ml, bool expect_race = false) {
+ Read(ml, 2, expect_race); }
+ void Read4(const MemLoc &ml, bool expect_race = false) {
+ Read(ml, 4, expect_race); }
+ void Read8(const MemLoc &ml, bool expect_race = false) {
+ Read(ml, 8, expect_race); }
+ void Write1(const MemLoc &ml, bool expect_race = false) {
+ Write(ml, 1, expect_race); }
+ void Write2(const MemLoc &ml, bool expect_race = false) {
+ Write(ml, 2, expect_race); }
+ void Write4(const MemLoc &ml, bool expect_race = false) {
+ Write(ml, 4, expect_race); }
+ void Write8(const MemLoc &ml, bool expect_race = false) {
+ Write(ml, 8, expect_race); }
+
+ void VptrUpdate(const MemLoc &vptr, const MemLoc &new_val,
+ bool expect_race = false);
+
+ void Call(void(*pc)());
+ void Return();
+
+ void Create(const Mutex &m);
+ void Destroy(const Mutex &m);
+ void Lock(const Mutex &m);
+ bool TryLock(const Mutex &m);
+ void Unlock(const Mutex &m);
+ void ReadLock(const Mutex &m);
+ bool TryReadLock(const Mutex &m);
+ void ReadUnlock(const Mutex &m);
+
+ void Memcpy(void *dst, const void *src, int size, bool expect_race = false);
+ void Memset(void *dst, int val, int size, bool expect_race = false);
+
+ private:
+ struct Impl;
+ Impl *impl_;
+ ScopedThread(const ScopedThread&); // Not implemented.
+ void operator = (const ScopedThread&); // Not implemented.
+};
+
+class MainThread : public ScopedThread {
+ public:
+ MainThread()
+ : ScopedThread(false, true) {
+ }
+};
+
+#endif // #ifndef TSAN_TEST_UTIL_H
diff --git a/lib/tsan/rtl_tests/tsan_test_util_linux.cc b/lib/tsan/rtl_tests/tsan_test_util_linux.cc
new file mode 100644
index 000000000000..5bc393bf6c2e
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_test_util_linux.cc
@@ -0,0 +1,465 @@
+
+//===-- tsan_test_util_linux.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Test utils, linux implementation.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "tsan_interface.h"
+#include "tsan_test_util.h"
+#include "tsan_report.h"
+
+#include "gtest/gtest.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+using namespace __tsan; // NOLINT
+
+static __thread bool expect_report;
+static __thread bool expect_report_reported;
+static __thread ReportType expect_report_type;
+
+extern "C" void *__interceptor_memcpy(void*, const void*, uptr);
+extern "C" void *__interceptor_memset(void*, int, uptr);
+
+static void *BeforeInitThread(void *param) {
+ (void)param;
+ return 0;
+}
+
+static void AtExit() {
+}
+
+void TestMutexBeforeInit() {
+ // Mutexes must be usable before __tsan_init();
+ pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_lock(&mtx);
+ pthread_mutex_unlock(&mtx);
+ pthread_mutex_destroy(&mtx);
+ pthread_t thr;
+ pthread_create(&thr, 0, BeforeInitThread, 0);
+ pthread_join(thr, 0);
+ atexit(AtExit);
+}
+
+namespace __tsan {
+bool OnReport(const ReportDesc *rep, bool suppressed) {
+ if (expect_report) {
+ if (rep->typ != expect_report_type) {
+ printf("Expected report of type %d, got type %d\n",
+ (int)expect_report_type, (int)rep->typ);
+ EXPECT_FALSE("Wrong report type");
+ return false;
+ }
+ } else {
+ EXPECT_FALSE("Unexpected report");
+ return false;
+ }
+ expect_report_reported = true;
+ return true;
+}
+}
+
+static void* allocate_addr(int size, int offset_from_aligned = 0) {
+ static uintptr_t foo;
+ static atomic_uintptr_t uniq = {(uintptr_t)&foo}; // Some real address.
+ const int kAlign = 16;
+ CHECK(offset_from_aligned < kAlign);
+ size = (size + 2 * kAlign) & ~(kAlign - 1);
+ uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed);
+ return (void*)(addr + offset_from_aligned);
+}
+
+MemLoc::MemLoc(int offset_from_aligned)
+ : loc_(allocate_addr(16, offset_from_aligned)) {
+}
+
+MemLoc::~MemLoc() {
+}
+
+Mutex::Mutex(Type type)
+ : alive_()
+ , type_(type) {
+}
+
+Mutex::~Mutex() {
+ CHECK(!alive_);
+}
+
+void Mutex::Init() {
+ CHECK(!alive_);
+ alive_ = true;
+ if (type_ == Normal)
+ CHECK_EQ(pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0);
+ else if (type_ == Spin)
+ CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0);
+ else if (type_ == RW)
+ CHECK_EQ(pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0);
+ else
+ CHECK(0);
+}
+
+void Mutex::StaticInit() {
+ CHECK(!alive_);
+ CHECK(type_ == Normal);
+ alive_ = true;
+ pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER;
+ memcpy(mtx_, &tmp, sizeof(tmp));
+}
+
+void Mutex::Destroy() {
+ CHECK(alive_);
+ alive_ = false;
+ if (type_ == Normal)
+ CHECK_EQ(pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0);
+ else if (type_ == Spin)
+ CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0);
+ else if (type_ == RW)
+ CHECK_EQ(pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0);
+}
+
+void Mutex::Lock() {
+ CHECK(alive_);
+ if (type_ == Normal)
+ CHECK_EQ(pthread_mutex_lock((pthread_mutex_t*)mtx_), 0);
+ else if (type_ == Spin)
+ CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0);
+ else if (type_ == RW)
+ CHECK_EQ(pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+bool Mutex::TryLock() {
+ CHECK(alive_);
+ if (type_ == Normal)
+ return pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0;
+ else if (type_ == Spin)
+ return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0;
+ else if (type_ == RW)
+ return pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0;
+ return false;
+}
+
+void Mutex::Unlock() {
+ CHECK(alive_);
+ if (type_ == Normal)
+ CHECK_EQ(pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0);
+ else if (type_ == Spin)
+ CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0);
+ else if (type_ == RW)
+ CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+void Mutex::ReadLock() {
+ CHECK(alive_);
+ CHECK(type_ == RW);
+ CHECK_EQ(pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+bool Mutex::TryReadLock() {
+ CHECK(alive_);
+ CHECK(type_ == RW);
+ return pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) == 0;
+}
+
+void Mutex::ReadUnlock() {
+ CHECK(alive_);
+ CHECK(type_ == RW);
+ CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+struct Event {
+ enum Type {
+ SHUTDOWN,
+ READ,
+ WRITE,
+ VPTR_UPDATE,
+ CALL,
+ RETURN,
+ MUTEX_CREATE,
+ MUTEX_DESTROY,
+ MUTEX_LOCK,
+ MUTEX_TRYLOCK,
+ MUTEX_UNLOCK,
+ MUTEX_READLOCK,
+ MUTEX_TRYREADLOCK,
+ MUTEX_READUNLOCK,
+ MEMCPY,
+ MEMSET
+ };
+ Type type;
+ void *ptr;
+ uptr arg;
+ uptr arg2;
+ bool res;
+ bool expect_report;
+ ReportType report_type;
+
+ Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0)
+ : type(type)
+ , ptr(const_cast<void*>(ptr))
+ , arg(arg)
+ , arg2(arg2)
+ , res()
+ , expect_report()
+ , report_type() {
+ }
+
+ void ExpectReport(ReportType type) {
+ expect_report = true;
+ report_type = type;
+ }
+};
+
+struct ScopedThread::Impl {
+ pthread_t thread;
+ bool main;
+ bool detached;
+ atomic_uintptr_t event; // Event*
+
+ static void *ScopedThreadCallback(void *arg);
+ void send(Event *ev);
+ void HandleEvent(Event *ev);
+};
+
+void ScopedThread::Impl::HandleEvent(Event *ev) {
+ CHECK_EQ(expect_report, false);
+ expect_report = ev->expect_report;
+ expect_report_reported = false;
+ expect_report_type = ev->report_type;
+ switch (ev->type) {
+ case Event::READ:
+ case Event::WRITE: {
+ void (*tsan_mop)(void *addr) = 0;
+ if (ev->type == Event::READ) {
+ switch (ev->arg /*size*/) {
+ case 1: tsan_mop = __tsan_read1; break;
+ case 2: tsan_mop = __tsan_read2; break;
+ case 4: tsan_mop = __tsan_read4; break;
+ case 8: tsan_mop = __tsan_read8; break;
+ case 16: tsan_mop = __tsan_read16; break;
+ }
+ } else {
+ switch (ev->arg /*size*/) {
+ case 1: tsan_mop = __tsan_write1; break;
+ case 2: tsan_mop = __tsan_write2; break;
+ case 4: tsan_mop = __tsan_write4; break;
+ case 8: tsan_mop = __tsan_write8; break;
+ case 16: tsan_mop = __tsan_write16; break;
+ }
+ }
+ CHECK_NE(tsan_mop, 0);
+ errno = ECHRNG;
+ tsan_mop(ev->ptr);
+ CHECK_EQ(errno, ECHRNG); // In no case must errno be changed.
+ break;
+ }
+ case Event::VPTR_UPDATE:
+ __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg);
+ break;
+ case Event::CALL:
+ __tsan_func_entry((void*)((uptr)ev->ptr));
+ break;
+ case Event::RETURN:
+ __tsan_func_exit();
+ break;
+ case Event::MUTEX_CREATE:
+ static_cast<Mutex*>(ev->ptr)->Init();
+ break;
+ case Event::MUTEX_DESTROY:
+ static_cast<Mutex*>(ev->ptr)->Destroy();
+ break;
+ case Event::MUTEX_LOCK:
+ static_cast<Mutex*>(ev->ptr)->Lock();
+ break;
+ case Event::MUTEX_TRYLOCK:
+ ev->res = static_cast<Mutex*>(ev->ptr)->TryLock();
+ break;
+ case Event::MUTEX_UNLOCK:
+ static_cast<Mutex*>(ev->ptr)->Unlock();
+ break;
+ case Event::MUTEX_READLOCK:
+ static_cast<Mutex*>(ev->ptr)->ReadLock();
+ break;
+ case Event::MUTEX_TRYREADLOCK:
+ ev->res = static_cast<Mutex*>(ev->ptr)->TryReadLock();
+ break;
+ case Event::MUTEX_READUNLOCK:
+ static_cast<Mutex*>(ev->ptr)->ReadUnlock();
+ break;
+ case Event::MEMCPY:
+ __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2);
+ break;
+ case Event::MEMSET:
+ __interceptor_memset(ev->ptr, ev->arg, ev->arg2);
+ break;
+ default: CHECK(0);
+ }
+ if (expect_report && !expect_report_reported) {
+ printf("Missed expected report of type %d\n", (int)ev->report_type);
+ EXPECT_FALSE("Missed expected race");
+ }
+ expect_report = false;
+}
+
+void *ScopedThread::Impl::ScopedThreadCallback(void *arg) {
+ __tsan_func_entry(__builtin_return_address(0));
+ Impl *impl = (Impl*)arg;
+ for (;;) {
+ Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire);
+ if (ev == 0) {
+ pthread_yield();
+ continue;
+ }
+ if (ev->type == Event::SHUTDOWN) {
+ atomic_store(&impl->event, 0, memory_order_release);
+ break;
+ }
+ impl->HandleEvent(ev);
+ atomic_store(&impl->event, 0, memory_order_release);
+ }
+ __tsan_func_exit();
+ return 0;
+}
+
+void ScopedThread::Impl::send(Event *e) {
+ if (main) {
+ HandleEvent(e);
+ } else {
+ CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0);
+ atomic_store(&event, (uintptr_t)e, memory_order_release);
+ while (atomic_load(&event, memory_order_acquire) != 0)
+ pthread_yield();
+ }
+}
+
+ScopedThread::ScopedThread(bool detached, bool main) {
+ impl_ = new Impl;
+ impl_->main = main;
+ impl_->detached = detached;
+ atomic_store(&impl_->event, 0, memory_order_relaxed);
+ if (!main) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, detached);
+ pthread_attr_setstacksize(&attr, 64*1024);
+ pthread_create(&impl_->thread, &attr,
+ ScopedThread::Impl::ScopedThreadCallback, impl_);
+ }
+}
+
+ScopedThread::~ScopedThread() {
+ if (!impl_->main) {
+ Event event(Event::SHUTDOWN);
+ impl_->send(&event);
+ if (!impl_->detached)
+ pthread_join(impl_->thread, 0);
+ }
+ delete impl_;
+}
+
+void ScopedThread::Detach() {
+ CHECK(!impl_->main);
+ CHECK(!impl_->detached);
+ impl_->detached = true;
+ pthread_detach(impl_->thread);
+}
+
+void ScopedThread::Access(void *addr, bool is_write,
+ int size, bool expect_race) {
+ Event event(is_write ? Event::WRITE : Event::READ, addr, size);
+ if (expect_race)
+ event.ExpectReport(ReportTypeRace);
+ impl_->send(&event);
+}
+
+void ScopedThread::VptrUpdate(const MemLoc &vptr,
+ const MemLoc &new_val,
+ bool expect_race) {
+ Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc());
+ if (expect_race)
+ event.ExpectReport(ReportTypeRace);
+ impl_->send(&event);
+}
+
+void ScopedThread::Call(void(*pc)()) {
+ Event event(Event::CALL, (void*)pc);
+ impl_->send(&event);
+}
+
+void ScopedThread::Return() {
+ Event event(Event::RETURN);
+ impl_->send(&event);
+}
+
+void ScopedThread::Create(const Mutex &m) {
+ Event event(Event::MUTEX_CREATE, &m);
+ impl_->send(&event);
+}
+
+void ScopedThread::Destroy(const Mutex &m) {
+ Event event(Event::MUTEX_DESTROY, &m);
+ impl_->send(&event);
+}
+
+void ScopedThread::Lock(const Mutex &m) {
+ Event event(Event::MUTEX_LOCK, &m);
+ impl_->send(&event);
+}
+
+bool ScopedThread::TryLock(const Mutex &m) {
+ Event event(Event::MUTEX_TRYLOCK, &m);
+ impl_->send(&event);
+ return event.res;
+}
+
+void ScopedThread::Unlock(const Mutex &m) {
+ Event event(Event::MUTEX_UNLOCK, &m);
+ impl_->send(&event);
+}
+
+void ScopedThread::ReadLock(const Mutex &m) {
+ Event event(Event::MUTEX_READLOCK, &m);
+ impl_->send(&event);
+}
+
+bool ScopedThread::TryReadLock(const Mutex &m) {
+ Event event(Event::MUTEX_TRYREADLOCK, &m);
+ impl_->send(&event);
+ return event.res;
+}
+
+void ScopedThread::ReadUnlock(const Mutex &m) {
+ Event event(Event::MUTEX_READUNLOCK, &m);
+ impl_->send(&event);
+}
+
+void ScopedThread::Memcpy(void *dst, const void *src, int size,
+ bool expect_race) {
+ Event event(Event::MEMCPY, dst, (uptr)src, size);
+ if (expect_race)
+ event.ExpectReport(ReportTypeRace);
+ impl_->send(&event);
+}
+
+void ScopedThread::Memset(void *dst, int val, int size,
+ bool expect_race) {
+ Event event(Event::MEMSET, dst, val, size);
+ if (expect_race)
+ event.ExpectReport(ReportTypeRace);
+ impl_->send(&event);
+}
diff --git a/lib/tsan/rtl_tests/tsan_thread.cc b/lib/tsan/rtl_tests/tsan_thread.cc
new file mode 100644
index 000000000000..5646415a79b8
--- /dev/null
+++ b/lib/tsan/rtl_tests/tsan_thread.cc
@@ -0,0 +1,59 @@
+//===-- tsan_thread.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_test_util.h"
+#include "gtest/gtest.h"
+
+TEST(ThreadSanitizer, ThreadSync) {
+ MainThread t0;
+ MemLoc l;
+ t0.Write1(l);
+ {
+ ScopedThread t1;
+ t1.Write1(l);
+ }
+ t0.Write1(l);
+}
+
+TEST(ThreadSanitizer, ThreadDetach1) {
+ ScopedThread t1(true);
+ MemLoc l;
+ t1.Write1(l);
+}
+
+TEST(ThreadSanitizer, ThreadDetach2) {
+ ScopedThread t1;
+ MemLoc l;
+ t1.Write1(l);
+ t1.Detach();
+}
+
+static void *thread_alot_func(void *arg) {
+ (void)arg;
+ int usleep(unsigned);
+ usleep(50);
+ return 0;
+}
+
+TEST(DISABLED_SLOW_ThreadSanitizer, ThreadALot) {
+ const int kThreads = 70000;
+ const int kAlive = 1000;
+ pthread_t threads[kAlive] = {};
+ for (int i = 0; i < kThreads; i++) {
+ if (threads[i % kAlive])
+ pthread_join(threads[i % kAlive], 0);
+ pthread_create(&threads[i % kAlive], 0, thread_alot_func, 0);
+ }
+ for (int i = 0; i < kAlive; i++) {
+ pthread_join(threads[i], 0);
+ }
+}
diff --git a/lib/tsan/unit_tests/tsan_clock_test.cc b/lib/tsan/unit_tests/tsan_clock_test.cc
new file mode 100644
index 000000000000..fe886e10bc57
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_clock_test.cc
@@ -0,0 +1,123 @@
+//===-- tsan_clock_test.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_clock.h"
+#include "tsan_rtl.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+TEST(Clock, VectorBasic) {
+ ScopedInRtl in_rtl;
+ ThreadClock clk;
+ CHECK_EQ(clk.size(), 0);
+ clk.tick(0);
+ CHECK_EQ(clk.size(), 1);
+ CHECK_EQ(clk.get(0), 1);
+ clk.tick(3);
+ CHECK_EQ(clk.size(), 4);
+ CHECK_EQ(clk.get(0), 1);
+ CHECK_EQ(clk.get(1), 0);
+ CHECK_EQ(clk.get(2), 0);
+ CHECK_EQ(clk.get(3), 1);
+ clk.tick(3);
+ CHECK_EQ(clk.get(3), 2);
+}
+
+TEST(Clock, ChunkedBasic) {
+ ScopedInRtl in_rtl;
+ ThreadClock vector;
+ SyncClock chunked;
+ CHECK_EQ(vector.size(), 0);
+ CHECK_EQ(chunked.size(), 0);
+ vector.acquire(&chunked);
+ CHECK_EQ(vector.size(), 0);
+ CHECK_EQ(chunked.size(), 0);
+ vector.release(&chunked);
+ CHECK_EQ(vector.size(), 0);
+ CHECK_EQ(chunked.size(), 0);
+ vector.acq_rel(&chunked);
+ CHECK_EQ(vector.size(), 0);
+ CHECK_EQ(chunked.size(), 0);
+}
+
+TEST(Clock, AcquireRelease) {
+ ScopedInRtl in_rtl;
+ ThreadClock vector1;
+ vector1.tick(100);
+ SyncClock chunked;
+ vector1.release(&chunked);
+ CHECK_EQ(chunked.size(), 101);
+ ThreadClock vector2;
+ vector2.acquire(&chunked);
+ CHECK_EQ(vector2.size(), 101);
+ CHECK_EQ(vector2.get(0), 0);
+ CHECK_EQ(vector2.get(1), 0);
+ CHECK_EQ(vector2.get(99), 0);
+ CHECK_EQ(vector2.get(100), 1);
+}
+
+TEST(Clock, ManyThreads) {
+ ScopedInRtl in_rtl;
+ SyncClock chunked;
+ for (int i = 0; i < 100; i++) {
+ ThreadClock vector;
+ vector.tick(i);
+ vector.release(&chunked);
+ CHECK_EQ(chunked.size(), i + 1);
+ vector.acquire(&chunked);
+ CHECK_EQ(vector.size(), i + 1);
+ }
+ ThreadClock vector;
+ vector.acquire(&chunked);
+ CHECK_EQ(vector.size(), 100);
+ for (int i = 0; i < 100; i++)
+ CHECK_EQ(vector.get(i), 1);
+}
+
+TEST(Clock, DifferentSizes) {
+ ScopedInRtl in_rtl;
+ {
+ ThreadClock vector1;
+ vector1.tick(10);
+ ThreadClock vector2;
+ vector2.tick(20);
+ {
+ SyncClock chunked;
+ vector1.release(&chunked);
+ CHECK_EQ(chunked.size(), 11);
+ vector2.release(&chunked);
+ CHECK_EQ(chunked.size(), 21);
+ }
+ {
+ SyncClock chunked;
+ vector2.release(&chunked);
+ CHECK_EQ(chunked.size(), 21);
+ vector1.release(&chunked);
+ CHECK_EQ(chunked.size(), 21);
+ }
+ {
+ SyncClock chunked;
+ vector1.release(&chunked);
+ vector2.acquire(&chunked);
+ CHECK_EQ(vector2.size(), 21);
+ }
+ {
+ SyncClock chunked;
+ vector2.release(&chunked);
+ vector1.acquire(&chunked);
+ CHECK_EQ(vector1.size(), 21);
+ }
+ }
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_flags_test.cc b/lib/tsan/unit_tests/tsan_flags_test.cc
new file mode 100644
index 000000000000..d344cb55b1ba
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_flags_test.cc
@@ -0,0 +1,38 @@
+//===-- tsan_flags_test.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_flags.h"
+#include "tsan_rtl.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+TEST(Flags, Basic) {
+ ScopedInRtl in_rtl;
+ // At least should not crash.
+ Flags f = {};
+ InitializeFlags(&f, 0);
+ InitializeFlags(&f, "");
+}
+
+TEST(Flags, DefaultValues) {
+ ScopedInRtl in_rtl;
+ Flags f = {};
+
+ f.enable_annotations = false;
+ f.exitcode = -11;
+ InitializeFlags(&f, "");
+ EXPECT_EQ(66, f.exitcode);
+ EXPECT_EQ(true, f.enable_annotations);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_mman_test.cc b/lib/tsan/unit_tests/tsan_mman_test.cc
new file mode 100644
index 000000000000..1a9a88f606fc
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_mman_test.cc
@@ -0,0 +1,109 @@
+//===-- tsan_mman_test.cc -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_mman.h"
+#include "tsan_rtl.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+TEST(Mman, Internal) {
+ ScopedInRtl in_rtl;
+ char *p = (char*)internal_alloc(MBlockScopedBuf, 10);
+ EXPECT_NE(p, (char*)0);
+ char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20);
+ EXPECT_NE(p2, (char*)0);
+ EXPECT_NE(p2, p);
+ for (int i = 0; i < 10; i++) {
+ p[i] = 42;
+ }
+ for (int i = 0; i < 20; i++) {
+ ((char*)p2)[i] = 42;
+ }
+ internal_free(p);
+ internal_free(p2);
+}
+
+TEST(Mman, User) {
+ ScopedInRtl in_rtl;
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ char *p = (char*)user_alloc(thr, pc, 10);
+ EXPECT_NE(p, (char*)0);
+ char *p2 = (char*)user_alloc(thr, pc, 20);
+ EXPECT_NE(p2, (char*)0);
+ EXPECT_NE(p2, p);
+ MBlock *b = user_mblock(thr, p);
+ EXPECT_NE(b, (MBlock*)0);
+ EXPECT_EQ(b->size, (uptr)10);
+ MBlock *b2 = user_mblock(thr, p2);
+ EXPECT_NE(b2, (MBlock*)0);
+ EXPECT_EQ(b2->size, (uptr)20);
+ for (int i = 0; i < 10; i++) {
+ p[i] = 42;
+ EXPECT_EQ(b, user_mblock(thr, p + i));
+ }
+ for (int i = 0; i < 20; i++) {
+ ((char*)p2)[i] = 42;
+ EXPECT_EQ(b2, user_mblock(thr, p2 + i));
+ }
+ user_free(thr, pc, p);
+ user_free(thr, pc, p2);
+}
+
+TEST(Mman, UserRealloc) {
+ ScopedInRtl in_rtl;
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ {
+ void *p = user_realloc(thr, pc, 0, 0);
+ // Strictly saying this is incorrect, realloc(NULL, N) is equivalent to
+ // malloc(N), thus must return non-NULL pointer.
+ EXPECT_EQ(p, (void*)0);
+ }
+ {
+ void *p = user_realloc(thr, pc, 0, 100);
+ EXPECT_NE(p, (void*)0);
+ memset(p, 0xde, 100);
+ user_free(thr, pc, p);
+ }
+ {
+ void *p = user_alloc(thr, pc, 100);
+ EXPECT_NE(p, (void*)0);
+ memset(p, 0xde, 100);
+ void *p2 = user_realloc(thr, pc, p, 0);
+ EXPECT_EQ(p2, (void*)0);
+ }
+ {
+ void *p = user_realloc(thr, pc, 0, 100);
+ EXPECT_NE(p, (void*)0);
+ memset(p, 0xde, 100);
+ void *p2 = user_realloc(thr, pc, p, 10000);
+ EXPECT_NE(p2, (void*)0);
+ for (int i = 0; i < 100; i++)
+ EXPECT_EQ(((char*)p2)[i], (char)0xde);
+ memset(p2, 0xde, 10000);
+ user_free(thr, pc, p2);
+ }
+ {
+ void *p = user_realloc(thr, pc, 0, 10000);
+ EXPECT_NE(p, (void*)0);
+ memset(p, 0xde, 10000);
+ void *p2 = user_realloc(thr, pc, p, 10);
+ EXPECT_NE(p2, (void*)0);
+ for (int i = 0; i < 10; i++)
+ EXPECT_EQ(((char*)p2)[i], (char)0xde);
+ user_free(thr, pc, p2);
+ }
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_mutex_test.cc b/lib/tsan/unit_tests/tsan_mutex_test.cc
new file mode 100644
index 000000000000..c39841ddcbb1
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_mutex_test.cc
@@ -0,0 +1,126 @@
+//===-- tsan_mutex_test.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "tsan_mutex.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+template<typename MutexType>
+class TestData {
+ public:
+ explicit TestData(MutexType *mtx)
+ : mtx_(mtx) {
+ for (int i = 0; i < kSize; i++)
+ data_[i] = 0;
+ }
+
+ void Write() {
+ Lock l(mtx_);
+ T v0 = data_[0];
+ for (int i = 0; i < kSize; i++) {
+ CHECK_EQ(data_[i], v0);
+ data_[i]++;
+ }
+ }
+
+ void Read() {
+ ReadLock l(mtx_);
+ T v0 = data_[0];
+ for (int i = 0; i < kSize; i++) {
+ CHECK_EQ(data_[i], v0);
+ }
+ }
+
+ void Backoff() {
+ volatile T data[kSize] = {};
+ for (int i = 0; i < kSize; i++) {
+ data[i]++;
+ CHECK_EQ(data[i], 1);
+ }
+ }
+
+ private:
+ typedef GenericScopedLock<MutexType> Lock;
+ static const int kSize = 64;
+ typedef u64 T;
+ MutexType *mtx_;
+ char pad_[kCacheLineSize];
+ T data_[kSize];
+};
+
+const int kThreads = 8;
+const int kWriteRate = 1024;
+#if TSAN_DEBUG
+const int kIters = 16*1024;
+#else
+const int kIters = 64*1024;
+#endif
+
+template<typename MutexType>
+static void *write_mutex_thread(void *param) {
+ TestData<MutexType> *data = (TestData<MutexType>*)param;
+ for (int i = 0; i < kIters; i++) {
+ data->Write();
+ data->Backoff();
+ }
+ return 0;
+}
+
+template<typename MutexType>
+static void *read_mutex_thread(void *param) {
+ TestData<MutexType> *data = (TestData<MutexType>*)param;
+ for (int i = 0; i < kIters; i++) {
+ if ((i % kWriteRate) == 0)
+ data->Write();
+ else
+ data->Read();
+ data->Backoff();
+ }
+ return 0;
+}
+
+TEST(Mutex, Write) {
+ Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations);
+ TestData<Mutex> data(&mtx);
+ pthread_t threads[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&threads[i], 0, write_mutex_thread<Mutex>, &data);
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(threads[i], 0);
+}
+
+TEST(Mutex, ReadWrite) {
+ Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations);
+ TestData<Mutex> data(&mtx);
+ pthread_t threads[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&threads[i], 0, read_mutex_thread<Mutex>, &data);
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(threads[i], 0);
+}
+
+TEST(Mutex, SpinWrite) {
+ SpinMutex mtx;
+ TestData<SpinMutex> data(&mtx);
+ pthread_t threads[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&threads[i], 0, write_mutex_thread<SpinMutex>, &data);
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(threads[i], 0);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_platform_test.cc b/lib/tsan/unit_tests/tsan_platform_test.cc
new file mode 100644
index 000000000000..64c4499fbeae
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_platform_test.cc
@@ -0,0 +1,88 @@
+//===-- tsan_platform_test.cc ---------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_platform.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+static void TestThreadInfo(bool main) {
+ ScopedInRtl in_rtl;
+ uptr stk_addr = 0;
+ uptr stk_size = 0;
+ uptr tls_addr = 0;
+ uptr tls_size = 0;
+ GetThreadStackAndTls(main, &stk_addr, &stk_size, &tls_addr, &tls_size);
+ // Printf("stk=%zx-%zx(%zu)\n", stk_addr, stk_addr + stk_size, stk_size);
+ // Printf("tls=%zx-%zx(%zu)\n", tls_addr, tls_addr + tls_size, tls_size);
+
+ int stack_var;
+ EXPECT_NE(stk_addr, (uptr)0);
+ EXPECT_NE(stk_size, (uptr)0);
+ EXPECT_GT((uptr)&stack_var, stk_addr);
+ EXPECT_LT((uptr)&stack_var, stk_addr + stk_size);
+
+ static __thread int thread_var;
+ EXPECT_NE(tls_addr, (uptr)0);
+ EXPECT_NE(tls_size, (uptr)0);
+ EXPECT_GT((uptr)&thread_var, tls_addr);
+ EXPECT_LT((uptr)&thread_var, tls_addr + tls_size);
+
+ // Ensure that tls and stack do not intersect.
+ uptr tls_end = tls_addr + tls_size;
+ EXPECT_TRUE(tls_addr < stk_addr || tls_addr >= stk_addr + stk_size);
+ EXPECT_TRUE(tls_end < stk_addr || tls_end >= stk_addr + stk_size);
+ EXPECT_TRUE((tls_addr < stk_addr) == (tls_end < stk_addr));
+}
+
+static void *WorkerThread(void *arg) {
+ TestThreadInfo(false);
+ return 0;
+}
+
+TEST(Platform, ThreadInfoMain) {
+ TestThreadInfo(true);
+}
+
+TEST(Platform, ThreadInfoWorker) {
+ pthread_t t;
+ pthread_create(&t, 0, WorkerThread, 0);
+ pthread_join(t, 0);
+}
+
+TEST(Platform, FileOps) {
+ const char *str1 = "qwerty";
+ uptr len1 = internal_strlen(str1);
+ const char *str2 = "zxcv";
+ uptr len2 = internal_strlen(str2);
+
+ fd_t fd = internal_open("./tsan_test.tmp", true);
+ EXPECT_NE(fd, kInvalidFd);
+ EXPECT_EQ(len1, internal_write(fd, str1, len1));
+ EXPECT_EQ(len2, internal_write(fd, str2, len2));
+ internal_close(fd);
+
+ fd = internal_open("./tsan_test.tmp", false);
+ EXPECT_NE(fd, kInvalidFd);
+ EXPECT_EQ(len1 + len2, internal_filesize(fd));
+ char buf[64] = {};
+ EXPECT_EQ(len1, internal_read(fd, buf, len1));
+ EXPECT_EQ(0, internal_memcmp(buf, str1, len1));
+ EXPECT_EQ((char)0, buf[len1 + 1]);
+ internal_memset(buf, 0, len1);
+ EXPECT_EQ(len2, internal_read(fd, buf, len2));
+ EXPECT_EQ(0, internal_memcmp(buf, str2, len2));
+ internal_close(fd);
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_printf_test.cc b/lib/tsan/unit_tests/tsan_printf_test.cc
new file mode 100644
index 000000000000..0dfd1d2dfe42
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_printf_test.cc
@@ -0,0 +1,106 @@
+//===-- tsan_printf_test.cc -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_rtl.h"
+#include "gtest/gtest.h"
+
+#include <string.h>
+#include <limits.h>
+
+namespace __tsan {
+
+TEST(Printf, Basic) {
+ char buf[1024];
+ uptr len = internal_snprintf(buf, sizeof(buf),
+ "a%db%zdc%ue%zuf%xh%zxq%pe%sr",
+ (int)-1, (long)-2, // NOLINT
+ (unsigned)-4, (unsigned long)5, // NOLINT
+ (unsigned)10, (unsigned long)11, // NOLINT
+ (void*)0x123, "_string_");
+ EXPECT_EQ(len, strlen(buf));
+ EXPECT_EQ(0, strcmp(buf, "a-1b-2c4294967292e5fahbq"
+ "0x000000000123e_string_r"));
+}
+
+TEST(Printf, OverflowStr) {
+ char buf[] = "123456789";
+ uptr len = internal_snprintf(buf, 4, "%s", "abcdef"); // NOLINT
+ EXPECT_EQ(len, (uptr)6);
+ EXPECT_EQ(0, strcmp(buf, "abc"));
+ EXPECT_EQ(buf[3], 0);
+ EXPECT_EQ(buf[4], '5');
+ EXPECT_EQ(buf[5], '6');
+ EXPECT_EQ(buf[6], '7');
+ EXPECT_EQ(buf[7], '8');
+ EXPECT_EQ(buf[8], '9');
+ EXPECT_EQ(buf[9], 0);
+}
+
+TEST(Printf, OverflowInt) {
+ char buf[] = "123456789";
+ internal_snprintf(buf, 4, "%d", -123456789); // NOLINT
+ EXPECT_EQ(0, strcmp(buf, "-12"));
+ EXPECT_EQ(buf[3], 0);
+ EXPECT_EQ(buf[4], '5');
+ EXPECT_EQ(buf[5], '6');
+ EXPECT_EQ(buf[6], '7');
+ EXPECT_EQ(buf[7], '8');
+ EXPECT_EQ(buf[8], '9');
+ EXPECT_EQ(buf[9], 0);
+}
+
+TEST(Printf, OverflowUint) {
+ char buf[] = "123456789";
+ internal_snprintf(buf, 4, "a%zx", (unsigned long)0x123456789); // NOLINT
+ EXPECT_EQ(0, strcmp(buf, "a12"));
+ EXPECT_EQ(buf[3], 0);
+ EXPECT_EQ(buf[4], '5');
+ EXPECT_EQ(buf[5], '6');
+ EXPECT_EQ(buf[6], '7');
+ EXPECT_EQ(buf[7], '8');
+ EXPECT_EQ(buf[8], '9');
+ EXPECT_EQ(buf[9], 0);
+}
+
+TEST(Printf, OverflowPtr) {
+ char buf[] = "123456789";
+ internal_snprintf(buf, 4, "%p", (void*)0x123456789); // NOLINT
+ EXPECT_EQ(0, strcmp(buf, "0x0"));
+ EXPECT_EQ(buf[3], 0);
+ EXPECT_EQ(buf[4], '5');
+ EXPECT_EQ(buf[5], '6');
+ EXPECT_EQ(buf[6], '7');
+ EXPECT_EQ(buf[7], '8');
+ EXPECT_EQ(buf[8], '9');
+ EXPECT_EQ(buf[9], 0);
+}
+
+template<typename T>
+static void TestMinMax(const char *fmt, T min, T max) {
+ char buf[1024];
+ uptr len = internal_snprintf(buf, sizeof(buf), fmt, min, max);
+ char buf2[1024];
+ snprintf(buf2, sizeof(buf2), fmt, min, max);
+ EXPECT_EQ(len, strlen(buf));
+ EXPECT_EQ(0, strcmp(buf, buf2));
+}
+
+TEST(Printf, MinMax) {
+ TestMinMax<int>("%d-%d", INT_MIN, INT_MAX); // NOLINT
+ TestMinMax<long>("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT
+ TestMinMax<unsigned>("%u-%u", 0, UINT_MAX); // NOLINT
+ TestMinMax<unsigned long>("%zu-%zu", 0, ULONG_MAX); // NOLINT
+ TestMinMax<unsigned>("%x-%x", 0, UINT_MAX); // NOLINT
+ TestMinMax<unsigned long>("%zx-%zx", 0, ULONG_MAX); // NOLINT
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_shadow_test.cc b/lib/tsan/unit_tests/tsan_shadow_test.cc
new file mode 100644
index 000000000000..41f9121be0fa
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_shadow_test.cc
@@ -0,0 +1,47 @@
+//===-- tsan_shadow_test.cc -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_platform.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+TEST(Shadow, Mapping) {
+ static int global;
+ int stack;
+ void *heap = malloc(0);
+ free(heap);
+
+ CHECK(IsAppMem((uptr)&global));
+ CHECK(IsAppMem((uptr)&stack));
+ CHECK(IsAppMem((uptr)heap));
+
+ CHECK(IsShadowMem(MemToShadow((uptr)&global)));
+ CHECK(IsShadowMem(MemToShadow((uptr)&stack)));
+ CHECK(IsShadowMem(MemToShadow((uptr)heap)));
+}
+
+TEST(Shadow, Celling) {
+ u64 aligned_data[4];
+ char *data = (char*)aligned_data;
+ CHECK_EQ((uptr)data % kShadowSize, 0);
+ uptr s0 = MemToShadow((uptr)&data[0]);
+ CHECK_EQ(s0 % kShadowSize, 0);
+ for (unsigned i = 1; i < kShadowCell; i++)
+ CHECK_EQ(s0, MemToShadow((uptr)&data[i]));
+ for (unsigned i = kShadowCell; i < 2*kShadowCell; i++)
+ CHECK_EQ(s0 + kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i]));
+ for (unsigned i = 2*kShadowCell; i < 3*kShadowCell; i++)
+ CHECK_EQ(s0 + 2*kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i]));
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_suppressions_test.cc b/lib/tsan/unit_tests/tsan_suppressions_test.cc
new file mode 100644
index 000000000000..e1e0c12c004c
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_suppressions_test.cc
@@ -0,0 +1,128 @@
+//===-- tsan_suppressions_test.cc -----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_suppressions.h"
+#include "tsan_rtl.h"
+#include "gtest/gtest.h"
+
+#include <string.h>
+
+namespace __tsan {
+
+TEST(Suppressions, Parse) {
+ ScopedInRtl in_rtl;
+ Suppression *supp0 = SuppressionParse(
+ "race:foo\n"
+ " race:bar\n" // NOLINT
+ "race:baz \n" // NOLINT
+ "# a comment\n"
+ "race:quz\n"
+ ); // NOLINT
+ Suppression *supp = supp0;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "quz"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "baz"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "bar"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "foo"));
+ supp = supp->next;
+ EXPECT_EQ((Suppression*)0, supp);
+}
+
+TEST(Suppressions, Parse2) {
+ ScopedInRtl in_rtl;
+ Suppression *supp0 = SuppressionParse(
+ " # first line comment\n" // NOLINT
+ " race:bar \n" // NOLINT
+ "race:baz* *baz\n"
+ "# a comment\n"
+ "# last line comment\n"
+ ); // NOLINT
+ Suppression *supp = supp0;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "baz* *baz"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "bar"));
+ supp = supp->next;
+ EXPECT_EQ((Suppression*)0, supp);
+}
+
+TEST(Suppressions, Parse3) {
+ ScopedInRtl in_rtl;
+ Suppression *supp0 = SuppressionParse(
+ "# last suppression w/o line-feed\n"
+ "race:foo\n"
+ "race:bar"
+ ); // NOLINT
+ Suppression *supp = supp0;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "bar"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "foo"));
+ supp = supp->next;
+ EXPECT_EQ((Suppression*)0, supp);
+}
+
+TEST(Suppressions, ParseType) {
+ ScopedInRtl in_rtl;
+ Suppression *supp0 = SuppressionParse(
+ "race:foo\n"
+ "thread:bar\n"
+ "mutex:baz\n"
+ "signal:quz\n"
+ ); // NOLINT
+ Suppression *supp = supp0;
+ EXPECT_EQ(supp->type, SuppressionSignal);
+ EXPECT_EQ(0, strcmp(supp->templ, "quz"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionMutex);
+ EXPECT_EQ(0, strcmp(supp->templ, "baz"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionThread);
+ EXPECT_EQ(0, strcmp(supp->templ, "bar"));
+ supp = supp->next;
+ EXPECT_EQ(supp->type, SuppressionRace);
+ EXPECT_EQ(0, strcmp(supp->templ, "foo"));
+ supp = supp->next;
+ EXPECT_EQ((Suppression*)0, supp);
+}
+
+static bool MyMatch(const char *templ, const char *func) {
+ char tmp[1024];
+ strcpy(tmp, templ); // NOLINT
+ return SuppressionMatch(tmp, func);
+}
+
+TEST(Suppressions, Match) {
+ EXPECT_TRUE(MyMatch("foobar", "foobar"));
+ EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix"));
+ EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix"));
+ EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar"));
+ EXPECT_TRUE(MyMatch("foo*bar", "foobar"));
+ EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz"));
+ EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz"));
+
+ EXPECT_FALSE(MyMatch("foo", "baz"));
+ EXPECT_FALSE(MyMatch("foobarbaz", "foobar"));
+ EXPECT_FALSE(MyMatch("foobarbaz", "barbaz"));
+ EXPECT_FALSE(MyMatch("foo*bar", "foobaz"));
+ EXPECT_FALSE(MyMatch("foo*bar", "foo_baz"));
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_sync_test.cc b/lib/tsan/unit_tests/tsan_sync_test.cc
new file mode 100644
index 000000000000..b7605a57a331
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_sync_test.cc
@@ -0,0 +1,65 @@
+//===-- tsan_sync_test.cc -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_sync.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "gtest/gtest.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <map>
+
+namespace __tsan {
+
+TEST(Sync, Table) {
+ const uintptr_t kIters = 512*1024;
+ const uintptr_t kRange = 10000;
+
+ ScopedInRtl in_rtl;
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+
+ SyncTab tab;
+ SyncVar *golden[kRange] = {};
+ unsigned seed = 0;
+ for (uintptr_t i = 0; i < kIters; i++) {
+ uintptr_t addr = rand_r(&seed) % (kRange - 1) + 1;
+ if (rand_r(&seed) % 2) {
+ // Get or add.
+ SyncVar *v = tab.GetAndLock(thr, pc, addr, true);
+ EXPECT_TRUE(golden[addr] == 0 || golden[addr] == v);
+ EXPECT_EQ(v->addr, addr);
+ golden[addr] = v;
+ v->mtx.Unlock();
+ } else {
+ // Remove.
+ SyncVar *v = tab.GetAndRemove(thr, pc, addr);
+ EXPECT_EQ(golden[addr], v);
+ if (v) {
+ EXPECT_EQ(v->addr, addr);
+ golden[addr] = 0;
+ DestroyAndFree(v);
+ }
+ }
+ }
+ for (uintptr_t addr = 0; addr < kRange; addr++) {
+ if (golden[addr] == 0)
+ continue;
+ SyncVar *v = tab.GetAndRemove(thr, pc, addr);
+ EXPECT_EQ(v, golden[addr]);
+ EXPECT_EQ(v->addr, addr);
+ DestroyAndFree(v);
+ }
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_vector_test.cc b/lib/tsan/unit_tests/tsan_vector_test.cc
new file mode 100644
index 000000000000..cfef6e528ff2
--- /dev/null
+++ b/lib/tsan/unit_tests/tsan_vector_test.cc
@@ -0,0 +1,45 @@
+//===-- tsan_vector_test.cc -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_vector.h"
+#include "tsan_rtl.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+TEST(Vector, Basic) {
+ ScopedInRtl in_rtl;
+ Vector<int> v(MBlockScopedBuf);
+ EXPECT_EQ(v.Size(), (uptr)0);
+ v.PushBack(42);
+ EXPECT_EQ(v.Size(), (uptr)1);
+ EXPECT_EQ(v[0], 42);
+ v.PushBack(43);
+ EXPECT_EQ(v.Size(), (uptr)2);
+ EXPECT_EQ(v[0], 42);
+ EXPECT_EQ(v[1], 43);
+}
+
+TEST(Vector, Stride) {
+ ScopedInRtl in_rtl;
+ Vector<int> v(MBlockScopedBuf);
+ for (int i = 0; i < 1000; i++) {
+ v.PushBack(i);
+ EXPECT_EQ(v.Size(), (uptr)(i + 1));
+ EXPECT_EQ(v[i], i);
+ }
+ for (int i = 0; i < 1000; i++) {
+ EXPECT_EQ(v[i], i);
+ }
+}
+
+} // namespace __tsan