Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1 @@
BasedOnStyle: Google

View File

@@ -0,0 +1,224 @@
# Build for the ThreadSanitizer runtime support library.
include_directories(..)
set(TSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
# SANITIZER_COMMON_CFLAGS contains -fPIC, but it's performance-critical for
# TSan runtime to be built with -fPIE to reduce the number of register spills.
append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE TSAN_CFLAGS)
append_rtti_flag(OFF TSAN_CFLAGS)
if(COMPILER_RT_TSAN_DEBUG_OUTPUT)
# Add extra debug information to TSan runtime. This configuration is rarely
# used, but we need to support it so that debug output will not bitrot.
list(APPEND TSAN_CFLAGS -DTSAN_COLLECT_STATS=1
-DTSAN_DEBUG_OUTPUT=2)
endif()
set(TSAN_RTL_CFLAGS ${TSAN_CFLAGS})
append_list_if(COMPILER_RT_HAS_MSSE3_FLAG -msse3 TSAN_RTL_CFLAGS)
append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=530
TSAN_RTL_CFLAGS)
append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors
TSAN_RTL_CFLAGS)
set(TSAN_SOURCES
rtl/tsan_clock.cc
rtl/tsan_debugging.cc
rtl/tsan_external.cc
rtl/tsan_fd.cc
rtl/tsan_flags.cc
rtl/tsan_ignoreset.cc
rtl/tsan_interceptors.cc
rtl/tsan_interface.cc
rtl/tsan_interface_ann.cc
rtl/tsan_interface_atomic.cc
rtl/tsan_interface_java.cc
rtl/tsan_malloc_mac.cc
rtl/tsan_md5.cc
rtl/tsan_mman.cc
rtl/tsan_mutex.cc
rtl/tsan_mutexset.cc
rtl/tsan_preinit.cc
rtl/tsan_report.cc
rtl/tsan_rtl.cc
rtl/tsan_rtl_mutex.cc
rtl/tsan_rtl_proc.cc
rtl/tsan_rtl_report.cc
rtl/tsan_rtl_thread.cc
rtl/tsan_stack_trace.cc
rtl/tsan_stat.cc
rtl/tsan_suppressions.cc
rtl/tsan_symbolize.cc
rtl/tsan_sync.cc)
set(TSAN_CXX_SOURCES
rtl/tsan_new_delete.cc)
if(APPLE)
list(APPEND TSAN_SOURCES
rtl/tsan_interceptors_mac.cc
rtl/tsan_libdispatch_mac.cc
rtl/tsan_platform_mac.cc
rtl/tsan_platform_posix.cc)
elseif(UNIX)
# Assume Linux
list(APPEND TSAN_SOURCES
rtl/tsan_platform_linux.cc
rtl/tsan_platform_posix.cc)
endif()
set(TSAN_HEADERS
rtl/tsan_clock.h
rtl/tsan_defs.h
rtl/tsan_dense_alloc.h
rtl/tsan_fd.h
rtl/tsan_flags.h
rtl/tsan_flags.inc
rtl/tsan_ignoreset.h
rtl/tsan_interceptors.h
rtl/tsan_interface_ann.h
rtl/tsan_interface.h
rtl/tsan_interface_inl.h
rtl/tsan_interface_java.h
rtl/tsan_mman.h
rtl/tsan_mutex.h
rtl/tsan_mutexset.h
rtl/tsan_platform.h
rtl/tsan_report.h
rtl/tsan_rtl.h
rtl/tsan_stack_trace.h
rtl/tsan_stat.h
rtl/tsan_suppressions.h
rtl/tsan_symbolize.h
rtl/tsan_sync.h
rtl/tsan_trace.h
rtl/tsan_update_shadow_word_inl.h)
set(TSAN_RUNTIME_LIBRARIES)
add_compiler_rt_component(tsan)
if(APPLE)
add_asm_sources(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S rtl/tsan_rtl_aarch64.S)
set(TSAN_LINK_LIBS ${SANITIZER_COMMON_LINK_LIBS})
add_weak_symbols("ubsan" WEAK_SYMBOL_LINK_FLAGS)
add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS)
add_compiler_rt_runtime(clang_rt.tsan
SHARED
OS ${TSAN_SUPPORTED_OS}
ARCHS ${TSAN_SUPPORTED_ARCH}
SOURCES ${TSAN_SOURCES} ${TSAN_CXX_SOURCES} ${TSAN_ASM_SOURCES}
OBJECT_LIBS RTInterception
RTSanitizerCommon
RTSanitizerCommonLibc
RTUbsan
CFLAGS ${TSAN_RTL_CFLAGS}
LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS}
LINK_LIBS ${TSAN_LINK_LIBS}
PARENT_TARGET tsan)
add_compiler_rt_object_libraries(RTTsan_dynamic
OS ${TSAN_SUPPORTED_OS}
ARCHS ${TSAN_SUPPORTED_ARCH}
SOURCES ${TSAN_SOURCES} ${TSAN_CXX_SOURCES} ${TSAN_ASM_SOURCES}
CFLAGS ${TSAN_RTL_CFLAGS})
# Build and check Go runtime.
set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh)
add_custom_target(GotsanRuntimeCheck
COMMAND env "CC=${CMAKE_C_COMPILER} ${OSX_SYSROOT_FLAG}"
IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT}
DEPENDS tsan ${BUILDGO_SCRIPT}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go
COMMENT "Checking TSan Go runtime..."
VERBATIM)
else()
foreach(arch ${TSAN_SUPPORTED_ARCH})
if(arch STREQUAL "x86_64")
add_asm_sources(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S)
# Sanity check for Go runtime.
set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh)
add_custom_target(GotsanRuntimeCheck
COMMAND env "CC=${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}"
IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT}
DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go
COMMENT "Checking TSan Go runtime..."
VERBATIM)
elseif(arch STREQUAL "aarch64")
add_asm_sources(TSAN_ASM_SOURCES rtl/tsan_rtl_aarch64.S)
elseif(arch MATCHES "powerpc64|powerpc64le")
add_asm_sources(TSAN_ASM_SOURCES rtl/tsan_rtl_ppc64.S)
elseif(arch MATCHES "mips64|mips64le")
add_asm_sources(TSAN_ASM_SOURCES rtl/tsan_rtl_mips64.S)
else()
set(TSAN_ASM_SOURCES)
endif()
add_compiler_rt_runtime(clang_rt.tsan
STATIC
ARCHS ${arch}
SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES}
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
$<TARGET_OBJECTS:RTUbsan.${arch}>
CFLAGS ${TSAN_RTL_CFLAGS}
PARENT_TARGET tsan)
add_compiler_rt_runtime(clang_rt.tsan_cxx
STATIC
ARCHS ${arch}
SOURCES ${TSAN_CXX_SOURCES}
$<TARGET_OBJECTS:RTUbsan_cxx.${arch}>
CFLAGS ${TSAN_RTL_CFLAGS}
PARENT_TARGET tsan)
list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}
clang_rt.tsan_cxx-${arch})
add_sanitizer_rt_symbols(clang_rt.tsan
ARCHS ${arch}
EXTRA rtl/tsan.syms.extra)
add_sanitizer_rt_symbols(clang_rt.tsan_cxx
ARCHS ${arch}
EXTRA rtl/tsan.syms.extra)
add_dependencies(tsan clang_rt.tsan-${arch}
clang_rt.tsan_cxx-${arch}
clang_rt.tsan-${arch}-symbols
clang_rt.tsan_cxx-${arch}-symbols)
endforeach()
endif()
# Make sure that non-platform-specific files don't include any system headers.
# FreeBSD does not install a number of Clang-provided headers for the compiler
# in the base system due to incompatibilities between FreeBSD's and Clang's
# versions. As a workaround do not use --sysroot=. on FreeBSD until this is
# addressed.
if(COMPILER_RT_HAS_SYSROOT_FLAG AND NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
file(GLOB _tsan_generic_sources rtl/tsan*)
file(GLOB _tsan_platform_sources rtl/tsan*posix* rtl/tsan*mac*
rtl/tsan*linux*)
list(REMOVE_ITEM _tsan_generic_sources ${_tsan_platform_sources})
set_source_files_properties(${_tsan_generic_sources}
PROPERTIES COMPILE_FLAGS "--sysroot=.")
endif()
# Build libcxx instrumented with TSan.
if(COMPILER_RT_LIBCXX_PATH AND
COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang" AND
NOT ANDROID)
set(libcxx_tsan_deps)
foreach(arch ${TSAN_SUPPORTED_ARCH})
get_target_flags_for_arch(${arch} TARGET_CFLAGS)
set(LIBCXX_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_tsan_${arch})
add_custom_libcxx(libcxx_tsan_${arch} ${LIBCXX_PREFIX}
DEPS ${TSAN_RUNTIME_LIBRARIES}
CFLAGS ${TARGET_CFLAGS} -fsanitize=thread)
list(APPEND libcxx_tsan_deps libcxx_tsan_${arch})
endforeach()
add_custom_target(libcxx_tsan DEPENDS ${libcxx_tsan_deps})
endif()
if(COMPILER_RT_INCLUDE_TESTS)
add_subdirectory(tests)
endif()

View File

@@ -0,0 +1,54 @@
#!/bin/bash
#
# Script that prints information about generated code in TSan runtime.
set -e
set -u
if [[ "$#" != 1 ]]; then
echo "Usage: $0 /path/to/binary/built/with/tsan"
exit 1
fi
get_asm() {
grep __tsan_$1.: -A 10000 ${OBJDUMP_CONTENTS} | \
awk "/[^:]$/ {print;} />:/ {c++; if (c == 2) {exit}}"
}
list="write1 \
write2 \
write4 \
write8 \
read1 \
read2 \
read4 \
read8 \
func_entry \
func_exit"
BIN=$1
OUTPUT_DIR=$(mktemp -t -d analyze_libtsan_out.XXXXXXXX)
OBJDUMP_CONTENTS=${OUTPUT_DIR}/libtsan_objdump
NM_CONTENTS=${OUTPUT_DIR}/libtsan_nm
objdump -d $BIN > ${OBJDUMP_CONTENTS}
nm -S $BIN | grep "__tsan_" > ${NM_CONTENTS}
for f in $list; do
file=${OUTPUT_DIR}/asm_$f.s
get_asm $f > $file
tot=$(wc -l < $file)
size=$(grep __tsan_$f$ ${NM_CONTENTS} | 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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,56 @@
#!/bin/bash
#
# Script that checks that critical functions in TSan runtime have correct number
# of push/pop/rsp instructions to verify that runtime is efficient enough.
#
# This test can fail when backend code generation changes the output for various
# tsan interceptors. When such a change happens, you can ensure that the
# performance has not regressed by running the following benchmarks before and
# after the breaking change to verify that the values in this file are safe to
# update:
# ./projects/compiler-rt/lib/tsan/tests/rtl/TsanRtlTest
# --gtest_also_run_disabled_tests --gtest_filter=DISABLED_BENCH.Mop*
set -u
if [[ "$#" != 1 ]]; then
echo "Usage: $0 /path/to/binary/built/with/tsan"
exit 1
fi
SCRIPTDIR=$(dirname $0)
RES=$(${SCRIPTDIR}/analyze_libtsan.sh $1)
PrintRes() {
printf "%s\n" "$RES"
}
PrintRes
check() {
res=$(PrintRes | egrep "$1 .* $2 $3; ")
if [ "$res" == "" ]; then
echo FAILED $1 must contain $2 $3
exit 1
fi
}
for f in write1 write2 write4 write8; do
check $f rsp 1
check $f push 2
check $f pop 12
done
for f in read1 read2 read4 read8; do
check $f rsp 1
check $f push 3
check $f pop 18
done
for f in func_entry func_exit; do
check $f rsp 0
check $f push 0
check $f pop 0
check $f call 1 # TraceSwitch()
done
echo LGTM

View File

@@ -0,0 +1,18 @@
#!/bin/bash
set -u
set -e
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ -d "$ROOT/llvm-build" ]; then
cd $ROOT/llvm-build
else
mkdir -p $ROOT/llvm-build
cd $ROOT/llvm-build
CC=clang CXX=clang++ cmake -G Ninja -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../..
fi
ninja
ninja check-sanitizer
ninja check-tsan
ninja check-asan
ninja check-msan
ninja check-lsan

View File

@@ -0,0 +1,49 @@
# Build for the experimental deadlock detector runtime library.
include_directories(../..)
set(DD_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_rtti_flag(OFF DD_CFLAGS)
set(DD_SOURCES
dd_rtl.cc
dd_interceptors.cc
)
set(DD_LINKLIBS ${SANITIZER_CXX_ABI_LIBRARY} ${SANITIZER_COMMON_LINK_LIBS})
append_list_if(COMPILER_RT_HAS_LIBDL dl DD_LINKLIBS)
append_list_if(COMPILER_RT_HAS_LIBRT rt DD_LINKLIBS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread DD_LINKLIBS)
add_custom_target(dd)
# Deadlock detector is currently supported on 64-bit Linux only.
if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE AND NOT ANDROID)
set(arch "x86_64")
add_compiler_rt_runtime(clang_rt.dd
STATIC
ARCHS ${arch}
SOURCES ${DD_SOURCES}
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
CFLAGS ${DD_CFLAGS}
PARENT_TARGET dd)
add_compiler_rt_object_libraries(RTDD
ARCHS ${arch}
SOURCES ${DD_SOURCES} CFLAGS ${DD_CFLAGS})
add_compiler_rt_runtime(clang_rt.dyndd
SHARED
ARCHS ${arch}
SOURCES $<TARGET_OBJECTS:RTDD.${arch}>
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS}
LINK_LIBS ${DD_LINKLIBS}
PARENT_TARGET dd)
endif()
add_dependencies(compiler-rt dd)

View File

@@ -0,0 +1,329 @@
//===-- dd_interceptors.cc ------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "dd_rtl.h"
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include <pthread.h>
#include <stdlib.h>
using namespace __dsan;
__attribute__((tls_model("initial-exec")))
static __thread Thread *thr;
__attribute__((tls_model("initial-exec")))
static __thread volatile int initing;
static bool inited;
static uptr g_data_start;
static uptr g_data_end;
static bool InitThread() {
if (initing)
return false;
if (thr != 0)
return true;
initing = true;
if (!inited) {
inited = true;
Initialize();
}
thr = (Thread*)InternalAlloc(sizeof(*thr));
internal_memset(thr, 0, sizeof(*thr));
ThreadInit(thr);
initing = false;
return true;
}
INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) {
InitThread();
MutexDestroy(thr, (uptr)m);
return REAL(pthread_mutex_destroy)(m);
}
INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) {
InitThread();
MutexBeforeLock(thr, (uptr)m, true);
int res = REAL(pthread_mutex_lock)(m);
MutexAfterLock(thr, (uptr)m, true, false);
return res;
}
INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) {
InitThread();
int res = REAL(pthread_mutex_trylock)(m);
if (res == 0)
MutexAfterLock(thr, (uptr)m, true, true);
return res;
}
INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) {
InitThread();
MutexBeforeUnlock(thr, (uptr)m, true);
return REAL(pthread_mutex_unlock)(m);
}
INTERCEPTOR(int, pthread_spin_destroy, pthread_spinlock_t *m) {
InitThread();
int res = REAL(pthread_spin_destroy)(m);
MutexDestroy(thr, (uptr)m);
return res;
}
INTERCEPTOR(int, pthread_spin_lock, pthread_spinlock_t *m) {
InitThread();
MutexBeforeLock(thr, (uptr)m, true);
int res = REAL(pthread_spin_lock)(m);
MutexAfterLock(thr, (uptr)m, true, false);
return res;
}
INTERCEPTOR(int, pthread_spin_trylock, pthread_spinlock_t *m) {
InitThread();
int res = REAL(pthread_spin_trylock)(m);
if (res == 0)
MutexAfterLock(thr, (uptr)m, true, true);
return res;
}
INTERCEPTOR(int, pthread_spin_unlock, pthread_spinlock_t *m) {
InitThread();
MutexBeforeUnlock(thr, (uptr)m, true);
return REAL(pthread_spin_unlock)(m);
}
INTERCEPTOR(int, pthread_rwlock_destroy, pthread_rwlock_t *m) {
InitThread();
MutexDestroy(thr, (uptr)m);
return REAL(pthread_rwlock_destroy)(m);
}
INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *m) {
InitThread();
MutexBeforeLock(thr, (uptr)m, false);
int res = REAL(pthread_rwlock_rdlock)(m);
MutexAfterLock(thr, (uptr)m, false, false);
return res;
}
INTERCEPTOR(int, pthread_rwlock_tryrdlock, pthread_rwlock_t *m) {
InitThread();
int res = REAL(pthread_rwlock_tryrdlock)(m);
if (res == 0)
MutexAfterLock(thr, (uptr)m, false, true);
return res;
}
INTERCEPTOR(int, pthread_rwlock_timedrdlock, pthread_rwlock_t *m,
const timespec *abstime) {
InitThread();
int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
if (res == 0)
MutexAfterLock(thr, (uptr)m, false, true);
return res;
}
INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *m) {
InitThread();
MutexBeforeLock(thr, (uptr)m, true);
int res = REAL(pthread_rwlock_wrlock)(m);
MutexAfterLock(thr, (uptr)m, true, false);
return res;
}
INTERCEPTOR(int, pthread_rwlock_trywrlock, pthread_rwlock_t *m) {
InitThread();
int res = REAL(pthread_rwlock_trywrlock)(m);
if (res == 0)
MutexAfterLock(thr, (uptr)m, true, true);
return res;
}
INTERCEPTOR(int, pthread_rwlock_timedwrlock, pthread_rwlock_t *m,
const timespec *abstime) {
InitThread();
int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
if (res == 0)
MutexAfterLock(thr, (uptr)m, true, true);
return res;
}
INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *m) {
InitThread();
MutexBeforeUnlock(thr, (uptr)m, true); // note: not necessary write unlock
return REAL(pthread_rwlock_unlock)(m);
}
static pthread_cond_t *init_cond(pthread_cond_t *c, bool force = false) {
atomic_uintptr_t *p = (atomic_uintptr_t*)c;
uptr cond = atomic_load(p, memory_order_acquire);
if (!force && cond != 0)
return (pthread_cond_t*)cond;
void *newcond = malloc(sizeof(pthread_cond_t));
internal_memset(newcond, 0, sizeof(pthread_cond_t));
if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
memory_order_acq_rel))
return (pthread_cond_t*)newcond;
free(newcond);
return (pthread_cond_t*)cond;
}
INTERCEPTOR(int, pthread_cond_init, pthread_cond_t *c,
const pthread_condattr_t *a) {
InitThread();
pthread_cond_t *cond = init_cond(c, true);
return REAL(pthread_cond_init)(cond, a);
}
INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *c, pthread_mutex_t *m) {
InitThread();
pthread_cond_t *cond = init_cond(c);
MutexBeforeUnlock(thr, (uptr)m, true);
MutexBeforeLock(thr, (uptr)m, true);
int res = REAL(pthread_cond_wait)(cond, m);
MutexAfterLock(thr, (uptr)m, true, false);
return res;
}
INTERCEPTOR(int, pthread_cond_timedwait, pthread_cond_t *c, pthread_mutex_t *m,
const timespec *abstime) {
InitThread();
pthread_cond_t *cond = init_cond(c);
MutexBeforeUnlock(thr, (uptr)m, true);
MutexBeforeLock(thr, (uptr)m, true);
int res = REAL(pthread_cond_timedwait)(cond, m, abstime);
MutexAfterLock(thr, (uptr)m, true, false);
return res;
}
INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *c) {
InitThread();
pthread_cond_t *cond = init_cond(c);
return REAL(pthread_cond_signal)(cond);
}
INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *c) {
InitThread();
pthread_cond_t *cond = init_cond(c);
return REAL(pthread_cond_broadcast)(cond);
}
INTERCEPTOR(int, pthread_cond_destroy, pthread_cond_t *c) {
InitThread();
pthread_cond_t *cond = init_cond(c);
int res = REAL(pthread_cond_destroy)(cond);
free(cond);
atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
return res;
}
// for symbolizer
INTERCEPTOR(char*, realpath, const char *path, char *resolved_path) {
InitThread();
return REAL(realpath)(path, resolved_path);
}
INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
InitThread();
return REAL(read)(fd, ptr, count);
}
INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
InitThread();
return REAL(pread)(fd, ptr, count, offset);
}
extern "C" {
void __dsan_before_mutex_lock(uptr m, int writelock) {
if (!InitThread())
return;
MutexBeforeLock(thr, m, writelock);
}
void __dsan_after_mutex_lock(uptr m, int writelock, int trylock) {
if (!InitThread())
return;
MutexAfterLock(thr, m, writelock, trylock);
}
void __dsan_before_mutex_unlock(uptr m, int writelock) {
if (!InitThread())
return;
MutexBeforeUnlock(thr, m, writelock);
}
void __dsan_mutex_destroy(uptr m) {
if (!InitThread())
return;
// if (m >= g_data_start && m < g_data_end)
// return;
MutexDestroy(thr, m);
}
} // extern "C"
namespace __dsan {
static void InitDataSeg() {
MemoryMappingLayout proc_maps(true);
char name[128];
MemoryMappedSegment segment(name, ARRAY_SIZE(name));
bool prev_is_data = false;
while (proc_maps.Next(&segment)) {
bool is_data = segment.offset != 0 && segment.filename[0] != 0;
// BSS may get merged with [heap] in /proc/self/maps. This is not very
// reliable.
bool is_bss = segment.offset == 0 &&
(segment.filename[0] == 0 ||
internal_strcmp(segment.filename, "[heap]") == 0) &&
prev_is_data;
if (g_data_start == 0 && is_data) g_data_start = segment.start;
if (is_bss) g_data_end = segment.end;
prev_is_data = is_data;
}
VPrintf(1, "guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
CHECK_LT(g_data_start, g_data_end);
CHECK_GE((uptr)&g_data_start, g_data_start);
CHECK_LT((uptr)&g_data_start, g_data_end);
}
void InitializeInterceptors() {
INTERCEPT_FUNCTION(pthread_mutex_destroy);
INTERCEPT_FUNCTION(pthread_mutex_lock);
INTERCEPT_FUNCTION(pthread_mutex_trylock);
INTERCEPT_FUNCTION(pthread_mutex_unlock);
INTERCEPT_FUNCTION(pthread_spin_destroy);
INTERCEPT_FUNCTION(pthread_spin_lock);
INTERCEPT_FUNCTION(pthread_spin_trylock);
INTERCEPT_FUNCTION(pthread_spin_unlock);
INTERCEPT_FUNCTION(pthread_rwlock_destroy);
INTERCEPT_FUNCTION(pthread_rwlock_rdlock);
INTERCEPT_FUNCTION(pthread_rwlock_tryrdlock);
INTERCEPT_FUNCTION(pthread_rwlock_timedrdlock);
INTERCEPT_FUNCTION(pthread_rwlock_wrlock);
INTERCEPT_FUNCTION(pthread_rwlock_trywrlock);
INTERCEPT_FUNCTION(pthread_rwlock_timedwrlock);
INTERCEPT_FUNCTION(pthread_rwlock_unlock);
INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2");
INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2");
INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2");
INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2");
INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2");
INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2");
// for symbolizer
INTERCEPT_FUNCTION(realpath);
INTERCEPT_FUNCTION(read);
INTERCEPT_FUNCTION(pread);
InitDataSeg();
}
} // namespace __dsan

View File

@@ -0,0 +1,159 @@
//===-- dd_rtl.cc ---------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "dd_rtl.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __dsan {
static Context *ctx;
static u32 CurrentStackTrace(Thread *thr, uptr skip) {
BufferedStackTrace stack;
thr->ignore_interceptors = true;
stack.Unwind(1000, 0, 0, 0, 0, 0, false);
thr->ignore_interceptors = false;
if (stack.size <= skip)
return 0;
return StackDepotPut(StackTrace(stack.trace + skip, stack.size - skip));
}
static void PrintStackTrace(Thread *thr, u32 stk) {
StackTrace stack = StackDepotGet(stk);
thr->ignore_interceptors = true;
stack.Print();
thr->ignore_interceptors = false;
}
static void ReportDeadlock(Thread *thr, DDReport *rep) {
if (rep == 0)
return;
BlockingMutexLock lock(&ctx->report_mutex);
Printf("==============================\n");
Printf("WARNING: lock-order-inversion (potential deadlock)\n");
for (int i = 0; i < rep->n; i++) {
Printf("Thread %d locks mutex %llu while holding mutex %llu:\n",
rep->loop[i].thr_ctx, rep->loop[i].mtx_ctx1, rep->loop[i].mtx_ctx0);
PrintStackTrace(thr, rep->loop[i].stk[1]);
if (rep->loop[i].stk[0]) {
Printf("Mutex %llu was acquired here:\n",
rep->loop[i].mtx_ctx0);
PrintStackTrace(thr, rep->loop[i].stk[0]);
}
}
Printf("==============================\n");
}
Callback::Callback(Thread *thr)
: thr(thr) {
lt = thr->dd_lt;
pt = thr->dd_pt;
}
u32 Callback::Unwind() {
return CurrentStackTrace(thr, 3);
}
static void InitializeFlags() {
Flags *f = flags();
// Default values.
f->second_deadlock_stack = false;
SetCommonFlagsDefaults();
{
// Override some common flags defaults.
CommonFlags cf;
cf.CopyFrom(*common_flags());
cf.allow_addr2line = true;
OverrideCommonFlags(cf);
}
// Override from command line.
FlagParser parser;
RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack);
RegisterCommonFlags(&parser);
parser.ParseString(GetEnv("DSAN_OPTIONS"));
SetVerbosity(common_flags()->verbosity);
}
void Initialize() {
static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1];
ctx = new(ctx_mem) Context();
InitializeInterceptors();
InitializeFlags();
ctx->dd = DDetector::Create(flags());
}
void ThreadInit(Thread *thr) {
static atomic_uintptr_t id_gen;
uptr id = atomic_fetch_add(&id_gen, 1, memory_order_relaxed);
thr->dd_pt = ctx->dd->CreatePhysicalThread();
thr->dd_lt = ctx->dd->CreateLogicalThread(id);
}
void ThreadDestroy(Thread *thr) {
ctx->dd->DestroyPhysicalThread(thr->dd_pt);
ctx->dd->DestroyLogicalThread(thr->dd_lt);
}
void MutexBeforeLock(Thread *thr, uptr m, bool writelock) {
if (thr->ignore_interceptors)
return;
Callback cb(thr);
{
MutexHashMap::Handle h(&ctx->mutex_map, m);
if (h.created())
ctx->dd->MutexInit(&cb, &h->dd);
ctx->dd->MutexBeforeLock(&cb, &h->dd, writelock);
}
ReportDeadlock(thr, ctx->dd->GetReport(&cb));
}
void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock) {
if (thr->ignore_interceptors)
return;
Callback cb(thr);
{
MutexHashMap::Handle h(&ctx->mutex_map, m);
if (h.created())
ctx->dd->MutexInit(&cb, &h->dd);
ctx->dd->MutexAfterLock(&cb, &h->dd, writelock, trylock);
}
ReportDeadlock(thr, ctx->dd->GetReport(&cb));
}
void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock) {
if (thr->ignore_interceptors)
return;
Callback cb(thr);
{
MutexHashMap::Handle h(&ctx->mutex_map, m);
ctx->dd->MutexBeforeUnlock(&cb, &h->dd, writelock);
}
ReportDeadlock(thr, ctx->dd->GetReport(&cb));
}
void MutexDestroy(Thread *thr, uptr m) {
if (thr->ignore_interceptors)
return;
Callback cb(thr);
MutexHashMap::Handle h(&ctx->mutex_map, m, true);
if (!h.exists())
return;
ctx->dd->MutexDestroy(&cb, &h->dd);
}
} // namespace __dsan

