Commit cd0be65c by Wei Mi Committed by Wei Mi

tsan: New directory.

libsanitizer/
	* tsan: New directory. Import tsan runtime from llvm.
	* configure.ac: Add 64 bits tsan build.
	* Makefile.am: Likewise.
	* configure: Regenerated.
	* Makefile.in: Likewise.

From-SVN: r193737
parent 32b4b7f5
2012-11-22 Wei Mi <wmi@google.com>
* tsan: New directory. Import tsan runtime from llvm.
* configure.ac: Add 64 bits tsan build.
* Makefile.am: Likewise.
* configure: Regenerated.
* Makefile.in: Likewise.
2012-11-21 Kostya Serebryany <kcc@google.com>
* README.gcc: Extend the README.gcc with mode details.
......
ACLOCAL_AMFLAGS = -I .. -I ../config
if MULTISUBDIR32
SUBDIRS = interception sanitizer_common asan
else
SUBDIRS = interception sanitizer_common asan tsan
endif
# Work around what appears to be a GNU make bug handling MAKEFLAGS
# values defined in terms of make variables, as is the case for CC and
......
......@@ -78,7 +78,7 @@ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
distdir dist dist-all distcheck
ETAGS = etags
CTAGS = ctags
DIST_SUBDIRS = $(SUBDIRS)
DIST_SUBDIRS = interception sanitizer_common asan tsan
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
......@@ -244,7 +244,8 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
ACLOCAL_AMFLAGS = -I .. -I ../config
SUBDIRS = interception sanitizer_common asan
@MULTISUBDIR32_FALSE@SUBDIRS = interception sanitizer_common asan tsan
@MULTISUBDIR32_TRUE@SUBDIRS = interception sanitizer_common asan
# Work around what appears to be a GNU make bug handling MAKEFLAGS
# values defined in terms of make variables, as is the case for CC and
......
......@@ -604,6 +604,8 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
MULTISUBDIR32_FALSE
MULTISUBDIR32_TRUE
enable_static
enable_shared
CXXCPP
......@@ -10898,7 +10900,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 10901 "configure"
#line 10903 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
......@@ -11004,7 +11006,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11007 "configure"
#line 11009 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
......@@ -14266,6 +14268,14 @@ if test "${multilib}" = "yes"; then
else
multilib_arg=
fi
if test "x$with_multisubdir" = "x32"; then
MULTISUBDIR32_TRUE=
MULTISUBDIR32_FALSE='#'
else
MULTISUBDIR32_TRUE='#'
MULTISUBDIR32_FALSE=
fi
ac_config_files="$ac_config_files Makefile"
......@@ -14273,6 +14283,11 @@ ac_config_files="$ac_config_files Makefile"
ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile asan/Makefile"
if test "x$with_multisubdir" != "x32"; then
ac_config_files="$ac_config_files tsan/Makefile"
fi
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
......@@ -14434,6 +14449,10 @@ if test -z "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then
as_fn_error "conditional \"am__fastdepCCAS\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${MULTISUBDIR32_TRUE}" && test -z "${MULTISUBDIR32_FALSE}"; then
as_fn_error "conditional \"MULTISUBDIR32\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
: ${CONFIG_STATUS=./config.status}
ac_write_fail=0
......@@ -15388,6 +15407,7 @@ do
"interception/Makefile") CONFIG_FILES="$CONFIG_FILES interception/Makefile" ;;
"sanitizer_common/Makefile") CONFIG_FILES="$CONFIG_FILES sanitizer_common/Makefile" ;;
"asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
"tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
*) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
......@@ -16752,6 +16772,17 @@ _EOF
. ${multi_basedir}/config-ml.in
{ ml_norecursion=; unset ml_norecursion;}
;;
"tsan/Makefile":F) cat > vpsed$$ << \_EOF
s!`test -f '$<' || echo '$(srcdir)/'`!!
_EOF
sed -f vpsed$$ $ac_file > tmp$$
mv tmp$$ $ac_file
rm vpsed$$
echo 'MULTISUBDIR =' >> $ac_file
ml_norecursion=yes
. ${multi_basedir}/config-ml.in
{ ml_norecursion=; unset ml_norecursion;}
;;
esac
done # for ac_tag
......
......@@ -72,6 +72,7 @@ if test "${multilib}" = "yes"; then
else
multilib_arg=
fi
AM_CONDITIONAL(MULTISUBDIR32, [test "x$with_multisubdir" = "x32"])
AC_CONFIG_FILES([Makefile])
......@@ -88,4 +89,19 @@ _EOF
AS_UNSET([ml_norecursion])
])
if test "x$with_multisubdir" != "x32"; then
AC_CONFIG_FILES(AC_FOREACH([DIR], [tsan], [DIR/Makefile ]),
[cat > vpsed$$ << \_EOF
s!`test -f '$<' || echo '$(srcdir)/'`!!
_EOF
sed -f vpsed$$ $ac_file > tmp$$
mv tmp$$ $ac_file
rm vpsed$$
echo 'MULTISUBDIR =' >> $ac_file
ml_norecursion=yes
. ${multi_basedir}/config-ml.in
AS_UNSET([ml_norecursion])
])
fi
AC_OUTPUT
AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libtsan.la
tsan_files = \
tsan_clock.cc \
tsan_interface_atomic.cc \
tsan_mutex.cc \
tsan_report.cc \
tsan_rtl_thread.cc \
tsan_symbolize.cc \
tsan_flags.cc \
tsan_interface.cc \
tsan_platform_linux.cc \
tsan_rtl.cc \
tsan_stat.cc \
tsan_sync.cc \
tsan_interceptors.cc \
tsan_md5.cc \
tsan_platform_mac.cc \
tsan_rtl_mutex.cc \
tsan_suppressions.cc \
tsan_interface_ann.cc \
tsan_mman.cc \
tsan_printf.cc \
tsan_rtl_report.cc \
tsan_symbolize_addr2line_linux.cc
libtsan_la_SOURCES = $(tsan_files)
libtsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(top_builddir)/../libstdc++-v3/src/libstdc++.la
libtsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` -lpthread -ldl
# Work around what appears to be a GNU make bug handling MAKEFLAGS
# values defined in terms of make variables, as is the case for CC and
# friends when we are called from the top level Makefile.
AM_MAKEFLAGS = \
"AR_FLAGS=$(AR_FLAGS)" \
"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
"CFLAGS=$(CFLAGS)" \
"CXXFLAGS=$(CXXFLAGS)" \
"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
"INSTALL=$(INSTALL)" \
"INSTALL_DATA=$(INSTALL_DATA)" \
"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
"JC1FLAGS=$(JC1FLAGS)" \
"LDFLAGS=$(LDFLAGS)" \
"LIBCFLAGS=$(LIBCFLAGS)" \
"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
"MAKE=$(MAKE)" \
"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
"PICFLAG=$(PICFLAG)" \
"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
"SHELL=$(SHELL)" \
"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
"exec_prefix=$(exec_prefix)" \
"infodir=$(infodir)" \
"libdir=$(libdir)" \
"prefix=$(prefix)" \
"includedir=$(includedir)" \
"AR=$(AR)" \
"AS=$(AS)" \
"CC=$(CC)" \
"CXX=$(CXX)" \
"LD=$(LD)" \
"LIBCFLAGS=$(LIBCFLAGS)" \
"NM=$(NM)" \
"PICFLAG=$(PICFLAG)" \
"RANLIB=$(RANLIB)" \
"DESTDIR=$(DESTDIR)"
MAKEOVERRIDES=
## ################################################################
# This file is used to maintain libtool version info for libmudflap. See
# the libtool manual to understand the meaning of the fields. This is
# a separate file so that version updates don't involve re-running
# automake.
# CURRENT:REVISION:AGE
0:0:0
//===-- tsan_clock.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_clock.h"
#include "tsan_rtl.h"
// It's possible to optimize clock operations for some important cases
// so that they are O(1). The cases include singletons, once's, local mutexes.
// First, SyncClock must be re-implemented to allow indexing by tid.
// It must not necessarily be a full vector clock, though. For example it may
// be a multi-level table.
// Then, each slot in SyncClock must contain a dirty bit (it's united with
// the clock value, so no space increase). The acquire algorithm looks
// as follows:
// void acquire(thr, tid, thr_clock, sync_clock) {
// if (!sync_clock[tid].dirty)
// return; // No new info to acquire.
// // This handles constant reads of singleton pointers and
// // stop-flags.
// acquire_impl(thr_clock, sync_clock); // As usual, O(N).
// sync_clock[tid].dirty = false;
// sync_clock.dirty_count--;
// }
// The release operation looks as follows:
// void release(thr, tid, thr_clock, sync_clock) {
// // thr->sync_cache is a simple fixed-size hash-based cache that holds
// // several previous sync_clock's.
// if (thr->sync_cache[sync_clock] >= thr->last_acquire_epoch) {
// // The thread did no acquire operations since last release on this clock.
// // So update only the thread's slot (other slots can't possibly change).
// sync_clock[tid].clock = thr->epoch;
// if (sync_clock.dirty_count == sync_clock.cnt
// || (sync_clock.dirty_count == sync_clock.cnt - 1
// && sync_clock[tid].dirty == false))
// // All dirty flags are set, bail out.
// return;
// set all dirty bits, but preserve the thread's bit. // O(N)
// update sync_clock.dirty_count;
// return;
// }
// release_impl(thr_clock, sync_clock); // As usual, O(N).
// set all dirty bits, but preserve the thread's bit.
// // The previous step is combined with release_impl(), so that
// // we scan the arrays only once.
// update sync_clock.dirty_count;
// }
namespace __tsan {
ThreadClock::ThreadClock() {
nclk_ = 0;
for (uptr i = 0; i < (uptr)kMaxTidInClock; i++)
clk_[i] = 0;
}
void ThreadClock::acquire(const SyncClock *src) {
DCHECK(nclk_ <= kMaxTid);
DCHECK(src->clk_.Size() <= kMaxTid);
const uptr nclk = src->clk_.Size();
if (nclk == 0)
return;
nclk_ = max(nclk_, nclk);
for (uptr i = 0; i < nclk; i++) {
if (clk_[i] < src->clk_[i])
clk_[i] = src->clk_[i];
}
}
void ThreadClock::release(SyncClock *dst) const {
DCHECK(nclk_ <= kMaxTid);
DCHECK(dst->clk_.Size() <= kMaxTid);
if (dst->clk_.Size() < nclk_)
dst->clk_.Resize(nclk_);
for (uptr i = 0; i < nclk_; i++) {
if (dst->clk_[i] < clk_[i])
dst->clk_[i] = clk_[i];
}
}
void ThreadClock::ReleaseStore(SyncClock *dst) const {
DCHECK(nclk_ <= kMaxTid);
DCHECK(dst->clk_.Size() <= kMaxTid);
if (dst->clk_.Size() < nclk_)
dst->clk_.Resize(nclk_);
for (uptr i = 0; i < nclk_; i++)
dst->clk_[i] = clk_[i];
for (uptr i = nclk_; i < dst->clk_.Size(); i++)
dst->clk_[i] = 0;
}
void ThreadClock::acq_rel(SyncClock *dst) {
acquire(dst);
release(dst);
}
void ThreadClock::Disable(unsigned tid) {
u64 c0 = clk_[tid];
for (uptr i = 0; i < kMaxTidInClock; i++)
clk_[i] = (u64)-1;
clk_[tid] = c0;
}
SyncClock::SyncClock()
: clk_(MBlockClock) {
}
} // namespace __tsan
//===-- tsan_clock.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_CLOCK_H
#define TSAN_CLOCK_H
#include "tsan_defs.h"
#include "tsan_vector.h"
namespace __tsan {
// The clock that lives in sync variables (mutexes, atomics, etc).
class SyncClock {
public:
SyncClock();
uptr size() const {
return clk_.Size();
}
void Reset() {
clk_.Reset();
}
private:
Vector<u64> clk_;
friend struct ThreadClock;
};
// The clock that lives in threads.
struct ThreadClock {
public:
ThreadClock();
u64 get(unsigned tid) const {
DCHECK_LT(tid, kMaxTidInClock);
return clk_[tid];
}
void set(unsigned tid, u64 v) {
DCHECK_LT(tid, kMaxTid);
DCHECK_GE(v, clk_[tid]);
clk_[tid] = v;
if (nclk_ <= tid)
nclk_ = tid + 1;
}
void tick(unsigned tid) {
DCHECK_LT(tid, kMaxTid);
clk_[tid]++;
if (nclk_ <= tid)
nclk_ = tid + 1;
}
void Disable(unsigned tid);
uptr size() const {
return nclk_;
}
void acquire(const SyncClock *src);
void release(SyncClock *dst) const;
void acq_rel(SyncClock *dst);
void ReleaseStore(SyncClock *dst) const;
private:
uptr nclk_;
u64 clk_[kMaxTidInClock];
};
} // namespace __tsan
#endif // TSAN_CLOCK_H
//===-- tsan_defs.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_DEFS_H
#define TSAN_DEFS_H
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_stat.h"
#ifndef TSAN_DEBUG
#define TSAN_DEBUG 0
#endif // TSAN_DEBUG
namespace __tsan {
const int kTidBits = 13;
const unsigned kMaxTid = 1 << kTidBits;
const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
const int kClkBits = 43;
#ifndef TSAN_GO
const int kShadowStackSize = 4 * 1024;
const int kTraceStackSize = 256;
#endif
#ifdef TSAN_SHADOW_COUNT
# if TSAN_SHADOW_COUNT == 2 \
|| TSAN_SHADOW_COUNT == 4 || TSAN_SHADOW_COUNT == 8
const unsigned kShadowCnt = TSAN_SHADOW_COUNT;
# else
# error "TSAN_SHADOW_COUNT must be one of 2,4,8"
# endif
#else
// Count of shadow values in a shadow cell.
const unsigned kShadowCnt = 8;
#endif
// That many user bytes are mapped onto a single shadow cell.
const unsigned kShadowCell = 8;
// Size of a single shadow value (u64).
const unsigned kShadowSize = 8;
#if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS
const bool kCollectStats = true;
#else
const bool kCollectStats = false;
#endif
// The following "build consistency" machinery ensures that all source files
// are built in the same configuration. Inconsistent builds lead to
// hard to debug crashes.
#if TSAN_DEBUG
void build_consistency_debug();
#else
void build_consistency_release();
#endif
#if TSAN_COLLECT_STATS
void build_consistency_stats();
#else
void build_consistency_nostats();
#endif
#if TSAN_SHADOW_COUNT == 1
void build_consistency_shadow1();
#elif TSAN_SHADOW_COUNT == 2
void build_consistency_shadow2();
#elif TSAN_SHADOW_COUNT == 4
void build_consistency_shadow4();
#else
void build_consistency_shadow8();
#endif
static inline void USED build_consistency() {
#if TSAN_DEBUG
build_consistency_debug();
#else
build_consistency_release();
#endif
#if TSAN_COLLECT_STATS
build_consistency_stats();
#else
build_consistency_nostats();
#endif
#if TSAN_SHADOW_COUNT == 1
build_consistency_shadow1();
#elif TSAN_SHADOW_COUNT == 2
build_consistency_shadow2();
#elif TSAN_SHADOW_COUNT == 4
build_consistency_shadow4();
#else
build_consistency_shadow8();
#endif
}
template<typename T>
T min(T a, T b) {
return a < b ? a : b;
}
template<typename T>
T max(T a, T b) {
return a > b ? a : b;
}
template<typename T>
T RoundUp(T p, int align) {
DCHECK_EQ(align & (align - 1), 0);
return (T)(((u64)p + align - 1) & ~(align - 1));
}
struct MD5Hash {
u64 hash[2];
bool operator==(const MD5Hash &other) const;
};
MD5Hash md5_hash(const void *data, uptr size);
struct ThreadState;
struct ThreadContext;
struct Context;
struct ReportStack;
class ReportDesc;
class RegionAlloc;
class StackTrace;
struct MBlock;
} // namespace __tsan
#endif // TSAN_DEFS_H
//===-- tsan_flags.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 "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_flags.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
namespace __tsan {
Flags *flags() {
return &CTX()->flags;
}
// Can be overriden in frontend.
#ifdef TSAN_EXTERNAL_HOOKS
void OverrideFlags(Flags *f);
#else
SANITIZER_INTERFACE_ATTRIBUTE
void WEAK OverrideFlags(Flags *f) {
(void)f;
}
#endif
void InitializeFlags(Flags *f, const char *env) {
internal_memset(f, 0, sizeof(*f));
// Default values.
f->enable_annotations = true;
f->suppress_equal_stacks = true;
f->suppress_equal_addresses = true;
f->report_thread_leaks = true;
f->report_destroy_locked = true;
f->report_signal_unsafe = true;
f->force_seq_cst_atomics = false;
f->strip_path_prefix = "";
f->suppressions = "";
f->exitcode = 66;
f->log_fileno = 2;
f->atexit_sleep_ms = 1000;
f->verbosity = 0;
f->profile_memory = "";
f->flush_memory_ms = 0;
f->stop_on_start = false;
f->running_on_valgrind = false;
f->external_symbolizer_path = "";
// Let a frontend override.
OverrideFlags(f);
// Override from command line.
ParseFlag(env, &f->enable_annotations, "enable_annotations");
ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks");
ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses");
ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks");
ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked");
ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe");
ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics");
ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix");
ParseFlag(env, &f->suppressions, "suppressions");
ParseFlag(env, &f->exitcode, "exitcode");
ParseFlag(env, &f->log_fileno, "log_fileno");
ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms");
ParseFlag(env, &f->verbosity, "verbosity");
ParseFlag(env, &f->profile_memory, "profile_memory");
ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms");
ParseFlag(env, &f->stop_on_start, "stop_on_start");
ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path");
}
} // namespace __tsan
//===-- tsan_flags.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.
// NOTE: This file may be included into user code.
//===----------------------------------------------------------------------===//
#ifndef TSAN_FLAGS_H
#define TSAN_FLAGS_H
// ----------- ATTENTION -------------
// ThreadSanitizer user may provide its implementation of weak
// symbol __tsan::OverrideFlags(__tsan::Flags). Therefore, this
// header may be included in the user code, and shouldn't include
// other headers from TSan or common sanitizer runtime.
namespace __tsan {
struct Flags {
// Enable dynamic annotations, otherwise they are no-ops.
bool enable_annotations;
// Supress a race report if we've already output another race report
// with the same stack.
bool suppress_equal_stacks;
// Supress a race report if we've already output another race report
// on the same address.
bool suppress_equal_addresses;
// Report thread leaks at exit?
bool report_thread_leaks;
// Report destruction of a locked mutex?
bool report_destroy_locked;
// Report violations of async signal-safety
// (e.g. malloc() call from a signal handler).
bool report_signal_unsafe;
// If set, all atomics are effectively sequentially consistent (seq_cst),
// regardless of what user actually specified.
bool force_seq_cst_atomics;
// Strip that prefix from file paths in reports.
const char *strip_path_prefix;
// Suppressions filename.
const char *suppressions;
// Override exit status if something was reported.
int exitcode;
// Log fileno (1 - stdout, 2 - stderr).
int log_fileno;
// Sleep in main thread before exiting for that many ms
// (useful to catch "at exit" races).
int atexit_sleep_ms;
// Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
int verbosity;
// If set, periodically write memory profile to that file.
const char *profile_memory;
// Flush shadow memory every X ms.
int flush_memory_ms;
// Stops on start until __tsan_resume() is called (for debugging).
bool stop_on_start;
// Controls whether RunningOnValgrind() returns true or false.
bool running_on_valgrind;
// Path to external symbolizer.
const char *external_symbolizer_path;
};
Flags *flags();
void InitializeFlags(Flags *flags, const char *env);
}
#endif // TSAN_FLAGS_H
//===-- tsan_interceptors.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_INTERCEPTORS_H
#define TSAN_INTERCEPTORS_H
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "tsan_rtl.h"
namespace __tsan {
class ScopedInterceptor {
public:
ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc);
~ScopedInterceptor();
private:
ThreadState *const thr_;
const int in_rtl_;
};
#define SCOPED_INTERCEPTOR_RAW(func, ...) \
ThreadState *thr = cur_thread(); \
StatInc(thr, StatInterceptor); \
StatInc(thr, StatInt_##func); \
const uptr caller_pc = GET_CALLER_PC(); \
ScopedInterceptor si(thr, #func, caller_pc); \
/* Subtract one from pc as we need current instruction address */ \
const uptr pc = __sanitizer::StackTrace::GetCurrentPc() - 1; \
(void)pc; \
/**/
#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
if (thr->in_rtl > 1) \
return REAL(func)(__VA_ARGS__); \
/**/
#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
} // namespace __tsan
#endif // TSAN_INTERCEPTORS_H
//===-- tsan_interface.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_interface.h"
#include "tsan_interface_ann.h"
#include "tsan_rtl.h"
#define CALLERPC ((uptr)__builtin_return_address(0))
using namespace __tsan; // NOLINT
void __tsan_init() {
Initialize(cur_thread());
}
void __tsan_read16(void *addr) {
MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr);
MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr + 8);
}
void __tsan_write16(void *addr) {
MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr);
MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr + 8);
}
void __tsan_acquire(void *addr) {
Acquire(cur_thread(), CALLERPC, (uptr)addr);
}
void __tsan_release(void *addr) {
Release(cur_thread(), CALLERPC, (uptr)addr);
}
//===-- tsan_interface.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.
//
// The functions declared in this header will be inserted by the instrumentation
// module.
// This header can be included by the instrumented program or by TSan tests.
//===----------------------------------------------------------------------===//
#ifndef TSAN_INTERFACE_H
#define TSAN_INTERFACE_H
#include <sanitizer/common_interface_defs.h>
// This header should NOT include any other headers.
// All functions in this header are extern "C" and start with __tsan_.
#ifdef __cplusplus
extern "C" {
#endif
// This function should be called at the very beginning of the process,
// before any instrumented code is executed and before any call to malloc.
void __tsan_init() SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_read1(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_read2(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_read4(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_read8(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_read16(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_write1(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_write2(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_write4(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_write8(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_write16(void *addr) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_vptr_update(void **vptr_p, void *new_val) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_func_entry(void *call_pc) SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_func_exit() SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TSAN_INTERFACE_H
//===-- tsan_interface_ann.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.
//
// Interface for dynamic annotations.
//===----------------------------------------------------------------------===//
#ifndef TSAN_INTERFACE_ANN_H
#define TSAN_INTERFACE_ANN_H
// This header should NOT include any other headers.
// All functions in this header are extern "C" and start with __tsan_.
#ifdef __cplusplus
extern "C" {
#endif
void __tsan_acquire(void *addr);
void __tsan_release(void *addr);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TSAN_INTERFACE_ANN_H
//===-- tsan_interface_atomic.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_INTERFACE_ATOMIC_H
#define TSAN_INTERFACE_ATOMIC_H
#ifdef __cplusplus
extern "C" {
#endif
typedef char __tsan_atomic8;
typedef short __tsan_atomic16; // NOLINT
typedef int __tsan_atomic32;
typedef long __tsan_atomic64; // NOLINT
// Part of ABI, do not change.
// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup
typedef enum {
__tsan_memory_order_relaxed = 1 << 0,
__tsan_memory_order_consume = 1 << 1,
__tsan_memory_order_acquire = 1 << 2,
__tsan_memory_order_release = 1 << 3,
__tsan_memory_order_acq_rel = 1 << 4,
__tsan_memory_order_seq_cst = 1 << 5
} __tsan_memory_order;
__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v,
__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a,
__tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a,
__tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a,
__tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a,
__tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a,
__tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a,
__tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a,
__tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a,
__tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_sub(volatile __tsan_atomic8 *a,
__tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_sub(volatile __tsan_atomic16 *a,
__tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_sub(volatile __tsan_atomic32 *a,
__tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_sub(volatile __tsan_atomic64 *a,
__tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a,
__tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a,
__tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a,
__tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a,
__tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a,
__tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a,
__tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a,
__tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a,
__tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a,
__tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a,
__tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a,
__tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a,
__tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a,
__tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a,
__tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a,
__tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a,
__tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a,
__tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a,
__tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a,
__tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a,
__tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_atomic_thread_fence(__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
void __tsan_atomic_signal_fence(__tsan_memory_order mo)
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef TSAN_INTERFACE_ATOMIC_H
//===-- tsan_interface_inl.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.
//
//===----------------------------------------------------------------------===//
#include "tsan_interface.h"
#include "tsan_rtl.h"
#define CALLERPC ((uptr)__builtin_return_address(0))
using namespace __tsan; // NOLINT
void __tsan_read1(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 0);
}
void __tsan_read2(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 0);
}
void __tsan_read4(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 0);
}
void __tsan_read8(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 0);
}
void __tsan_write1(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 1);
}
void __tsan_write2(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 1);
}
void __tsan_write4(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 1);
}
void __tsan_write8(void *addr) {
MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 1);
}
void __tsan_vptr_update(void **vptr_p, void *new_val) {
CHECK_EQ(sizeof(vptr_p), 8);
if (*vptr_p != new_val)
MemoryAccess(cur_thread(), CALLERPC, (uptr)vptr_p, 3, 1);
}
void __tsan_func_entry(void *pc) {
FuncEntry(cur_thread(), (uptr)pc);
}
void __tsan_func_exit() {
FuncExit(cur_thread());
}
//===-- tsan_md5.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_defs.h"
namespace __tsan {
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define STEP(f, a, b, c, d, x, t, s) \
(a) += f((b), (c), (d)) + (x) + (t); \
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
(a) += (b);
#define SET(n) \
(*(MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \
SET(n)
typedef unsigned int MD5_u32plus;
typedef unsigned long ulong_t; // NOLINT
typedef struct {
MD5_u32plus lo, hi;
MD5_u32plus a, b, c, d;
unsigned char buffer[64];
MD5_u32plus block[16];
} MD5_CTX;
static void *body(MD5_CTX *ctx, void *data, ulong_t size) {
unsigned char *ptr;
MD5_u32plus a, b, c, d;
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = (unsigned char*)data;
a = ctx->a;
b = ctx->b;
c = ctx->c;
d = ctx->d;
do {
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx->a = a;
ctx->b = b;
ctx->c = c;
ctx->d = d;
return ptr;
}
void MD5_Init(MD5_CTX *ctx) {
ctx->a = 0x67452301;
ctx->b = 0xefcdab89;
ctx->c = 0x98badcfe;
ctx->d = 0x10325476;
ctx->lo = 0;
ctx->hi = 0;
}
void MD5_Update(MD5_CTX *ctx, void *data, ulong_t size) {
MD5_u32plus saved_lo;
ulong_t used, free;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
ctx->hi++;
ctx->hi += size >> 29;
used = saved_lo & 0x3f;
if (used) {
free = 64 - used;
if (size < free) {
internal_memcpy(&ctx->buffer[used], data, size);
return;
}
internal_memcpy(&ctx->buffer[used], data, free);
data = (unsigned char *)data + free;
size -= free;
body(ctx, ctx->buffer, 64);
}
if (size >= 64) {
data = body(ctx, data, size & ~(ulong_t)0x3f);
size &= 0x3f;
}
internal_memcpy(ctx->buffer, data, size);
}
void MD5_Final(unsigned char *result, MD5_CTX *ctx) {
ulong_t used, free;
used = ctx->lo & 0x3f;
ctx->buffer[used++] = 0x80;
free = 64 - used;
if (free < 8) {
internal_memset(&ctx->buffer[used], 0, free);
body(ctx, ctx->buffer, 64);
used = 0;
free = 64;
}
internal_memset(&ctx->buffer[used], 0, free - 8);
ctx->lo <<= 3;
ctx->buffer[56] = ctx->lo;
ctx->buffer[57] = ctx->lo >> 8;
ctx->buffer[58] = ctx->lo >> 16;
ctx->buffer[59] = ctx->lo >> 24;
ctx->buffer[60] = ctx->hi;
ctx->buffer[61] = ctx->hi >> 8;
ctx->buffer[62] = ctx->hi >> 16;
ctx->buffer[63] = ctx->hi >> 24;
body(ctx, ctx->buffer, 64);
result[0] = ctx->a;
result[1] = ctx->a >> 8;
result[2] = ctx->a >> 16;
result[3] = ctx->a >> 24;
result[4] = ctx->b;
result[5] = ctx->b >> 8;
result[6] = ctx->b >> 16;
result[7] = ctx->b >> 24;
result[8] = ctx->c;
result[9] = ctx->c >> 8;
result[10] = ctx->c >> 16;
result[11] = ctx->c >> 24;
result[12] = ctx->d;
result[13] = ctx->d >> 8;
result[14] = ctx->d >> 16;
result[15] = ctx->d >> 24;
internal_memset(ctx, 0, sizeof(*ctx));
}
MD5Hash md5_hash(const void *data, uptr size) {
MD5Hash res;
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, (void*)data, size);
MD5_Final((unsigned char*)&res.hash[0], &ctx);
return res;
}
}
//===-- tsan_mman.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 "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_mman.h"
#include "tsan_rtl.h"
#include "tsan_report.h"
#include "tsan_flags.h"
// May be overriden by front-end.
extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
(void)ptr;
(void)size;
}
extern "C" void WEAK __tsan_free_hook(void *ptr) {
(void)ptr;
}
namespace __tsan {
static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
Allocator *allocator() {
return reinterpret_cast<Allocator*>(&allocator_placeholder);
}
void InitializeAllocator() {
allocator()->Init();
}
void AlloctorThreadFinish(ThreadState *thr) {
allocator()->SwallowCache(&thr->alloc_cache);
}
static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
if (!thr->in_signal_handler || !flags()->report_signal_unsafe)
return;
Context *ctx = CTX();
StackTrace stack;
stack.ObtainCurrent(thr, pc);
ScopedReport rep(ReportTypeSignalUnsafe);
if (!IsFiredSuppression(ctx, rep, stack)) {
rep.AddStack(&stack);
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
}
}
void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
CHECK_GT(thr->in_rtl, 0);
void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
if (p == 0)
return 0;
MBlock *b = (MBlock*)allocator()->GetMetaData(p);
b->size = sz;
b->alloc_tid = thr->unique_id;
b->alloc_stack_id = CurrentStackId(thr, pc);
if (CTX() && CTX()->initialized) {
MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
}
DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
SignalUnsafeCall(thr, pc);
return p;
}
void user_free(ThreadState *thr, uptr pc, void *p) {
CHECK_GT(thr->in_rtl, 0);
CHECK_NE(p, (void*)0);
DPrintf("#%d: free(%p)\n", thr->tid, p);
MBlock *b = (MBlock*)allocator()->GetMetaData(p);
if (b->head) {
Lock l(&b->mtx);
for (SyncVar *s = b->head; s;) {
SyncVar *res = s;
s = s->next;
StatInc(thr, StatSyncDestroyed);
res->mtx.Lock();
res->mtx.Unlock();
DestroyAndFree(res);
}
b->head = 0;
}
if (CTX() && CTX()->initialized && thr->in_rtl == 1) {
MemoryRangeFreed(thr, pc, (uptr)p, b->size);
}
allocator()->Deallocate(&thr->alloc_cache, p);
SignalUnsafeCall(thr, pc);
}
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
CHECK_GT(thr->in_rtl, 0);
void *p2 = 0;
// FIXME: Handle "shrinking" more efficiently,
// it seems that some software actually does this.
if (sz) {
p2 = user_alloc(thr, pc, sz);
if (p2 == 0)
return 0;
if (p) {
MBlock *b = user_mblock(thr, p);
internal_memcpy(p2, p, min(b->size, sz));
}
}
if (p) {
user_free(thr, pc, p);
}
return p2;
}
MBlock *user_mblock(ThreadState *thr, void *p) {
// CHECK_GT(thr->in_rtl, 0);
CHECK_NE(p, (void*)0);
return (MBlock*)allocator()->GetMetaData(p);
}
void invoke_malloc_hook(void *ptr, uptr size) {
Context *ctx = CTX();
ThreadState *thr = cur_thread();
if (ctx == 0 || !ctx->initialized || thr->in_rtl)
return;
__tsan_malloc_hook(ptr, size);
}
void invoke_free_hook(void *ptr) {
Context *ctx = CTX();
ThreadState *thr = cur_thread();
if (ctx == 0 || !ctx->initialized || thr->in_rtl)
return;
__tsan_free_hook(ptr);
}
void *internal_alloc(MBlockType typ, uptr sz) {
ThreadState *thr = cur_thread();
CHECK_GT(thr->in_rtl, 0);
if (thr->nomalloc) {
thr->nomalloc = 0; // CHECK calls internal_malloc().
CHECK(0);
}
return InternalAlloc(sz);
}
void internal_free(void *p) {
ThreadState *thr = cur_thread();
CHECK_GT(thr->in_rtl, 0);
if (thr->nomalloc) {
thr->nomalloc = 0; // CHECK calls internal_malloc().
CHECK(0);
}
InternalFree(p);
}
} // namespace __tsan
//===-- tsan_mman.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_MMAN_H
#define TSAN_MMAN_H
#include "tsan_defs.h"
namespace __tsan {
const uptr kDefaultAlignment = 16;
void InitializeAllocator();
void AlloctorThreadFinish(ThreadState *thr);
// For user allocations.
void *user_alloc(ThreadState *thr, uptr pc, uptr sz,
uptr align = kDefaultAlignment);
// Does not accept NULL.
void user_free(ThreadState *thr, uptr pc, void *p);
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz);
void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align);
// Given the pointer p into a valid allocated block,
// returns the descriptor of the block.
MBlock *user_mblock(ThreadState *thr, void *p);
// Invoking malloc/free hooks that may be installed by the user.
void invoke_malloc_hook(void *ptr, uptr size);
void invoke_free_hook(void *ptr);
enum MBlockType {
MBlockScopedBuf,
MBlockString,
MBlockStackTrace,
MBlockShadowStack,
MBlockSync,
MBlockClock,
MBlockThreadContex,
MBlockDeadInfo,
MBlockRacyStacks,
MBlockRacyAddresses,
MBlockAtExit,
MBlockFlag,
MBlockReport,
MBlockReportMop,
MBlockReportThread,
MBlockReportMutex,
MBlockReportLoc,
MBlockReportStack,
MBlockSuppression,
MBlockExpectRace,
MBlockSignal,
// This must be the last.
MBlockTypeCount
};
// For internal data structures.
void *internal_alloc(MBlockType typ, uptr sz);
void internal_free(void *p);
template<typename T>
void DestroyAndFree(T *&p) {
p->~T();
internal_free(p);
p = 0;
}
} // namespace __tsan
#endif // TSAN_MMAN_H
//===-- tsan_mutex.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 "sanitizer_common/sanitizer_libc.h"
#include "tsan_mutex.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
namespace __tsan {
// Simple reader-writer spin-mutex. Optimized for not-so-contended case.
// Readers have preference, can possibly starvate writers.
// The table fixes what mutexes can be locked under what mutexes.
// E.g. if the row for MutexTypeThreads contains MutexTypeReport,
// then Report mutex can be locked while under Threads mutex.
// The leaf mutexes can be locked under any other mutexes.
// Recursive locking is not supported.
const MutexType MutexTypeLeaf = (MutexType)-1;
static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
/*0 MutexTypeInvalid*/ {},
/*1 MutexTypeTrace*/ {MutexTypeLeaf},
/*2 MutexTypeThreads*/ {MutexTypeReport},
/*3 MutexTypeReport*/ {},
/*4 MutexTypeSyncVar*/ {},
/*5 MutexTypeSyncTab*/ {MutexTypeSyncVar},
/*6 MutexTypeSlab*/ {MutexTypeLeaf},
/*7 MutexTypeAnnotations*/ {},
/*8 MutexTypeAtExit*/ {MutexTypeSyncTab},
};
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
void InitializeMutex() {
// Build the "can lock" adjacency matrix.
// If [i][j]==true, then one can lock mutex j while under mutex i.
const int N = MutexTypeCount;
int cnt[N] = {};
bool leaf[N] = {};
for (int i = 1; i < N; i++) {
for (int j = 0; j < N; j++) {
MutexType z = CanLockTab[i][j];
if (z == MutexTypeInvalid)
continue;
if (z == MutexTypeLeaf) {
CHECK(!leaf[i]);
leaf[i] = true;
continue;
}
CHECK(!CanLockAdj[i][(int)z]);
CanLockAdj[i][(int)z] = true;
cnt[i]++;
}
}
for (int i = 0; i < N; i++) {
CHECK(!leaf[i] || cnt[i] == 0);
}
// Add leaf mutexes.
for (int i = 0; i < N; i++) {
if (!leaf[i])
continue;
for (int j = 0; j < N; j++) {
if (i == j || leaf[j] || j == MutexTypeInvalid)
continue;
CHECK(!CanLockAdj[j][i]);
CanLockAdj[j][i] = true;
}
}
// Build the transitive closure.
bool CanLockAdj2[MutexTypeCount][MutexTypeCount];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
CanLockAdj2[i][j] = CanLockAdj[i][j];
}
}
for (int k = 0; k < N; k++) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) {
CanLockAdj2[i][j] = true;
}
}
}
}
#if 0
TsanPrintf("Can lock graph:\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
TsanPrintf("%d ", CanLockAdj[i][j]);
}
TsanPrintf("\n");
}
TsanPrintf("Can lock graph closure:\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
TsanPrintf("%d ", CanLockAdj2[i][j]);
}
TsanPrintf("\n");
}
#endif
// Verify that the graph is acyclic.
for (int i = 0; i < N; i++) {
if (CanLockAdj2[i][i]) {
TsanPrintf("Mutex %d participates in a cycle\n", i);
Die();
}
}
}
DeadlockDetector::DeadlockDetector() {
// Rely on zero initialization because some mutexes can be locked before ctor.
}
void DeadlockDetector::Lock(MutexType t) {
// TsanPrintf("LOCK %d @%zu\n", t, seq_ + 1);
u64 max_seq = 0;
u64 max_idx = MutexTypeInvalid;
for (int i = 0; i != MutexTypeCount; i++) {
if (locked_[i] == 0)
continue;
CHECK_NE(locked_[i], max_seq);
if (max_seq < locked_[i]) {
max_seq = locked_[i];
max_idx = i;
}
}
locked_[t] = ++seq_;
if (max_idx == MutexTypeInvalid)
return;
// TsanPrintf(" last %d @%zu\n", max_idx, max_seq);
if (!CanLockAdj[max_idx][t]) {
TsanPrintf("ThreadSanitizer: internal deadlock detected\n");
TsanPrintf("ThreadSanitizer: can't lock %d while under %zu\n",
t, (uptr)max_idx);
CHECK(0);
}
}
void DeadlockDetector::Unlock(MutexType t) {
// TsanPrintf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
CHECK(locked_[t]);
locked_[t] = 0;
}
const uptr kUnlocked = 0;
const uptr kWriteLock = 1;
const uptr kReadLock = 2;
class Backoff {
public:
Backoff()
: iter_() {
}
bool Do() {
if (iter_++ < kActiveSpinIters)
proc_yield(kActiveSpinCnt);
else
internal_sched_yield();
return true;
}
u64 Contention() const {
u64 active = iter_ % kActiveSpinIters;
u64 passive = iter_ - active;
return active + 10 * passive;
}
private:
int iter_;
static const int kActiveSpinIters = 10;
static const int kActiveSpinCnt = 20;
};
Mutex::Mutex(MutexType type, StatType stat_type) {
CHECK_GT(type, MutexTypeInvalid);
CHECK_LT(type, MutexTypeCount);
#if TSAN_DEBUG
type_ = type;
#endif
#if TSAN_COLLECT_STATS
stat_type_ = stat_type;
#endif
atomic_store(&state_, kUnlocked, memory_order_relaxed);
}
Mutex::~Mutex() {
CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
}
void Mutex::Lock() {
#if TSAN_DEBUG && !TSAN_GO
cur_thread()->deadlock_detector.Lock(type_);
#endif
uptr cmp = kUnlocked;
if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
memory_order_acquire))
return;
for (Backoff backoff; backoff.Do();) {
if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) {
cmp = kUnlocked;
if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
memory_order_acquire)) {
#if TSAN_COLLECT_STATS
StatInc(cur_thread(), stat_type_, backoff.Contention());
#endif
return;
}
}
}
}
void Mutex::Unlock() {
uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
(void)prev;
DCHECK_NE(prev & kWriteLock, 0);
#if TSAN_DEBUG && !TSAN_GO
cur_thread()->deadlock_detector.Unlock(type_);
#endif
}
void Mutex::ReadLock() {
#if TSAN_DEBUG && !TSAN_GO
cur_thread()->deadlock_detector.Lock(type_);
#endif
uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
if ((prev & kWriteLock) == 0)
return;
for (Backoff backoff; backoff.Do();) {
prev = atomic_load(&state_, memory_order_acquire);
if ((prev & kWriteLock) == 0) {
#if TSAN_COLLECT_STATS
StatInc(cur_thread(), stat_type_, backoff.Contention());
#endif
return;
}
}
}
void Mutex::ReadUnlock() {
uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
(void)prev;
DCHECK_EQ(prev & kWriteLock, 0);
DCHECK_GT(prev & ~kWriteLock, 0);
#if TSAN_DEBUG && !TSAN_GO
cur_thread()->deadlock_detector.Unlock(type_);
#endif
}
void Mutex::CheckLocked() {
CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0);
}
} // namespace __tsan
//===-- tsan_mutex.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_MUTEX_H
#define TSAN_MUTEX_H
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "tsan_defs.h"
namespace __tsan {
enum MutexType {
MutexTypeInvalid,
MutexTypeTrace,
MutexTypeThreads,
MutexTypeReport,
MutexTypeSyncVar,
MutexTypeSyncTab,
MutexTypeSlab,
MutexTypeAnnotations,
MutexTypeAtExit,
// This must be the last.
MutexTypeCount
};
class Mutex {
public:
explicit Mutex(MutexType type, StatType stat_type);
~Mutex();
void Lock();
void Unlock();
void ReadLock();
void ReadUnlock();
void CheckLocked();
private:
atomic_uintptr_t state_;
#if TSAN_DEBUG
MutexType type_;
#endif
#if TSAN_COLLECT_STATS
StatType stat_type_;
#endif
Mutex(const Mutex&);
void operator = (const Mutex&);
};
typedef GenericScopedLock<Mutex> Lock;
typedef GenericScopedReadLock<Mutex> ReadLock;
class DeadlockDetector {
public:
DeadlockDetector();
void Lock(MutexType t);
void Unlock(MutexType t);
private:
u64 seq_;
u64 locked_[MutexTypeCount];
};
void InitializeMutex();
} // namespace __tsan
#endif // TSAN_MUTEX_H
//===-- tsan_platform.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.
//
// Platform-specific code.
//===----------------------------------------------------------------------===//
#ifndef TSAN_PLATFORM_H
#define TSAN_PLATFORM_H
#include "tsan_rtl.h"
#if __LP64__
namespace __tsan {
#if defined(TSAN_GO)
static const uptr kLinuxAppMemBeg = 0x000000000000ULL;
static const uptr kLinuxAppMemEnd = 0x00fcffffffffULL;
static const uptr kLinuxShadowMsk = 0x100000000000ULL;
// TSAN_COMPAT_SHADOW is intended for COMPAT virtual memory layout,
// when memory addresses are of the 0x2axxxxxxxxxx form.
// The option is enabled with 'setarch x86_64 -L'.
#elif defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
static const uptr kLinuxAppMemBeg = 0x290000000000ULL;
static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
#else
static const uptr kLinuxAppMemBeg = 0x7cf000000000ULL;
static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
#endif
static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL;
// This has to be a macro to allow constant initialization of constants below.
#ifndef TSAN_GO
#define MemToShadow(addr) \
(((addr) & ~(kLinuxAppMemMsk | (kShadowCell - 1))) * kShadowCnt)
#else
#define MemToShadow(addr) \
((((addr) & ~(kShadowCell - 1)) * kShadowCnt) | kLinuxShadowMsk)
#endif
static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg);
static const uptr kLinuxShadowEnd =
MemToShadow(kLinuxAppMemEnd) | (kPageSize - 1);
static inline bool IsAppMem(uptr mem) {
return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd;
}
static inline bool IsShadowMem(uptr mem) {
return mem >= kLinuxShadowBeg && mem <= kLinuxShadowEnd;
}
static inline uptr ShadowToMem(uptr shadow) {
CHECK(IsShadowMem(shadow));
#ifdef TSAN_GO
return (shadow & ~kLinuxShadowMsk) / kShadowCnt;
#else
return (shadow / kShadowCnt) | kLinuxAppMemMsk;
#endif
}
// For COMPAT mapping returns an alternative address
// that mapped to the same shadow address.
// COMPAT mapping is not quite one-to-one.
static inline uptr AlternativeAddress(uptr addr) {
#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
return (addr & ~kLinuxAppMemMsk) | 0x280000000000ULL;
#else
return 0;
#endif
}
uptr GetShadowMemoryConsumption();
void FlushShadowMemory();
const char *InitializePlatform();
void FinalizePlatform();
void internal_start_thread(void(*func)(void*), void *arg);
// Says whether the addr relates to a global var.
// Guesses with high probability, may yield both false positives and negatives.
bool IsGlobalVar(uptr addr);
uptr GetTlsSize();
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
uptr *tls_addr, uptr *tls_size);
} // namespace __tsan
#else // __LP64__
# error "Only 64-bit is supported"
#endif
#endif // TSAN_PLATFORM_H
//===-- tsan_platform_linux.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.
//
// Linux-specific code.
//===----------------------------------------------------------------------===//
#ifdef __linux__
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_flags.h"
#include <asm/prctl.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <dlfcn.h>
extern "C" int arch_prctl(int code, __sanitizer::uptr *addr);
namespace __tsan {
#ifndef TSAN_GO
ScopedInRtl::ScopedInRtl()
: thr_(cur_thread()) {
in_rtl_ = thr_->in_rtl;
thr_->in_rtl++;
errno_ = errno;
}
ScopedInRtl::~ScopedInRtl() {
thr_->in_rtl--;
errno = errno_;
CHECK_EQ(in_rtl_, thr_->in_rtl);
}
#else
ScopedInRtl::ScopedInRtl() {
}
ScopedInRtl::~ScopedInRtl() {
}
#endif
uptr GetShadowMemoryConsumption() {
return 0;
}
void FlushShadowMemory() {
madvise((void*)kLinuxShadowBeg,
kLinuxShadowEnd - kLinuxShadowBeg,
MADV_DONTNEED);
}
#ifndef TSAN_GO
static void ProtectRange(uptr beg, uptr end) {
ScopedInRtl in_rtl;
CHECK_LE(beg, end);
if (beg == end)
return;
if (beg != (uptr)Mprotect(beg, end - beg)) {
TsanPrintf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
TsanPrintf("FATAL: Make sure you are not using unlimited stack\n");
Die();
}
}
#endif
void InitializeShadowMemory() {
uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
kLinuxShadowEnd - kLinuxShadowBeg);
if (shadow != kLinuxShadowBeg) {
TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
TsanPrintf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie (%p, %p).\n", shadow, kLinuxShadowBeg);
Die();
}
#ifndef TSAN_GO
const uptr kClosedLowBeg = 0x200000;
const uptr kClosedLowEnd = kLinuxShadowBeg - 1;
const uptr kClosedMidBeg = kLinuxShadowEnd + 1;
const uptr kClosedMidEnd = kLinuxAppMemBeg - 1;
ProtectRange(kClosedLowBeg, kClosedLowEnd);
ProtectRange(kClosedMidBeg, kClosedMidEnd);
#endif
#ifndef TSAN_GO
DPrintf("kClosedLow %zx-%zx (%zuGB)\n",
kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30);
#endif
DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
kLinuxShadowBeg, kLinuxShadowEnd,
(kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
#ifndef TSAN_GO
DPrintf("kClosedMid %zx-%zx (%zuGB)\n",
kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30);
#endif
DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
kLinuxAppMemBeg, kLinuxAppMemEnd,
(kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
DPrintf("stack %zx\n", (uptr)&shadow);
}
static uptr g_data_start;
static uptr g_data_end;
#ifndef TSAN_GO
static void CheckPIE() {
// Ensure that the binary is indeed compiled with -pie.
MemoryMappingLayout proc_maps;
uptr start, end;
if (proc_maps.Next(&start, &end,
/*offset*/0, /*filename*/0, /*filename_size*/0)) {
if ((u64)start < kLinuxAppMemBeg) {
TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
"something is mapped at 0x%zx < 0x%zx)\n",
start, kLinuxAppMemBeg);
TsanPrintf("FATAL: Make sure to compile with -fPIE"
" and to link with -pie.\n");
Die();
}
}
}
static void InitDataSeg() {
MemoryMappingLayout proc_maps;
uptr start, end, offset;
char name[128];
bool prev_is_data = false;
while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name))) {
DPrintf("%p-%p %p %s\n", start, end, offset, name);
bool is_data = offset != 0 && name[0] != 0;
// BSS may get merged with [heap] in /proc/self/maps. This is not very
// reliable.
bool is_bss = offset == 0 &&
(name[0] == 0 || internal_strcmp(name, "[heap]") == 0) && prev_is_data;
if (g_data_start == 0 && is_data)
g_data_start = start;
if (is_bss)
g_data_end = end;
prev_is_data = is_data;
}
DPrintf("guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
CHECK_LT(g_data_start, g_data_end);
CHECK_GE((uptr)&g_data_start, g_data_start);
CHECK_LT((uptr)&g_data_start, g_data_end);
}
static uptr g_tls_size;
#ifdef __i386__
# define INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
#else
# define INTERNAL_FUNCTION
#endif
extern "C" void _dl_get_tls_static_info(size_t*, size_t*)
__attribute__((weak)) INTERNAL_FUNCTION;
static int InitTlsSize() {
typedef void (*get_tls_func)(size_t*, size_t*) INTERNAL_FUNCTION;
get_tls_func get_tls = &_dl_get_tls_static_info;
if (get_tls == 0) {
void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr));
internal_memcpy(&get_tls, &get_tls_static_info_ptr,
sizeof(get_tls_static_info_ptr));
}
CHECK_NE(get_tls, 0);
size_t tls_size = 0;
size_t tls_align = 0;
get_tls(&tls_size, &tls_align);
return tls_size;
}
#endif // #ifndef TSAN_GO
const char *InitializePlatform() {
void *p = 0;
if (sizeof(p) == 8) {
// Disable core dumps, dumping of 16TB usually takes a bit long.
// The following magic is to prevent clang from replacing it with memset.
volatile rlimit lim;
lim.rlim_cur = 0;
lim.rlim_max = 0;
setrlimit(RLIMIT_CORE, (rlimit*)&lim);
}
// TSan doesn't play well with unlimited stack size (as stack
// overlaps with shadow memory). If we detect unlimited stack size,
// we re-exec the program with limited stack size as a best effort.
if (StackSizeIsUnlimited()) {
const uptr kMaxStackSize = 32 * 1024 * 1024; // 32 Mb
Report("WARNING: Program is run with unlimited stack size, which "
"wouldn't work with ThreadSanitizer.\n");
Report("Re-execing with stack size limited to %zd bytes.\n", kMaxStackSize);
SetStackSizeLimitInBytes(kMaxStackSize);
ReExec();
}
#ifndef TSAN_GO
CheckPIE();
g_tls_size = (uptr)InitTlsSize();
InitDataSeg();
#endif
return getenv("TSAN_OPTIONS");
}
void FinalizePlatform() {
fflush(0);
}
uptr GetTlsSize() {
#ifndef TSAN_GO
return g_tls_size;
#else
return 0;
#endif
}
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
uptr *tls_addr, uptr *tls_size) {
#ifndef TSAN_GO
arch_prctl(ARCH_GET_FS, tls_addr);
*tls_addr -= g_tls_size;
*tls_size = g_tls_size;
uptr stack_top, stack_bottom;
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
*stk_addr = stack_bottom;
*stk_size = stack_top - stack_bottom;
if (!main) {
// If stack and tls intersect, make them non-intersecting.
if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
CHECK_GT(*tls_addr + *tls_size, *stk_addr);
CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
*stk_size -= *tls_size;
*tls_addr = *stk_addr + *stk_size;
}
}
#else
*stk_addr = 0;
*stk_size = 0;
*tls_addr = 0;
*tls_size = 0;
#endif
}
bool IsGlobalVar(uptr addr) {
return g_data_start && addr >= g_data_start && addr < g_data_end;
}
} // namespace __tsan
#endif // #ifdef __linux__
//===-- tsan_platform_mac.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.
//
// Mac-specific code.
//===----------------------------------------------------------------------===//
#ifdef __APPLE__
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_flags.h"
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
namespace __tsan {
ScopedInRtl::ScopedInRtl() {
}
ScopedInRtl::~ScopedInRtl() {
}
uptr GetShadowMemoryConsumption() {
return 0;
}
void FlushShadowMemory() {
}
void InitializeShadowMemory() {
uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
kLinuxShadowEnd - kLinuxShadowBeg);
if (shadow != kLinuxShadowBeg) {
TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
TsanPrintf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie.\n");
Die();
}
DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
kLinuxShadowBeg, kLinuxShadowEnd,
(kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
kLinuxAppMemBeg, kLinuxAppMemEnd,
(kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
}
const char *InitializePlatform() {
void *p = 0;
if (sizeof(p) == 8) {
// Disable core dumps, dumping of 16TB usually takes a bit long.
// The following magic is to prevent clang from replacing it with memset.
volatile rlimit lim;
lim.rlim_cur = 0;
lim.rlim_max = 0;
setrlimit(RLIMIT_CORE, (rlimit*)&lim);
}
return getenv("TSAN_OPTIONS");
}
void FinalizePlatform() {
fflush(0);
}
uptr GetTlsSize() {
return 0;
}
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
uptr *tls_addr, uptr *tls_size) {
*stk_addr = 0;
*stk_size = 0;
*tls_addr = 0;
*tls_size = 0;
}
} // namespace __tsan
#endif // #ifdef __APPLE__
//===-- tsan_printf.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 "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_defs.h"
#include "tsan_mman.h"
#include "tsan_platform.h"
#include <stdarg.h> // va_list
namespace __sanitizer {
int VSNPrintf(char *buff, int buff_length, const char *format, va_list args);
} // namespace __sanitizer
namespace __tsan {
void TsanPrintf(const char *format, ...) {
ScopedInRtl in_rtl;
const uptr kMaxLen = 16 * 1024;
InternalScopedBuffer<char> buffer(kMaxLen);
va_list args;
va_start(args, format);
uptr len = VSNPrintf(buffer.data(), buffer.size(), format, args);
va_end(args);
internal_write(CTX() ? flags()->log_fileno : 2,
buffer.data(), len < buffer.size() ? len : buffer.size() - 1);
}
} // namespace __tsan
//===-- tsan_report.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_report.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
namespace __tsan {
ReportDesc::ReportDesc()
: stacks(MBlockReportStack)
, mops(MBlockReportMop)
, locs(MBlockReportLoc)
, mutexes(MBlockReportMutex)
, threads(MBlockReportThread)
, sleep() {
}
ReportDesc::~ReportDesc() {
}
#ifndef TSAN_GO
static void PrintHeader(ReportType typ) {
TsanPrintf("WARNING: ThreadSanitizer: ");
if (typ == ReportTypeRace)
TsanPrintf("data race");
else if (typ == ReportTypeUseAfterFree)
TsanPrintf("heap-use-after-free");
else if (typ == ReportTypeThreadLeak)
TsanPrintf("thread leak");
else if (typ == ReportTypeMutexDestroyLocked)
TsanPrintf("destroy of a locked mutex");
else if (typ == ReportTypeSignalUnsafe)
TsanPrintf("signal-unsafe call inside of a signal");
else if (typ == ReportTypeErrnoInSignal)
TsanPrintf("signal handler spoils errno");
TsanPrintf(" (pid=%d)\n", GetPid());
}
void PrintStack(const ReportStack *ent) {
for (int i = 0; ent; ent = ent->next, i++) {
TsanPrintf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line);
if (ent->col)
TsanPrintf(":%d", ent->col);
if (ent->module && ent->offset)
TsanPrintf(" (%s+%p)\n", ent->module, (void*)ent->offset);
else
TsanPrintf(" (%p)\n", (void*)ent->pc);
}
TsanPrintf("\n");
}
static void PrintMop(const ReportMop *mop, bool first) {
TsanPrintf(" %s of size %d at %p",
(first ? (mop->write ? "Write" : "Read")
: (mop->write ? "Previous write" : "Previous read")),
mop->size, (void*)mop->addr);
if (mop->tid == 0)
TsanPrintf(" by main thread:\n");
else
TsanPrintf(" by thread %d:\n", mop->tid);
PrintStack(mop->stack);
}
static void PrintLocation(const ReportLocation *loc) {
if (loc->type == ReportLocationGlobal) {
TsanPrintf(" Location is global '%s' of size %zu at %zx %s:%d\n",
loc->name, loc->size, loc->addr, loc->file, loc->line);
} else if (loc->type == ReportLocationHeap) {
TsanPrintf(" Location is heap block of size %zu at %p allocated",
loc->size, loc->addr);
if (loc->tid == 0)
TsanPrintf(" by main thread:\n");
else
TsanPrintf(" by thread %d:\n", loc->tid);
PrintStack(loc->stack);
} else if (loc->type == ReportLocationStack) {
TsanPrintf(" Location is stack of thread %d:\n", loc->tid);
}
}
static void PrintMutex(const ReportMutex *rm) {
if (rm->stack == 0)
return;
TsanPrintf(" Mutex %d created at:\n", rm->id);
PrintStack(rm->stack);
}
static void PrintThread(const ReportThread *rt) {
if (rt->id == 0) // Little sense in describing the main thread.
return;
TsanPrintf(" Thread %d", rt->id);
if (rt->name)
TsanPrintf(" '%s'", rt->name);
TsanPrintf(" (tid=%zu, %s)", rt->pid, rt->running ? "running" : "finished");
if (rt->stack)
TsanPrintf(" created at:");
TsanPrintf("\n");
PrintStack(rt->stack);
}
static void PrintSleep(const ReportStack *s) {
TsanPrintf(" As if synchronized via sleep:\n");
PrintStack(s);
}
void PrintReport(const ReportDesc *rep) {
TsanPrintf("==================\n");
PrintHeader(rep->typ);
for (uptr i = 0; i < rep->stacks.Size(); i++) {
if (i)
TsanPrintf(" and:\n");
PrintStack(rep->stacks[i]);
}
for (uptr i = 0; i < rep->mops.Size(); i++)
PrintMop(rep->mops[i], i == 0);
if (rep->sleep)
PrintSleep(rep->sleep);
for (uptr i = 0; i < rep->locs.Size(); i++)
PrintLocation(rep->locs[i]);
for (uptr i = 0; i < rep->mutexes.Size(); i++)
PrintMutex(rep->mutexes[i]);
for (uptr i = 0; i < rep->threads.Size(); i++)
PrintThread(rep->threads[i]);
TsanPrintf("==================\n");
}
#else
void PrintStack(const ReportStack *ent) {
for (int i = 0; ent; ent = ent->next, i++) {
TsanPrintf(" %s()\n %s:%d +0x%zx\n",
ent->func, ent->file, ent->line, (void*)ent->offset);
}
TsanPrintf("\n");
}
static void PrintMop(const ReportMop *mop, bool first) {
TsanPrintf("%s by goroutine %d:\n",
(first ? (mop->write ? "Write" : "Read")
: (mop->write ? "Previous write" : "Previous read")),
mop->tid);
PrintStack(mop->stack);
}
static void PrintThread(const ReportThread *rt) {
if (rt->id == 0) // Little sense in describing the main thread.
return;
TsanPrintf("Goroutine %d (%s) created at:\n",
rt->id, rt->running ? "running" : "finished");
PrintStack(rt->stack);
}
void PrintReport(const ReportDesc *rep) {
TsanPrintf("==================\n");
TsanPrintf("WARNING: DATA RACE\n");
for (uptr i = 0; i < rep->mops.Size(); i++)
PrintMop(rep->mops[i], i == 0);
for (uptr i = 0; i < rep->threads.Size(); i++)
PrintThread(rep->threads[i]);
TsanPrintf("==================\n");
}
#endif
} // namespace __tsan
//===-- tsan_report.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_REPORT_H
#define TSAN_REPORT_H
#include "tsan_defs.h"
#include "tsan_vector.h"
namespace __tsan {
enum ReportType {
ReportTypeRace,
ReportTypeUseAfterFree,
ReportTypeThreadLeak,
ReportTypeMutexDestroyLocked,
ReportTypeSignalUnsafe,
ReportTypeErrnoInSignal
};
struct ReportStack {
ReportStack *next;
char *module;
uptr offset;
uptr pc;
char *func;
char *file;
int line;
int col;
};
struct ReportMop {
int tid;
uptr addr;
int size;
bool write;
int nmutex;
int *mutex;
ReportStack *stack;
};
enum ReportLocationType {
ReportLocationGlobal,
ReportLocationHeap,
ReportLocationStack
};
struct ReportLocation {
ReportLocationType type;
uptr addr;
uptr size;
int tid;
char *name;
char *file;
int line;
ReportStack *stack;
};
struct ReportThread {
int id;
uptr pid;
bool running;
char *name;
ReportStack *stack;
};
struct ReportMutex {
int id;
ReportStack *stack;
};
class ReportDesc {
public:
ReportType typ;
Vector<ReportStack*> stacks;
Vector<ReportMop*> mops;
Vector<ReportLocation*> locs;
Vector<ReportMutex*> mutexes;
Vector<ReportThread*> threads;
ReportStack *sleep;
ReportDesc();
~ReportDesc();
private:
ReportDesc(const ReportDesc&);
void operator = (const ReportDesc&);
};
// Format and output the report to the console/log. No additional logic.
void PrintReport(const ReportDesc *rep);
void PrintStack(const ReportStack *stack);
} // namespace __tsan
#endif // TSAN_REPORT_H
.section .text
.globl __tsan_trace_switch_thunk
__tsan_trace_switch_thunk:
.cfi_startproc
# Save scratch registers.
push %rax
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rax, 0
push %rcx
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rcx, 0
push %rdx
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rdx, 0
push %rsi
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rsi, 0
push %rdi
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rdi, 0
push %r8
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r8, 0
push %r9
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r9, 0
push %r10
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r10, 0
push %r11
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r11, 0
# Align stack frame.
push %rbx # non-scratch
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbx, 0
mov %rsp, %rbx # save current rsp
.cfi_def_cfa_register %rbx
shr $4, %rsp # clear 4 lsb, align to 16
shl $4, %rsp
call __tsan_trace_switch
# Unalign stack frame back.
mov %rbx, %rsp # restore the original rsp
.cfi_def_cfa_register %rsp
pop %rbx
.cfi_adjust_cfa_offset -8
# Restore scratch registers.
pop %r11
.cfi_adjust_cfa_offset -8
pop %r10
.cfi_adjust_cfa_offset -8
pop %r9
.cfi_adjust_cfa_offset -8
pop %r8
.cfi_adjust_cfa_offset -8
pop %rdi
.cfi_adjust_cfa_offset -8
pop %rsi
.cfi_adjust_cfa_offset -8
pop %rdx
.cfi_adjust_cfa_offset -8
pop %rcx
.cfi_adjust_cfa_offset -8
pop %rax
.cfi_adjust_cfa_offset -8
.cfi_restore %rax
.cfi_restore %rbx
.cfi_restore %rcx
.cfi_restore %rdx
.cfi_restore %rsi
.cfi_restore %rdi
.cfi_restore %r8
.cfi_restore %r9
.cfi_restore %r10
.cfi_restore %r11
ret
.cfi_endproc
.globl __tsan_report_race_thunk
__tsan_report_race_thunk:
.cfi_startproc
# Save scratch registers.
push %rax
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rax, 0
push %rcx
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rcx, 0
push %rdx
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rdx, 0
push %rsi
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rsi, 0
push %rdi
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rdi, 0
push %r8
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r8, 0
push %r9
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r9, 0
push %r10
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r10, 0
push %r11
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r11, 0
# Align stack frame.
push %rbx # non-scratch
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbx, 0
mov %rsp, %rbx # save current rsp
.cfi_def_cfa_register %rbx
shr $4, %rsp # clear 4 lsb, align to 16
shl $4, %rsp
call __tsan_report_race
# Unalign stack frame back.
mov %rbx, %rsp # restore the original rsp
.cfi_def_cfa_register %rsp
pop %rbx
.cfi_adjust_cfa_offset -8
# Restore scratch registers.
pop %r11
.cfi_adjust_cfa_offset -8
pop %r10
.cfi_adjust_cfa_offset -8
pop %r9
.cfi_adjust_cfa_offset -8
pop %r8
.cfi_adjust_cfa_offset -8
pop %rdi
.cfi_adjust_cfa_offset -8
pop %rsi
.cfi_adjust_cfa_offset -8
pop %rdx
.cfi_adjust_cfa_offset -8
pop %rcx
.cfi_adjust_cfa_offset -8
pop %rax
.cfi_adjust_cfa_offset -8
.cfi_restore %rax
.cfi_restore %rbx
.cfi_restore %rcx
.cfi_restore %rdx
.cfi_restore %rsi
.cfi_restore %rdi
.cfi_restore %r8
.cfi_restore %r9
.cfi_restore %r10
.cfi_restore %r11
ret
.cfi_endproc
#ifdef __linux__
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif
//===-- tsan_rtl_mutex.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_rtl.h"
#include "tsan_flags.h"
#include "tsan_sync.h"
#include "tsan_report.h"
#include "tsan_symbolize.h"
#include "tsan_platform.h"
namespace __tsan {
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
bool rw, bool recursive, bool linker_init) {
Context *ctx = CTX();
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
StatInc(thr, StatMutexCreate);
if (!linker_init && IsAppMem(addr))
MemoryWrite1Byte(thr, pc, addr);
SyncVar *s = ctx->synctab.GetAndLock(thr, pc, addr, true);
s->is_rw = rw;
s->is_recursive = recursive;
s->is_linker_init = linker_init;
s->mtx.Unlock();
}
void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
Context *ctx = CTX();
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
StatInc(thr, StatMutexDestroy);
#ifndef TSAN_GO
// Global mutexes not marked as LINKER_INITIALIZED
// cause tons of not interesting reports, so just ignore it.
if (IsGlobalVar(addr))
return;
#endif
SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr);
if (s == 0)
return;
if (IsAppMem(addr))
MemoryWrite1Byte(thr, pc, addr);
if (flags()->report_destroy_locked
&& s->owner_tid != SyncVar::kInvalidTid
&& !s->is_broken) {
s->is_broken = true;
ScopedReport rep(ReportTypeMutexDestroyLocked);
rep.AddMutex(s);
StackTrace trace;
trace.ObtainCurrent(thr, pc);
rep.AddStack(&trace);
FastState last(s->last_lock);
RestoreStack(last.tid(), last.epoch(), &trace);
rep.AddStack(&trace);
rep.AddLocation(s->addr, 1);
OutputReport(ctx, rep);
}
DestroyAndFree(s);
}
void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeLock, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
if (s->owner_tid == SyncVar::kInvalidTid) {
CHECK_EQ(s->recursion, 0);
s->owner_tid = thr->tid;
s->last_lock = thr->fast_state.raw();
} else if (s->owner_tid == thr->tid) {
CHECK_GT(s->recursion, 0);
} else {
TsanPrintf("ThreadSanitizer WARNING: double lock\n");
PrintCurrentStack(thr, pc);
}
if (s->recursion == 0) {
StatInc(thr, StatMutexLock);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.acquire(&s->clock);
StatInc(thr, StatSyncAcquire);
thr->clock.acquire(&s->read_clock);
StatInc(thr, StatSyncAcquire);
} else if (!s->is_recursive) {
StatInc(thr, StatMutexRecLock);
}
s->recursion++;
s->mtx.Unlock();
}
void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeUnlock, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
if (s->recursion == 0) {
if (!s->is_broken) {
s->is_broken = true;
TsanPrintf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
PrintCurrentStack(thr, pc);
}
} else if (s->owner_tid != thr->tid) {
if (!s->is_broken) {
s->is_broken = true;
TsanPrintf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
PrintCurrentStack(thr, pc);
}
} else {
s->recursion--;
if (s->recursion == 0) {
StatInc(thr, StatMutexUnlock);
s->owner_tid = SyncVar::kInvalidTid;
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.ReleaseStore(&s->clock);
StatInc(thr, StatSyncRelease);
} else {
StatInc(thr, StatMutexRecUnlock);
}
}
s->mtx.Unlock();
}
void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
StatInc(thr, StatMutexReadLock);
if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRLock, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
if (s->owner_tid != SyncVar::kInvalidTid) {
TsanPrintf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
PrintCurrentStack(thr, pc);
}
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.acquire(&s->clock);
s->last_lock = thr->fast_state.raw();
StatInc(thr, StatSyncAcquire);
s->mtx.ReadUnlock();
}
void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
StatInc(thr, StatMutexReadUnlock);
if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRUnlock, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
if (s->owner_tid != SyncVar::kInvalidTid) {
TsanPrintf("ThreadSanitizer WARNING: read unlock of a write "
"locked mutex\n");
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);
s->mtx.Unlock();
}
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
if (s->owner_tid == SyncVar::kInvalidTid) {
// Seems to be read unlock.
StatInc(thr, StatMutexReadUnlock);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRUnlock, addr);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.release(&s->read_clock);
StatInc(thr, StatSyncRelease);
} else if (s->owner_tid == thr->tid) {
// Seems to be write unlock.
CHECK_GT(s->recursion, 0);
s->recursion--;
if (s->recursion == 0) {
StatInc(thr, StatMutexUnlock);
s->owner_tid = SyncVar::kInvalidTid;
// FIXME: Refactor me, plz.
// The sequence of events is quite tricky and doubled in several places.
// First, it's a bug to increment the epoch w/o writing to the trace.
// Then, the acquire/release logic can be factored out as well.
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeUnlock, addr);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.ReleaseStore(&s->clock);
StatInc(thr, StatSyncRelease);
} else {
StatInc(thr, StatMutexRecUnlock);
}
} else if (!s->is_broken) {
s->is_broken = true;
TsanPrintf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
PrintCurrentStack(thr, pc);
}
s->mtx.Unlock();
}
void Acquire(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.acquire(&s->clock);
StatInc(thr, StatSyncAcquire);
s->mtx.ReadUnlock();
}
void Release(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Release %zx\n", thr->tid, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.release(&s->clock);
StatInc(thr, StatSyncRelease);
s->mtx.Unlock();
}
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.ReleaseStore(&s->clock);
StatInc(thr, StatSyncRelease);
s->mtx.Unlock();
}
#ifndef TSAN_GO
void AfterSleep(ThreadState *thr, uptr pc) {
Context *ctx = CTX();
thr->last_sleep_stack_id = CurrentStackId(thr, pc);
Lock l(&ctx->thread_mtx);
for (unsigned i = 0; i < kMaxTid; i++) {
ThreadContext *tctx = ctx->threads[i];
if (tctx == 0)
continue;
if (tctx->status == ThreadStatusRunning)
thr->last_sleep_clock.set(i, tctx->thr->fast_state.epoch());
else
thr->last_sleep_clock.set(i, tctx->epoch1);
}
}
#endif
} // namespace __tsan
//===-- tsan_stat.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_STAT_H
#define TSAN_STAT_H
namespace __tsan {
enum StatType {
// Memory access processing related stuff.
StatMop,
StatMopRead,
StatMopWrite,
StatMop1, // These must be consequtive.
StatMop2,
StatMop4,
StatMop8,
StatMopSame,
StatMopRange,
StatShadowProcessed,
StatShadowZero,
StatShadowNonZero, // Derived.
StatShadowSameSize,
StatShadowIntersect,
StatShadowNotIntersect,
StatShadowSameThread,
StatShadowAnotherThread,
StatShadowReplace,
// Func processing.
StatFuncEnter,
StatFuncExit,
// Trace processing.
StatEvents,
// Threads.
StatThreadCreate,
StatThreadFinish,
StatThreadReuse,
StatThreadMaxTid,
StatThreadMaxAlive,
// Mutexes.
StatMutexCreate,
StatMutexDestroy,
StatMutexLock,
StatMutexUnlock,
StatMutexRecLock,
StatMutexRecUnlock,
StatMutexReadLock,
StatMutexReadUnlock,
// Synchronization.
StatSyncCreated,
StatSyncDestroyed,
StatSyncAcquire,
StatSyncRelease,
// Atomics.
StatAtomic,
StatAtomicLoad,
StatAtomicStore,
StatAtomicExchange,
StatAtomicFetchAdd,
StatAtomicFetchSub,
StatAtomicFetchAnd,
StatAtomicFetchOr,
StatAtomicFetchXor,
StatAtomicCAS,
StatAtomicFence,
StatAtomicRelaxed,
StatAtomicConsume,
StatAtomicAcquire,
StatAtomicRelease,
StatAtomicAcq_Rel,
StatAtomicSeq_Cst,
StatAtomic1,
StatAtomic2,
StatAtomic4,
StatAtomic8,
// Interceptors.
StatInterceptor,
StatInt_longjmp,
StatInt_siglongjmp,
StatInt_malloc,
StatInt_calloc,
StatInt_realloc,
StatInt_free,
StatInt_cfree,
StatInt_mmap,
StatInt_mmap64,
StatInt_munmap,
StatInt_memalign,
StatInt_valloc,
StatInt_pvalloc,
StatInt_posix_memalign,
StatInt__Znwm,
StatInt__ZnwmRKSt9nothrow_t,
StatInt__Znam,
StatInt__ZnamRKSt9nothrow_t,
StatInt__ZdlPv,
StatInt__ZdlPvRKSt9nothrow_t,
StatInt__ZdaPv,
StatInt__ZdaPvRKSt9nothrow_t,
StatInt_strlen,
StatInt_memset,
StatInt_memcpy,
StatInt_strcmp,
StatInt_memchr,
StatInt_memrchr,
StatInt_memmove,
StatInt_memcmp,
StatInt_strchr,
StatInt_strchrnul,
StatInt_strrchr,
StatInt_strncmp,
StatInt_strcpy,
StatInt_strncpy,
StatInt_strstr,
StatInt_atexit,
StatInt___cxa_guard_acquire,
StatInt___cxa_guard_release,
StatInt_pthread_create,
StatInt_pthread_join,
StatInt_pthread_detach,
StatInt_pthread_mutex_init,
StatInt_pthread_mutex_destroy,
StatInt_pthread_mutex_lock,
StatInt_pthread_mutex_trylock,
StatInt_pthread_mutex_timedlock,
StatInt_pthread_mutex_unlock,
StatInt_pthread_spin_init,
StatInt_pthread_spin_destroy,
StatInt_pthread_spin_lock,
StatInt_pthread_spin_trylock,
StatInt_pthread_spin_unlock,
StatInt_pthread_rwlock_init,
StatInt_pthread_rwlock_destroy,
StatInt_pthread_rwlock_rdlock,
StatInt_pthread_rwlock_tryrdlock,
StatInt_pthread_rwlock_timedrdlock,
StatInt_pthread_rwlock_wrlock,
StatInt_pthread_rwlock_trywrlock,
StatInt_pthread_rwlock_timedwrlock,
StatInt_pthread_rwlock_unlock,
StatInt_pthread_cond_init,
StatInt_pthread_cond_destroy,
StatInt_pthread_cond_signal,
StatInt_pthread_cond_broadcast,
StatInt_pthread_cond_wait,
StatInt_pthread_cond_timedwait,
StatInt_pthread_barrier_init,
StatInt_pthread_barrier_destroy,
StatInt_pthread_barrier_wait,
StatInt_pthread_once,
StatInt_sem_init,
StatInt_sem_destroy,
StatInt_sem_wait,
StatInt_sem_trywait,
StatInt_sem_timedwait,
StatInt_sem_post,
StatInt_sem_getvalue,
StatInt_read,
StatInt_pread,
StatInt_pread64,
StatInt_readv,
StatInt_preadv64,
StatInt_write,
StatInt_pwrite,
StatInt_pwrite64,
StatInt_writev,
StatInt_pwritev64,
StatInt_send,
StatInt_sendmsg,
StatInt_recv,
StatInt_recvmsg,
StatInt_unlink,
StatInt_fopen,
StatInt_fread,
StatInt_fwrite,
StatInt_puts,
StatInt_rmdir,
StatInt_opendir,
StatInt_epoll_ctl,
StatInt_epoll_wait,
StatInt_sigaction,
StatInt_signal,
StatInt_raise,
StatInt_kill,
StatInt_pthread_kill,
StatInt_sleep,
StatInt_usleep,
StatInt_nanosleep,
// Dynamic annotations.
StatAnnotation,
StatAnnotateHappensBefore,
StatAnnotateHappensAfter,
StatAnnotateCondVarSignal,
StatAnnotateCondVarSignalAll,
StatAnnotateMutexIsNotPHB,
StatAnnotateCondVarWait,
StatAnnotateRWLockCreate,
StatAnnotateRWLockCreateStatic,
StatAnnotateRWLockDestroy,
StatAnnotateRWLockAcquired,
StatAnnotateRWLockReleased,
StatAnnotateTraceMemory,
StatAnnotateFlushState,
StatAnnotateNewMemory,
StatAnnotateNoOp,
StatAnnotateFlushExpectedRaces,
StatAnnotateEnableRaceDetection,
StatAnnotateMutexIsUsedAsCondVar,
StatAnnotatePCQGet,
StatAnnotatePCQPut,
StatAnnotatePCQDestroy,
StatAnnotatePCQCreate,
StatAnnotateExpectRace,
StatAnnotateBenignRaceSized,
StatAnnotateBenignRace,
StatAnnotateIgnoreReadsBegin,
StatAnnotateIgnoreReadsEnd,
StatAnnotateIgnoreWritesBegin,
StatAnnotateIgnoreWritesEnd,
StatAnnotatePublishMemoryRange,
StatAnnotateUnpublishMemoryRange,
StatAnnotateThreadName,
// Internal mutex contentionz.
StatMtxTotal,
StatMtxTrace,
StatMtxThreads,
StatMtxReport,
StatMtxSyncVar,
StatMtxSyncTab,
StatMtxSlab,
StatMtxAnnotations,
StatMtxAtExit,
// This must be the last.
StatCnt
};
} // namespace __tsan
#endif // TSAN_STAT_H
//===-- tsan_suppressions.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 "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_suppressions.h"
#include "tsan_rtl.h"
#include "tsan_flags.h"
#include "tsan_mman.h"
#include "tsan_platform.h"
namespace __tsan {
static Suppression *g_suppressions;
static char *ReadFile(const char *filename) {
if (filename == 0 || filename[0] == 0)
return 0;
InternalScopedBuffer<char> tmp(4*1024);
if (filename[0] == '/')
internal_snprintf(tmp.data(), tmp.size(), "%s", filename);
else
internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename);
fd_t fd = internal_open(tmp.data(), false);
if (fd == kInvalidFd) {
TsanPrintf("ThreadSanitizer: failed to open suppressions file '%s'\n",
tmp.data());
Die();
}
const uptr fsize = internal_filesize(fd);
if (fsize == (uptr)-1) {
TsanPrintf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
tmp.data());
Die();
}
char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
if (fsize != internal_read(fd, buf, fsize)) {
TsanPrintf("ThreadSanitizer: failed to read suppressions file '%s'\n",
tmp.data());
Die();
}
internal_close(fd);
buf[fsize] = 0;
return buf;
}
bool SuppressionMatch(char *templ, const char *str) {
if (str == 0 || str[0] == 0)
return false;
char *tpos;
const char *spos;
while (templ && templ[0]) {
if (templ[0] == '*') {
templ++;
continue;
}
if (str[0] == 0)
return false;
tpos = (char*)internal_strchr(templ, '*');
if (tpos != 0)
tpos[0] = 0;
spos = internal_strstr(str, templ);
str = spos + internal_strlen(templ);
templ = tpos;
if (tpos)
tpos[0] = '*';
if (spos == 0)
return false;
}
return true;
}
Suppression *SuppressionParse(const char* supp) {
Suppression *head = 0;
const char *line = supp;
while (line) {
while (line[0] == ' ' || line[0] == '\t')
line++;
const char *end = internal_strchr(line, '\n');
if (end == 0)
end = line + internal_strlen(line);
if (line != end && line[0] != '#') {
const char *end2 = end;
while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
end2--;
SuppressionType stype;
if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) {
stype = SuppressionRace;
line += sizeof("race:") - 1;
} else if (0 == internal_strncmp(line, "thread:",
sizeof("thread:") - 1)) {
stype = SuppressionThread;
line += sizeof("thread:") - 1;
} else if (0 == internal_strncmp(line, "mutex:",
sizeof("mutex:") - 1)) {
stype = SuppressionMutex;
line += sizeof("mutex:") - 1;
} else if (0 == internal_strncmp(line, "signal:",
sizeof("signal:") - 1)) {
stype = SuppressionSignal;
line += sizeof("signal:") - 1;
} else {
TsanPrintf("ThreadSanitizer: failed to parse suppressions file\n");
Die();
}
Suppression *s = (Suppression*)internal_alloc(MBlockSuppression,
sizeof(Suppression));
s->next = head;
head = s;
s->type = stype;
s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1);
internal_memcpy(s->templ, line, end2 - line);
s->templ[end2 - line] = 0;
}
if (end[0] == 0)
break;
line = end + 1;
}
return head;
}
void InitializeSuppressions() {
char *supp = ReadFile(flags()->suppressions);
g_suppressions = SuppressionParse(supp);
}
uptr IsSuppressed(ReportType typ, const ReportStack *stack) {
if (g_suppressions == 0 || stack == 0)
return 0;
SuppressionType stype;
if (typ == ReportTypeRace)
stype = SuppressionRace;
else if (typ == ReportTypeThreadLeak)
stype = SuppressionThread;
else if (typ == ReportTypeMutexDestroyLocked)
stype = SuppressionMutex;
else if (typ == ReportTypeSignalUnsafe)
stype = SuppressionSignal;
else
return 0;
for (const ReportStack *frame = stack; frame; frame = frame->next) {
for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
if (stype == supp->type &&
(SuppressionMatch(supp->templ, frame->func) ||
SuppressionMatch(supp->templ, frame->file))) {
DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
return frame->pc;
}
}
}
return 0;
}
} // namespace __tsan
//===-- tsan_suppressions.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_SUPPRESSIONS_H
#define TSAN_SUPPRESSIONS_H
#include "tsan_report.h"
namespace __tsan {
void InitializeSuppressions();
void FinalizeSuppressions();
uptr IsSuppressed(ReportType typ, const ReportStack *stack);
// Exposed for testing.
enum SuppressionType {
SuppressionRace,
SuppressionMutex,
SuppressionThread,
SuppressionSignal
};
struct Suppression {
Suppression *next;
SuppressionType type;
char *templ;
};
Suppression *SuppressionParse(const char* supp);
bool SuppressionMatch(char *templ, const char *str);
} // namespace __tsan
#endif // TSAN_SUPPRESSIONS_H
//===-- tsan_symbolize.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_symbolize.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "tsan_flags.h"
#include "tsan_report.h"
namespace __tsan {
ReportStack *NewReportStackEntry(uptr addr) {
ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
sizeof(ReportStack));
internal_memset(ent, 0, sizeof(*ent));
ent->pc = addr;
return ent;
}
static ReportStack *NewReportStackEntry(const AddressInfo &info) {
ReportStack *ent = NewReportStackEntry(info.address);
if (info.module) {
// Strip module path to make output shorter.
const char *short_module_name = internal_strrchr(info.module, '/');
if (short_module_name)
short_module_name += 1;
else
short_module_name = info.module;
ent->module = internal_strdup(short_module_name);
}
ent->offset = info.module_offset;
if (info.function) {
ent->func = internal_strdup(info.function);
}
if (info.file)
ent->file = internal_strdup(info.file);
ent->line = info.line;
ent->col = info.column;
return ent;
}
ReportStack *SymbolizeCode(uptr addr) {
if (0 != internal_strcmp(flags()->external_symbolizer_path, "")) {
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 = __sanitizer::SymbolizeCode(addr, addr_frames.data(),
kMaxAddrFrames);
if (addr_frames_num == 0)
return NewReportStackEntry(addr);
ReportStack *top = 0;
ReportStack *bottom = 0;
for (uptr i = 0; i < addr_frames_num; i++) {
ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]);
CHECK(cur_entry);
addr_frames[i].Clear();
if (i == 0)
top = cur_entry;
else
bottom->next = cur_entry;
bottom = cur_entry;
}
return top;
}
return SymbolizeCodeAddr2Line(addr);
}
ReportStack *SymbolizeData(uptr addr) {
return SymbolizeDataAddr2Line(addr);
}
} // namespace __tsan
//===-- tsan_symbolize.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_SYMBOLIZE_H
#define TSAN_SYMBOLIZE_H
#include "tsan_defs.h"
#include "tsan_report.h"
namespace __tsan {
ReportStack *SymbolizeCode(uptr addr);
ReportStack *SymbolizeData(uptr addr);
ReportStack *SymbolizeCodeAddr2Line(uptr addr);
ReportStack *SymbolizeDataAddr2Line(uptr addr);
ReportStack *NewReportStackEntry(uptr addr);
} // namespace __tsan
#endif // TSAN_SYMBOLIZE_H
//===-- tsan_symbolize_addr2line.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 "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_symbolize.h"
#include "tsan_mman.h"
#include "tsan_rtl.h"
#include "tsan_platform.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <link.h>
#include <linux/limits.h>
#include <sys/types.h>
namespace __tsan {
struct ModuleDesc {
const char *fullname;
const char *name;
uptr base;
int inp_fd;
int out_fd;
};
struct SectionDesc {
SectionDesc *next;
ModuleDesc *module;
uptr base;
uptr end;
};
struct DlIteratePhdrCtx {
SectionDesc *sections;
bool is_first;
};
static void NOINLINE InitModule(ModuleDesc *m) {
int outfd[2] = {};
if (pipe(&outfd[0])) {
TsanPrintf("ThreadSanitizer: outfd pipe() failed (%d)\n", errno);
Die();
}
int infd[2] = {};
if (pipe(&infd[0])) {
TsanPrintf("ThreadSanitizer: infd pipe() failed (%d)\n", errno);
Die();
}
int pid = fork();
if (pid == 0) {
flags()->log_fileno = STDERR_FILENO;
internal_close(STDOUT_FILENO);
internal_close(STDIN_FILENO);
internal_dup2(outfd[0], STDIN_FILENO);
internal_dup2(infd[1], STDOUT_FILENO);
internal_close(outfd[0]);
internal_close(outfd[1]);
internal_close(infd[0]);
internal_close(infd[1]);
for (int fd = getdtablesize(); fd > 2; fd--)
internal_close(fd);
execl("/usr/bin/addr2line", "/usr/bin/addr2line", "-Cfe", m->fullname, 0);
_exit(0);
} else if (pid < 0) {
TsanPrintf("ThreadSanitizer: failed to fork symbolizer\n");
Die();
}
internal_close(outfd[0]);
internal_close(infd[1]);
m->inp_fd = infd[0];
m->out_fd = outfd[1];
}
static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
DlIteratePhdrCtx *ctx = (DlIteratePhdrCtx*)arg;
InternalScopedBuffer<char> tmp(128);
if (ctx->is_first) {
internal_snprintf(tmp.data(), tmp.size(), "/proc/%d/exe", GetPid());
info->dlpi_name = tmp.data();
}
ctx->is_first = false;
if (info->dlpi_name == 0 || info->dlpi_name[0] == 0)
return 0;
ModuleDesc *m = (ModuleDesc*)internal_alloc(MBlockReportStack,
sizeof(ModuleDesc));
m->fullname = internal_strdup(info->dlpi_name);
m->name = internal_strrchr(m->fullname, '/');
if (m->name)
m->name += 1;
else
m->name = m->fullname;
m->base = (uptr)info->dlpi_addr;
m->inp_fd = -1;
m->out_fd = -1;
DPrintf("Module %s %zx\n", m->name, m->base);
for (int i = 0; i < info->dlpi_phnum; i++) {
const Elf64_Phdr *s = &info->dlpi_phdr[i];
DPrintf(" Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx"
" p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n",
(uptr)s->p_type, (uptr)s->p_offset, (uptr)s->p_vaddr,
(uptr)s->p_paddr, (uptr)s->p_filesz, (uptr)s->p_memsz,
(uptr)s->p_flags, (uptr)s->p_align);
if (s->p_type != PT_LOAD)
continue;
SectionDesc *sec = (SectionDesc*)internal_alloc(MBlockReportStack,
sizeof(SectionDesc));
sec->module = m;
sec->base = info->dlpi_addr + s->p_vaddr;
sec->end = sec->base + s->p_memsz;
sec->next = ctx->sections;
ctx->sections = sec;
DPrintf(" Section %zx-%zx\n", sec->base, sec->end);
}
return 0;
}
static SectionDesc *InitSections() {
DlIteratePhdrCtx ctx = {0, true};
dl_iterate_phdr(dl_iterate_phdr_cb, &ctx);
return ctx.sections;
}
static SectionDesc *GetSectionDesc(uptr addr) {
static SectionDesc *sections = 0;
if (sections == 0)
sections = InitSections();
for (SectionDesc *s = sections; s; s = s->next) {
if (addr >= s->base && addr < s->end) {
if (s->module->inp_fd == -1)
InitModule(s->module);
return s;
}
}
return 0;
}
ReportStack *SymbolizeCodeAddr2Line(uptr addr) {
SectionDesc *s = GetSectionDesc(addr);
if (s == 0)
return NewReportStackEntry(addr);
ModuleDesc *m = s->module;
uptr offset = addr - m->base;
char addrstr[32];
internal_snprintf(addrstr, sizeof(addrstr), "%p\n", (void*)offset);
if (0 >= internal_write(m->out_fd, addrstr, internal_strlen(addrstr))) {
TsanPrintf("ThreadSanitizer: can't write from symbolizer (%d, %d)\n",
m->out_fd, errno);
Die();
}
InternalScopedBuffer<char> func(1024);
ssize_t len = internal_read(m->inp_fd, func.data(), func.size() - 1);
if (len <= 0) {
TsanPrintf("ThreadSanitizer: can't read from symbolizer (%d, %d)\n",
m->inp_fd, errno);
Die();
}
func.data()[len] = 0;
ReportStack *res = NewReportStackEntry(addr);
res->module = internal_strdup(m->name);
res->offset = offset;
char *pos = (char*)internal_strchr(func.data(), '\n');
if (pos && func[0] != '?') {
res->func = (char*)internal_alloc(MBlockReportStack, pos - func.data() + 1);
internal_memcpy(res->func, func.data(), pos - func.data());
res->func[pos - func.data()] = 0;
char *pos2 = (char*)internal_strchr(pos, ':');
if (pos2) {
res->file = (char*)internal_alloc(MBlockReportStack, pos2 - pos - 1 + 1);
internal_memcpy(res->file, pos + 1, pos2 - pos - 1);
res->file[pos2 - pos - 1] = 0;
res->line = atoi(pos2 + 1);
}
}
return res;
}
ReportStack *SymbolizeDataAddr2Line(uptr addr) {
return 0;
}
} // namespace __tsan
//===-- tsan_sync.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 "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_sync.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
namespace __tsan {
SyncVar::SyncVar(uptr addr)
: mtx(MutexTypeSyncVar, StatMtxSyncVar)
, addr(addr)
, owner_tid(kInvalidTid)
, last_lock()
, recursion()
, is_rw()
, is_recursive()
, is_broken()
, is_linker_init() {
}
SyncTab::Part::Part()
: mtx(MutexTypeSyncTab, StatMtxSyncTab)
, val() {
}
SyncTab::SyncTab() {
}
SyncTab::~SyncTab() {
for (int i = 0; i < kPartCount; i++) {
while (tab_[i].val) {
SyncVar *tmp = tab_[i].val;
tab_[i].val = tmp->next;
DestroyAndFree(tmp);
}
}
}
SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
uptr addr, bool write_lock) {
#ifndef TSAN_GO
if (PrimaryAllocator::PointerIsMine((void*)addr)) {
MBlock *b = user_mblock(thr, (void*)addr);
Lock l(&b->mtx);
SyncVar *res = 0;
for (res = b->head; res; res = res->next) {
if (res->addr == addr)
break;
}
if (res == 0) {
StatInc(thr, StatSyncCreated);
void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
res = new(mem) SyncVar(addr);
res->creation_stack.ObtainCurrent(thr, pc);
res->next = b->head;
b->head = res;
}
if (write_lock)
res->mtx.Lock();
else
res->mtx.ReadLock();
return res;
}
#endif
Part *p = &tab_[PartIdx(addr)];
{
ReadLock l(&p->mtx);
for (SyncVar *res = p->val; res; res = res->next) {
if (res->addr == addr) {
if (write_lock)
res->mtx.Lock();
else
res->mtx.ReadLock();
return res;
}
}
}
{
Lock l(&p->mtx);
SyncVar *res = p->val;
for (; res; res = res->next) {
if (res->addr == addr)
break;
}
if (res == 0) {
StatInc(thr, StatSyncCreated);
void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
res = new(mem) SyncVar(addr);
#ifndef TSAN_GO
res->creation_stack.ObtainCurrent(thr, pc);
#endif
res->next = p->val;
p->val = res;
}
if (write_lock)
res->mtx.Lock();
else
res->mtx.ReadLock();
return res;
}
}
SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) {
#ifndef TSAN_GO
if (PrimaryAllocator::PointerIsMine((void*)addr)) {
MBlock *b = user_mblock(thr, (void*)addr);
SyncVar *res = 0;
{
Lock l(&b->mtx);
SyncVar **prev = &b->head;
res = *prev;
while (res) {
if (res->addr == addr) {
if (res->is_linker_init)
return 0;
*prev = res->next;
break;
}
prev = &res->next;
res = *prev;
}
}
if (res) {
StatInc(thr, StatSyncDestroyed);
res->mtx.Lock();
res->mtx.Unlock();
}
return res;
}
#endif
Part *p = &tab_[PartIdx(addr)];
SyncVar *res = 0;
{
Lock l(&p->mtx);
SyncVar **prev = &p->val;
res = *prev;
while (res) {
if (res->addr == addr) {
if (res->is_linker_init)
return 0;
*prev = res->next;
break;
}
prev = &res->next;
res = *prev;
}
}
if (res) {
StatInc(thr, StatSyncDestroyed);
res->mtx.Lock();
res->mtx.Unlock();
}
return res;
}
uptr SyncVar::GetMemoryConsumption() {
return sizeof(*this)
+ clock.size() * sizeof(u64)
+ read_clock.size() * sizeof(u64)
+ creation_stack.Size() * sizeof(uptr);
}
uptr SyncTab::GetMemoryConsumption(uptr *nsync) {
uptr mem = 0;
for (int i = 0; i < kPartCount; i++) {
Part *p = &tab_[i];
Lock l(&p->mtx);
for (SyncVar *s = p->val; s; s = s->next) {
*nsync += 1;
mem += s->GetMemoryConsumption();
}
}
return mem;
}
int SyncTab::PartIdx(uptr addr) {
return (addr >> 3) % kPartCount;
}
StackTrace::StackTrace()
: n_()
, s_()
, c_() {
}
StackTrace::StackTrace(uptr *buf, uptr cnt)
: n_()
, s_(buf)
, c_(cnt) {
CHECK_NE(buf, 0);
CHECK_NE(cnt, 0);
}
StackTrace::~StackTrace() {
Reset();
}
void StackTrace::Reset() {
if (s_ && !c_) {
CHECK_NE(n_, 0);
internal_free(s_);
s_ = 0;
}
n_ = 0;
}
void StackTrace::Init(const uptr *pcs, uptr cnt) {
Reset();
if (cnt == 0)
return;
if (c_) {
CHECK_NE(s_, 0);
CHECK_LE(cnt, c_);
} else {
s_ = (uptr*)internal_alloc(MBlockStackTrace, cnt * sizeof(s_[0]));
}
n_ = cnt;
internal_memcpy(s_, pcs, cnt * sizeof(s_[0]));
}
void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) {
Reset();
n_ = thr->shadow_stack_pos - thr->shadow_stack;
if (n_ + !!toppc == 0)
return;
uptr start = 0;
if (c_) {
CHECK_NE(s_, 0);
if (n_ + !!toppc > c_) {
start = n_ - c_ + !!toppc;
n_ = c_ - !!toppc;
}
} else {
s_ = (uptr*)internal_alloc(MBlockStackTrace,
(n_ + !!toppc) * sizeof(s_[0]));
}
for (uptr i = 0; i < n_; i++)
s_[i] = thr->shadow_stack[start + i];
if (toppc) {
s_[n_] = toppc;
n_++;
}
}
void StackTrace::CopyFrom(const StackTrace& other) {
Reset();
Init(other.Begin(), other.Size());
}
bool StackTrace::IsEmpty() const {
return n_ == 0;
}
uptr StackTrace::Size() const {
return n_;
}
uptr StackTrace::Get(uptr i) const {
CHECK_LT(i, n_);
return s_[i];
}
const uptr *StackTrace::Begin() const {
return s_;
}
} // namespace __tsan
//===-- tsan_sync.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_SYNC_H
#define TSAN_SYNC_H
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "tsan_clock.h"
#include "tsan_defs.h"
#include "tsan_mutex.h"
namespace __tsan {
class SlabCache;
class StackTrace {
public:
StackTrace();
// Initialized the object in "static mode",
// in this mode it never calls malloc/free but uses the provided buffer.
StackTrace(uptr *buf, uptr cnt);
~StackTrace();
void Reset();
void Init(const uptr *pcs, uptr cnt);
void ObtainCurrent(ThreadState *thr, uptr toppc);
bool IsEmpty() const;
uptr Size() const;
uptr Get(uptr i) const;
const uptr *Begin() const;
void CopyFrom(const StackTrace& other);
private:
uptr n_;
uptr *s_;
const uptr c_;
StackTrace(const StackTrace&);
void operator = (const StackTrace&);
};
struct SyncVar {
explicit SyncVar(uptr addr);
static const int kInvalidTid = -1;
Mutex mtx;
const uptr addr;
SyncClock clock;
SyncClock read_clock; // Used for rw mutexes only.
StackTrace creation_stack;
int owner_tid; // Set only by exclusive owners.
u64 last_lock;
int recursion;
bool is_rw;
bool is_recursive;
bool is_broken;
bool is_linker_init;
SyncVar *next; // In SyncTab hashtable.
uptr GetMemoryConsumption();
};
class SyncTab {
public:
SyncTab();
~SyncTab();
// If the SyncVar does not exist yet, it is created.
SyncVar* GetAndLock(ThreadState *thr, uptr pc,
uptr addr, bool write_lock);
// If the SyncVar does not exist, returns 0.
SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr);
uptr GetMemoryConsumption(uptr *nsync);
private:
struct Part {
Mutex mtx;
SyncVar *val;
char pad[kCacheLineSize - sizeof(Mutex) - sizeof(SyncVar*)]; // NOLINT
Part();
};
// FIXME: Implement something more sane.
static const int kPartCount = 1009;
Part tab_[kPartCount];
int PartIdx(uptr addr);
SyncTab(const SyncTab&); // Not implemented.
void operator = (const SyncTab&); // Not implemented.
};
} // namespace __tsan
#endif // TSAN_SYNC_H
//===-- tsan_trace.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.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_TRACE_H
#define TSAN_TRACE_H
#include "tsan_defs.h"
#include "tsan_mutex.h"
#include "tsan_sync.h"
namespace __tsan {
#ifndef TSAN_HISTORY_SIZE // in kibitraces
#define TSAN_HISTORY_SIZE 128
#endif
const int kTracePartSize = 16 * 1024;
const int kTraceParts = TSAN_HISTORY_SIZE * 1024 / kTracePartSize;
const int kTraceSize = kTracePartSize * kTraceParts;
// Must fit into 3 bits.
enum EventType {
EventTypeMop,
EventTypeFuncEnter,
EventTypeFuncExit,
EventTypeLock,
EventTypeUnlock,
EventTypeRLock,
EventTypeRUnlock
};
// Represents a thread event (from most significant bit):
// u64 typ : 3; // EventType.
// u64 addr : 61; // Associated pc.
typedef u64 Event;
struct TraceHeader {
StackTrace stack0; // Start stack for the trace.
u64 epoch0; // Start epoch for the trace.
#ifndef TSAN_GO
uptr stack0buf[kTraceStackSize];
#endif
TraceHeader()
#ifndef TSAN_GO
: stack0(stack0buf, kTraceStackSize)
#else
: stack0()
#endif
, epoch0() {
}
};
struct Trace {
Event events[kTraceSize];
TraceHeader headers[kTraceParts];
Mutex mtx;
Trace()
: mtx(MutexTypeTrace, StatMtxTrace) {
}
};
} // namespace __tsan
#endif // TSAN_TRACE_H
//===-- tsan_update_shadow_word_inl.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.
//
// Body of the hottest inner loop.
// If we wrap this body into a function, compilers (both gcc and clang)
// produce sligtly less efficient code.
//===----------------------------------------------------------------------===//
do {
StatInc(thr, StatShadowProcessed);
const unsigned kAccessSize = 1 << kAccessSizeLog;
unsigned off = cur.ComputeSearchOffset();
u64 *sp = &shadow_mem[(idx + off) % kShadowCnt];
old = LoadShadow(sp);
if (old.IsZero()) {
StatInc(thr, StatShadowZero);
if (store_word)
StoreIfNotYetStored(sp, &store_word);
// The above StoreIfNotYetStored could be done unconditionally
// and it even shows 4% gain on synthetic benchmarks (r4307).
break;
}
// is the memory access equal to the previous?
if (Shadow::Addr0AndSizeAreEqual(cur, old)) {
StatInc(thr, StatShadowSameSize);
// same thread?
if (Shadow::TidsAreEqual(old, cur)) {
StatInc(thr, StatShadowSameThread);
if (OldIsInSameSynchEpoch(old, thr)) {
if (OldIsRWStronger(old, kAccessIsWrite)) {
// found a slot that holds effectively the same info
// (that is, same tid, same sync epoch and same size)
StatInc(thr, StatMopSame);
return;
}
StoreIfNotYetStored(sp, &store_word);
break;
}
if (OldIsRWWeaker(old, kAccessIsWrite))
StoreIfNotYetStored(sp, &store_word);
break;
}
StatInc(thr, StatShadowAnotherThread);
if (HappensBefore(old, thr)) {
StoreIfNotYetStored(sp, &store_word);
break;
}
if (BothReads(old, kAccessIsWrite))
break;
goto RACE;
}
// Do the memory access intersect?
if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) {
StatInc(thr, StatShadowIntersect);
if (Shadow::TidsAreEqual(old, cur)) {
StatInc(thr, StatShadowSameThread);
break;
}
StatInc(thr, StatShadowAnotherThread);
if (HappensBefore(old, thr))
break;
if (BothReads(old, kAccessIsWrite))
break;
goto RACE;
}
// The accesses do not intersect.
StatInc(thr, StatShadowNotIntersect);
break;
} while (0);
//===-- tsan_vector.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.
//
//===----------------------------------------------------------------------===//
// Low-fat STL-like vector container.
#ifndef TSAN_VECTOR_H
#define TSAN_VECTOR_H
#include "tsan_defs.h"
#include "tsan_mman.h"
namespace __tsan {
template<typename T>
class Vector {
public:
explicit Vector(MBlockType typ)
: typ_(typ)
, begin_()
, end_()
, last_() {
}
~Vector() {
if (begin_)
internal_free(begin_);
}
void Reset() {
if (begin_)
internal_free(begin_);
begin_ = 0;
end_ = 0;
last_ = 0;
}
uptr Size() const {
return end_ - begin_;
}
T &operator[](uptr i) {
DCHECK_LT(i, end_ - begin_);
return begin_[i];
}
const T &operator[](uptr i) const {
DCHECK_LT(i, end_ - begin_);
return begin_[i];
}
T *PushBack(T v = T()) {
EnsureSize(Size() + 1);
end_[-1] = v;
return &end_[-1];
}
void Resize(uptr size) {
uptr old_size = Size();
EnsureSize(size);
if (old_size < size) {
for (uptr i = old_size; i < size; i++)
begin_[i] = T();
}
}
private:
const MBlockType typ_;
T *begin_;
T *end_;
T *last_;
void EnsureSize(uptr size) {
if (size <= Size())
return;
if (size <= (uptr)(last_ - begin_)) {
end_ = begin_ + size;
return;
}
uptr cap0 = last_ - begin_;
uptr cap = 2 * cap0;
if (cap == 0)
cap = 16;
if (cap < size)
cap = size;
T *p = (T*)internal_alloc(typ_, cap * sizeof(T));
if (cap0) {
internal_memcpy(p, begin_, cap0 * sizeof(T));
internal_free(begin_);
}
begin_ = p;
end_ = begin_ + size;
last_ = begin_ + cap;
}
Vector(const Vector&);
void operator=(const Vector&);
};
}
#endif // #ifndef TSAN_VECTOR_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment