aboutsummaryrefslogtreecommitdiff
path: root/libsanitizer/tsan
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2013-12-05 09:18:38 +0000
committerKostya Serebryany <kcc@gcc.gnu.org>2013-12-05 09:18:38 +0000
commitdf77f0e4ec043bc4fa155efbd5c1c74ce73d2b50 (patch)
tree20d85354103063e38b162a6a90b7ae51fb4b6104 /libsanitizer/tsan
parent649d196dbd78a119786f204d36b7c5d4dcb3a949 (diff)
downloadgcc-df77f0e4ec043bc4fa155efbd5c1c74ce73d2b50.zip
gcc-df77f0e4ec043bc4fa155efbd5c1c74ce73d2b50.tar.gz
gcc-df77f0e4ec043bc4fa155efbd5c1c74ce73d2b50.tar.bz2
libsanitizer merge from upstream r196090
From-SVN: r205695
Diffstat (limited to 'libsanitizer/tsan')
-rw-r--r--libsanitizer/tsan/Makefile.am1
-rw-r--r--libsanitizer/tsan/Makefile.in14
-rw-r--r--libsanitizer/tsan/tsan_defs.h7
-rw-r--r--libsanitizer/tsan/tsan_fd.cc34
-rw-r--r--libsanitizer/tsan/tsan_flags.cc78
-rw-r--r--libsanitizer/tsan/tsan_flags.h19
-rw-r--r--libsanitizer/tsan/tsan_ignoreset.cc45
-rw-r--r--libsanitizer/tsan/tsan_ignoreset.h36
-rw-r--r--libsanitizer/tsan/tsan_interceptors.cc374
-rw-r--r--libsanitizer/tsan/tsan_interface_ann.cc26
-rw-r--r--libsanitizer/tsan/tsan_interface_atomic.cc33
-rw-r--r--libsanitizer/tsan/tsan_interface_java.cc13
-rw-r--r--libsanitizer/tsan/tsan_interface_java.h6
-rw-r--r--libsanitizer/tsan/tsan_mutexset.h2
-rw-r--r--libsanitizer/tsan/tsan_platform.h12
-rw-r--r--libsanitizer/tsan/tsan_platform_linux.cc48
-rw-r--r--libsanitizer/tsan/tsan_report.cc2
-rw-r--r--libsanitizer/tsan/tsan_rtl.cc100
-rw-r--r--libsanitizer/tsan/tsan_rtl.h40
-rw-r--r--libsanitizer/tsan/tsan_rtl_mutex.cc111
-rw-r--r--libsanitizer/tsan/tsan_rtl_report.cc58
-rw-r--r--libsanitizer/tsan/tsan_rtl_thread.cc68
-rw-r--r--libsanitizer/tsan/tsan_stat.cc59
-rw-r--r--libsanitizer/tsan/tsan_stat.h59
-rw-r--r--libsanitizer/tsan/tsan_suppressions.cc5
-rw-r--r--libsanitizer/tsan/tsan_suppressions.h1
-rw-r--r--libsanitizer/tsan/tsan_symbolize.cc51
-rw-r--r--libsanitizer/tsan/tsan_symbolize.h2
-rw-r--r--libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc1
-rw-r--r--libsanitizer/tsan/tsan_sync.cc5
-rw-r--r--libsanitizer/tsan/tsan_trace.h5
31 files changed, 900 insertions, 415 deletions
diff --git a/libsanitizer/tsan/Makefile.am b/libsanitizer/tsan/Makefile.am
index 9b039d2..ca41550 100644
--- a/libsanitizer/tsan/Makefile.am
+++ b/libsanitizer/tsan/Makefile.am
@@ -23,6 +23,7 @@ tsan_files = \
tsan_rtl.cc \
tsan_stat.cc \
tsan_sync.cc \
+ tsan_ignoreset.cc \
tsan_interceptors.cc \
tsan_md5.cc \
tsan_platform_mac.cc \
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
index eb5c7e7..0bd1608 100644
--- a/libsanitizer/tsan/Makefile.in
+++ b/libsanitizer/tsan/Makefile.in
@@ -84,12 +84,12 @@ libtsan_la_DEPENDENCIES = \
am__objects_1 = tsan_clock.lo tsan_interface_atomic.lo tsan_mutex.lo \
tsan_report.lo tsan_rtl_thread.lo tsan_symbolize.lo \
tsan_flags.lo tsan_interface.lo tsan_platform_linux.lo \
- tsan_rtl.lo tsan_stat.lo tsan_sync.lo tsan_interceptors.lo \
- tsan_md5.lo tsan_platform_mac.lo tsan_rtl_mutex.lo \
- tsan_suppressions.lo tsan_interface_ann.lo tsan_mman.lo \
- tsan_rtl_report.lo tsan_fd.lo tsan_interface_java.lo \
- tsan_mutexset.lo tsan_symbolize_addr2line_linux.lo \
- tsan_rtl_amd64.lo
+ tsan_rtl.lo tsan_stat.lo tsan_sync.lo tsan_ignoreset.lo \
+ tsan_interceptors.lo tsan_md5.lo tsan_platform_mac.lo \
+ tsan_rtl_mutex.lo tsan_suppressions.lo tsan_interface_ann.lo \
+ tsan_mman.lo tsan_rtl_report.lo tsan_fd.lo \
+ tsan_interface_java.lo tsan_mutexset.lo \
+ tsan_symbolize_addr2line_linux.lo tsan_rtl_amd64.lo
am_libtsan_la_OBJECTS = $(am__objects_1)
libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS)
libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
@@ -283,6 +283,7 @@ tsan_files = \
tsan_rtl.cc \
tsan_stat.cc \
tsan_sync.cc \
+ tsan_ignoreset.cc \
tsan_interceptors.cc \
tsan_md5.cc \
tsan_platform_mac.cc \
@@ -417,6 +418,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_clock.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_fd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_flags.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_ignoreset.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@
diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h
index 68936e0..3f20797 100644
--- a/libsanitizer/tsan/tsan_defs.h
+++ b/libsanitizer/tsan/tsan_defs.h
@@ -39,10 +39,8 @@ const int kTidBits = 13;
const unsigned kMaxTid = 1 << kTidBits;
const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
const int kClkBits = 42;
-#ifndef TSAN_GO
-const int kShadowStackSize = 4 * 1024;
-const int kTraceStackSize = 256;
-#endif
+const uptr kShadowStackSize = 64 * 1024;
+const uptr kTraceStackSize = 256;
#ifdef TSAN_SHADOW_COUNT
# if TSAN_SHADOW_COUNT == 2 \
@@ -154,6 +152,7 @@ struct MD5Hash {
MD5Hash md5_hash(const void *data, uptr size);
struct ThreadState;
+class ThreadContext;
struct Context;
struct ReportStack;
class ReportDesc;
diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc
index de852b1..b7ac311 100644
--- a/libsanitizer/tsan/tsan_fd.cc
+++ b/libsanitizer/tsan/tsan_fd.cc
@@ -40,6 +40,11 @@ struct FdContext {
static FdContext fdctx;
+static bool bogusfd(int fd) {
+ // Apparently a bogus fd value.
+ return fd < 0 || fd >= kTableSize;
+}
+
static FdSync *allocsync() {
FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync));
atomic_store(&s->rc, 1, memory_order_relaxed);
@@ -67,6 +72,7 @@ static void unref(ThreadState *thr, uptr pc, FdSync *s) {
}
static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
+ CHECK_GE(fd, 0);
CHECK_LT(fd, kTableSize);
atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];
uptr l1 = atomic_load(pl1, memory_order_consume);
@@ -146,6 +152,8 @@ bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
}
void FdAcquire(ThreadState *thr, uptr pc, int fd) {
+ if (bogusfd(fd))
+ return;
FdDesc *d = fddesc(thr, pc, fd);
FdSync *s = d->sync;
DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
@@ -155,6 +163,8 @@ void FdAcquire(ThreadState *thr, uptr pc, int fd) {
}
void FdRelease(ThreadState *thr, uptr pc, int fd) {
+ if (bogusfd(fd))
+ return;
FdDesc *d = fddesc(thr, pc, fd);
FdSync *s = d->sync;
DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
@@ -165,12 +175,16 @@ void FdRelease(ThreadState *thr, uptr pc, int fd) {
void FdAccess(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
FdDesc *d = fddesc(thr, pc, fd);
MemoryRead(thr, pc, (uptr)d, kSizeLog8);
}
void FdClose(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
FdDesc *d = fddesc(thr, pc, fd);
// To catch races between fd usage and close.
MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
@@ -185,11 +199,15 @@ void FdClose(ThreadState *thr, uptr pc, int fd) {
void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
init(thr, pc, fd, &fdctx.filesync);
}
void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
+ if (bogusfd(oldfd) || bogusfd(newfd))
+ return;
// Ignore the case when user dups not yet connected socket.
FdDesc *od = fddesc(thr, pc, oldfd);
MemoryRead(thr, pc, (uptr)od, kSizeLog8);
@@ -207,32 +225,44 @@ void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
init(thr, pc, fd, allocsync());
}
void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
init(thr, pc, fd, 0);
}
void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
init(thr, pc, fd, 0);
}
void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
init(thr, pc, fd, allocsync());
}
void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
// It can be a UDP socket.
init(thr, pc, fd, &fdctx.socksync);
}
void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
+ if (bogusfd(fd))
+ return;
// Synchronize connect->accept.
Acquire(thr, pc, (uptr)&fdctx.connectsync);
init(thr, pc, newfd, &fdctx.socksync);
@@ -240,12 +270,16 @@ void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
// Synchronize connect->accept.
Release(thr, pc, (uptr)&fdctx.connectsync);
}
void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
init(thr, pc, fd, &fdctx.socksync);
}
diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc
index dbde421..5bcf42e 100644
--- a/libsanitizer/tsan/tsan_flags.cc
+++ b/libsanitizer/tsan/tsan_flags.cc
@@ -24,13 +24,43 @@ Flags *flags() {
// Can be overriden in frontend.
#ifdef TSAN_EXTERNAL_HOOKS
void OverrideFlags(Flags *f);
+extern "C" const char* __tsan_default_options();
#else
-SANITIZER_INTERFACE_ATTRIBUTE
void WEAK OverrideFlags(Flags *f) {
(void)f;
}
+extern "C" const char *WEAK __tsan_default_options() {
+ return "";
+}
#endif
+static void ParseFlags(Flags *f, const char *env) {
+ 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->suppress_java, "suppress_java");
+ ParseFlag(env, &f->report_bugs, "report_bugs");
+ ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks");
+ ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked");
+ ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe");
+ ParseFlag(env, &f->report_atomic_races, "report_atomic_races");
+ ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics");
+ ParseFlag(env, &f->suppressions, "suppressions");
+ ParseFlag(env, &f->print_suppressions, "print_suppressions");
+ ParseFlag(env, &f->print_benign, "print_benign");
+ ParseFlag(env, &f->exitcode, "exitcode");
+ ParseFlag(env, &f->halt_on_error, "halt_on_error");
+ ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms");
+ ParseFlag(env, &f->profile_memory, "profile_memory");
+ ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms");
+ ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms");
+ ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb");
+ ParseFlag(env, &f->stop_on_start, "stop_on_start");
+ ParseFlag(env, &f->running_on_valgrind, "running_on_valgrind");
+ ParseFlag(env, &f->history_size, "history_size");
+ ParseFlag(env, &f->io_sync, "io_sync");
+}
+
void InitializeFlags(Flags *f, const char *env) {
internal_memset(f, 0, sizeof(*f));
@@ -45,57 +75,35 @@ void InitializeFlags(Flags *f, const char *env) {
f->report_signal_unsafe = true;
f->report_atomic_races = true;
f->force_seq_cst_atomics = false;
- f->strip_path_prefix = "";
f->suppressions = "";
f->print_suppressions = false;
f->print_benign = false;
f->exitcode = 66;
f->halt_on_error = false;
- f->log_path = "stderr";
f->atexit_sleep_ms = 1000;
- f->verbosity = 0;
f->profile_memory = "";
f->flush_memory_ms = 0;
f->flush_symbolizer_ms = 5000;
+ f->memory_limit_mb = 0;
f->stop_on_start = false;
f->running_on_valgrind = false;
- f->external_symbolizer_path = "";
f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go.
f->io_sync = 1;
- f->allocator_may_return_null = false;
+
+ SetCommonFlagsDefaults(f);
// Let a frontend override.
OverrideFlags(f);
-
+ ParseFlags(f, __tsan_default_options());
+ ParseCommonFlagsFromString(f, __tsan_default_options());
// 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->suppress_java, "suppress_java");
- ParseFlag(env, &f->report_bugs, "report_bugs");
- ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks");
- ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked");
- ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe");
- ParseFlag(env, &f->report_atomic_races, "report_atomic_races");
- 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->print_suppressions, "print_suppressions");
- ParseFlag(env, &f->print_benign, "print_benign");
- ParseFlag(env, &f->exitcode, "exitcode");
- ParseFlag(env, &f->halt_on_error, "halt_on_error");
- ParseFlag(env, &f->log_path, "log_path");
- 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->flush_symbolizer_ms, "flush_symbolizer_ms");
- ParseFlag(env, &f->stop_on_start, "stop_on_start");
- ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path");
- ParseFlag(env, &f->history_size, "history_size");
- ParseFlag(env, &f->io_sync, "io_sync");
- ParseFlag(env, &f->allocator_may_return_null, "allocator_may_return_null");
+ ParseFlags(f, env);
+ ParseCommonFlagsFromString(f, env);
+
+ // Copy back to common flags.
+ *common_flags() = *f;
+ // Sanity check.
if (!f->report_bugs) {
f->report_thread_leaks = false;
f->report_destroy_locked = false;
@@ -113,8 +121,6 @@ void InitializeFlags(Flags *f, const char *env) {
" (must be [0..2])\n");
Die();
}
-
- common_flags()->allocator_may_return_null = f->allocator_may_return_null;
}
} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_flags.h b/libsanitizer/tsan/tsan_flags.h
index a7571c9..05d11a4 100644
--- a/libsanitizer/tsan/tsan_flags.h
+++ b/libsanitizer/tsan/tsan_flags.h
@@ -18,9 +18,11 @@
// header may be included in the user code, and shouldn't include
// other headers from TSan or common sanitizer runtime.
+#include "sanitizer_common/sanitizer_flags.h"
+
namespace __tsan {
-struct Flags {
+struct Flags : CommonFlags {
// Enable dynamic annotations, otherwise they are no-ops.
bool enable_annotations;
// Supress a race report if we've already output another race report
@@ -46,8 +48,6 @@ struct Flags {
// 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;
// Print matched suppressions at exit.
@@ -58,27 +58,22 @@ struct Flags {
int exitcode;
// Exit after first reported error.
bool halt_on_error;
- // Write logs to "log_path.pid".
- // The special values are "stdout" and "stderr".
- // The default is "stderr".
- const char *log_path;
// 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;
// Flush symbolizer caches every X ms.
int flush_symbolizer_ms;
+ // Resident memory limit in MB to aim at.
+ // If the process consumes more memory, then TSan will flush shadow memory.
+ int memory_limit_mb;
// 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;
- // Path to external symbolizer.
- const char *external_symbolizer_path;
// Per-thread history size, controls how many previous memory accesses
// are remembered per thread. Possible values are [0..7].
// history_size=0 amounts to 32K memory accesses. Each next value doubles
@@ -90,8 +85,6 @@ struct Flags {
// 1 - reasonable level of synchronization (write->read)
// 2 - global synchronization of all IO operations
int io_sync;
- // If false, the allocator will crash instead of returning 0 on out-of-memory.
- bool allocator_may_return_null;
};
Flags *flags();
diff --git a/libsanitizer/tsan/tsan_ignoreset.cc b/libsanitizer/tsan/tsan_ignoreset.cc
new file mode 100644
index 0000000..f0aec42
--- /dev/null
+++ b/libsanitizer/tsan/tsan_ignoreset.cc
@@ -0,0 +1,45 @@
+//===-- tsan_ignoreset.cc -------------------------------------------------===//
+//
+// 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_ignoreset.h"
+
+namespace __tsan {
+
+const uptr IgnoreSet::kMaxSize;
+
+IgnoreSet::IgnoreSet()
+ : size_() {
+}
+
+void IgnoreSet::Add(u32 stack_id) {
+ if (size_ == kMaxSize)
+ return;
+ for (uptr i = 0; i < size_; i++) {
+ if (stacks_[i] == stack_id)
+ return;
+ }
+ stacks_[size_++] = stack_id;
+}
+
+void IgnoreSet::Reset() {
+ size_ = 0;
+}
+
+uptr IgnoreSet::Size() const {
+ return size_;
+}
+
+u32 IgnoreSet::At(uptr i) const {
+ CHECK_LT(i, size_);
+ CHECK_LE(size_, kMaxSize);
+ return stacks_[i];
+}
+
+} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_ignoreset.h b/libsanitizer/tsan/tsan_ignoreset.h
new file mode 100644
index 0000000..5a250b7
--- /dev/null
+++ b/libsanitizer/tsan/tsan_ignoreset.h
@@ -0,0 +1,36 @@
+//===-- tsan_ignoreset.h ----------------------------------------*- C++ -*-===//
+//
+// 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.
+//
+// IgnoreSet holds a set of stack traces where ignores were enabled.
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_IGNORESET_H
+#define TSAN_IGNORESET_H
+
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+class IgnoreSet {
+ public:
+ static const uptr kMaxSize = 16;
+
+ IgnoreSet();
+ void Add(u32 stack_id);
+ void Reset();
+ uptr Size() const;
+ u32 At(uptr i) const;
+
+ private:
+ uptr size_;
+ u32 stacks_[kMaxSize];
+};
+
+} // namespace __tsan
+
+#endif // TSAN_IGNORESET_H
diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc
index eaaf9e3..0574beb 100644
--- a/libsanitizer/tsan/tsan_interceptors.cc
+++ b/libsanitizer/tsan/tsan_interceptors.cc
@@ -20,6 +20,7 @@
#include "interception/interception.h"
#include "tsan_interface.h"
#include "tsan_platform.h"
+#include "tsan_suppressions.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
#include "tsan_fd.h"
@@ -40,9 +41,8 @@ struct ucontext_t {
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);
+DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *)
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);
@@ -64,6 +64,7 @@ const int PTHREAD_MUTEX_RECURSIVE = 1;
const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
const int EINVAL = 22;
const int EBUSY = 16;
+const int EOWNERDEAD = 130;
const int EPOLL_CTL_ADD = 1;
const int SIGILL = 4;
const int SIGABRT = 6;
@@ -124,18 +125,16 @@ struct SignalContext {
SignalDesc pending_signals[kSigCount];
};
-// Used to ignore interceptors coming directly from libjvm.so.
-atomic_uintptr_t libjvm_begin;
-atomic_uintptr_t libjvm_end;
+// The object is 64-byte aligned, because we want hot data to be located in
+// a single cache line if possible (it's accessed in every interceptor).
+static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)];
+static LibIgnore *libignore() {
+ return reinterpret_cast<LibIgnore*>(&libignore_placeholder[0]);
+}
-static bool libjvm_check(uptr pc) {
- uptr begin = atomic_load(&libjvm_begin, memory_order_relaxed);
- if (begin != 0 && pc >= begin) {
- uptr end = atomic_load(&libjvm_end, memory_order_relaxed);
- if (end != 0 && pc < end)
- return true;
- }
- return false;
+void InitializeLibIgnore() {
+ libignore()->Init(*GetSuppressionContext());
+ libignore()->OnLibraryLoaded(0);
}
} // namespace __tsan
@@ -159,13 +158,17 @@ class ScopedInterceptor {
~ScopedInterceptor();
private:
ThreadState *const thr_;
+ const uptr pc_;
const int in_rtl_;
+ bool in_ignored_lib_;
};
ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
uptr pc)
: thr_(thr)
- , in_rtl_(thr->in_rtl) {
+ , pc_(pc)
+ , in_rtl_(thr->in_rtl)
+ , in_ignored_lib_(false) {
if (thr_->in_rtl == 0) {
Initialize(thr);
FuncEntry(thr, pc);
@@ -174,9 +177,18 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
} else {
thr_->in_rtl++;
}
+ if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) {
+ in_ignored_lib_ = true;
+ thr_->in_ignored_lib = true;
+ ThreadIgnoreBegin(thr_, pc_);
+ }
}
ScopedInterceptor::~ScopedInterceptor() {
+ if (in_ignored_lib_) {
+ thr_->in_ignored_lib = false;
+ ThreadIgnoreEnd(thr_, pc_);
+ }
thr_->in_rtl--;
if (thr_->in_rtl == 0) {
FuncExit(thr_);
@@ -201,7 +213,7 @@ ScopedInterceptor::~ScopedInterceptor() {
Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
Die(); \
} \
- if (thr->in_rtl > 1 || libjvm_check(pc)) \
+ if (thr->in_rtl > 1 || thr->in_ignored_lib) \
return REAL(func)(__VA_ARGS__); \
/**/
@@ -244,6 +256,28 @@ TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) {
return res;
}
+TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
+ SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag);
+ // dlopen will execute global constructors, so it must be not in rtl.
+ CHECK_EQ(thr->in_rtl, 1);
+ thr->in_rtl = 0;
+ void *res = REAL(dlopen)(filename, flag);
+ thr->in_rtl = 1;
+ libignore()->OnLibraryLoaded(filename);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, dlclose, void *handle) {
+ SCOPED_INTERCEPTOR_RAW(dlclose, handle);
+ // dlclose will execute global destructors, so it must be not in rtl.
+ CHECK_EQ(thr->in_rtl, 1);
+ thr->in_rtl = 0;
+ int res = REAL(dlclose)(handle);
+ thr->in_rtl = 1;
+ libignore()->OnLibraryUnloaded();
+ return res;
+}
+
class AtExitContext {
public:
AtExitContext()
@@ -326,9 +360,9 @@ TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) {
if (dso) {
// Memory allocation in __cxa_atexit will race with free during exit,
// because we do not see synchronization around atexit callback list.
- ThreadIgnoreBegin(thr);
+ ThreadIgnoreBegin(thr, pc);
int res = REAL(__cxa_atexit)(f, arg, dso);
- ThreadIgnoreEnd(thr);
+ ThreadIgnoreEnd(thr, pc);
return res;
}
return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg);
@@ -439,7 +473,7 @@ TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) {
}
TSAN_INTERCEPTOR(void*, malloc, uptr size) {
- if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+ if (cur_thread()->in_symbolizer)
return __libc_malloc(size);
void *p = 0;
{
@@ -456,7 +490,7 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) {
}
TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
- if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+ if (cur_thread()->in_symbolizer)
return __libc_calloc(size, n);
if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n))
return AllocatorReturnNull();
@@ -472,7 +506,7 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
}
TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
- if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+ if (cur_thread()->in_symbolizer)
return __libc_realloc(p, size);
if (p)
invoke_free_hook(p);
@@ -487,7 +521,7 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
TSAN_INTERCEPTOR(void, free, void *p) {
if (p == 0)
return;
- if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+ if (cur_thread()->in_symbolizer)
return __libc_free(p);
invoke_free_hook(p);
SCOPED_INTERCEPTOR_RAW(free, p);
@@ -497,7 +531,7 @@ TSAN_INTERCEPTOR(void, free, void *p) {
TSAN_INTERCEPTOR(void, cfree, void *p) {
if (p == 0)
return;
- if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+ if (cur_thread()->in_symbolizer)
return __libc_free(p);
invoke_free_hook(p);
SCOPED_INTERCEPTOR_RAW(cfree, p);
@@ -506,13 +540,11 @@ TSAN_INTERCEPTOR(void, cfree, void *p) {
TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p);
- if (libjvm_check(pc))
- return malloc_usable_size(p);
return user_alloc_usable_size(thr, pc, p);
}
#define OPERATOR_NEW_BODY(mangled_name) \
- if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \
+ if (cur_thread()->in_symbolizer) \
return __libc_malloc(size); \
void *p = 0; \
{ \
@@ -548,7 +580,7 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
#define OPERATOR_DELETE_BODY(mangled_name) \
if (ptr == 0) return; \
- if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \
+ if (cur_thread()->in_symbolizer) \
return __libc_free(ptr); \
invoke_free_hook(ptr); \
SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
@@ -682,15 +714,6 @@ TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) {
TSAN_INTERCEPTOR(char*, strdup, const char *str) {
SCOPED_TSAN_INTERCEPTOR(strdup, str);
- if (libjvm_check(pc)) {
- // The memory must come from libc malloc,
- // and we must not instrument accesses in this case.
- uptr n = internal_strlen(str) + 1;
- void *p = __libc_malloc(n);
- if (p == 0)
- return 0;
- return (char*)internal_memcpy(p, str, n);
- }
// strdup will call malloc, so no instrumentation is required here.
return REAL(strdup)(str);
}
@@ -745,23 +768,23 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
}
TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
- SCOPED_TSAN_INTERCEPTOR(memalign, align, sz);
+ SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align);
}
TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
- SCOPED_TSAN_INTERCEPTOR(valloc, sz);
+ SCOPED_INTERCEPTOR_RAW(valloc, sz);
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
- SCOPED_TSAN_INTERCEPTOR(pvalloc, sz);
+ SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
sz = RoundUp(sz, GetPageSizeCached());
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
- SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz);
+ SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
*memptr = user_alloc(thr, pc, sz, align);
return 0;
}
@@ -830,7 +853,8 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
{
ThreadState *thr = cur_thread();
ScopedInRtl in_rtl;
- if (pthread_setspecific(g_thread_finalize_key, (void*)4)) {
+ if (pthread_setspecific(g_thread_finalize_key,
+ (void *)kPthreadDestructorIterations)) {
Printf("ThreadSanitizer: failed to set thread key\n");
Die();
}
@@ -850,21 +874,15 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
TSAN_INTERCEPTOR(int, pthread_create,
void *th, void *attr, void *(*callback)(void*), void * param) {
- SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param);
+ SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param);
__sanitizer_pthread_attr_t myattr;
if (attr == 0) {
pthread_attr_init(&myattr);
attr = &myattr;
}
int detached = 0;
- pthread_attr_getdetachstate(attr, &detached);
-
-#if defined(TSAN_DEBUG_OUTPUT)
- int verbosity = (TSAN_DEBUG_OUTPUT);
-#else
- int verbosity = 0;
-#endif
- AdjustStackSizeLinux(attr, verbosity);
+ REAL(pthread_attr_getdetachstate)(attr, &detached);
+ AdjustStackSizeLinux(attr);
ThreadParam p;
p.callback = callback;
@@ -884,7 +902,7 @@ TSAN_INTERCEPTOR(int, pthread_create,
}
TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
- SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret);
+ SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
int tid = ThreadTid(thr, pc, (uptr)th);
int res = BLOCK_REAL(pthread_join)(th, ret);
if (res == 0) {
@@ -928,21 +946,13 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *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(thr, 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) {
+ if (res == EOWNERDEAD)
+ MutexRepair(thr, pc, (uptr)m);
+ if (res == 0 || res == EOWNERDEAD)
MutexLock(thr, pc, (uptr)m);
- }
return res;
}
@@ -955,13 +965,6 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
return res;
}
-TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m);
- MutexUnlock(thr, 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);
@@ -1084,13 +1087,6 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
return res;
}
-TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a);
- MemoryWrite(thr, pc, (uptr)c, kSizeLog1);
- 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);
MemoryWrite(thr, pc, (uptr)c, kSizeLog1);
@@ -1098,29 +1094,6 @@ TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) {
return res;
}
-TSAN_INTERCEPTOR(int, pthread_cond_signal, void *c) {
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c);
- MemoryRead(thr, pc, (uptr)c, kSizeLog1);
- int res = REAL(pthread_cond_signal)(c);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c);
- MemoryRead(thr, pc, (uptr)c, kSizeLog1);
- 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(thr, pc, (uptr)m);
- MemoryRead(thr, pc, (uptr)c, kSizeLog1);
- int res = REAL(pthread_cond_wait)(c, m);
- MutexLock(thr, 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);
@@ -1158,7 +1131,9 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
}
TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
- SCOPED_TSAN_INTERCEPTOR(pthread_once, o, f);
+ SCOPED_INTERCEPTOR_RAW(pthread_once, o, f);
+ // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib,
+ // the user callback must be executed with thr->in_rtl == 0.
if (o == 0 || f == 0)
return EINVAL;
atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o);
@@ -1170,14 +1145,16 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
(*f)();
CHECK_EQ(thr->in_rtl, 0);
thr->in_rtl = old_in_rtl;
- Release(thr, pc, (uptr)o);
+ if (!thr->in_ignored_lib)
+ Release(thr, pc, (uptr)o);
atomic_store(a, 2, memory_order_release);
} else {
while (v != 2) {
pthread_yield();
v = atomic_load(a, memory_order_acquire);
}
- Acquire(thr, pc, (uptr)o);
+ if (!thr->in_ignored_lib)
+ Acquire(thr, pc, (uptr)o);
}
return 0;
}
@@ -1496,22 +1473,28 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
- if (fd >= 0)
+ if (fd >= 0) {
+ FdAccess(thr, pc, fd);
FdRelease(thr, pc, 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);
- if (fd >= 0)
+ if (fd >= 0) {
+ FdAccess(thr, pc, fd);
FdRelease(thr, pc, 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);
+ if (fd >= 0)
+ FdAccess(thr, pc, fd);
int res = REAL(recv)(fd, buf, len, flags);
if (res >= 0 && fd >= 0) {
FdAcquire(thr, pc, fd);
@@ -1556,6 +1539,7 @@ TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) {
}
TSAN_INTERCEPTOR(int, fclose, void *stream) {
+ // libc file streams can call user-supplied functions, see fopencookie.
{
SCOPED_TSAN_INTERCEPTOR(fclose, stream);
if (stream) {
@@ -1568,6 +1552,7 @@ TSAN_INTERCEPTOR(int, fclose, void *stream) {
}
TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
+ // libc file streams can call user-supplied functions, see fopencookie.
{
SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f);
MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true);
@@ -1576,6 +1561,7 @@ TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
}
TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) {
+ // libc file streams can call user-supplied functions, see fopencookie.
{
SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f);
MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false);
@@ -1584,7 +1570,10 @@ TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) {
}
TSAN_INTERCEPTOR(int, fflush, void *stream) {
- SCOPED_TSAN_INTERCEPTOR(fflush, stream);
+ // libc file streams can call user-supplied functions, see fopencookie.
+ {
+ SCOPED_TSAN_INTERCEPTOR(fflush, stream);
+ }
return REAL(fflush)(stream);
}
@@ -1617,21 +1606,23 @@ TSAN_INTERCEPTOR(void*, opendir, char *path) {
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 && epfd >= 0) {
+ if (epfd >= 0)
+ FdAccess(thr, pc, epfd);
+ if (epfd >= 0 && fd >= 0)
+ FdAccess(thr, pc, fd);
+ if (op == EPOLL_CTL_ADD && epfd >= 0)
FdRelease(thr, pc, epfd);
- }
int res = REAL(epoll_ctl)(epfd, op, fd, ev);
- if (fd >= 0)
- FdAccess(thr, pc, fd);
return res;
}
TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
+ if (epfd >= 0)
+ FdAccess(thr, pc, epfd);
int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout);
- if (res > 0 && epfd >= 0) {
+ if (res > 0 && epfd >= 0)
FdAcquire(thr, pc, epfd);
- }
return res;
}
@@ -1777,13 +1768,13 @@ TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service,
// We miss atomic synchronization in getaddrinfo,
// and can report false race between malloc and free
// inside of getaddrinfo. So ignore memory accesses.
- ThreadIgnoreBegin(thr);
+ ThreadIgnoreBegin(thr, pc);
// getaddrinfo calls fopen, which can be intercepted by user.
thr->in_rtl--;
CHECK_EQ(thr->in_rtl, 0);
int res = REAL(getaddrinfo)(node, service, hints, rv);
thr->in_rtl++;
- ThreadIgnoreEnd(thr);
+ ThreadIgnoreEnd(thr, pc);
return res;
}
@@ -1818,7 +1809,7 @@ TSAN_INTERCEPTOR(int, munlockall, void) {
}
TSAN_INTERCEPTOR(int, fork, int fake) {
- SCOPED_TSAN_INTERCEPTOR(fork, fake);
+ SCOPED_INTERCEPTOR_RAW(fork, fake);
int pid = REAL(fork)(fake);
if (pid == 0) {
// child
@@ -1829,12 +1820,26 @@ TSAN_INTERCEPTOR(int, fork, int fake) {
return pid;
}
+static int OnExit(ThreadState *thr) {
+ int status = Finalize(thr);
+ REAL(fflush)(0);
+ return status;
+}
+
struct TsanInterceptorContext {
ThreadState *thr;
const uptr caller_pc;
const uptr pc;
};
+static void HandleRecvmsg(ThreadState *thr, uptr pc,
+ __sanitizer_msghdr *msg) {
+ int fds[64];
+ int cnt = ExtractRecvmsgFDs(msg, fds, ARRAY_SIZE(fds));
+ for (int i = 0; i < cnt; i++)
+ FdEventCreate(thr, pc, fds[i]);
+}
+
#include "sanitizer_common/sanitizer_platform_interceptors.h"
// Causes interceptor recursion (getpwuid_r() calls fopen())
#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
@@ -1845,31 +1850,66 @@ struct TsanInterceptorContext {
// Causes interceptor recursion (glob64() calls lstat64())
#undef SANITIZER_INTERCEPT_GLOB
+#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \
do { \
} while (false)
+
#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \
true)
+
#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \
((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \
false)
+
#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \
TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
ctx = (void *)&_ctx; \
(void) ctx;
+
#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd)
+
#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd)
+
+#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \
+ FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd)
+
#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd)
+
#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name)
+
+#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
+ CTX()->thread_registry->SetThreadNameByUserId(thread, name)
+
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name)
+
+#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \
+ OnExit(((TsanInterceptorContext *) ctx)->thr)
+
+#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \
+ MutexLock(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \
+ MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \
+ MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
+ HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, msg)
+
#include "sanitizer_common/sanitizer_common_interceptors.inc"
#define TSAN_SYSCALL() \
@@ -1899,10 +1939,33 @@ static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) {
MemoryAccessRange(thr, pc, p, s, write);
}
+static void syscall_acquire(uptr pc, uptr addr) {
+ TSAN_SYSCALL();
+ Acquire(thr, pc, addr);
+ Printf("syscall_acquire(%p)\n", addr);
+}
+
+static void syscall_release(uptr pc, uptr addr) {
+ TSAN_SYSCALL();
+ Printf("syscall_release(%p)\n", addr);
+ Release(thr, pc, addr);
+}
+
static void syscall_fd_close(uptr pc, int fd) {
TSAN_SYSCALL();
- if (fd >= 0)
- FdClose(thr, pc, fd);
+ FdClose(thr, pc, fd);
+}
+
+static USED void syscall_fd_acquire(uptr pc, int fd) {
+ TSAN_SYSCALL();
+ FdAcquire(thr, pc, fd);
+ Printf("syscall_fd_acquire(%p)\n", fd);
+}
+
+static USED void syscall_fd_release(uptr pc, int fd) {
+ TSAN_SYSCALL();
+ Printf("syscall_fd_release(%p)\n", fd);
+ FdRelease(thr, pc, fd);
}
static void syscall_pre_fork(uptr pc) {
@@ -1921,22 +1984,54 @@ static void syscall_post_fork(uptr pc, int res) {
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \
syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false)
+
#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true)
+
#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
- do { } while (false)
+ do { \
+ (void)(p); \
+ (void)(s); \
+ } while (false)
+
#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
- do { } while (false)
-#define COMMON_SYSCALL_FD_CLOSE(fd) \
- syscall_fd_close(GET_CALLER_PC(), fd)
+ do { \
+ (void)(p); \
+ (void)(s); \
+ } while (false)
+
+#define COMMON_SYSCALL_ACQUIRE(addr) \
+ syscall_acquire(GET_CALLER_PC(), (uptr)(addr))
+
+#define COMMON_SYSCALL_RELEASE(addr) \
+ syscall_release(GET_CALLER_PC(), (uptr)(addr))
+
+#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd)
+
+#define COMMON_SYSCALL_FD_ACQUIRE(fd) syscall_fd_acquire(GET_CALLER_PC(), fd)
+
+#define COMMON_SYSCALL_FD_RELEASE(fd) syscall_fd_release(GET_CALLER_PC(), fd)
+
#define COMMON_SYSCALL_PRE_FORK() \
syscall_pre_fork(GET_CALLER_PC())
+
#define COMMON_SYSCALL_POST_FORK(res) \
syscall_post_fork(GET_CALLER_PC(), res)
+
#include "sanitizer_common/sanitizer_common_syscalls.inc"
namespace __tsan {
+static void finalize(void *arg) {
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ atexit_ctx->exit(thr, pc);
+ int status = Finalize(thr);
+ REAL(fflush)(0);
+ if (status)
+ REAL(_exit)(status);
+}
+
void ProcessPendingSignals(ThreadState *thr) {
CHECK_EQ(thr->in_rtl, 0);
SignalContext *sctx = SigCtx(thr);
@@ -1986,16 +2081,6 @@ void ProcessPendingSignals(ThreadState *thr) {
thr->in_signal_handler = false;
}
-static void finalize(void *arg) {
- ThreadState * thr = cur_thread();
- uptr pc = 0;
- atexit_ctx->exit(thr, pc);
- int status = Finalize(cur_thread());
- REAL(fflush)(0);
- if (status)
- _exit(status);
-}
-
static void unreachable() {
Printf("FATAL: ThreadSanitizer: unreachable called\n");
Die();
@@ -2015,10 +2100,14 @@ void InitializeInterceptors() {
SANITIZER_COMMON_INTERCEPTORS_INIT;
- TSAN_INTERCEPT(setjmp);
- TSAN_INTERCEPT(_setjmp);
- TSAN_INTERCEPT(sigsetjmp);
- TSAN_INTERCEPT(__sigsetjmp);
+ // We can not use TSAN_INTERCEPT to get setjmp addr,
+ // because it does &setjmp and setjmp is not present in some versions of libc.
+ using __interception::GetRealFunctionAddress;
+ GetRealFunctionAddress("setjmp", (uptr*)&REAL(setjmp), 0, 0);
+ GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0);
+ GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0);
+ GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0);
+
TSAN_INTERCEPT(longjmp);
TSAN_INTERCEPT(siglongjmp);
@@ -2057,10 +2146,8 @@ void InitializeInterceptors() {
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);
@@ -2078,12 +2165,8 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
TSAN_INTERCEPT(pthread_rwlock_unlock);
- INTERCEPT_FUNCTION_VER(pthread_cond_init, GLIBC_2.3.2);
- INTERCEPT_FUNCTION_VER(pthread_cond_destroy, 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");
+ INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2");
TSAN_INTERCEPT(pthread_barrier_init);
TSAN_INTERCEPT(pthread_barrier_destroy);
@@ -2172,8 +2255,11 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(munlockall);
TSAN_INTERCEPT(fork);
+ TSAN_INTERCEPT(dlopen);
+ TSAN_INTERCEPT(dlclose);
TSAN_INTERCEPT(on_exit);
TSAN_INTERCEPT(__cxa_atexit);
+ TSAN_INTERCEPT(_exit);
// Need to setup it, because interceptors check that the function is resolved.
// But atexit is emitted directly into the module, so can't be resolved.
@@ -2195,9 +2281,15 @@ void InitializeInterceptors() {
}
void internal_start_thread(void(*func)(void *arg), void *arg) {
+ // Start the thread with signals blocked, otherwise it can steal users
+ // signals.
+ __sanitizer_kernel_sigset_t set, old;
+ internal_sigfillset(&set);
+ internal_sigprocmask(SIG_SETMASK, &set, &old);
void *th;
REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg);
REAL(pthread_detach)(th);
+ internal_sigprocmask(SIG_SETMASK, &old, 0);
}
} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_interface_ann.cc b/libsanitizer/tsan/tsan_interface_ann.cc
index 46d9e3e..38224f4 100644
--- a/libsanitizer/tsan/tsan_interface_ann.cc
+++ b/libsanitizer/tsan/tsan_interface_ann.cc
@@ -53,11 +53,11 @@ class ScopedAnnotation {
if (!flags()->enable_annotations) \
return; \
ThreadState *thr = cur_thread(); \
- const uptr pc = (uptr)__builtin_return_address(0); \
+ const uptr caller_pc = (uptr)__builtin_return_address(0); \
StatInc(thr, StatAnnotation); \
StatInc(thr, Stat##typ); \
- ScopedAnnotation sa(thr, __FUNCTION__, f, l, \
- (uptr)__builtin_return_address(0)); \
+ ScopedAnnotation sa(thr, __FUNCTION__, f, l, caller_pc); \
+ const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
(void)pc; \
/**/
@@ -381,22 +381,32 @@ void INTERFACE_ATTRIBUTE AnnotateBenignRace(
void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin);
- ThreadIgnoreBegin(thr);
+ ThreadIgnoreBegin(thr, pc);
}
void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd);
- ThreadIgnoreEnd(thr);
+ ThreadIgnoreEnd(thr, pc);
}
void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin);
- ThreadIgnoreBegin(thr);
+ ThreadIgnoreBegin(thr, pc);
}
void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd);
- ThreadIgnoreEnd(thr);
+ ThreadIgnoreEnd(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin);
+ ThreadIgnoreSyncBegin(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd);
+ ThreadIgnoreSyncEnd(thr, pc);
}
void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange(
@@ -429,7 +439,7 @@ void INTERFACE_ATTRIBUTE WTFAnnotateHappensAfter(char *f, int l, uptr addr) {
void INTERFACE_ATTRIBUTE WTFAnnotateBenignRaceSized(
char *f, int l, uptr mem, uptr sz, char *desc) {
SCOPED_ANNOTATION(AnnotateBenignRaceSized);
- BenignRaceImpl(f, l, mem, 1, desc);
+ BenignRaceImpl(f, l, mem, sz, desc);
}
int INTERFACE_ATTRIBUTE RunningOnValgrind() {
diff --git a/libsanitizer/tsan/tsan_interface_atomic.cc b/libsanitizer/tsan/tsan_interface_atomic.cc
index 02ebb47..180d87b 100644
--- a/libsanitizer/tsan/tsan_interface_atomic.cc
+++ b/libsanitizer/tsan/tsan_interface_atomic.cc
@@ -249,11 +249,10 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
// Assume the access is atomic.
if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) {
MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
- return *a;
+ return *a; // as if atomic
}
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->clock.acquire(&s->clock);
+ AcquireImpl(thr, pc, &s->clock);
T v = *a;
s->mtx.ReadUnlock();
__sync_synchronize();
@@ -271,13 +270,15 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
// Strictly saying even relaxed store cuts off release sequence,
// so must reset the clock.
if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) {
- *a = v;
+ *a = v; // as if atomic
return;
}
__sync_synchronize();
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->clock.ReleaseStore(&s->clock);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ ReleaseImpl(thr, pc, &s->clock);
*a = v;
s->mtx.Unlock();
// Trainling memory barrier to provide sequential consistency
@@ -291,13 +292,15 @@ static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
SyncVar *s = 0;
if (mo != mo_relaxed) {
s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
if (IsAcqRelOrder(mo))
- thr->clock.acq_rel(&s->clock);
+ AcquireReleaseImpl(thr, pc, &s->clock);
else if (IsReleaseOrder(mo))
- thr->clock.release(&s->clock);
+ ReleaseImpl(thr, pc, &s->clock);
else if (IsAcquireOrder(mo))
- thr->clock.acquire(&s->clock);
+ AcquireImpl(thr, pc, &s->clock);
}
v = F(a, v);
if (s)
@@ -355,13 +358,15 @@ static bool AtomicCAS(ThreadState *thr, uptr pc,
SyncVar *s = 0;
if (mo != mo_relaxed) {
s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
if (IsAcqRelOrder(mo))
- thr->clock.acq_rel(&s->clock);
+ AcquireReleaseImpl(thr, pc, &s->clock);
else if (IsReleaseOrder(mo))
- thr->clock.release(&s->clock);
+ ReleaseImpl(thr, pc, &s->clock);
else if (IsAcquireOrder(mo))
- thr->clock.acquire(&s->clock);
+ AcquireImpl(thr, pc, &s->clock);
}
T cc = *c;
T pr = func_cas(a, cc, v);
diff --git a/libsanitizer/tsan/tsan_interface_java.cc b/libsanitizer/tsan/tsan_interface_java.cc
index 7cc7257..70b5e5f 100644
--- a/libsanitizer/tsan/tsan_interface_java.cc
+++ b/libsanitizer/tsan/tsan_interface_java.cc
@@ -94,8 +94,6 @@ class ScopedJavaFunc {
static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
static JavaContext *jctx;
-extern atomic_uintptr_t libjvm_begin;
-extern atomic_uintptr_t libjvm_end;
static BlockDesc *getblock(uptr addr) {
uptr i = (addr - jctx->heap_begin) / kHeapAlignment;
@@ -164,17 +162,6 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) {
ScopedJavaFunc scoped(thr, caller_pc); \
/**/
-void __tsan_java_preinit(const char *libjvm_path) {
- SCOPED_JAVA_FUNC(__tsan_java_preinit);
- if (libjvm_path) {
- uptr begin, end;
- if (GetCodeRangeForFile(libjvm_path, &begin, &end)) {
- atomic_store(&libjvm_begin, begin, memory_order_relaxed);
- atomic_store(&libjvm_end, end, memory_order_relaxed);
- }
- }
-}
-
void __tsan_java_init(jptr heap_begin, jptr heap_size) {
SCOPED_JAVA_FUNC(__tsan_java_init);
DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size);
diff --git a/libsanitizer/tsan/tsan_interface_java.h b/libsanitizer/tsan/tsan_interface_java.h
index 818c07b..885ff28 100644
--- a/libsanitizer/tsan/tsan_interface_java.h
+++ b/libsanitizer/tsan/tsan_interface_java.h
@@ -32,11 +32,7 @@ extern "C" {
typedef unsigned long jptr; // NOLINT
-// Must be called before any other callback from Java, right after dlopen
-// of JVM shared lib. If libjvm_path is specified, then all interceptors
-// coming directly from JVM will be ignored.
-void __tsan_java_preinit(const char *libjvm_path) INTERFACE_ATTRIBUTE;
-// Must be called after __tsan_java_preinit but before any other callback.
+// Must be called before any other callback from Java.
void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE;
// Must be called when the application exits.
// Not necessary the last callback (concurrently running threads are OK).
diff --git a/libsanitizer/tsan/tsan_mutexset.h b/libsanitizer/tsan/tsan_mutexset.h
index ef60bd4..df36b46 100644
--- a/libsanitizer/tsan/tsan_mutexset.h
+++ b/libsanitizer/tsan/tsan_mutexset.h
@@ -60,4 +60,4 @@ MutexSet::Desc MutexSet::Get(uptr i) const { return Desc(); }
} // namespace __tsan
-#endif // TSAN_REPORT_H
+#endif // TSAN_MUTEXSET_H
diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h
index ac36c5a..164ee45 100644
--- a/libsanitizer/tsan/tsan_platform.h
+++ b/libsanitizer/tsan/tsan_platform.h
@@ -132,17 +132,24 @@ static inline uptr AlternativeAddress(uptr addr) {
void FlushShadowMemory();
void WriteMemoryProfile(char *buf, uptr buf_size);
+uptr GetRSS();
const char *InitializePlatform();
void FinalizePlatform();
+
+// The additional page is to catch shadow stack overflow as paging fault.
+const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + 4096
+ + 4095) & ~4095;
+
uptr ALWAYS_INLINE GetThreadTrace(int tid) {
- uptr p = kTraceMemBegin + (uptr)(tid * 2) * kTraceSize * sizeof(Event);
+ uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize;
DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
return p;
}
uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) {
- uptr p = kTraceMemBegin + (uptr)(tid * 2 + 1) * kTraceSize * sizeof(Event);
+ uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize
+ + kTraceSize * sizeof(Event);
DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
return p;
}
@@ -153,6 +160,7 @@ void internal_start_thread(void(*func)(void*), void *arg);
// Guesses with high probability, may yield both false positives and negatives.
bool IsGlobalVar(uptr addr);
int ExtractResolvFDs(void *state, int *fds, int nfd);
+int ExtractRecvmsgFDs(void *msg, int *fds, int nfd);
} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc
index f13e3c8..fe69430 100644
--- a/libsanitizer/tsan/tsan_platform_linux.cc
+++ b/libsanitizer/tsan/tsan_platform_linux.cc
@@ -17,6 +17,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_flags.h"
@@ -31,6 +32,7 @@
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
+#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
@@ -43,6 +45,14 @@
#include <resolv.h>
#include <malloc.h>
+#ifdef sa_handler
+# undef sa_handler
+#endif
+
+#ifdef sa_sigaction
+# undef sa_sigaction
+#endif
+
extern "C" struct mallinfo __libc_mallinfo();
namespace __tsan {
@@ -104,10 +114,23 @@ void WriteMemoryProfile(char *buf, uptr buf_size) {
mi.arena >> 20, mi.hblkhd >> 20, mi.fordblks >> 20, mi.keepcost >> 20);
}
-void FlushShadowMemory() {
+uptr GetRSS() {
+ uptr mem[7] = {};
+ __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
+ return mem[6];
+}
+
+
+void FlushShadowMemoryCallback(
+ const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg);
}
+void FlushShadowMemory() {
+ StopTheWorld(FlushShadowMemoryCallback, 0);
+}
+
#ifndef TSAN_GO
static void ProtectRange(uptr beg, uptr end) {
ScopedInRtl in_rtl;
@@ -323,6 +346,9 @@ bool IsGlobalVar(uptr addr) {
}
#ifndef TSAN_GO
+// Extract file descriptors passed to glibc internal __res_iclose function.
+// This is required to properly "close" the fds, because we do not see internal
+// closes within glibc. The code is a pure hack.
int ExtractResolvFDs(void *state, int *fds, int nfd) {
int cnt = 0;
__res_state *statp = (__res_state*)state;
@@ -332,6 +358,26 @@ int ExtractResolvFDs(void *state, int *fds, int nfd) {
}
return cnt;
}
+
+// Extract file descriptors passed via UNIX domain sockets.
+// This is requried to properly handle "open" of these fds.
+// see 'man recvmsg' and 'man 3 cmsg'.
+int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
+ int res = 0;
+ msghdr *msg = (msghdr*)msgp;
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+ for (; cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
+ continue;
+ int n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(fds[0]);
+ for (int i = 0; i < n; i++) {
+ fds[res++] = ((int*)CMSG_DATA(cmsg))[i];
+ if (res == nfd)
+ return res;
+ }
+ }
+ return res;
+}
#endif
diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc
index 15ab22b..f248416 100644
--- a/libsanitizer/tsan/tsan_report.cc
+++ b/libsanitizer/tsan/tsan_report.cc
@@ -132,7 +132,7 @@ static void PrintLocation(const ReportLocation *loc) {
bool print_stack = false;
Printf("%s", d.Location());
if (loc->type == ReportLocationGlobal) {
- Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n",
+ Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
loc->name, loc->size, loc->addr, loc->module, loc->offset);
} else if (loc->type == ReportLocationHeap) {
char thrbuf[kThreadBufSize];
diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc
index 7f18064..a0f3267 100644
--- a/libsanitizer/tsan/tsan_rtl.cc
+++ b/libsanitizer/tsan/tsan_rtl.cc
@@ -37,9 +37,13 @@ THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
// Can be overriden by a front-end.
-bool CPP_WEAK OnFinalize(bool failed) {
+#ifdef TSAN_EXTERNAL_HOOKS
+bool OnFinalize(bool failed);
+#else
+bool WEAK OnFinalize(bool failed) {
return failed;
}
+#endif
static Context *ctx;
Context *CTX() {
@@ -84,7 +88,6 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
// they may be accessed before the ctor.
// , ignore_reads_and_writes()
// , in_rtl()
- , shadow_stack_pos(&shadow_stack[0])
#ifndef TSAN_GO
, jmp_bufs(MBlockJmpBuf)
#endif
@@ -128,17 +131,38 @@ static void BackgroundThread(void *arg) {
}
u64 last_flush = NanoTime();
+ uptr last_rss = 0;
for (int i = 0; ; i++) {
SleepForSeconds(1);
u64 now = NanoTime();
// Flush memory if requested.
- if (flags()->flush_memory_ms) {
+ if (flags()->flush_memory_ms > 0) {
if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) {
+ if (flags()->verbosity > 0)
+ Printf("ThreadSanitizer: periodic memory flush\n");
FlushShadowMemory();
last_flush = NanoTime();
}
}
+ if (flags()->memory_limit_mb > 0) {
+ uptr rss = GetRSS();
+ uptr limit = uptr(flags()->memory_limit_mb) << 20;
+ if (flags()->verbosity > 0) {
+ Printf("ThreadSanitizer: memory flush check"
+ " RSS=%llu LAST=%llu LIMIT=%llu\n",
+ (u64)rss>>20, (u64)last_rss>>20, (u64)limit>>20);
+ }
+ if (2 * rss > limit + last_rss) {
+ if (flags()->verbosity > 0)
+ Printf("ThreadSanitizer: flushing memory due to RSS\n");
+ FlushShadowMemory();
+ rss = GetRSS();
+ if (flags()->verbosity > 0)
+ Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20);
+ }
+ last_rss = rss;
+ }
// Write memory profile if requested.
if (mprof_fd != kInvalidFd)
@@ -174,8 +198,10 @@ void MapThreadTrace(uptr addr, uptr size) {
DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
CHECK_GE(addr, kTraceMemBegin);
CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize);
- if (addr != (uptr)MmapFixedNoReserve(addr, size)) {
- Printf("FATAL: ThreadSanitizer can not mmap thread trace\n");
+ uptr addr1 = (uptr)MmapFixedNoReserve(addr, size);
+ if (addr1 != addr) {
+ Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n",
+ addr, size, addr1);
Die();
}
}
@@ -204,23 +230,21 @@ void Initialize(ThreadState *thr) {
#endif
InitializeFlags(&ctx->flags, env);
// Setup correct file descriptor for error reports.
- if (internal_strcmp(flags()->log_path, "stdout") == 0)
- __sanitizer_set_report_fd(kStdoutFd);
- else if (internal_strcmp(flags()->log_path, "stderr") == 0)
- __sanitizer_set_report_fd(kStderrFd);
- else
- __sanitizer_set_report_path(flags()->log_path);
+ __sanitizer_set_report_path(flags()->log_path);
InitializeSuppressions();
#ifndef TSAN_GO
+ InitializeLibIgnore();
// Initialize external symbolizer before internal threads are started.
const char *external_symbolizer = flags()->external_symbolizer_path;
- if (external_symbolizer != 0 && external_symbolizer[0] != '\0') {
- if (!getSymbolizer()->InitializeExternal(external_symbolizer)) {
- Printf("Failed to start external symbolizer: '%s'\n",
- external_symbolizer);
- Die();
- }
+ bool external_symbolizer_started =
+ Symbolizer::Init(external_symbolizer)->IsExternalAvailable();
+ if (external_symbolizer != 0 && external_symbolizer[0] != '\0' &&
+ !external_symbolizer_started) {
+ Printf("Failed to start external symbolizer: '%s'\n",
+ external_symbolizer);
+ Die();
}
+ Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer);
#endif
internal_start_thread(&BackgroundThread, 0);
@@ -636,9 +660,9 @@ void FuncEntry(ThreadState *thr, uptr 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]);
+ DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack);
#ifndef TSAN_GO
- DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]);
+ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
#else
if (thr->shadow_stack_pos == thr->shadow_stack_end) {
const int sz = thr->shadow_stack_end - thr->shadow_stack;
@@ -664,26 +688,52 @@ void FuncExit(ThreadState *thr) {
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0);
- DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]);
+ DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack);
#ifndef TSAN_GO
- DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]);
+ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
#endif
thr->shadow_stack_pos--;
}
-void ThreadIgnoreBegin(ThreadState *thr) {
+void ThreadIgnoreBegin(ThreadState *thr, uptr pc) {
DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid);
thr->ignore_reads_and_writes++;
- CHECK_GE(thr->ignore_reads_and_writes, 0);
+ CHECK_GT(thr->ignore_reads_and_writes, 0);
thr->fast_state.SetIgnoreBit();
+#ifndef TSAN_GO
+ thr->mop_ignore_set.Add(CurrentStackId(thr, pc));
+#endif
}
-void ThreadIgnoreEnd(ThreadState *thr) {
+void ThreadIgnoreEnd(ThreadState *thr, uptr pc) {
DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid);
thr->ignore_reads_and_writes--;
CHECK_GE(thr->ignore_reads_and_writes, 0);
- if (thr->ignore_reads_and_writes == 0)
+ if (thr->ignore_reads_and_writes == 0) {
thr->fast_state.ClearIgnoreBit();
+#ifndef TSAN_GO
+ thr->mop_ignore_set.Reset();
+#endif
+ }
+}
+
+void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid);
+ thr->ignore_sync++;
+ CHECK_GT(thr->ignore_sync, 0);
+#ifndef TSAN_GO
+ thr->sync_ignore_set.Add(CurrentStackId(thr, pc));
+#endif
+}
+
+void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid);
+ thr->ignore_sync--;
+ CHECK_GE(thr->ignore_sync, 0);
+#ifndef TSAN_GO
+ if (thr->ignore_sync == 0)
+ thr->mop_ignore_set.Reset();
+#endif
}
bool MD5Hash::operator==(const MD5Hash &other) const {
diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h
index 2548f67..45ed096 100644
--- a/libsanitizer/tsan/tsan_rtl.h
+++ b/libsanitizer/tsan/tsan_rtl.h
@@ -27,6 +27,7 @@
#include "sanitizer_common/sanitizer_allocator.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libignore.h"
#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_thread_registry.h"
#include "tsan_clock.h"
@@ -38,6 +39,7 @@
#include "tsan_report.h"
#include "tsan_platform.h"
#include "tsan_mutexset.h"
+#include "tsan_ignoreset.h"
#if SANITIZER_WORDSIZE != 64
# error "ThreadSanitizer is supported only on 64-bit platforms"
@@ -409,17 +411,19 @@ struct ThreadState {
// 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];
+ int ignore_sync;
+ // Go does not support ignores.
#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.
+ IgnoreSet mop_ignore_set;
+ IgnoreSet sync_ignore_set;
+#endif
+ // C/C++ uses fixed size shadow stack embed into Trace.
+ // Go uses malloc-allocated shadow stack with dynamic size.
uptr *shadow_stack;
uptr *shadow_stack_end;
-#endif
+ uptr *shadow_stack_pos;
+ u64 *racy_shadow_addr;
+ u64 racy_state[2];
MutexSet mset;
ThreadClock clock;
#ifndef TSAN_GO
@@ -432,6 +436,7 @@ struct ThreadState {
const int unique_id;
int in_rtl;
bool in_symbolizer;
+ bool in_ignored_lib;
bool is_alive;
bool is_freeing;
bool is_vptr_access;
@@ -439,6 +444,7 @@ struct ThreadState {
const uptr stk_size;
const uptr tls_addr;
const uptr tls_size;
+ ThreadContext *tctx;
DeadlockDetector deadlock_detector;
@@ -596,6 +602,7 @@ void MapThreadTrace(uptr addr, uptr size);
void DontNeedShadowFor(uptr addr, uptr size);
void InitializeShadowMemory();
void InitializeInterceptors();
+void InitializeLibIgnore();
void InitializeDynamicAnnotations();
void ReportRace(ThreadState *thr);
@@ -625,6 +632,7 @@ ReportStack *SkipTsanInternalFrames(ReportStack *ent);
#endif
u32 CurrentStackId(ThreadState *thr, uptr pc);
+ReportStack *SymbolizeStackId(u32 stack_id);
void PrintCurrentStack(ThreadState *thr, uptr pc);
void PrintCurrentStackSlow(); // uses libunwind
@@ -675,8 +683,11 @@ void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc,
void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size);
void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size);
void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size);
-void ThreadIgnoreBegin(ThreadState *thr);
-void ThreadIgnoreEnd(ThreadState *thr);
+
+void ThreadIgnoreBegin(ThreadState *thr, uptr pc);
+void ThreadIgnoreEnd(ThreadState *thr, uptr pc);
+void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc);
+void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc);
void FuncEntry(ThreadState *thr, uptr pc);
void FuncExit(ThreadState *thr);
@@ -700,12 +711,17 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false);
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 MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD
void Acquire(ThreadState *thr, uptr pc, uptr addr);
void AcquireGlobal(ThreadState *thr, uptr pc);
void Release(ThreadState *thr, uptr pc, uptr addr);
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr);
void AfterSleep(ThreadState *thr, uptr pc);
+void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c);
+void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c);
+void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c);
+void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c);
// The hacky call uses custom calling convention and an assembly thunk.
// It is considerably faster that a normal call for the caller
@@ -718,11 +734,11 @@ void AfterSleep(ThreadState *thr, uptr pc);
// so we create a reserve stack frame for it (1024b must be enough).
#define HACKY_CALL(f) \
__asm__ __volatile__("sub $1024, %%rsp;" \
- "/*.cfi_adjust_cfa_offset 1024;*/" \
+ ".cfi_adjust_cfa_offset 1024;" \
".hidden " #f "_thunk;" \
"call " #f "_thunk;" \
"add $1024, %%rsp;" \
- "/*.cfi_adjust_cfa_offset -1024;*/" \
+ ".cfi_adjust_cfa_offset -1024;" \
::: "memory", "cc");
#else
#define HACKY_CALL(f) f()
diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc
index d274a7a..d9a3a3b 100644
--- a/libsanitizer/tsan/tsan_rtl_mutex.cc
+++ b/libsanitizer/tsan/tsan_rtl_mutex.cc
@@ -98,11 +98,8 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
}
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);
+ AcquireImpl(thr, pc, &s->clock);
+ AcquireImpl(thr, pc, &s->read_clock);
} else if (!s->is_recursive) {
StatInc(thr, StatMutexRecLock);
}
@@ -139,10 +136,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) {
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.ReleaseStore(&s->clock);
- StatInc(thr, StatSyncRelease);
+ ReleaseStoreImpl(thr, pc, &s->clock);
} else {
StatInc(thr, StatMutexRecUnlock);
}
@@ -166,10 +160,8 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
addr);
PrintCurrentStack(thr, pc);
}
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->clock.acquire(&s->clock);
+ AcquireImpl(thr, pc, &s->clock);
s->last_lock = thr->fast_state.raw();
- StatInc(thr, StatSyncAcquire);
thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
s->mtx.ReadUnlock();
}
@@ -188,10 +180,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
addr);
PrintCurrentStack(thr, pc);
}
- 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);
+ ReleaseImpl(thr, pc, &s->read_clock);
s->mtx.Unlock();
thr->mset.Del(s->GetId(), false);
}
@@ -209,10 +198,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
StatInc(thr, StatMutexReadUnlock);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
- 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);
+ ReleaseImpl(thr, pc, &s->read_clock);
} else if (s->owner_tid == thr->tid) {
// Seems to be write unlock.
thr->fast_state.IncrementEpoch();
@@ -222,14 +208,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
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->clock.set(thr->tid, thr->fast_state.epoch());
- thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.ReleaseStore(&s->clock);
- StatInc(thr, StatSyncRelease);
+ ReleaseImpl(thr, pc, &s->clock);
} else {
StatInc(thr, StatMutexRecUnlock);
}
@@ -243,13 +222,23 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
s->mtx.Unlock();
}
+void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
+ Context *ctx = CTX();
+ CHECK_GT(thr->in_rtl, 0);
+ DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
+ SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ s->owner_tid = SyncVar::kInvalidTid;
+ s->recursion = 0;
+ 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);
+ if (thr->ignore_sync)
+ return;
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->clock.acquire(&s->clock);
- StatInc(thr, StatSyncAcquire);
+ AcquireImpl(thr, pc, &s->clock);
s->mtx.ReadUnlock();
}
@@ -263,6 +252,9 @@ static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) {
}
void AcquireGlobal(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: AcquireGlobal\n", thr->tid);
+ if (thr->ignore_sync)
+ return;
ThreadRegistryLock l(CTX()->thread_registry);
CTX()->thread_registry->RunCallbackForEachThreadLocked(
UpdateClockCallback, thr);
@@ -271,20 +263,26 @@ void AcquireGlobal(ThreadState *thr, uptr pc) {
void Release(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Release %zx\n", thr->tid, addr);
+ if (thr->ignore_sync)
+ return;
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->clock.release(&s->clock);
- StatInc(thr, StatSyncRelease);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ ReleaseImpl(thr, pc, &s->clock);
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);
+ if (thr->ignore_sync)
+ return;
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->clock.ReleaseStore(&s->clock);
- StatInc(thr, StatSyncRelease);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ ReleaseStoreImpl(thr, pc, &s->clock);
s->mtx.Unlock();
}
@@ -299,6 +297,9 @@ static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) {
}
void AfterSleep(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: AfterSleep %zx\n", thr->tid);
+ if (thr->ignore_sync)
+ return;
thr->last_sleep_stack_id = CurrentStackId(thr, pc);
ThreadRegistryLock l(CTX()->thread_registry);
CTX()->thread_registry->RunCallbackForEachThreadLocked(
@@ -306,4 +307,40 @@ void AfterSleep(ThreadState *thr, uptr pc) {
}
#endif
+void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.acquire(c);
+ StatInc(thr, StatSyncAcquire);
+}
+
+void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(c);
+ StatInc(thr, StatSyncRelease);
+}
+
+void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.ReleaseStore(c);
+ StatInc(thr, StatSyncRelease);
+}
+
+void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.acq_rel(c);
+ StatInc(thr, StatSyncAcquire);
+ StatInc(thr, StatSyncRelease);
+}
+
} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc
index 7c0a028..f2248af 100644
--- a/libsanitizer/tsan/tsan_rtl_report.cc
+++ b/libsanitizer/tsan/tsan_rtl_report.cc
@@ -99,6 +99,18 @@ static void StackStripMain(ReportStack *stack) {
#endif
}
+#ifndef TSAN_GO
+ReportStack *SymbolizeStackId(u32 stack_id) {
+ uptr ssz = 0;
+ const uptr *stack = StackDepotGet(stack_id, &ssz);
+ if (stack == 0)
+ return 0;
+ StackTrace trace;
+ trace.Init(stack, ssz);
+ return SymbolizeStack(trace);
+}
+#endif
+
static ReportStack *SymbolizeStack(const StackTrace& trace) {
if (trace.IsEmpty())
return 0;
@@ -201,13 +213,7 @@ void ScopedReport::AddThread(const ThreadContext *tctx) {
#ifdef TSAN_GO
rt->stack = SymbolizeStack(tctx->creation_stack);
#else
- uptr ssz = 0;
- const uptr *stack = StackDepotGet(tctx->creation_stack_id, &ssz);
- if (stack) {
- StackTrace trace;
- trace.Init(stack, ssz);
- rt->stack = SymbolizeStack(trace);
- }
+ rt->stack = SymbolizeStackId(tctx->creation_stack_id);
#endif
}
@@ -270,13 +276,7 @@ void ScopedReport::AddMutex(const SyncVar *s) {
rm->destroyed = false;
rm->stack = 0;
#ifndef TSAN_GO
- uptr ssz = 0;
- const uptr *stack = StackDepotGet(s->creation_stack_id, &ssz);
- if (stack) {
- StackTrace trace;
- trace.Init(stack, ssz);
- rm->stack = SymbolizeStack(trace);
- }
+ rm->stack = SymbolizeStackId(s->creation_stack_id);
#endif
}
@@ -308,13 +308,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
loc->type = ReportLocationFD;
loc->fd = fd;
loc->tid = creat_tid;
- uptr ssz = 0;
- const uptr *stack = StackDepotGet(creat_stack, &ssz);
- if (stack) {
- StackTrace trace;
- trace.Init(stack, ssz);
- loc->stack = SymbolizeStack(trace);
- }
+ loc->stack = SymbolizeStackId(creat_stack);
ThreadContext *tctx = FindThreadByUidLocked(creat_tid);
if (tctx)
AddThread(tctx);
@@ -335,13 +329,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
loc->file = 0;
loc->line = 0;
loc->stack = 0;
- uptr ssz = 0;
- const uptr *stack = StackDepotGet(b->StackId(), &ssz);
- if (stack) {
- StackTrace trace;
- trace.Init(stack, ssz);
- loc->stack = SymbolizeStack(trace);
- }
+ loc->stack = SymbolizeStackId(b->StackId());
if (tctx)
AddThread(tctx);
return;
@@ -365,13 +353,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
#ifndef TSAN_GO
void ScopedReport::AddSleep(u32 stack_id) {
- uptr ssz = 0;
- const uptr *stack = StackDepotGet(stack_id, &ssz);
- if (stack) {
- StackTrace trace;
- trace.Init(stack, ssz);
- rep_->sleep = SymbolizeStack(trace);
- }
+ rep_->sleep = SymbolizeStackId(stack_id);
}
#endif
@@ -408,7 +390,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
const u64 ebegin = RoundDown(eend, kTracePartSize);
DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
- InternalScopedBuffer<uptr> stack(1024); // FIXME: de-hardcode 1024
+ InternalScopedBuffer<uptr> stack(kShadowStackSize);
for (uptr i = 0; i < hdr->stack0.Size(); i++) {
stack[i] = hdr->stack0.Get(i);
DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
@@ -724,8 +706,8 @@ void PrintCurrentStackSlow() {
#ifndef TSAN_GO
__sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace,
sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace;
- ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(),
- kStackTraceMax);
+ ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(),
+ 0, 0, 0, false);
for (uptr i = 0; i < ptrace->size / 2; i++) {
uptr tmp = ptrace->trace[i];
ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1];
diff --git a/libsanitizer/tsan/tsan_rtl_thread.cc b/libsanitizer/tsan/tsan_rtl_thread.cc
index 9811e1c..dea6698 100644
--- a/libsanitizer/tsan/tsan_rtl_thread.cc
+++ b/libsanitizer/tsan/tsan_rtl_thread.cc
@@ -39,8 +39,7 @@ void ThreadContext::OnDead() {
void ThreadContext::OnJoined(void *arg) {
ThreadState *caller_thr = static_cast<ThreadState *>(arg);
- caller_thr->clock.acquire(&sync);
- StatInc(caller_thr, StatSyncAcquire);
+ AcquireImpl(caller_thr, 0, &sync);
sync.Reset();
}
@@ -57,10 +56,7 @@ void ThreadContext::OnCreated(void *arg) {
args->thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0);
- args->thr->clock.set(args->thr->tid, args->thr->fast_state.epoch());
- args->thr->fast_synch_epoch = args->thr->fast_state.epoch();
- args->thr->clock.release(&sync);
- StatInc(args->thr, StatSyncRelease);
+ ReleaseImpl(args->thr, 0, &sync);
#ifdef TSAN_GO
creation_stack.ObtainCurrent(args->thr, args->pc);
#else
@@ -93,21 +89,23 @@ void ThreadContext::OnStarted(void *arg) {
epoch1 = (u64)-1;
new(thr) ThreadState(CTX(), tid, unique_id,
epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size);
-#ifdef TSAN_GO
+#ifndef TSAN_GO
+ thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0];
+ thr->shadow_stack_pos = thr->shadow_stack;
+ thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize;
+#else
// Setup dynamic shadow stack.
const int kInitStackSize = 8;
- args->thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack,
+ thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack,
kInitStackSize * sizeof(uptr));
- args->thr->shadow_stack_pos = thr->shadow_stack;
- args->thr->shadow_stack_end = thr->shadow_stack + kInitStackSize;
+ thr->shadow_stack_pos = thr->shadow_stack;
+ thr->shadow_stack_end = thr->shadow_stack + kInitStackSize;
#endif
#ifndef TSAN_GO
- AllocatorThreadStart(args->thr);
+ AllocatorThreadStart(thr);
#endif
- thr = args->thr;
thr->fast_synch_epoch = epoch0;
- thr->clock.set(tid, epoch0);
- thr->clock.acquire(&sync);
+ AcquireImpl(thr, 0, &sync);
thr->fast_state.SetHistorySize(flags()->history_size);
const uptr trace = (epoch0 / kTracePartSize) % TraceParts();
Trace *thr_trace = ThreadTrace(thr->tid);
@@ -126,10 +124,7 @@ void ThreadContext::OnFinished() {
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.release(&sync);
- StatInc(thr, StatSyncRelease);
+ ReleaseImpl(thr, 0, &sync);
}
epoch1 = thr->fast_state.epoch();
@@ -163,13 +158,34 @@ static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) {
}
#endif
-static void ThreadCheckIgnore(ThreadState *thr) {
- if (thr->ignore_reads_and_writes) {
- Printf("ThreadSanitizer: thread T%d finished with ignores enabled.\n",
- thr->tid);
+#ifndef TSAN_GO
+static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) {
+ if (tctx->tid == 0) {
+ Printf("ThreadSanitizer: main thread finished with ignores enabled\n");
+ } else {
+ Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled,"
+ " created at:\n", tctx->tid, tctx->name);
+ PrintStack(SymbolizeStackId(tctx->creation_stack_id));
+ }
+ Printf(" One of the following ignores was not ended"
+ " (in order of probability)\n");
+ for (uptr i = 0; i < set->Size(); i++) {
+ Printf(" Ignore was enabled at:\n");
+ PrintStack(SymbolizeStackId(set->At(i)));
}
+ Die();
}
+static void ThreadCheckIgnore(ThreadState *thr) {
+ if (thr->ignore_reads_and_writes)
+ ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set);
+ if (thr->ignore_sync)
+ ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set);
+}
+#else
+static void ThreadCheckIgnore(ThreadState *thr) {}
+#endif
+
void ThreadFinalize(ThreadState *thr) {
CHECK_GT(thr->in_rtl, 0);
ThreadCheckIgnore(thr);
@@ -209,6 +225,7 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
}
void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
+ Context *ctx = CTX();
CHECK_GT(thr->in_rtl, 0);
uptr stk_addr = 0;
uptr stk_size = 0;
@@ -235,8 +252,13 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
}
}
+ ThreadRegistry *tr = ctx->thread_registry;
OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size };
- CTX()->thread_registry->StartThread(tid, os_id, &args);
+ tr->StartThread(tid, os_id, &args);
+
+ tr->Lock();
+ thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid);
+ tr->Unlock();
}
void ThreadFinish(ThreadState *thr) {
diff --git a/libsanitizer/tsan/tsan_stat.cc b/libsanitizer/tsan/tsan_stat.cc
index 3a3ac11..cdd11ca 100644
--- a/libsanitizer/tsan/tsan_stat.cc
+++ b/libsanitizer/tsan/tsan_stat.cc
@@ -140,6 +140,7 @@ void StatOutput(u64 *stat) {
name[StatInt_strcasecmp] = " strcasecmp ";
name[StatInt_strncasecmp] = " strncasecmp ";
name[StatInt_atexit] = " atexit ";
+ name[StatInt__exit] = " _exit ";
name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire ";
name[StatInt___cxa_guard_release] = " __cxa_guard_release ";
name[StatInt___cxa_guard_abort] = " __cxa_guard_abort ";
@@ -179,6 +180,7 @@ void StatOutput(u64 *stat) {
name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait ";
name[StatInt_pthread_once] = " pthread_once ";
name[StatInt_pthread_getschedparam] = " pthread_getschedparam ";
+ name[StatInt_pthread_setname_np] = " pthread_setname_np ";
name[StatInt_sem_init] = " sem_init ";
name[StatInt_sem_destroy] = " sem_destroy ";
name[StatInt_sem_wait] = " sem_wait ";
@@ -288,6 +290,7 @@ void StatOutput(u64 *stat) {
name[StatInt_ctime_r] = " ctime_r ";
name[StatInt_asctime] = " asctime ";
name[StatInt_asctime_r] = " asctime_r ";
+ name[StatInt_strptime] = " strptime ";
name[StatInt_frexp] = " frexp ";
name[StatInt_frexpf] = " frexpf ";
name[StatInt_frexpl] = " frexpl ";
@@ -356,6 +359,7 @@ void StatOutput(u64 *stat) {
name[StatInt_sched_getaffinity] = " sched_getaffinity ";
name[StatInt_strerror] = " strerror ";
name[StatInt_strerror_r] = " strerror_r ";
+ name[StatInt___xpg_strerror_r] = " __xpg_strerror_r ";
name[StatInt_scandir] = " scandir ";
name[StatInt_scandir64] = " scandir64 ";
name[StatInt_getgroups] = " getgroups ";
@@ -369,6 +373,59 @@ void StatOutput(u64 *stat) {
name[StatInt_sigprocmask] = " sigprocmask ";
name[StatInt_backtrace] = " backtrace ";
name[StatInt_backtrace_symbols] = " backtrace_symbols ";
+ name[StatInt_dlopen] = " dlopen ";
+ name[StatInt_dlclose] = " dlclose ";
+ name[StatInt_getmntent] = " getmntent ";
+ name[StatInt_getmntent_r] = " getmntent_r ";
+ name[StatInt_statfs] = " statfs ";
+ name[StatInt_statfs64] = " statfs64 ";
+ name[StatInt_fstatfs] = " fstatfs ";
+ name[StatInt_fstatfs64] = " fstatfs64 ";
+ name[StatInt_statvfs] = " statvfs ";
+ name[StatInt_statvfs64] = " statvfs64 ";
+ name[StatInt_fstatvfs] = " fstatvfs ";
+ name[StatInt_fstatvfs64] = " fstatvfs64 ";
+ name[StatInt_initgroups] = " initgroups ";
+ name[StatInt_ether_ntoa] = " ether_ntoa ";
+ name[StatInt_ether_aton] = " ether_aton ";
+ name[StatInt_ether_ntoa_r] = " ether_ntoa_r ";
+ name[StatInt_ether_aton_r] = " ether_aton_r ";
+ name[StatInt_ether_ntohost] = " ether_ntohost ";
+ name[StatInt_ether_hostton] = " ether_hostton ";
+ name[StatInt_ether_line] = " ether_line ";
+ name[StatInt_shmctl] = " shmctl ";
+ name[StatInt_random_r] = " random_r ";
+ name[StatInt_tmpnam] = " tmpnam ";
+ name[StatInt_tmpnam_r] = " tmpnam_r ";
+ name[StatInt_tempnam] = " tempnam ";
+ name[StatInt_sincos] = " sincos ";
+ name[StatInt_sincosf] = " sincosf ";
+ name[StatInt_sincosl] = " sincosl ";
+ name[StatInt_remquo] = " remquo ";
+ name[StatInt_remquof] = " remquof ";
+ name[StatInt_remquol] = " remquol ";
+ name[StatInt_lgamma] = " lgamma ";
+ name[StatInt_lgammaf] = " lgammaf ";
+ name[StatInt_lgammal] = " lgammal ";
+ name[StatInt_lgamma_r] = " lgamma_r ";
+ name[StatInt_lgammaf_r] = " lgammaf_r ";
+ name[StatInt_lgammal_r] = " lgammal_r ";
+ name[StatInt_drand48_r] = " drand48_r ";
+ name[StatInt_lrand48_r] = " lrand48_r ";
+ name[StatInt_getline] = " getline ";
+ name[StatInt_getdelim] = " getdelim ";
+ name[StatInt_iconv] = " iconv ";
+ name[StatInt_times] = " times ";
+
+ name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT
+ name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT
+ name[StatInt_pthread_attr_getschedparam] = " pthread_addr_getschedparam "; // NOLINT
+ name[StatInt_pthread_attr_getschedpolicy] = " pthread_addr_getschedpolicy "; // NOLINT
+ name[StatInt_pthread_attr_getinheritsched] = " pthread_addr_getinheritsched "; // NOLINT
+ name[StatInt_pthread_attr_getscope] = " pthread_addr_getscope "; // NOLINT
+ name[StatInt_pthread_attr_getstacksize] = " pthread_addr_getstacksize "; // NOLINT
+ name[StatInt_pthread_attr_getstack] = " pthread_addr_getstack "; // NOLINT
+ name[StatInt_pthread_attr_getaffinity_np] = " pthread_addr_getaffinity_np "; // NOLINT
name[StatAnnotation] = "Dynamic annotations ";
name[StatAnnotateHappensBefore] = " HappensBefore ";
@@ -400,6 +457,8 @@ void StatOutput(u64 *stat) {
name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd ";
name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin ";
name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd ";
+ name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin ";
+ name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd ";
name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange ";
name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange ";
name[StatAnnotateThreadName] = " ThreadName ";
diff --git a/libsanitizer/tsan/tsan_stat.h b/libsanitizer/tsan/tsan_stat.h
index e392ff6..998f1cd 100644
--- a/libsanitizer/tsan/tsan_stat.h
+++ b/libsanitizer/tsan/tsan_stat.h
@@ -137,6 +137,7 @@ enum StatType {
StatInt_strstr,
StatInt_strdup,
StatInt_atexit,
+ StatInt__exit,
StatInt___cxa_guard_acquire,
StatInt___cxa_guard_release,
StatInt___cxa_guard_abort,
@@ -174,6 +175,7 @@ enum StatType {
StatInt_pthread_barrier_wait,
StatInt_pthread_once,
StatInt_pthread_getschedparam,
+ StatInt_pthread_setname_np,
StatInt_sem_init,
StatInt_sem_destroy,
StatInt_sem_wait,
@@ -283,6 +285,7 @@ enum StatType {
StatInt_ctime_r,
StatInt_asctime,
StatInt_asctime_r,
+ StatInt_strptime,
StatInt_frexp,
StatInt_frexpf,
StatInt_frexpl,
@@ -351,6 +354,7 @@ enum StatType {
StatInt_sched_getaffinity,
StatInt_strerror,
StatInt_strerror_r,
+ StatInt___xpg_strerror_r,
StatInt_scandir,
StatInt_scandir64,
StatInt_getgroups,
@@ -364,6 +368,59 @@ enum StatType {
StatInt_sigprocmask,
StatInt_backtrace,
StatInt_backtrace_symbols,
+ StatInt_dlopen,
+ StatInt_dlclose,
+ StatInt_getmntent,
+ StatInt_getmntent_r,
+ StatInt_statfs,
+ StatInt_statfs64,
+ StatInt_fstatfs,
+ StatInt_fstatfs64,
+ StatInt_statvfs,
+ StatInt_statvfs64,
+ StatInt_fstatvfs,
+ StatInt_fstatvfs64,
+ StatInt_initgroups,
+ StatInt_ether_ntoa,
+ StatInt_ether_aton,
+ StatInt_ether_ntoa_r,
+ StatInt_ether_aton_r,
+ StatInt_ether_ntohost,
+ StatInt_ether_hostton,
+ StatInt_ether_line,
+ StatInt_shmctl,
+ StatInt_random_r,
+ StatInt_tmpnam,
+ StatInt_tmpnam_r,
+ StatInt_tempnam,
+ StatInt_sincos,
+ StatInt_sincosf,
+ StatInt_sincosl,
+ StatInt_remquo,
+ StatInt_remquof,
+ StatInt_remquol,
+ StatInt_lgamma,
+ StatInt_lgammaf,
+ StatInt_lgammal,
+ StatInt_lgamma_r,
+ StatInt_lgammaf_r,
+ StatInt_lgammal_r,
+ StatInt_drand48_r,
+ StatInt_lrand48_r,
+ StatInt_getline,
+ StatInt_getdelim,
+ StatInt_iconv,
+ StatInt_times,
+
+ StatInt_pthread_attr_getdetachstate,
+ StatInt_pthread_attr_getguardsize,
+ StatInt_pthread_attr_getschedparam,
+ StatInt_pthread_attr_getschedpolicy,
+ StatInt_pthread_attr_getinheritsched,
+ StatInt_pthread_attr_getscope,
+ StatInt_pthread_attr_getstacksize,
+ StatInt_pthread_attr_getstack,
+ StatInt_pthread_attr_getaffinity_np,
// Dynamic annotations.
StatAnnotation,
@@ -396,6 +453,8 @@ enum StatType {
StatAnnotateIgnoreReadsEnd,
StatAnnotateIgnoreWritesBegin,
StatAnnotateIgnoreWritesEnd,
+ StatAnnotateIgnoreSyncBegin,
+ StatAnnotateIgnoreSyncEnd,
StatAnnotatePublishMemoryRange,
StatAnnotateUnpublishMemoryRange,
StatAnnotateThreadName,
diff --git a/libsanitizer/tsan/tsan_suppressions.cc b/libsanitizer/tsan/tsan_suppressions.cc
index 912b383..fa0c30d 100644
--- a/libsanitizer/tsan/tsan_suppressions.cc
+++ b/libsanitizer/tsan/tsan_suppressions.cc
@@ -85,6 +85,11 @@ void InitializeSuppressions() {
#endif
}
+SuppressionContext *GetSuppressionContext() {
+ CHECK_NE(g_ctx, 0);
+ return g_ctx;
+}
+
SuppressionType conv(ReportType typ) {
if (typ == ReportTypeRace)
return SuppressionRace;
diff --git a/libsanitizer/tsan/tsan_suppressions.h b/libsanitizer/tsan/tsan_suppressions.h
index e38d81e..2939e9a 100644
--- a/libsanitizer/tsan/tsan_suppressions.h
+++ b/libsanitizer/tsan/tsan_suppressions.h
@@ -20,6 +20,7 @@ void InitializeSuppressions();
void PrintMatchedSuppressions();
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp);
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp);
+SuppressionContext *GetSuppressionContext();
} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc
index 0c7efac7..c0e794b 100644
--- a/libsanitizer/tsan/tsan_symbolize.cc
+++ b/libsanitizer/tsan/tsan_symbolize.cc
@@ -20,19 +20,17 @@
namespace __tsan {
-struct ScopedInSymbolizer {
- ScopedInSymbolizer() {
- ThreadState *thr = cur_thread();
- CHECK(!thr->in_symbolizer);
- thr->in_symbolizer = true;
- }
+void EnterSymbolizer() {
+ ThreadState *thr = cur_thread();
+ CHECK(!thr->in_symbolizer);
+ thr->in_symbolizer = true;
+}
- ~ScopedInSymbolizer() {
- ThreadState *thr = cur_thread();
- CHECK(thr->in_symbolizer);
- thr->in_symbolizer = false;
- }
-};
+void ExitSymbolizer() {
+ ThreadState *thr = cur_thread();
+ CHECK(thr->in_symbolizer);
+ thr->in_symbolizer = false;
+}
ReportStack *NewReportStackEntry(uptr addr) {
ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
@@ -42,18 +40,6 @@ ReportStack *NewReportStackEntry(uptr addr) {
return ent;
}
-// Strip module path to make output shorter.
-static char *StripModuleName(const char *module) {
- if (module == 0)
- return 0;
- const char *short_module_name = internal_strrchr(module, '/');
- if (short_module_name)
- short_module_name += 1;
- else
- short_module_name = module;
- return internal_strdup(short_module_name);
-}
-
static ReportStack *NewReportStackEntry(const AddressInfo &info) {
ReportStack *ent = NewReportStackEntry(info.address);
ent->module = StripModuleName(info.module);
@@ -117,15 +103,14 @@ ReportStack *SymbolizeCode(uptr addr) {
ent->col = col;
return ent;
}
- if (!getSymbolizer()->IsAvailable())
+ if (!Symbolizer::Get()->IsAvailable())
return SymbolizeCodeAddr2Line(addr);
- ScopedInSymbolizer in_symbolizer;
static const uptr kMaxAddrFrames = 16;
InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
for (uptr i = 0; i < kMaxAddrFrames; i++)
new(&addr_frames[i]) AddressInfo();
- uptr addr_frames_num =
- getSymbolizer()->SymbolizeCode(addr, addr_frames.data(), kMaxAddrFrames);
+ uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode(
+ addr, addr_frames.data(), kMaxAddrFrames);
if (addr_frames_num == 0)
return NewReportStackEntry(addr);
ReportStack *top = 0;
@@ -144,11 +129,10 @@ ReportStack *SymbolizeCode(uptr addr) {
}
ReportLocation *SymbolizeData(uptr addr) {
- if (!getSymbolizer()->IsAvailable())
+ if (!Symbolizer::Get()->IsAvailable())
return 0;
- ScopedInSymbolizer in_symbolizer;
DataInfo info;
- if (!getSymbolizer()->SymbolizeData(addr, &info))
+ if (!Symbolizer::Get()->SymbolizeData(addr, &info))
return 0;
ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack,
sizeof(ReportLocation));
@@ -164,10 +148,9 @@ ReportLocation *SymbolizeData(uptr addr) {
}
void SymbolizeFlush() {
- if (!getSymbolizer()->IsAvailable())
+ if (!Symbolizer::Get()->IsAvailable())
return;
- ScopedInSymbolizer in_symbolizer;
- getSymbolizer()->Flush();
+ Symbolizer::Get()->Flush();
}
} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_symbolize.h b/libsanitizer/tsan/tsan_symbolize.h
index c610d1f..892c11c 100644
--- a/libsanitizer/tsan/tsan_symbolize.h
+++ b/libsanitizer/tsan/tsan_symbolize.h
@@ -16,6 +16,8 @@
namespace __tsan {
+void EnterSymbolizer();
+void ExitSymbolizer();
ReportStack *SymbolizeCode(uptr addr);
ReportLocation *SymbolizeData(uptr addr);
void SymbolizeFlush();
diff --git a/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc b/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc
index da0c87d..c278a42 100644
--- a/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc
+++ b/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc
@@ -58,7 +58,6 @@ static void NOINLINE InitModule(ModuleDesc *m) {
}
int pid = fork();
if (pid == 0) {
- __sanitizer_set_report_fd(STDERR_FILENO);
internal_close(STDOUT_FILENO);
internal_close(STDIN_FILENO);
internal_dup2(outfd[0], STDIN_FILENO);
diff --git a/libsanitizer/tsan/tsan_sync.cc b/libsanitizer/tsan/tsan_sync.cc
index 04fef61..0c5be10 100644
--- a/libsanitizer/tsan/tsan_sync.cc
+++ b/libsanitizer/tsan/tsan_sync.cc
@@ -263,6 +263,11 @@ void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) {
n_ = c_ - !!toppc;
}
} else {
+ // Cap potentially huge stacks.
+ if (n_ + !!toppc > kTraceStackSize) {
+ start = n_ - kTraceStackSize + !!toppc;
+ n_ = kTraceStackSize - !!toppc;
+ }
s_ = (uptr*)internal_alloc(MBlockStackTrace,
(n_ + !!toppc) * sizeof(s_[0]));
}
diff --git a/libsanitizer/tsan/tsan_trace.h b/libsanitizer/tsan/tsan_trace.h
index 6986483..93ed8d9 100644
--- a/libsanitizer/tsan/tsan_trace.h
+++ b/libsanitizer/tsan/tsan_trace.h
@@ -60,6 +60,11 @@ struct TraceHeader {
struct Trace {
TraceHeader headers[kTraceParts];
Mutex mtx;
+#ifndef TSAN_GO
+ // Must be last to catch overflow as paging fault.
+ // Go shadow stack is dynamically allocated.
+ uptr shadow_stack[kShadowStackSize];
+#endif
Trace()
: mtx(MutexTypeTrace, StatMtxTrace) {