View File

@@ -0,0 +1,67 @@
//===-- dd_rtl.h ----------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef DD_RTL_H
#define DD_RTL_H
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_addrhashmap.h"
#include "sanitizer_common/sanitizer_mutex.h"
namespace __dsan {
typedef DDFlags Flags;
struct Mutex {
DDMutex dd;
};
struct Thread {
DDPhysicalThread *dd_pt;
DDLogicalThread *dd_lt;
bool ignore_interceptors;
};
struct Callback : DDCallback {
Thread *thr;
Callback(Thread *thr);
u32 Unwind() override;
};
typedef AddrHashMap<Mutex, 31051> MutexHashMap;
struct Context {
DDetector *dd;
BlockingMutex report_mutex;
MutexHashMap mutex_map;
};
inline Flags* flags() {
static Flags flags;
return &flags;
}
void Initialize();
void InitializeInterceptors();
void ThreadInit(Thread *thr);
void ThreadDestroy(Thread *thr);
void MutexBeforeLock(Thread *thr, uptr m, bool writelock);
void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock);
void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock);
void MutexDestroy(Thread *thr, uptr m);
} // namespace __dsan
#endif // DD_RTL_H

View File

@@ -0,0 +1,4 @@
type tsan_go.cc ..\rtl\tsan_interface_atomic.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.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_rtl_proc.cc ..\rtl\tsan_stat.cc ..\rtl\tsan_suppressions.cc ..\rtl\tsan_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc ..\..\sanitizer_common\sanitizer_flag_parser.cc ..\..\sanitizer_common\sanitizer_symbolizer.cc ..\..\sanitizer_common\sanitizer_termination.cc > gotsan.cc
gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO=1 -Wno-error=attributes -Wno-attributes -Wno-format -Wno-maybe-uninitialized -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer -std=c++11

View File

@@ -0,0 +1,152 @@
#!/bin/sh
set -e
SRCS="
tsan_go.cc
../rtl/tsan_clock.cc
../rtl/tsan_external.cc
../rtl/tsan_flags.cc
../rtl/tsan_interface_atomic.cc
../rtl/tsan_md5.cc
../rtl/tsan_mutex.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_rtl_proc.cc
../rtl/tsan_stack_trace.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_common_libcdep.cc
../../sanitizer_common/sanitizer_deadlock_detector2.cc
../../sanitizer_common/sanitizer_file.cc
../../sanitizer_common/sanitizer_flag_parser.cc
../../sanitizer_common/sanitizer_flags.cc
../../sanitizer_common/sanitizer_libc.cc
../../sanitizer_common/sanitizer_persistent_allocator.cc
../../sanitizer_common/sanitizer_printf.cc
../../sanitizer_common/sanitizer_suppressions.cc
../../sanitizer_common/sanitizer_thread_registry.cc
../../sanitizer_common/sanitizer_stackdepot.cc
../../sanitizer_common/sanitizer_stacktrace.cc
../../sanitizer_common/sanitizer_symbolizer.cc
../../sanitizer_common/sanitizer_termination.cc
"
if [ "`uname -a | grep Linux`" != "" ]; then
SUFFIX="linux_amd64"
OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option"
OSLDFLAGS="-lpthread -fPIC -fpie"
SRCS="
$SRCS
../rtl/tsan_platform_linux.cc
../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_posix_libcdep.cc
../../sanitizer_common/sanitizer_procmaps_common.cc
../../sanitizer_common/sanitizer_procmaps_linux.cc
../../sanitizer_common/sanitizer_linux.cc
../../sanitizer_common/sanitizer_linux_libcdep.cc
../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
"
elif [ "`uname -a | grep FreeBSD`" != "" ]; then
SUFFIX="freebsd_amd64"
OSCFLAGS="-fno-strict-aliasing -fPIC -Werror"
OSLDFLAGS="-lpthread -fPIC -fpie"
SRCS="
$SRCS
../rtl/tsan_platform_linux.cc
../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_posix_libcdep.cc
../../sanitizer_common/sanitizer_procmaps_common.cc
../../sanitizer_common/sanitizer_procmaps_freebsd.cc
../../sanitizer_common/sanitizer_linux.cc
../../sanitizer_common/sanitizer_linux_libcdep.cc
../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
"
elif [ "`uname -a | grep NetBSD`" != "" ]; then
SUFFIX="netbsd_amd64"
OSCFLAGS="-fno-strict-aliasing -fPIC -Werror"
OSLDFLAGS="-lpthread -fPIC -fpie"
SRCS="
$SRCS
../rtl/tsan_platform_linux.cc
../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_posix_libcdep.cc
../../sanitizer_common/sanitizer_procmaps_common.cc
../../sanitizer_common/sanitizer_procmaps_freebsd.cc
../../sanitizer_common/sanitizer_linux.cc
../../sanitizer_common/sanitizer_linux_libcdep.cc
../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
"
elif [ "`uname -a | grep Darwin`" != "" ]; then
SUFFIX="darwin_amd64"
OSCFLAGS="-fPIC -Wno-unused-const-variable -Wno-unknown-warning-option -isysroot $(xcodebuild -version -sdk macosx Path) -mmacosx-version-min=10.7"
OSLDFLAGS="-lpthread -fPIC -fpie -mmacosx-version-min=10.7"
SRCS="
$SRCS
../rtl/tsan_platform_mac.cc
../../sanitizer_common/sanitizer_mac.cc
../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_posix_libcdep.cc
../../sanitizer_common/sanitizer_procmaps_mac.cc
"
elif [ "`uname -a | grep MINGW`" != "" ]; then
SUFFIX="windows_amd64"
OSCFLAGS="-Wno-error=attributes -Wno-attributes -Wno-unused-const-variable -Wno-unknown-warning-option"
OSLDFLAGS=""
SRCS="
$SRCS
../rtl/tsan_platform_windows.cc
../../sanitizer_common/sanitizer_win.cc
"
else
echo Unknown platform
exit 1
fi
CC=${CC:-gcc}
IN_TMPDIR=${IN_TMPDIR:-0}
SILENT=${SILENT:-0}
if [ $IN_TMPDIR != "0" ]; then
DIR=$(mktemp -qd /tmp/gotsan.XXXXXXXXXX)
cleanup() {
rm -rf $DIR
}
trap cleanup EXIT
else
DIR=.
fi
SRCS="$SRCS $ADD_SRCS"
rm -f $DIR/gotsan.cc
for F in $SRCS; do
cat $F >> $DIR/gotsan.cc
done
FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO=1 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS"
if [ "$DEBUG" = "" ]; then
FLAGS="$FLAGS -DSANITIZER_DEBUG=0 -O3 -msse3 -fomit-frame-pointer"
else
FLAGS="$FLAGS -DSANITIZER_DEBUG=1 -g"
fi
if [ "$SILENT" != "1" ]; then
echo $CC gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS
fi
$CC $DIR/gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS
$CC $OSCFLAGS test.c $DIR/race_$SUFFIX.syso -m64 -g -o $DIR/test $OSLDFLAGS $LDFLAGS
export GORACE="exitcode=0 atexit_sleep_ms=0"
if [ "$SILENT" != "1" ]; then
$DIR/test
else
$DIR/test 2>/dev/null
fi

View File

@@ -0,0 +1,89 @@
//===-- 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>
#include <stdlib.h>
void __tsan_init(void **thr, void **proc, void (*cb)(long, void*));
void __tsan_fini();
void __tsan_map_shadow(void *addr, unsigned long size);
void __tsan_go_start(void *thr, void **chthr, void *pc);
void __tsan_go_end(void *thr);
void __tsan_proc_create(void **pproc);
void __tsan_proc_destroy(void *proc);
void __tsan_proc_wire(void *proc, void *thr);
void __tsan_proc_unwire(void *proc, void *thr);
void __tsan_read(void *thr, void *addr, void *pc);
void __tsan_write(void *thr, void *addr, void *pc);
void __tsan_func_enter(void *thr, void *pc);
void __tsan_func_exit(void *thr);
void __tsan_malloc(void *thr, void *pc, void *p, unsigned long sz);
void __tsan_free(void *p, unsigned long sz);
void __tsan_acquire(void *thr, void *addr);
void __tsan_release(void *thr, void *addr);
void __tsan_release_merge(void *thr, void *addr);
void *current_proc;
void symbolize_cb(long cmd, void *ctx) {
switch (cmd) {
case 0:
if (current_proc == 0)
abort();
*(void**)ctx = current_proc;
}
}
char buf0[100<<10];
void foobar() {}
void barfoo() {}
int main(void) {
void *thr0 = 0;
void *proc0 = 0;
__tsan_init(&thr0, &proc0, symbolize_cb);
current_proc = proc0;
char *buf = (char*)((unsigned long)buf0 + (64<<10) - 1 & ~((64<<10) - 1));
__tsan_map_shadow(buf, 4096);
__tsan_malloc(thr0, (char*)&barfoo + 1, buf, 10);
__tsan_free(buf, 10);
__tsan_func_enter(thr0, (char*)&main + 1);
__tsan_malloc(thr0, (char*)&barfoo + 1, buf, 10);
__tsan_release(thr0, buf);
__tsan_release_merge(thr0, buf);
void *thr1 = 0;
__tsan_go_start(thr0, &thr1, (char*)&barfoo + 1);
void *thr2 = 0;
__tsan_go_start(thr0, &thr2, (char*)&barfoo + 1);
__tsan_func_exit(thr0);
__tsan_func_enter(thr1, (char*)&foobar + 1);
__tsan_func_enter(thr1, (char*)&foobar + 1);
__tsan_write(thr1, buf, (char*)&barfoo + 1);
__tsan_acquire(thr1, buf);
__tsan_func_exit(thr1);
__tsan_func_exit(thr1);
__tsan_go_end(thr1);
void *proc1 = 0;
__tsan_proc_create(&proc1);
current_proc = proc1;
__tsan_func_enter(thr2, (char*)&foobar + 1);
__tsan_read(thr2, buf, (char*)&barfoo + 1);
__tsan_free(buf, 10);
__tsan_func_exit(thr2);
__tsan_go_end(thr2);
__tsan_proc_destroy(proc1);
current_proc = proc0;
__tsan_fini();
return 0;
}

View File

@@ -0,0 +1,292 @@
//===-- 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 {
void InitializeInterceptors() {
}
void InitializeDynamicAnnotations() {
}
bool IsExpectedReport(uptr addr, uptr size) {
return false;
}
void *internal_alloc(MBlockType typ, uptr sz) {
return InternalAlloc(sz);
}
void internal_free(void *p) {
InternalFree(p);
}
// Callback into Go.
static void (*go_runtime_cb)(uptr cmd, void *ctx);
enum {
CallbackGetProc = 0,
CallbackSymbolizeCode = 1,
CallbackSymbolizeData = 2,
};
struct SymbolizeCodeContext {
uptr pc;
char *func;
char *file;
uptr line;
uptr off;
uptr res;
};
SymbolizedStack *SymbolizeCode(uptr addr) {
SymbolizedStack *s = SymbolizedStack::New(addr);
SymbolizeCodeContext cbctx;
internal_memset(&cbctx, 0, sizeof(cbctx));
cbctx.pc = addr;
go_runtime_cb(CallbackSymbolizeCode, &cbctx);
if (cbctx.res) {
AddressInfo &info = s->info;
info.module_offset = cbctx.off;
info.function = internal_strdup(cbctx.func ? cbctx.func : "??");
info.file = internal_strdup(cbctx.file ? cbctx.file : "-");
info.line = cbctx.line;
info.column = 0;
}
return s;
}
struct SymbolizeDataContext {
uptr addr;
uptr heap;
uptr start;
uptr size;
char *name;
char *file;
uptr line;
uptr res;
};
ReportLocation *SymbolizeData(uptr addr) {
SymbolizeDataContext cbctx;
internal_memset(&cbctx, 0, sizeof(cbctx));
cbctx.addr = addr;
go_runtime_cb(CallbackSymbolizeData, &cbctx);
if (!cbctx.res)
return 0;
if (cbctx.heap) {
MBlock *b = ctx->metamap.GetBlock(cbctx.start);
if (!b)
return 0;
ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
loc->heap_chunk_start = cbctx.start;
loc->heap_chunk_size = b->siz;
loc->tid = b->tid;
loc->stack = SymbolizeStackId(b->stk);
return loc;
} else {
ReportLocation *loc = ReportLocation::New(ReportLocationGlobal);
loc->global.name = internal_strdup(cbctx.name ? cbctx.name : "??");
loc->global.file = internal_strdup(cbctx.file ? cbctx.file : "??");
loc->global.line = cbctx.line;
loc->global.start = cbctx.start;
loc->global.size = cbctx.size;
return loc;
}
}
static ThreadState *main_thr;
static bool inited;
static Processor* get_cur_proc() {
if (UNLIKELY(!inited)) {
// Running Initialize().
// We have not yet returned the Processor to Go, so we cannot ask it back.
// Currently, Initialize() does not use the Processor, so return nullptr.
return nullptr;
}
Processor *proc;
go_runtime_cb(CallbackGetProc, &proc);
return proc;
}
Processor *ThreadState::proc() {
return get_cur_proc();
}
extern "C" {
static ThreadState *AllocGoroutine() {
ThreadState *thr = (ThreadState*)internal_alloc(MBlockThreadContex,
sizeof(ThreadState));
internal_memset(thr, 0, sizeof(*thr));
return thr;
}
void __tsan_init(ThreadState **thrp, Processor **procp,
void (*cb)(uptr cmd, void *cb)) {
go_runtime_cb = cb;
ThreadState *thr = AllocGoroutine();
main_thr = *thrp = thr;
Initialize(thr);
*procp = thr->proc1;
inited = true;
}
void __tsan_fini() {
// FIXME: Not necessary thread 0.
ThreadState *thr = main_thr;
int res = Finalize(thr);
exit(res);
}
void __tsan_map_shadow(uptr addr, uptr size) {
MapShadow(addr, size);
}
void __tsan_read(ThreadState *thr, void *addr, void *pc) {
MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1);
}
void __tsan_read_pc(ThreadState *thr, void *addr, uptr callpc, uptr pc) {
if (callpc != 0)
FuncEntry(thr, callpc);
MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1);
if (callpc != 0)
FuncExit(thr);
}
void __tsan_write(ThreadState *thr, void *addr, void *pc) {
MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1);
}
void __tsan_write_pc(ThreadState *thr, void *addr, uptr callpc, uptr pc) {
if (callpc != 0)
FuncEntry(thr, callpc);
MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1);
if (callpc != 0)
FuncExit(thr);
}
void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr pc) {
MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, false);
}
void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr pc) {
MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, true);
}
void __tsan_func_enter(ThreadState *thr, void *pc) {
FuncEntry(thr, (uptr)pc);
}
void __tsan_func_exit(ThreadState *thr) {
FuncExit(thr);
}
void __tsan_malloc(ThreadState *thr, uptr pc, uptr p, uptr sz) {
CHECK(inited);
if (thr && pc)
ctx->metamap.AllocBlock(thr, pc, p, sz);
MemoryResetRange(0, 0, (uptr)p, sz);
}
void __tsan_free(uptr p, uptr sz) {
ctx->metamap.FreeRange(get_cur_proc(), p, sz);
}
void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) {
ThreadState *thr = AllocGoroutine();
*pthr = thr;
int goid = ThreadCreate(parent, (uptr)pc, 0, true);
ThreadStart(thr, goid, 0, /*workerthread*/ false);
}
void __tsan_go_end(ThreadState *thr) {
ThreadFinish(thr);
internal_free(thr);
}
void __tsan_proc_create(Processor **pproc) {
*pproc = ProcCreate();
}
void __tsan_proc_destroy(Processor *proc) {
ProcDestroy(proc);
}
void __tsan_acquire(ThreadState *thr, void *addr) {
Acquire(thr, 0, (uptr)addr);
}
void __tsan_release(ThreadState *thr, void *addr) {
ReleaseStore(thr, 0, (uptr)addr);
}
void __tsan_release_merge(ThreadState *thr, void *addr) {
Release(thr, 0, (uptr)addr);
}
void __tsan_finalizer_goroutine(ThreadState *thr) {
AcquireGlobal(thr, 0);
}
void __tsan_mutex_before_lock(ThreadState *thr, uptr addr, uptr write) {
if (write)
MutexPreLock(thr, 0, addr);
else
MutexPreReadLock(thr, 0, addr);
}
void __tsan_mutex_after_lock(ThreadState *thr, uptr addr, uptr write) {
if (write)
MutexPostLock(thr, 0, addr);
else
MutexPostReadLock(thr, 0, addr);
}
void __tsan_mutex_before_unlock(ThreadState *thr, uptr addr, uptr write) {
if (write)
MutexUnlock(thr, 0, addr);
else
MutexReadUnlock(thr, 0, addr);
}
void __tsan_go_ignore_sync_begin(ThreadState *thr) {
ThreadIgnoreSyncBegin(thr, 0);
}
void __tsan_go_ignore_sync_end(ThreadState *thr) {
ThreadIgnoreSyncEnd(thr, 0);
}
void __tsan_report_count(u64 *pn) {
Lock lock(&ctx->report_mtx);
*pn = ctx->nreported;
}
} // extern "C"
} // namespace __tsan
namespace __sanitizer {
void SymbolizerPrepareForSandboxing() {
// Nothing to do here for Go.
}
} // namespace __sanitizer

View File

@@ -0,0 +1,26 @@
__tsan_init
__tsan_flush_memory
__tsan_read*
__tsan_write*
__tsan_vptr*
__tsan_func*
__tsan_atomic*
__tsan_java*
__tsan_unaligned*
__tsan_release
__tsan_acquire
__tsan_mutex_create
__tsan_mutex_destroy
__tsan_mutex_pre_lock
__tsan_mutex_post_lock
__tsan_mutex_pre_unlock
__tsan_mutex_post_unlock
__tsan_mutex_pre_signal
__tsan_mutex_post_signal
__tsan_mutex_pre_divert
__tsan_mutex_post_divert
__ubsan_*
Annotate*
WTFAnnotate*
RunningOnValgrind
ValgrindSlowdown

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,226 @@
//===-- 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_dense_alloc.h"
namespace __tsan {
typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc;
typedef DenseSlabAllocCache ClockCache;
// The clock that lives in sync variables (mutexes, atomics, etc).
class SyncClock {
public:
SyncClock();
~SyncClock();
uptr size() const;
// These are used only in tests.
u64 get(unsigned tid) const;
u64 get_clean(unsigned tid) const;
void Resize(ClockCache *c, uptr nclk);
void Reset(ClockCache *c);
void DebugDump(int(*printf)(const char *s, ...));
// Clock element iterator.
// Note: it iterates only over the table without regard to dirty entries.
class Iter {
public:
explicit Iter(SyncClock* parent);
Iter& operator++();
bool operator!=(const Iter& other);
ClockElem &operator*();
private:
SyncClock *parent_;
// [pos_, end_) is the current continuous range of clock elements.
ClockElem *pos_;
ClockElem *end_;
int block_; // Current number of second level block.
NOINLINE void Next();
};
Iter begin();
Iter end();
private:
friend class ThreadClock;
friend class Iter;
static const uptr kDirtyTids = 2;
struct Dirty {
u64 epoch : kClkBits;
u64 tid : 64 - kClkBits; // kInvalidId if not active
};
unsigned release_store_tid_;
unsigned release_store_reused_;
Dirty dirty_[kDirtyTids];
// If size_ is 0, tab_ is nullptr.
// If size <= 64 (kClockCount), tab_ contains pointer to an array with
// 64 ClockElem's (ClockBlock::clock).
// Otherwise, tab_ points to an array with up to 127 u32 elements,
// each pointing to the second-level 512b block with 64 ClockElem's.
// Unused space in the first level ClockBlock is used to store additional
// clock elements.
// The last u32 element in the first level ClockBlock is always used as
// reference counter.
//
// See the following scheme for details.
// All memory blocks are 512 bytes (allocated from ClockAlloc).
// Clock (clk) elements are 64 bits.
// Idx and ref are 32 bits.
//
// tab_
// |
// \/
// +----------------------------------------------------+
// | clk128 | clk129 | ...unused... | idx1 | idx0 | ref |
// +----------------------------------------------------+
// | |
// | \/
// | +----------------+
// | | clk0 ... clk63 |
// | +----------------+
// \/
// +------------------+
// | clk64 ... clk127 |
// +------------------+
//
// Note: dirty entries, if active, always override what's stored in the clock.
ClockBlock *tab_;
u32 tab_idx_;
u16 size_;
u16 blocks_; // Number of second level blocks.
void Unshare(ClockCache *c);
bool IsShared() const;
bool Cachable() const;
void ResetImpl();
void FlushDirty();
uptr capacity() const;
u32 get_block(uptr bi) const;
void append_block(u32 idx);
ClockElem &elem(unsigned tid) const;
};
// The clock that lives in threads.
class ThreadClock {
public:
typedef DenseSlabAllocCache Cache;
explicit ThreadClock(unsigned tid, unsigned reused = 0);
u64 get(unsigned tid) const;
void set(ClockCache *c, unsigned tid, u64 v);
void set(u64 v);
void tick();
uptr size() const;
void acquire(ClockCache *c, SyncClock *src);
void release(ClockCache *c, SyncClock *dst);
void acq_rel(ClockCache *c, SyncClock *dst);
void ReleaseStore(ClockCache *c, SyncClock *dst);
void ResetCached(ClockCache *c);
void DebugReset();
void DebugDump(int(*printf)(const char *s, ...));
private:
static const uptr kDirtyTids = SyncClock::kDirtyTids;
// Index of the thread associated with he clock ("current thread").
const unsigned tid_;
const unsigned reused_; // tid_ reuse count.
// Current thread time when it acquired something from other threads.
u64 last_acquire_;
// Cached SyncClock (without dirty entries and release_store_tid_).
// We reuse it for subsequent store-release operations without intervening
// acquire operations. Since it is shared (and thus constant), clock value
// for the current thread is then stored in dirty entries in the SyncClock.
// We host a refernece to the table while it is cached here.
u32 cached_idx_;
u16 cached_size_;
u16 cached_blocks_;
// Number of active elements in the clk_ table (the rest is zeros).
uptr nclk_;
u64 clk_[kMaxTidInClock]; // Fixed size vector clock.
bool IsAlreadyAcquired(const SyncClock *src) const;
void UpdateCurrentThread(ClockCache *c, SyncClock *dst) const;
};
ALWAYS_INLINE u64 ThreadClock::get(unsigned tid) const {
DCHECK_LT(tid, kMaxTidInClock);
return clk_[tid];
}
ALWAYS_INLINE void ThreadClock::set(u64 v) {
DCHECK_GE(v, clk_[tid_]);
clk_[tid_] = v;
}
ALWAYS_INLINE void ThreadClock::tick() {
clk_[tid_]++;
}
ALWAYS_INLINE uptr ThreadClock::size() const {
return nclk_;
}
ALWAYS_INLINE SyncClock::Iter SyncClock::begin() {
return Iter(this);
}
ALWAYS_INLINE SyncClock::Iter SyncClock::end() {
return Iter(nullptr);
}
ALWAYS_INLINE uptr SyncClock::size() const {
return size_;
}
ALWAYS_INLINE SyncClock::Iter::Iter(SyncClock* parent)
: parent_(parent)
, pos_(nullptr)
, end_(nullptr)
, block_(-1) {
if (parent)
Next();
}
ALWAYS_INLINE SyncClock::Iter& SyncClock::Iter::operator++() {
pos_++;
if (UNLIKELY(pos_ >= end_))
Next();
return *this;
}
ALWAYS_INLINE bool SyncClock::Iter::operator!=(const SyncClock::Iter& other) {
return parent_ != other.parent_;
}
ALWAYS_INLINE ClockElem &SyncClock::Iter::operator*() {
return *pos_;
}
} // namespace __tsan
#endif // TSAN_CLOCK_H

Some files were not shown because too many files have changed in this diff Show More