tsan: New directory.
authorWei Mi <wmi@google.com>
Thu, 22 Nov 2012 22:03:11 +0000 (22:03 +0000)
committerWei Mi <wmi@gcc.gnu.org>
Thu, 22 Nov 2012 22:03:11 +0000 (22:03 +0000)
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

51 files changed:
libsanitizer/ChangeLog
libsanitizer/Makefile.am
libsanitizer/Makefile.in
libsanitizer/configure
libsanitizer/configure.ac
libsanitizer/tsan/Makefile.am [new file with mode: 0644]
libsanitizer/tsan/Makefile.in [new file with mode: 0644]
libsanitizer/tsan/libtool-version [new file with mode: 0644]
libsanitizer/tsan/tsan_clock.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_clock.h [new file with mode: 0644]
libsanitizer/tsan/tsan_defs.h [new file with mode: 0644]
libsanitizer/tsan/tsan_flags.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_flags.h [new file with mode: 0644]
libsanitizer/tsan/tsan_interceptors.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_interceptors.h [new file with mode: 0644]
libsanitizer/tsan/tsan_interface.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_interface.h [new file with mode: 0644]
libsanitizer/tsan/tsan_interface_ann.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_interface_ann.h [new file with mode: 0644]
libsanitizer/tsan/tsan_interface_atomic.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_interface_atomic.h [new file with mode: 0644]
libsanitizer/tsan/tsan_interface_inl.h [new file with mode: 0644]
libsanitizer/tsan/tsan_md5.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_mman.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_mman.h [new file with mode: 0644]
libsanitizer/tsan/tsan_mutex.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_mutex.h [new file with mode: 0644]
libsanitizer/tsan/tsan_platform.h [new file with mode: 0644]
libsanitizer/tsan/tsan_platform_linux.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_platform_mac.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_printf.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_report.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_report.h [new file with mode: 0644]
libsanitizer/tsan/tsan_rtl.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_rtl.h [new file with mode: 0644]
libsanitizer/tsan/tsan_rtl_amd64.S [new file with mode: 0644]
libsanitizer/tsan/tsan_rtl_mutex.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_rtl_report.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_rtl_thread.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_stat.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_stat.h [new file with mode: 0644]
libsanitizer/tsan/tsan_suppressions.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_suppressions.h [new file with mode: 0644]
libsanitizer/tsan/tsan_symbolize.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_symbolize.h [new file with mode: 0644]
libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_sync.cc [new file with mode: 0644]
libsanitizer/tsan/tsan_sync.h [new file with mode: 0644]
libsanitizer/tsan/tsan_trace.h [new file with mode: 0644]
libsanitizer/tsan/tsan_update_shadow_word_inl.h [new file with mode: 0644]
libsanitizer/tsan/tsan_vector.h [new file with mode: 0644]

index 488a153..3d6bde3 100644 (file)
@@ -1,3 +1,11 @@
+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.
index 91e3434..d2192b2 100644 (file)
@@ -1,6 +1,10 @@
 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
index 8d685fd..09eb6e2 100644 (file)
@@ -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
index e3b3928..519b1db 100755 (executable)
@@ -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
index 4fb576f..0f9e185 100644 (file)
@@ -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
diff --git a/libsanitizer/tsan/Makefile.am b/libsanitizer/tsan/Makefile.am
new file mode 100644 (file)
index 0000000..96b4fab
--- /dev/null
@@ -0,0 +1,80 @@
+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=
+
+## ################################################################
+
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
new file mode 100644 (file)
index 0000000..71439d6
--- /dev/null
@@ -0,0 +1,641 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = tsan
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \
+       $(top_srcdir)/../config/lead-dot.m4 \
+       $(top_srcdir)/../config/multi.m4 \
+       $(top_srcdir)/../config/override.m4 \
+       $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
+       $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
+       $(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
+       $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
+LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
+libtsan_la_DEPENDENCIES =  \
+       $(top_builddir)/sanitizer_common/libsanitizer_common.la \
+       $(top_builddir)/interception/libinterception.la \
+       $(top_builddir)/../libstdc++-v3/src/libstdc++.la
+am__objects_1 = tsan_clock.lo tsan_interface_atomic.lo tsan_mutex.lo \
+       tsan_report.lo tsan_rtl_thread.lo tsan_symbolize.lo \
+       tsan_flags.lo tsan_interface.lo tsan_platform_linux.lo \
+       tsan_rtl.lo tsan_stat.lo tsan_sync.lo tsan_interceptors.lo \
+       tsan_md5.lo tsan_platform_mac.lo tsan_rtl_mutex.lo \
+       tsan_suppressions.lo tsan_interface_ann.lo tsan_mman.lo \
+       tsan_printf.lo tsan_rtl_report.lo \
+       tsan_symbolize_addr2line_linux.lo
+am_libtsan_la_OBJECTS = $(am__objects_1)
+libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS)
+libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+       $(CXXFLAGS) $(libtsan_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/../depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+       --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+       --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+       $(LDFLAGS) -o $@
+SOURCES = $(libtsan_la_SOURCES)
+DIST_SOURCES = $(libtsan_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_shared = @enable_shared@
+enable_static = @enable_static@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+multi_basedir = @multi_basedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+toolexecdir = @toolexecdir@
+toolexeclibdir = @toolexeclibdir@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include
+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 = 
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+               && { if test -f $@; then exit 0; else break; fi; }; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tsan/Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --foreign tsan/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(toolexeclibdir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)"
+       @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
+       }
+
+uninstall-toolexeclibLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
+       done
+
+clean-toolexeclibLTLIBRARIES:
+       -test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
+       @list='$(toolexeclib_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+libtsan.la: $(libtsan_la_OBJECTS) $(libtsan_la_DEPENDENCIES) 
+       $(libtsan_la_LINK) -rpath $(toolexeclibdir) $(libtsan_la_OBJECTS) $(libtsan_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_clock.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_flags.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_printf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mutex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_stat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_suppressions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_symbolize.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_symbolize_addr2line_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_sync.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@  $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@  $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@  $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@  $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@  $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@  $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       set x; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       shift; \
+       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         if test $$# -gt 0; then \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             "$$@" $$unique; \
+         else \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             $$unique; \
+         fi; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       test -z "$(CTAGS_ARGS)$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && $(am__cd) $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d "$(distdir)/$$file"; then \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+         else \
+           test -f "$(distdir)/$$file" \
+           || cp -p $$d/$$file "$(distdir)/$$file" \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+       for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+       mostlyclean-am
+
+distclean: distclean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-toolexeclibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-toolexeclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+       clean-libtool clean-toolexeclibLTLIBRARIES ctags distclean \
+       distclean-compile distclean-generic distclean-libtool \
+       distclean-tags distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am install-man \
+       install-pdf install-pdf-am install-ps install-ps-am \
+       install-strip install-toolexeclibLTLIBRARIES installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-toolexeclibLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsanitizer/tsan/libtool-version b/libsanitizer/tsan/libtool-version
new file mode 100644 (file)
index 0000000..204fdd2
--- /dev/null
@@ -0,0 +1,6 @@
+# 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
diff --git a/libsanitizer/tsan/tsan_clock.cc b/libsanitizer/tsan/tsan_clock.cc
new file mode 100644 (file)
index 0000000..937f861
--- /dev/null
@@ -0,0 +1,116 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_clock.h b/libsanitizer/tsan/tsan_clock.h
new file mode 100644 (file)
index 0000000..d5c1730
--- /dev/null
@@ -0,0 +1,80 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h
new file mode 100644 (file)
index 0000000..6a6f6b9
--- /dev/null
@@ -0,0 +1,139 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc
new file mode 100644 (file)
index 0000000..e8563f5
--- /dev/null
@@ -0,0 +1,80 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_flags.h b/libsanitizer/tsan/tsan_flags.h
new file mode 100644 (file)
index 0000000..a6310e3
--- /dev/null
@@ -0,0 +1,71 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc
new file mode 100644 (file)
index 0000000..194e236
--- /dev/null
@@ -0,0 +1,1515 @@
+//===-- tsan_interceptors.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_atomic.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "tsan_interceptors.h"
+#include "tsan_interface.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+
+using namespace __tsan;  // NOLINT
+
+const int kSigCount = 128;
+
+struct my_siginfo_t {
+  int opaque[128];
+};
+
+struct sigset_t {
+  u64 val[1024 / 8 / sizeof(u64)];
+};
+
+struct ucontext_t {
+  uptr opaque[117];
+};
+
+extern "C" int pthread_attr_init(void *attr);
+extern "C" int pthread_attr_destroy(void *attr);
+extern "C" int pthread_attr_getdetachstate(void *attr, int *v);
+extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize);
+extern "C" int pthread_attr_getstacksize(void *attr, uptr *stacksize);
+extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
+extern "C" int pthread_setspecific(unsigned key, const void *v);
+extern "C" int pthread_mutexattr_gettype(void *a, int *type);
+extern "C" int pthread_yield();
+extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
+extern "C" int sigfillset(sigset_t *set);
+extern "C" void *pthread_self();
+extern "C" void _exit(int status);
+extern "C" int __cxa_atexit(void (*func)(void *arg), void *arg, void *dso);
+extern "C" int *__errno_location();
+const int PTHREAD_MUTEX_RECURSIVE = 1;
+const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
+const int kPthreadAttrSize = 56;
+const int EINVAL = 22;
+const int EBUSY = 16;
+const int EPOLL_CTL_ADD = 1;
+const int SIGILL = 4;
+const int SIGABRT = 6;
+const int SIGFPE = 8;
+const int SIGSEGV = 11;
+const int SIGPIPE = 13;
+const int SIGBUS = 7;
+void *const MAP_FAILED = (void*)-1;
+const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
+const int MAP_FIXED = 0x10;
+typedef long long_t;  // NOLINT
+
+// From /usr/include/unistd.h
+# define F_ULOCK 0      /* Unlock a previously locked region.  */
+# define F_LOCK  1      /* Lock a region for exclusive use.  */
+# define F_TLOCK 2      /* Test and lock a region for exclusive use.  */
+# define F_TEST  3      /* Test a region for other processes locks.  */
+
+typedef void (*sighandler_t)(int sig);
+
+#define errno (*__errno_location())
+
+union pthread_attr_t {
+  char size[kPthreadAttrSize];
+  void *align;
+};
+
+struct sigaction_t {
+  union {
+    sighandler_t sa_handler;
+    void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx);
+  };
+  sigset_t sa_mask;
+  int sa_flags;
+  void (*sa_restorer)();
+};
+
+const sighandler_t SIG_DFL = (sighandler_t)0;
+const sighandler_t SIG_IGN = (sighandler_t)1;
+const sighandler_t SIG_ERR = (sighandler_t)-1;
+const int SA_SIGINFO = 4;
+const int SIG_SETMASK = 2;
+
+namespace std {
+struct nothrow_t {};
+}  // namespace std
+
+static sigaction_t sigactions[kSigCount];
+
+namespace __tsan {
+struct SignalDesc {
+  bool armed;
+  bool sigaction;
+  my_siginfo_t siginfo;
+  ucontext_t ctx;
+};
+
+struct SignalContext {
+  int int_signal_send;
+  int pending_signal_count;
+  SignalDesc pending_signals[kSigCount];
+};
+}
+
+static SignalContext *SigCtx(ThreadState *thr) {
+  SignalContext *ctx = (SignalContext*)thr->signal_ctx;
+  if (ctx == 0 && thr->is_alive) {
+    ScopedInRtl in_rtl;
+    ctx = (SignalContext*)internal_alloc(
+        MBlockSignal, sizeof(*ctx));
+    MemoryResetRange(thr, 0, (uptr)ctx, sizeof(*ctx));
+    internal_memset(ctx, 0, sizeof(*ctx));
+    thr->signal_ctx = ctx;
+  }
+  return ctx;
+}
+
+static unsigned g_thread_finalize_key;
+
+static void process_pending_signals(ThreadState *thr);
+
+ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
+                                     uptr pc)
+    : thr_(thr)
+    , in_rtl_(thr->in_rtl) {
+  if (thr_->in_rtl == 0) {
+    Initialize(thr);
+    FuncEntry(thr, pc);
+    thr_->in_rtl++;
+    DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
+  } else {
+    thr_->in_rtl++;
+  }
+}
+
+ScopedInterceptor::~ScopedInterceptor() {
+  thr_->in_rtl--;
+  if (thr_->in_rtl == 0) {
+    FuncExit(thr_);
+    process_pending_signals(thr_);
+  }
+  CHECK_EQ(in_rtl_, thr_->in_rtl);
+}
+
+TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
+  SCOPED_TSAN_INTERCEPTOR(sleep, sec);
+  unsigned res = sleep(sec);
+  AfterSleep(thr, pc);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, usleep, long_t usec) {
+  SCOPED_TSAN_INTERCEPTOR(usleep, usec);
+  int res = usleep(usec);
+  AfterSleep(thr, pc);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) {
+  SCOPED_TSAN_INTERCEPTOR(nanosleep, req, rem);
+  int res = nanosleep(req, rem);
+  AfterSleep(thr, pc);
+  return res;
+}
+
+class AtExitContext {
+ public:
+  AtExitContext()
+    : mtx_(MutexTypeAtExit, StatMtxAtExit)
+    , pos_() {
+  }
+
+  typedef void(*atexit_t)();
+
+  int atexit(ThreadState *thr, uptr pc, atexit_t f) {
+    Lock l(&mtx_);
+    if (pos_ == kMaxAtExit)
+      return 1;
+    Release(thr, pc, (uptr)this);
+    stack_[pos_] = f;
+    pos_++;
+    return 0;
+  }
+
+  void exit(ThreadState *thr, uptr pc) {
+    CHECK_EQ(thr->in_rtl, 0);
+    for (;;) {
+      atexit_t f = 0;
+      {
+        Lock l(&mtx_);
+        if (pos_) {
+          pos_--;
+          f = stack_[pos_];
+          ScopedInRtl in_rtl;
+          Acquire(thr, pc, (uptr)this);
+        }
+      }
+      if (f == 0)
+        break;
+      DPrintf("#%d: executing atexit func %p\n", thr->tid, f);
+      CHECK_EQ(thr->in_rtl, 0);
+      f();
+    }
+  }
+
+ private:
+  static const int kMaxAtExit = 128;
+  Mutex mtx_;
+  atexit_t stack_[kMaxAtExit];
+  int pos_;
+};
+
+static AtExitContext *atexit_ctx;
+
+static void finalize(void *arg) {
+  ThreadState * thr = cur_thread();
+  uptr pc = 0;
+  atexit_ctx->exit(thr, pc);
+  {
+    ScopedInRtl in_rtl;
+    DestroyAndFree(atexit_ctx);
+    REAL(usleep)(flags()->atexit_sleep_ms * 1000);
+  }
+  int status = Finalize(cur_thread());
+  if (status)
+    _exit(status);
+}
+
+TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
+  SCOPED_TSAN_INTERCEPTOR(atexit, f);
+  return atexit_ctx->atexit(thr, pc, f);
+  return 0;
+}
+
+TSAN_INTERCEPTOR(void, longjmp, void *env, int val) {
+  SCOPED_TSAN_INTERCEPTOR(longjmp, env, val);
+  TsanPrintf("ThreadSanitizer: longjmp() is not supported\n");
+  Die();
+}
+
+TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) {
+  SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val);
+  TsanPrintf("ThreadSanitizer: siglongjmp() is not supported\n");
+  Die();
+}
+
+static uptr fd2addr(int fd) {
+  (void)fd;
+  static u64 addr;
+  return (uptr)&addr;
+}
+
+static uptr epollfd2addr(int fd) {
+  (void)fd;
+  static u64 addr;
+  return (uptr)&addr;
+}
+
+static uptr file2addr(char *path) {
+  (void)path;
+  static u64 addr;
+  return (uptr)&addr;
+}
+
+static uptr dir2addr(char *path) {
+  (void)path;
+  static u64 addr;
+  return (uptr)&addr;
+}
+
+TSAN_INTERCEPTOR(void*, malloc, uptr size) {
+  void *p = 0;
+  {
+    SCOPED_INTERCEPTOR_RAW(malloc, size);
+    p = user_alloc(thr, pc, size);
+  }
+  invoke_malloc_hook(p, size);
+  return p;
+}
+
+TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
+  void *p = 0;
+  {
+    SCOPED_INTERCEPTOR_RAW(calloc, size, n);
+    p = user_alloc(thr, pc, n * size);
+    if (p) internal_memset(p, 0, n * size);
+  }
+  invoke_malloc_hook(p, n * size);
+  return p;
+}
+
+TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
+  if (p)
+    invoke_free_hook(p);
+  {
+    SCOPED_INTERCEPTOR_RAW(realloc, p, size);
+    p = user_realloc(thr, pc, p, size);
+  }
+  invoke_malloc_hook(p, size);
+  return p;
+}
+
+TSAN_INTERCEPTOR(void, free, void *p) {
+  if (p == 0)
+    return;
+  invoke_free_hook(p);
+  SCOPED_INTERCEPTOR_RAW(free, p);
+  user_free(thr, pc, p);
+}
+
+TSAN_INTERCEPTOR(void, cfree, void *p) {
+  if (p == 0)
+    return;
+  invoke_free_hook(p);
+  SCOPED_INTERCEPTOR_RAW(cfree, p);
+  user_free(thr, pc, p);
+}
+
+#define OPERATOR_NEW_BODY(mangled_name) \
+  void *p = 0; \
+  {  \
+    SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
+    p = user_alloc(thr, pc, size); \
+  }  \
+  invoke_malloc_hook(p, size);  \
+  return p;
+
+void *operator new(__sanitizer::uptr size) {
+  OPERATOR_NEW_BODY(_Znwm);
+}
+void *operator new[](__sanitizer::uptr size) {
+  OPERATOR_NEW_BODY(_Znam);
+}
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
+  OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t);
+}
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
+  OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t);
+}
+
+#define OPERATOR_DELETE_BODY(mangled_name) \
+  if (ptr == 0) return;  \
+  invoke_free_hook(ptr);  \
+  SCOPED_INTERCEPTOR_RAW(mangled_name, ptr);  \
+  user_free(thr, pc, ptr);
+
+void operator delete(void *ptr) {
+  OPERATOR_DELETE_BODY(_ZdlPv);
+}
+void operator delete[](void *ptr) {
+  OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
+}
+void operator delete(void *ptr, std::nothrow_t const&) {
+  OPERATOR_DELETE_BODY(_ZdaPv);
+}
+void operator delete[](void *ptr, std::nothrow_t const&) {
+  OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
+}
+
+TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
+  SCOPED_TSAN_INTERCEPTOR(strlen, s);
+  uptr len = internal_strlen(s);
+  MemoryAccessRange(thr, pc, (uptr)s, len + 1, false);
+  return len;
+}
+
+TSAN_INTERCEPTOR(void*, memset, void *dst, int v, uptr size) {
+  SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size);
+  MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+  return internal_memset(dst, v, size);
+}
+
+TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) {
+  SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size);
+  MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+  MemoryAccessRange(thr, pc, (uptr)src, size, false);
+  return internal_memcpy(dst, src, size);
+}
+
+TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) {
+  SCOPED_TSAN_INTERCEPTOR(memcmp, s1, s2, n);
+  int res = 0;
+  uptr len = 0;
+  for (; len < n; len++) {
+    if ((res = ((unsigned char*)s1)[len] - ((unsigned char*)s2)[len]))
+      break;
+  }
+  MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false);
+  MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
+  SCOPED_TSAN_INTERCEPTOR(strcmp, s1, s2);
+  uptr len = 0;
+  for (; s1[len] && s2[len]; len++) {
+    if (s1[len] != s2[len])
+      break;
+  }
+  MemoryAccessRange(thr, pc, (uptr)s1, len + 1, false);
+  MemoryAccessRange(thr, pc, (uptr)s2, len + 1, false);
+  return s1[len] - s2[len];
+}
+
+TSAN_INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr n) {
+  SCOPED_TSAN_INTERCEPTOR(strncmp, s1, s2, n);
+  uptr len = 0;
+  for (; len < n && s1[len] && s2[len]; len++) {
+    if (s1[len] != s2[len])
+      break;
+  }
+  MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false);
+  MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false);
+  return len == n ? 0 : s1[len] - s2[len];
+}
+
+TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) {
+  SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n);
+  void *res = REAL(memchr)(s, c, n);
+  uptr len = res ? (char*)res - (char*)s + 1 : n;
+  MemoryAccessRange(thr, pc, (uptr)s, len, false);
+  return res;
+}
+
+TSAN_INTERCEPTOR(void*, memrchr, char *s, int c, uptr n) {
+  SCOPED_TSAN_INTERCEPTOR(memrchr, s, c, n);
+  MemoryAccessRange(thr, pc, (uptr)s, n, false);
+  return REAL(memrchr)(s, c, n);
+}
+
+TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) {
+  SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n);
+  MemoryAccessRange(thr, pc, (uptr)dst, n, true);
+  MemoryAccessRange(thr, pc, (uptr)src, n, false);
+  return REAL(memmove)(dst, src, n);
+}
+
+TSAN_INTERCEPTOR(char*, strchr, char *s, int c) {
+  SCOPED_TSAN_INTERCEPTOR(strchr, s, c);
+  char *res = REAL(strchr)(s, c);
+  uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1;
+  MemoryAccessRange(thr, pc, (uptr)s, len, false);
+  return res;
+}
+
+TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
+  SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c);
+  char *res = REAL(strchrnul)(s, c);
+  uptr len = (char*)res - (char*)s + 1;
+  MemoryAccessRange(thr, pc, (uptr)s, len, false);
+  return res;
+}
+
+TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) {
+  SCOPED_TSAN_INTERCEPTOR(strrchr, s, c);
+  MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s) + 1, false);
+  return REAL(strrchr)(s, c);
+}
+
+TSAN_INTERCEPTOR(char*, strcpy, char *dst, const char *src) {  // NOLINT
+  SCOPED_TSAN_INTERCEPTOR(strcpy, dst, src);  // NOLINT
+  uptr srclen = internal_strlen(src);
+  MemoryAccessRange(thr, pc, (uptr)dst, srclen + 1, true);
+  MemoryAccessRange(thr, pc, (uptr)src, srclen + 1, false);
+  return REAL(strcpy)(dst, src);  // NOLINT
+}
+
+TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) {
+  SCOPED_TSAN_INTERCEPTOR(strncpy, dst, src, n);
+  uptr srclen = internal_strnlen(src, n);
+  MemoryAccessRange(thr, pc, (uptr)dst, n, true);
+  MemoryAccessRange(thr, pc, (uptr)src, min(srclen + 1, n), false);
+  return REAL(strncpy)(dst, src, n);
+}
+
+TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) {
+  SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2);
+  const char *res = REAL(strstr)(s1, s2);
+  uptr len1 = internal_strlen(s1);
+  uptr len2 = internal_strlen(s2);
+  MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false);
+  MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false);
+  return res;
+}
+
+static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
+  if (*addr) {
+    if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) {
+      if (flags & MAP_FIXED) {
+        errno = EINVAL;
+        return false;
+      } else {
+        *addr = 0;
+      }
+    }
+  }
+  return true;
+}
+
+TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot,
+                         int flags, int fd, unsigned off) {
+  SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off);
+  if (!fix_mmap_addr(&addr, sz, flags))
+    return MAP_FAILED;
+  void *res = REAL(mmap)(addr, sz, prot, flags, fd, off);
+  if (res != MAP_FAILED) {
+    MemoryResetRange(thr, pc, (uptr)res, sz);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
+                           int flags, int fd, u64 off) {
+  SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off);
+  if (!fix_mmap_addr(&addr, sz, flags))
+    return MAP_FAILED;
+  void *res = REAL(mmap64)(addr, sz, prot, flags, fd, off);
+  if (res != MAP_FAILED) {
+    MemoryResetRange(thr, pc, (uptr)res, sz);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
+  SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
+  int res = REAL(munmap)(addr, sz);
+  return res;
+}
+
+TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
+  SCOPED_TSAN_INTERCEPTOR(memalign, align, sz);
+  return user_alloc(thr, pc, sz, align);
+}
+
+TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
+  SCOPED_TSAN_INTERCEPTOR(valloc, sz);
+  return user_alloc(thr, pc, sz, kPageSize);
+}
+
+TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
+  SCOPED_TSAN_INTERCEPTOR(pvalloc, sz);
+  sz = RoundUp(sz, kPageSize);
+  return user_alloc(thr, pc, sz, kPageSize);
+}
+
+TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
+  SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz);
+  *memptr = user_alloc(thr, pc, sz, align);
+  return 0;
+}
+
+// Used in thread-safe function static initialization.
+TSAN_INTERCEPTOR(int, __cxa_guard_acquire, char *m) {
+  SCOPED_TSAN_INTERCEPTOR(__cxa_guard_acquire, m);
+  int res = REAL(__cxa_guard_acquire)(m);
+  if (res) {
+    // This thread does the init.
+  } else {
+    Acquire(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(void, __cxa_guard_release, char *m) {
+  SCOPED_TSAN_INTERCEPTOR(__cxa_guard_release, m);
+  Release(thr, pc, (uptr)m);
+  REAL(__cxa_guard_release)(m);
+}
+
+static void thread_finalize(void *v) {
+  uptr iter = (uptr)v;
+  if (iter > 1) {
+    if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) {
+      TsanPrintf("ThreadSanitizer: failed to set thread key\n");
+      Die();
+    }
+    return;
+  }
+  {
+    ScopedInRtl in_rtl;
+    ThreadState *thr = cur_thread();
+    ThreadFinish(thr);
+    SignalContext *sctx = thr->signal_ctx;
+    if (sctx) {
+      thr->signal_ctx = 0;
+      internal_free(sctx);
+    }
+  }
+}
+
+
+struct ThreadParam {
+  void* (*callback)(void *arg);
+  void *param;
+  atomic_uintptr_t tid;
+};
+
+extern "C" void *__tsan_thread_start_func(void *arg) {
+  ThreadParam *p = (ThreadParam*)arg;
+  void* (*callback)(void *arg) = p->callback;
+  void *param = p->param;
+  int tid = 0;
+  {
+    ThreadState *thr = cur_thread();
+    ScopedInRtl in_rtl;
+    if (pthread_setspecific(g_thread_finalize_key, (void*)4)) {
+      TsanPrintf("ThreadSanitizer: failed to set thread key\n");
+      Die();
+    }
+    while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
+      pthread_yield();
+    atomic_store(&p->tid, 0, memory_order_release);
+    ThreadStart(thr, tid, GetTid());
+    CHECK_EQ(thr->in_rtl, 1);
+  }
+  void *res = callback(param);
+  // Prevent the callback from being tail called,
+  // it mixes up stack traces.
+  volatile int foo = 42;
+  foo++;
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_create,
+    void *th, void *attr, void *(*callback)(void*), void * param) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param);
+  pthread_attr_t myattr;
+  if (attr == 0) {
+    pthread_attr_init(&myattr);
+    attr = &myattr;
+  }
+  int detached = 0;
+  pthread_attr_getdetachstate(attr, &detached);
+  uptr stacksize = 0;
+  pthread_attr_getstacksize(attr, &stacksize);
+  // We place the huge ThreadState object into TLS, account for that.
+  const uptr minstacksize = GetTlsSize() + 128*1024;
+  if (stacksize < minstacksize) {
+    DPrintf("ThreadSanitizer: stacksize %zu->%zu\n", stacksize, minstacksize);
+    pthread_attr_setstacksize(attr, minstacksize);
+  }
+  ThreadParam p;
+  p.callback = callback;
+  p.param = param;
+  atomic_store(&p.tid, 0, memory_order_relaxed);
+  int res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p);
+  if (res == 0) {
+    int tid = ThreadCreate(thr, pc, *(uptr*)th, detached);
+    CHECK_NE(tid, 0);
+    atomic_store(&p.tid, tid, memory_order_release);
+    while (atomic_load(&p.tid, memory_order_acquire) != 0)
+      pthread_yield();
+  }
+  if (attr == &myattr)
+    pthread_attr_destroy(&myattr);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret);
+  int tid = ThreadTid(thr, pc, (uptr)th);
+  int res = REAL(pthread_join)(th, ret);
+  if (res == 0) {
+    ThreadJoin(thr, pc, tid);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_detach, th);
+  int tid = ThreadTid(thr, pc, (uptr)th);
+  int res = REAL(pthread_detach)(th);
+  if (res == 0) {
+    ThreadDetach(thr, pc, tid);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a);
+  int res = REAL(pthread_mutex_init)(m, a);
+  if (res == 0) {
+    bool recursive = false;
+    if (a) {
+      int type = 0;
+      if (pthread_mutexattr_gettype(a, &type) == 0)
+        recursive = (type == PTHREAD_MUTEX_RECURSIVE
+            || type == PTHREAD_MUTEX_RECURSIVE_NP);
+    }
+    MutexCreate(thr, pc, (uptr)m, false, recursive, false);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_mutex_destroy, m);
+  int res = REAL(pthread_mutex_destroy)(m);
+  if (res == 0 || res == EBUSY) {
+    MutexDestroy(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m);
+  int res = REAL(pthread_mutex_lock)(m);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m);
+  int res = REAL(pthread_mutex_trylock)(m);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
+  int res = REAL(pthread_mutex_timedlock)(m, abstime);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m);
+  MutexUnlock(thr, pc, (uptr)m);
+  int res = REAL(pthread_mutex_unlock)(m);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
+  int res = REAL(pthread_spin_init)(m, pshared);
+  if (res == 0) {
+    MutexCreate(thr, pc, (uptr)m, false, false, false);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_spin_destroy, m);
+  int res = REAL(pthread_spin_destroy)(m);
+  if (res == 0) {
+    MutexDestroy(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m);
+  int res = REAL(pthread_spin_lock)(m);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
+  int res = REAL(pthread_spin_trylock)(m);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m);
+  MutexUnlock(thr, pc, (uptr)m);
+  int res = REAL(pthread_spin_unlock)(m);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
+  int res = REAL(pthread_rwlock_init)(m, a);
+  if (res == 0) {
+    MutexCreate(thr, pc, (uptr)m, true, false, false);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_destroy, m);
+  int res = REAL(pthread_rwlock_destroy)(m);
+  if (res == 0) {
+    MutexDestroy(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m);
+  int res = REAL(pthread_rwlock_rdlock)(m);
+  if (res == 0) {
+    MutexReadLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m);
+  int res = REAL(pthread_rwlock_tryrdlock)(m);
+  if (res == 0) {
+    MutexReadLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
+  int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
+  if (res == 0) {
+    MutexReadLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
+  int res = REAL(pthread_rwlock_wrlock)(m);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m);
+  int res = REAL(pthread_rwlock_trywrlock)(m);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
+  int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
+  if (res == 0) {
+    MutexLock(thr, pc, (uptr)m);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
+  MutexReadOrWriteUnlock(thr, pc, (uptr)m);
+  int res = REAL(pthread_rwlock_unlock)(m);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a);
+  int res = REAL(pthread_cond_init)(c, a);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c);
+  int res = REAL(pthread_cond_destroy)(c);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_signal, void *c) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c);
+  int res = REAL(pthread_cond_signal)(c);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c);
+  int res = REAL(pthread_cond_broadcast)(c);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m);
+  MutexUnlock(thr, pc, (uptr)m);
+  int res = REAL(pthread_cond_wait)(c, m);
+  MutexLock(thr, pc, (uptr)m);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime);
+  MutexUnlock(thr, pc, (uptr)m);
+  int res = REAL(pthread_cond_timedwait)(c, m, abstime);
+  MutexLock(thr, pc, (uptr)m);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count);
+  MemoryWrite1Byte(thr, pc, (uptr)b);
+  int res = REAL(pthread_barrier_init)(b, a, count);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b);
+  MemoryWrite1Byte(thr, pc, (uptr)b);
+  int res = REAL(pthread_barrier_destroy)(b);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b);
+  Release(thr, pc, (uptr)b);
+  MemoryRead1Byte(thr, pc, (uptr)b);
+  int res = REAL(pthread_barrier_wait)(b);
+  MemoryRead1Byte(thr, pc, (uptr)b);
+  if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) {
+    Acquire(thr, pc, (uptr)b);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_once, o, f);
+  if (o == 0 || f == 0)
+    return EINVAL;
+  atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o);
+  u32 v = atomic_load(a, memory_order_acquire);
+  if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
+                                               memory_order_relaxed)) {
+    const int old_in_rtl = thr->in_rtl;
+    thr->in_rtl = 0;
+    (*f)();
+    CHECK_EQ(thr->in_rtl, 0);
+    thr->in_rtl = old_in_rtl;
+    Release(thr, pc, (uptr)o);
+    atomic_store(a, 2, memory_order_release);
+  } else {
+    while (v != 2) {
+      pthread_yield();
+      v = atomic_load(a, memory_order_acquire);
+    }
+    Acquire(thr, pc, (uptr)o);
+  }
+  return 0;
+}
+
+TSAN_INTERCEPTOR(int, sem_init, void *s, int pshared, unsigned value) {
+  SCOPED_TSAN_INTERCEPTOR(sem_init, s, pshared, value);
+  int res = REAL(sem_init)(s, pshared, value);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_destroy, void *s) {
+  SCOPED_TSAN_INTERCEPTOR(sem_destroy, s);
+  int res = REAL(sem_destroy)(s);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_wait, void *s) {
+  SCOPED_TSAN_INTERCEPTOR(sem_wait, s);
+  int res = REAL(sem_wait)(s);
+  if (res == 0) {
+    Acquire(thr, pc, (uptr)s);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_trywait, void *s) {
+  SCOPED_TSAN_INTERCEPTOR(sem_trywait, s);
+  int res = REAL(sem_trywait)(s);
+  if (res == 0) {
+    Acquire(thr, pc, (uptr)s);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_timedwait, void *s, void *abstime) {
+  SCOPED_TSAN_INTERCEPTOR(sem_timedwait, s, abstime);
+  int res = REAL(sem_timedwait)(s, abstime);
+  if (res == 0) {
+    Acquire(thr, pc, (uptr)s);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_post, void *s) {
+  SCOPED_TSAN_INTERCEPTOR(sem_post, s);
+  Release(thr, pc, (uptr)s);
+  int res = REAL(sem_post)(s);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
+  SCOPED_TSAN_INTERCEPTOR(sem_getvalue, s, sval);
+  int res = REAL(sem_getvalue)(s, sval);
+  if (res == 0) {
+    Acquire(thr, pc, (uptr)s);
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, read, int fd, void *buf, long_t sz) {
+  SCOPED_TSAN_INTERCEPTOR(read, fd, buf, sz);
+  int res = REAL(read)(fd, buf, sz);
+  if (res >= 0) {
+    Acquire(thr, pc, fd2addr(fd));
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pread, int fd, void *buf, long_t sz, unsigned off) {
+  SCOPED_TSAN_INTERCEPTOR(pread, fd, buf, sz, off);
+  int res = REAL(pread)(fd, buf, sz, off);
+  if (res >= 0) {
+    Acquire(thr, pc, fd2addr(fd));
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pread64, int fd, void *buf, long_t sz, u64 off) {
+  SCOPED_TSAN_INTERCEPTOR(pread64, fd, buf, sz, off);
+  int res = REAL(pread64)(fd, buf, sz, off);
+  if (res >= 0) {
+    Acquire(thr, pc, fd2addr(fd));
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) {
+  SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt);
+  int res = REAL(readv)(fd, vec, cnt);
+  if (res >= 0) {
+    Acquire(thr, pc, fd2addr(fd));
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) {
+  SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off);
+  int res = REAL(preadv64)(fd, vec, cnt, off);
+  if (res >= 0) {
+    Acquire(thr, pc, fd2addr(fd));
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, write, int fd, void *buf, long_t sz) {
+  SCOPED_TSAN_INTERCEPTOR(write, fd, buf, sz);
+  Release(thr, pc, fd2addr(fd));
+  int res = REAL(write)(fd, buf, sz);
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pwrite, int fd, void *buf, long_t sz, unsigned off) {
+  SCOPED_TSAN_INTERCEPTOR(pwrite, fd, buf, sz, off);
+  Release(thr, pc, fd2addr(fd));
+  int res = REAL(pwrite)(fd, buf, sz, off);
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pwrite64, int fd, void *buf, long_t sz, u64 off) {
+  SCOPED_TSAN_INTERCEPTOR(pwrite64, fd, buf, sz, off);
+  Release(thr, pc, fd2addr(fd));
+  int res = REAL(pwrite64)(fd, buf, sz, off);
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) {
+  SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt);
+  Release(thr, pc, fd2addr(fd));
+  int res = REAL(writev)(fd, vec, cnt);
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) {
+  SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off);
+  Release(thr, pc, fd2addr(fd));
+  int res = REAL(pwritev64)(fd, vec, cnt, off);
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
+  Release(thr, pc, fd2addr(fd));
+  int res = REAL(send)(fd, buf, len, flags);
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags);
+  Release(thr, pc, fd2addr(fd));
+  int res = REAL(sendmsg)(fd, msg, flags);
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags);
+  int res = REAL(recv)(fd, buf, len, flags);
+  if (res >= 0) {
+    Acquire(thr, pc, fd2addr(fd));
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) {
+  SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags);
+  int res = REAL(recvmsg)(fd, msg, flags);
+  if (res >= 0) {
+    Acquire(thr, pc, fd2addr(fd));
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, unlink, char *path) {
+  SCOPED_TSAN_INTERCEPTOR(unlink, path);
+  Release(thr, pc, file2addr(path));
+  int res = REAL(unlink)(path);
+  return res;
+}
+
+TSAN_INTERCEPTOR(void*, fopen, char *path, char *mode) {
+  SCOPED_TSAN_INTERCEPTOR(fopen, path, mode);
+  void *res = REAL(fopen)(path, mode);
+  Acquire(thr, pc, file2addr(path));
+  return res;
+}
+
+TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
+  SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f);
+  MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true);
+  return REAL(fread)(ptr, size, nmemb, f);
+}
+
+TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) {
+  SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f);
+  MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false);
+  return REAL(fwrite)(p, size, nmemb, f);
+}
+
+TSAN_INTERCEPTOR(int, puts, const char *s) {
+  SCOPED_TSAN_INTERCEPTOR(puts, s);
+  MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s), false);
+  return REAL(puts)(s);
+}
+
+TSAN_INTERCEPTOR(int, rmdir, char *path) {
+  SCOPED_TSAN_INTERCEPTOR(rmdir, path);
+  Release(thr, pc, dir2addr(path));
+  int res = REAL(rmdir)(path);
+  return res;
+}
+
+TSAN_INTERCEPTOR(void*, opendir, char *path) {
+  SCOPED_TSAN_INTERCEPTOR(opendir, path);
+  void *res = REAL(opendir)(path);
+  Acquire(thr, pc, dir2addr(path));
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
+  SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
+  if (op == EPOLL_CTL_ADD) {
+    Release(thr, pc, epollfd2addr(epfd));
+  }
+  int res = REAL(epoll_ctl)(epfd, op, fd, ev);
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
+  SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
+  int res = REAL(epoll_wait)(epfd, ev, cnt, timeout);
+  if (res > 0) {
+    Acquire(thr, pc, epollfd2addr(epfd));
+  }
+  return res;
+}
+
+static void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
+    my_siginfo_t *info, void *ctx) {
+  ThreadState *thr = cur_thread();
+  SignalContext *sctx = SigCtx(thr);
+  // Don't mess with synchronous signals.
+  if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
+      sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE ||
+      (sctx && sig == sctx->int_signal_send)) {
+    CHECK(thr->in_rtl == 0 || thr->in_rtl == 1);
+    int in_rtl = thr->in_rtl;
+    thr->in_rtl = 0;
+    CHECK_EQ(thr->in_signal_handler, false);
+    thr->in_signal_handler = true;
+    if (sigact)
+      sigactions[sig].sa_sigaction(sig, info, ctx);
+    else
+      sigactions[sig].sa_handler(sig);
+    CHECK_EQ(thr->in_signal_handler, true);
+    thr->in_signal_handler = false;
+    thr->in_rtl = in_rtl;
+    return;
+  }
+
+  if (sctx == 0)
+    return;
+  SignalDesc *signal = &sctx->pending_signals[sig];
+  if (signal->armed == false) {
+    signal->armed = true;
+    signal->sigaction = sigact;
+    if (info)
+      internal_memcpy(&signal->siginfo, info, sizeof(*info));
+    if (ctx)
+      internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx));
+    sctx->pending_signal_count++;
+  }
+}
+
+static void rtl_sighandler(int sig) {
+  rtl_generic_sighandler(false, sig, 0, 0);
+}
+
+static void rtl_sigaction(int sig, my_siginfo_t *info, void *ctx) {
+  rtl_generic_sighandler(true, sig, info, ctx);
+}
+
+TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) {
+  SCOPED_TSAN_INTERCEPTOR(sigaction, sig, act, old);
+  if (old)
+    internal_memcpy(old, &sigactions[sig], sizeof(*old));
+  if (act == 0)
+    return 0;
+  internal_memcpy(&sigactions[sig], act, sizeof(*act));
+  sigaction_t newact;
+  internal_memcpy(&newact, act, sizeof(newact));
+  sigfillset(&newact.sa_mask);
+  if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) {
+    if (newact.sa_flags & SA_SIGINFO)
+      newact.sa_sigaction = rtl_sigaction;
+    else
+      newact.sa_handler = rtl_sighandler;
+  }
+  int res = REAL(sigaction)(sig, &newact, 0);
+  return res;
+}
+
+TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) {
+  sigaction_t act;
+  act.sa_handler = h;
+  REAL(memset)(&act.sa_mask, -1, sizeof(act.sa_mask));
+  act.sa_flags = 0;
+  sigaction_t old;
+  int res = sigaction(sig, &act, &old);
+  if (res)
+    return SIG_ERR;
+  return old.sa_handler;
+}
+
+TSAN_INTERCEPTOR(int, raise, int sig) {
+  SCOPED_TSAN_INTERCEPTOR(raise, sig);
+  SignalContext *sctx = SigCtx(thr);
+  CHECK_NE(sctx, 0);
+  int prev = sctx->int_signal_send;
+  sctx->int_signal_send = sig;
+  int res = REAL(raise)(sig);
+  CHECK_EQ(sctx->int_signal_send, sig);
+  sctx->int_signal_send = prev;
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
+  SCOPED_TSAN_INTERCEPTOR(kill, pid, sig);
+  SignalContext *sctx = SigCtx(thr);
+  CHECK_NE(sctx, 0);
+  int prev = sctx->int_signal_send;
+  if (pid == GetPid()) {
+    sctx->int_signal_send = sig;
+  }
+  int res = REAL(kill)(pid, sig);
+  if (pid == GetPid()) {
+    CHECK_EQ(sctx->int_signal_send, sig);
+    sctx->int_signal_send = prev;
+  }
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
+  SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig);
+  SignalContext *sctx = SigCtx(thr);
+  CHECK_NE(sctx, 0);
+  int prev = sctx->int_signal_send;
+  if (tid == pthread_self()) {
+    sctx->int_signal_send = sig;
+  }
+  int res = REAL(pthread_kill)(tid, sig);
+  if (tid == pthread_self()) {
+    CHECK_EQ(sctx->int_signal_send, sig);
+    sctx->int_signal_send = prev;
+  }
+  return res;
+}
+
+static void process_pending_signals(ThreadState *thr) {
+  CHECK_EQ(thr->in_rtl, 0);
+  SignalContext *sctx = SigCtx(thr);
+  if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
+    return;
+  Context *ctx = CTX();
+  thr->in_signal_handler = true;
+  sctx->pending_signal_count = 0;
+  // These are too big for stack.
+  static THREADLOCAL sigset_t emptyset, oldset;
+  sigfillset(&emptyset);
+  pthread_sigmask(SIG_SETMASK, &emptyset, &oldset);
+  for (int sig = 0; sig < kSigCount; sig++) {
+    SignalDesc *signal = &sctx->pending_signals[sig];
+    if (signal->armed) {
+      signal->armed = false;
+      if (sigactions[sig].sa_handler != SIG_DFL
+          && sigactions[sig].sa_handler != SIG_IGN) {
+        // Insure that the handler does not spoil errno.
+        const int saved_errno = errno;
+        errno = 0;
+        if (signal->sigaction)
+          sigactions[sig].sa_sigaction(sig, &signal->siginfo, &signal->ctx);
+        else
+          sigactions[sig].sa_handler(sig);
+        if (errno != 0) {
+          ScopedInRtl in_rtl;
+          __tsan::StackTrace stack;
+          uptr pc = signal->sigaction ?
+              (uptr)sigactions[sig].sa_sigaction :
+              (uptr)sigactions[sig].sa_handler;
+          stack.Init(&pc, 1);
+          ScopedReport rep(ReportTypeErrnoInSignal);
+          if (!IsFiredSuppression(ctx, rep, stack)) {
+            rep.AddStack(&stack);
+            OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
+          }
+        }
+        errno = saved_errno;
+      }
+    }
+  }
+  pthread_sigmask(SIG_SETMASK, &oldset, 0);
+  CHECK_EQ(thr->in_signal_handler, true);
+  thr->in_signal_handler = false;
+}
+
+namespace __tsan {
+
+void InitializeInterceptors() {
+  CHECK_GT(cur_thread()->in_rtl, 0);
+
+  // We need to setup it early, because functions like dlsym() can call it.
+  REAL(memset) = internal_memset;
+  REAL(memcpy) = internal_memcpy;
+  REAL(memcmp) = internal_memcmp;
+
+  TSAN_INTERCEPT(longjmp);
+  TSAN_INTERCEPT(siglongjmp);
+
+  TSAN_INTERCEPT(malloc);
+  TSAN_INTERCEPT(calloc);
+  TSAN_INTERCEPT(realloc);
+  TSAN_INTERCEPT(free);
+  TSAN_INTERCEPT(cfree);
+  TSAN_INTERCEPT(mmap);
+  TSAN_INTERCEPT(mmap64);
+  TSAN_INTERCEPT(munmap);
+  TSAN_INTERCEPT(memalign);
+  TSAN_INTERCEPT(valloc);
+  TSAN_INTERCEPT(pvalloc);
+  TSAN_INTERCEPT(posix_memalign);
+
+  TSAN_INTERCEPT(strlen);
+  TSAN_INTERCEPT(memset);
+  TSAN_INTERCEPT(memcpy);
+  TSAN_INTERCEPT(strcmp);
+  TSAN_INTERCEPT(memchr);
+  TSAN_INTERCEPT(memrchr);
+  TSAN_INTERCEPT(memmove);
+  TSAN_INTERCEPT(memcmp);
+  TSAN_INTERCEPT(strchr);
+  TSAN_INTERCEPT(strchrnul);
+  TSAN_INTERCEPT(strrchr);
+  TSAN_INTERCEPT(strncmp);
+  TSAN_INTERCEPT(strcpy);  // NOLINT
+  TSAN_INTERCEPT(strncpy);
+  TSAN_INTERCEPT(strstr);
+
+  TSAN_INTERCEPT(__cxa_guard_acquire);
+  TSAN_INTERCEPT(__cxa_guard_release);
+
+  TSAN_INTERCEPT(pthread_create);
+  TSAN_INTERCEPT(pthread_join);
+  TSAN_INTERCEPT(pthread_detach);
+
+  TSAN_INTERCEPT(pthread_mutex_init);
+  TSAN_INTERCEPT(pthread_mutex_destroy);
+  TSAN_INTERCEPT(pthread_mutex_lock);
+  TSAN_INTERCEPT(pthread_mutex_trylock);
+  TSAN_INTERCEPT(pthread_mutex_timedlock);
+  TSAN_INTERCEPT(pthread_mutex_unlock);
+
+  TSAN_INTERCEPT(pthread_spin_init);
+  TSAN_INTERCEPT(pthread_spin_destroy);
+  TSAN_INTERCEPT(pthread_spin_lock);
+  TSAN_INTERCEPT(pthread_spin_trylock);
+  TSAN_INTERCEPT(pthread_spin_unlock);
+
+  TSAN_INTERCEPT(pthread_rwlock_init);
+  TSAN_INTERCEPT(pthread_rwlock_destroy);
+  TSAN_INTERCEPT(pthread_rwlock_rdlock);
+  TSAN_INTERCEPT(pthread_rwlock_tryrdlock);
+  TSAN_INTERCEPT(pthread_rwlock_timedrdlock);
+  TSAN_INTERCEPT(pthread_rwlock_wrlock);
+  TSAN_INTERCEPT(pthread_rwlock_trywrlock);
+  TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
+  TSAN_INTERCEPT(pthread_rwlock_unlock);
+
+  TSAN_INTERCEPT(pthread_cond_init);
+  TSAN_INTERCEPT(pthread_cond_destroy);
+  TSAN_INTERCEPT(pthread_cond_signal);
+  TSAN_INTERCEPT(pthread_cond_broadcast);
+  TSAN_INTERCEPT(pthread_cond_wait);
+  TSAN_INTERCEPT(pthread_cond_timedwait);
+
+  TSAN_INTERCEPT(pthread_barrier_init);
+  TSAN_INTERCEPT(pthread_barrier_destroy);
+  TSAN_INTERCEPT(pthread_barrier_wait);
+
+  TSAN_INTERCEPT(pthread_once);
+
+  TSAN_INTERCEPT(sem_init);
+  TSAN_INTERCEPT(sem_destroy);
+  TSAN_INTERCEPT(sem_wait);
+  TSAN_INTERCEPT(sem_trywait);
+  TSAN_INTERCEPT(sem_timedwait);
+  TSAN_INTERCEPT(sem_post);
+  TSAN_INTERCEPT(sem_getvalue);
+
+  TSAN_INTERCEPT(read);
+  TSAN_INTERCEPT(pread);
+  TSAN_INTERCEPT(pread64);
+  TSAN_INTERCEPT(readv);
+  TSAN_INTERCEPT(preadv64);
+  TSAN_INTERCEPT(write);
+  TSAN_INTERCEPT(pwrite);
+  TSAN_INTERCEPT(pwrite64);
+  TSAN_INTERCEPT(writev);
+  TSAN_INTERCEPT(pwritev64);
+  TSAN_INTERCEPT(send);
+  TSAN_INTERCEPT(sendmsg);
+  TSAN_INTERCEPT(recv);
+  TSAN_INTERCEPT(recvmsg);
+
+  TSAN_INTERCEPT(unlink);
+  TSAN_INTERCEPT(fopen);
+  TSAN_INTERCEPT(fread);
+  TSAN_INTERCEPT(fwrite);
+  TSAN_INTERCEPT(puts);
+  TSAN_INTERCEPT(rmdir);
+  TSAN_INTERCEPT(opendir);
+
+  TSAN_INTERCEPT(epoll_ctl);
+  TSAN_INTERCEPT(epoll_wait);
+
+  TSAN_INTERCEPT(sigaction);
+  TSAN_INTERCEPT(signal);
+  TSAN_INTERCEPT(raise);
+  TSAN_INTERCEPT(kill);
+  TSAN_INTERCEPT(pthread_kill);
+  TSAN_INTERCEPT(sleep);
+  TSAN_INTERCEPT(usleep);
+  TSAN_INTERCEPT(nanosleep);
+
+  atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext)))
+      AtExitContext();
+
+  if (__cxa_atexit(&finalize, 0, 0)) {
+    TsanPrintf("ThreadSanitizer: failed to setup atexit callback\n");
+    Die();
+  }
+
+  if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) {
+    TsanPrintf("ThreadSanitizer: failed to create thread key\n");
+    Die();
+  }
+}
+
+void internal_start_thread(void(*func)(void *arg), void *arg) {
+  void *th;
+  REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg);
+  REAL(pthread_detach)(th);
+}
+
+}  // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_interceptors.h b/libsanitizer/tsan/tsan_interceptors.h
new file mode 100644 (file)
index 0000000..2e8c553
--- /dev/null
@@ -0,0 +1,52 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_interface.cc b/libsanitizer/tsan/tsan_interface.cc
new file mode 100644 (file)
index 0000000..08a5165
--- /dev/null
@@ -0,0 +1,40 @@
+//===-- 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);
+}
diff --git a/libsanitizer/tsan/tsan_interface.h b/libsanitizer/tsan/tsan_interface.h
new file mode 100644 (file)
index 0000000..72bb1e9
--- /dev/null
@@ -0,0 +1,50 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_interface_ann.cc b/libsanitizer/tsan/tsan_interface_ann.cc
new file mode 100644 (file)
index 0000000..fd5c1cc
--- /dev/null
@@ -0,0 +1,402 @@
+//===-- tsan_interface_ann.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 "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_interface_ann.h"
+#include "tsan_mutex.h"
+#include "tsan_report.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_flags.h"
+#include "tsan_platform.h"
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+using namespace __tsan;  // NOLINT
+
+namespace __tsan {
+
+class ScopedAnnotation {
+ public:
+  ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l,
+                   uptr pc)
+      : thr_(thr)
+      , in_rtl_(thr->in_rtl) {
+    CHECK_EQ(thr_->in_rtl, 0);
+    FuncEntry(thr_, pc);
+    thr_->in_rtl++;
+    DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l);
+  }
+
+  ~ScopedAnnotation() {
+    thr_->in_rtl--;
+    CHECK_EQ(in_rtl_, thr_->in_rtl);
+    FuncExit(thr_);
+  }
+ private:
+  ThreadState *const thr_;
+  const int in_rtl_;
+};
+
+#define SCOPED_ANNOTATION(typ) \
+    if (!flags()->enable_annotations) \
+      return; \
+    ThreadState *thr = cur_thread(); \
+    const uptr pc = (uptr)__builtin_return_address(0); \
+    StatInc(thr, StatAnnotation); \
+    StatInc(thr, Stat##typ); \
+    ScopedAnnotation sa(thr, __FUNCTION__, f, l, \
+        (uptr)__builtin_return_address(0)); \
+    (void)pc; \
+/**/
+
+static const int kMaxDescLen = 128;
+
+struct ExpectRace {
+  ExpectRace *next;
+  ExpectRace *prev;
+  int hitcount;
+  uptr addr;
+  uptr size;
+  char *file;
+  int line;
+  char desc[kMaxDescLen];
+};
+
+struct DynamicAnnContext {
+  Mutex mtx;
+  ExpectRace expect;
+  ExpectRace benign;
+
+  DynamicAnnContext()
+    : mtx(MutexTypeAnnotations, StatMtxAnnotations) {
+  }
+};
+
+static DynamicAnnContext *dyn_ann_ctx;
+static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)] ALIGNED(64);
+
+static void AddExpectRace(ExpectRace *list,
+    char *f, int l, uptr addr, uptr size, char *desc) {
+  ExpectRace *race = list->next;
+  for (; race != list; race = race->next) {
+    if (race->addr == addr && race->size == size)
+      return;
+  }
+  race = (ExpectRace*)internal_alloc(MBlockExpectRace, sizeof(ExpectRace));
+  race->hitcount = 0;
+  race->addr = addr;
+  race->size = size;
+  race->file = f;
+  race->line = l;
+  race->desc[0] = 0;
+  if (desc) {
+    int i = 0;
+    for (; i < kMaxDescLen - 1 && desc[i]; i++)
+      race->desc[i] = desc[i];
+    race->desc[i] = 0;
+  }
+  race->prev = list;
+  race->next = list->next;
+  race->next->prev = race;
+  list->next = race;
+}
+
+static ExpectRace *FindRace(ExpectRace *list, uptr addr, uptr size) {
+  for (ExpectRace *race = list->next; race != list; race = race->next) {
+    uptr maxbegin = max(race->addr, addr);
+    uptr minend = min(race->addr + race->size, addr + size);
+    if (maxbegin < minend)
+      return race;
+  }
+  return 0;
+}
+
+static bool CheckContains(ExpectRace *list, uptr addr, uptr size) {
+  ExpectRace *race = FindRace(list, addr, size);
+  if (race == 0 && AlternativeAddress(addr))
+    race = FindRace(list, AlternativeAddress(addr), size);
+  if (race == 0)
+    return false;
+  DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n",
+      race->desc, race->addr, (int)race->size, race->file, race->line);
+  race->hitcount++;
+  return true;
+}
+
+static void InitList(ExpectRace *list) {
+  list->next = list;
+  list->prev = list;
+}
+
+void InitializeDynamicAnnotations() {
+  dyn_ann_ctx = new(dyn_ann_ctx_placeholder) DynamicAnnContext;
+  InitList(&dyn_ann_ctx->expect);
+  InitList(&dyn_ann_ctx->benign);
+}
+
+bool IsExpectedReport(uptr addr, uptr size) {
+  Lock lock(&dyn_ann_ctx->mtx);
+  if (CheckContains(&dyn_ann_ctx->expect, addr, size))
+    return true;
+  if (CheckContains(&dyn_ann_ctx->benign, addr, size))
+    return true;
+  return false;
+}
+
+}  // namespace __tsan
+
+using namespace __tsan;  // NOLINT
+
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateHappensBefore(char *f, int l, uptr addr) {
+  SCOPED_ANNOTATION(AnnotateHappensBefore);
+  Release(cur_thread(), CALLERPC, addr);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateHappensAfter(char *f, int l, uptr addr) {
+  SCOPED_ANNOTATION(AnnotateHappensAfter);
+  Acquire(cur_thread(), CALLERPC, addr);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateCondVarSignal(char *f, int l, uptr cv) {
+  SCOPED_ANNOTATION(AnnotateCondVarSignal);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateCondVarSignalAll(char *f, int l, uptr cv) {
+  SCOPED_ANNOTATION(AnnotateCondVarSignalAll);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateMutexIsNotPHB(char *f, int l, uptr mu) {
+  SCOPED_ANNOTATION(AnnotateMutexIsNotPHB);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateCondVarWait(char *f, int l, uptr cv, uptr lock) {
+  SCOPED_ANNOTATION(AnnotateCondVarWait);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateRWLockCreate(char *f, int l, uptr m) {
+  SCOPED_ANNOTATION(AnnotateRWLockCreate);
+  MutexCreate(thr, pc, m, true, true, false);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateRWLockCreateStatic(char *f, int l, uptr m) {
+  SCOPED_ANNOTATION(AnnotateRWLockCreateStatic);
+  MutexCreate(thr, pc, m, true, true, true);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateRWLockDestroy(char *f, int l, uptr m) {
+  SCOPED_ANNOTATION(AnnotateRWLockDestroy);
+  MutexDestroy(thr, pc, m);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateRWLockAcquired(char *f, int l, uptr m, uptr is_w) {
+  SCOPED_ANNOTATION(AnnotateRWLockAcquired);
+  if (is_w)
+    MutexLock(thr, pc, m);
+  else
+    MutexReadLock(thr, pc, m);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateRWLockReleased(char *f, int l, uptr m, uptr is_w) {
+  SCOPED_ANNOTATION(AnnotateRWLockReleased);
+  if (is_w)
+    MutexUnlock(thr, pc, m);
+  else
+    MutexReadUnlock(thr, pc, m);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateTraceMemory(char *f, int l, uptr mem) {
+  SCOPED_ANNOTATION(AnnotateTraceMemory);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateFlushState(char *f, int l) {
+  SCOPED_ANNOTATION(AnnotateFlushState);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateNewMemory(char *f, int l, uptr mem, uptr size) {
+  SCOPED_ANNOTATION(AnnotateNewMemory);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateNoOp(char *f, int l, uptr mem) {
+  SCOPED_ANNOTATION(AnnotateNoOp);
+}
+
+static void ReportMissedExpectedRace(ExpectRace *race) {
+  TsanPrintf("==================\n");
+  TsanPrintf("WARNING: ThreadSanitizer: missed expected data race\n");
+  TsanPrintf("  %s addr=%zx %s:%d\n",
+      race->desc, race->addr, race->file, race->line);
+  TsanPrintf("==================\n");
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateFlushExpectedRaces(char *f, int l) {
+  SCOPED_ANNOTATION(AnnotateFlushExpectedRaces);
+  Lock lock(&dyn_ann_ctx->mtx);
+  while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) {
+    ExpectRace *race = dyn_ann_ctx->expect.next;
+    if (race->hitcount == 0) {
+      CTX()->nmissed_expected++;
+      ReportMissedExpectedRace(race);
+    }
+    race->prev->next = race->next;
+    race->next->prev = race->prev;
+    internal_free(race);
+  }
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateEnableRaceDetection(char *f, int l, int enable) {
+  SCOPED_ANNOTATION(AnnotateEnableRaceDetection);
+  // FIXME: Reconsider this functionality later. It may be irrelevant.
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateMutexIsUsedAsCondVar(char *f, int l, uptr mu) {
+  SCOPED_ANNOTATION(AnnotateMutexIsUsedAsCondVar);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotatePCQGet(char *f, int l, uptr pcq) {
+  SCOPED_ANNOTATION(AnnotatePCQGet);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotatePCQPut(char *f, int l, uptr pcq) {
+  SCOPED_ANNOTATION(AnnotatePCQPut);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotatePCQDestroy(char *f, int l, uptr pcq) {
+  SCOPED_ANNOTATION(AnnotatePCQDestroy);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotatePCQCreate(char *f, int l, uptr pcq) {
+  SCOPED_ANNOTATION(AnnotatePCQCreate);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateExpectRace(char *f, int l, uptr mem, char *desc) {
+  SCOPED_ANNOTATION(AnnotateExpectRace);
+  Lock lock(&dyn_ann_ctx->mtx);
+  AddExpectRace(&dyn_ann_ctx->expect,
+                f, l, mem, 1, desc);
+  DPrintf("Add expected race: %s addr=%zx %s:%d\n", desc, mem, f, l);
+}
+
+static void BenignRaceImpl(char *f, int l, uptr mem, uptr size, char *desc) {
+  Lock lock(&dyn_ann_ctx->mtx);
+  AddExpectRace(&dyn_ann_ctx->benign,
+                f, l, mem, size, desc);
+  DPrintf("Add benign race: %s addr=%zx %s:%d\n", desc, mem, f, l);
+}
+
+// FIXME: Turn it off later. WTF is benign race?1?? Go talk to Hans Boehm.
+// SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateBenignRaceSized(char *f, int l, uptr mem, uptr size, char *desc) {
+  SCOPED_ANNOTATION(AnnotateBenignRaceSized);
+  BenignRaceImpl(f, l, mem, size, desc);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateBenignRace(char *f, int l, uptr mem, char *desc) {
+  SCOPED_ANNOTATION(AnnotateBenignRace);
+  BenignRaceImpl(f, l, mem, 1, desc);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateIgnoreReadsBegin(char *f, int l) {
+  SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin);
+  IgnoreCtl(cur_thread(), false, true);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateIgnoreReadsEnd(char *f, int l) {
+  SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd);
+  IgnoreCtl(cur_thread(), false, false);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateIgnoreWritesBegin(char *f, int l) {
+  SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin);
+  IgnoreCtl(cur_thread(), true, true);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateIgnoreWritesEnd(char *f, int l) {
+  SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd);
+  IgnoreCtl(cur_thread(), true, false);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotatePublishMemoryRange(char *f, int l, uptr addr, uptr size) {
+  SCOPED_ANNOTATION(AnnotatePublishMemoryRange);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateUnpublishMemoryRange(char *f, int l, uptr addr, uptr size) {
+  SCOPED_ANNOTATION(AnnotateUnpublishMemoryRange);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void AnnotateThreadName(char *f, int l, char *name) {
+  SCOPED_ANNOTATION(AnnotateThreadName);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void WTFAnnotateHappensBefore(char *f, int l, uptr addr) {
+  SCOPED_ANNOTATION(AnnotateHappensBefore);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void WTFAnnotateHappensAfter(char *f, int l, uptr addr) {
+  SCOPED_ANNOTATION(AnnotateHappensAfter);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void WTFAnnotateBenignRaceSized(char *f, int l, uptr mem, uptr sz, char *desc) {
+  SCOPED_ANNOTATION(AnnotateBenignRaceSized);
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+int RunningOnValgrind() {
+  return flags()->running_on_valgrind;
+}
+
+double __attribute__((weak)) ValgrindSlowdown(void) {
+  return 10.0;
+}
+
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+const char *ThreadSanitizerQuery(const char *query) {
+  if (internal_strcmp(query, "pure_happens_before") == 0)
+    return "1";
+  else
+    return "0";
+}
+}  // extern "C"
diff --git a/libsanitizer/tsan/tsan_interface_ann.h b/libsanitizer/tsan/tsan_interface_ann.h
new file mode 100644 (file)
index 0000000..1dafe61
--- /dev/null
@@ -0,0 +1,29 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_interface_atomic.cc b/libsanitizer/tsan/tsan_interface_atomic.cc
new file mode 100644 (file)
index 0000000..7b459f1
--- /dev/null
@@ -0,0 +1,368 @@
+//===-- tsan_interface_atomic.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_interface_atomic.h"
+#include "tsan_flags.h"
+#include "tsan_rtl.h"
+
+using namespace __tsan;  // NOLINT
+
+class ScopedAtomic {
+ public:
+  ScopedAtomic(ThreadState *thr, uptr pc, const char *func)
+      : thr_(thr) {
+    CHECK_EQ(thr_->in_rtl, 1);  // 1 due to our own ScopedInRtl member.
+    DPrintf("#%d: %s\n", thr_->tid, func);
+  }
+  ~ScopedAtomic() {
+    CHECK_EQ(thr_->in_rtl, 1);
+  }
+ private:
+  ThreadState *thr_;
+  ScopedInRtl in_rtl_;
+};
+
+// Some shortcuts.
+typedef __tsan_memory_order morder;
+typedef __tsan_atomic8 a8;
+typedef __tsan_atomic16 a16;
+typedef __tsan_atomic32 a32;
+typedef __tsan_atomic64 a64;
+const int mo_relaxed = __tsan_memory_order_relaxed;
+const int mo_consume = __tsan_memory_order_consume;
+const int mo_acquire = __tsan_memory_order_acquire;
+const int mo_release = __tsan_memory_order_release;
+const int mo_acq_rel = __tsan_memory_order_acq_rel;
+const int mo_seq_cst = __tsan_memory_order_seq_cst;
+
+static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
+  StatInc(thr, StatAtomic);
+  StatInc(thr, t);
+  StatInc(thr, size == 1 ? StatAtomic1
+             : size == 2 ? StatAtomic2
+             : size == 4 ? StatAtomic4
+             :             StatAtomic8);
+  StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
+             : mo == mo_consume ? StatAtomicConsume
+             : mo == mo_acquire ? StatAtomicAcquire
+             : mo == mo_release ? StatAtomicRelease
+             : mo == mo_acq_rel ? StatAtomicAcq_Rel
+             :                    StatAtomicSeq_Cst);
+}
+
+static bool IsLoadOrder(morder mo) {
+  return mo == mo_relaxed || mo == mo_consume
+      || mo == mo_acquire || mo == mo_seq_cst;
+}
+
+static bool IsStoreOrder(morder mo) {
+  return mo == mo_relaxed || mo == mo_release || mo == mo_seq_cst;
+}
+
+static bool IsReleaseOrder(morder mo) {
+  return mo == mo_release || mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+static bool IsAcquireOrder(morder mo) {
+  return mo == mo_consume || mo == mo_acquire
+      || mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+#define SCOPED_ATOMIC(func, ...) \
+    if ((u32)mo > 100500) mo = (morder)((u32)mo - 100500); \
+    mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
+    ThreadState *const thr = cur_thread(); \
+    const uptr pc = (uptr)__builtin_return_address(0); \
+    AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
+    ScopedAtomic sa(thr, pc, __FUNCTION__); \
+    return Atomic##func(thr, pc, __VA_ARGS__); \
+/**/
+
+template<typename T>
+static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
+    morder mo) {
+  CHECK(IsLoadOrder(mo));
+  T v = *a;
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  return v;
+}
+
+template<typename T>
+static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
+    morder mo) {
+  CHECK(IsStoreOrder(mo));
+  if (IsReleaseOrder(mo))
+    ReleaseStore(thr, pc, (uptr)a);
+  *a = v;
+}
+
+template<typename T>
+static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v,
+    morder mo) {
+  if (IsReleaseOrder(mo))
+    Release(thr, pc, (uptr)a);
+  v = __sync_lock_test_and_set(a, v);
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  return v;
+}
+
+template<typename T>
+static T AtomicFetchAdd(ThreadState *thr, uptr pc, volatile T *a, T v,
+    morder mo) {
+  if (IsReleaseOrder(mo))
+    Release(thr, pc, (uptr)a);
+  v = __sync_fetch_and_add(a, v);
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  return v;
+}
+
+template<typename T>
+static T AtomicFetchSub(ThreadState *thr, uptr pc, volatile T *a, T v,
+    morder mo) {
+  if (IsReleaseOrder(mo))
+    Release(thr, pc, (uptr)a);
+  v = __sync_fetch_and_sub(a, v);
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  return v;
+}
+
+template<typename T>
+static T AtomicFetchAnd(ThreadState *thr, uptr pc, volatile T *a, T v,
+    morder mo) {
+  if (IsReleaseOrder(mo))
+    Release(thr, pc, (uptr)a);
+  v = __sync_fetch_and_and(a, v);
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  return v;
+}
+
+template<typename T>
+static T AtomicFetchOr(ThreadState *thr, uptr pc, volatile T *a, T v,
+    morder mo) {
+  if (IsReleaseOrder(mo))
+    Release(thr, pc, (uptr)a);
+  v = __sync_fetch_and_or(a, v);
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  return v;
+}
+
+template<typename T>
+static T AtomicFetchXor(ThreadState *thr, uptr pc, volatile T *a, T v,
+    morder mo) {
+  if (IsReleaseOrder(mo))
+    Release(thr, pc, (uptr)a);
+  v = __sync_fetch_and_xor(a, v);
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  return v;
+}
+
+template<typename T>
+static bool AtomicCAS(ThreadState *thr, uptr pc,
+    volatile T *a, T *c, T v, morder mo) {
+  if (IsReleaseOrder(mo))
+    Release(thr, pc, (uptr)a);
+  T cc = *c;
+  T pr = __sync_val_compare_and_swap(a, cc, v);
+  if (IsAcquireOrder(mo))
+    Acquire(thr, pc, (uptr)a);
+  if (pr == cc)
+    return true;
+  *c = pr;
+  return false;
+}
+
+static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
+  __sync_synchronize();
+}
+
+a8 __tsan_atomic8_load(const volatile a8 *a, morder mo) {
+  SCOPED_ATOMIC(Load, a, mo);
+}
+
+a16 __tsan_atomic16_load(const volatile a16 *a, morder mo) {
+  SCOPED_ATOMIC(Load, a, mo);
+}
+
+a32 __tsan_atomic32_load(const volatile a32 *a, morder mo) {
+  SCOPED_ATOMIC(Load, a, mo);
+}
+
+a64 __tsan_atomic64_load(const volatile a64 *a, morder mo) {
+  SCOPED_ATOMIC(Load, a, mo);
+}
+
+void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo) {
+  SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo) {
+  SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo) {
+  SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo) {
+  SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo) {
+  SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo) {
+  SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo) {
+  SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo) {
+  SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo) {
+  SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo) {
+  SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo) {
+  SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo) {
+  SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo) {
+  SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo) {
+  SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo) {
+  SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo) {
+  SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo) {
+  SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo) {
+  SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo) {
+  SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo) {
+  SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo) {
+  SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo) {
+  SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo) {
+  SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo) {
+  SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo) {
+  SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo) {
+  SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo) {
+  SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo) {
+  SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
+    morder mo) {
+  SCOPED_ATOMIC(CAS, a, c, v, mo);
+}
+
+void __tsan_atomic_thread_fence(morder mo) {
+  char* a;
+  SCOPED_ATOMIC(Fence, mo);
+}
+
+void __tsan_atomic_signal_fence(morder mo) {
+}
diff --git a/libsanitizer/tsan/tsan_interface_atomic.h b/libsanitizer/tsan/tsan_interface_atomic.h
new file mode 100644 (file)
index 0000000..37532ce
--- /dev/null
@@ -0,0 +1,173 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_interface_inl.h b/libsanitizer/tsan/tsan_interface_inl.h
new file mode 100644 (file)
index 0000000..493e3f7
--- /dev/null
@@ -0,0 +1,63 @@
+//===-- 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());
+}
diff --git a/libsanitizer/tsan/tsan_md5.cc b/libsanitizer/tsan/tsan_md5.cc
new file mode 100644 (file)
index 0000000..6df823d
--- /dev/null
@@ -0,0 +1,243 @@
+//===-- 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;
+}
+}
diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc
new file mode 100644 (file)
index 0000000..ba4252e
--- /dev/null
@@ -0,0 +1,159 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_mman.h b/libsanitizer/tsan/tsan_mman.h
new file mode 100644 (file)
index 0000000..326bda7
--- /dev/null
@@ -0,0 +1,77 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_mutex.cc b/libsanitizer/tsan/tsan_mutex.cc
new file mode 100644 (file)
index 0000000..83af029
--- /dev/null
@@ -0,0 +1,261 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_mutex.h b/libsanitizer/tsan/tsan_mutex.h
new file mode 100644 (file)
index 0000000..118066e
--- /dev/null
@@ -0,0 +1,78 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h
new file mode 100644 (file)
index 0000000..d7a4cf7
--- /dev/null
@@ -0,0 +1,100 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc
new file mode 100644 (file)
index 0000000..d7de503
--- /dev/null
@@ -0,0 +1,274 @@
+//===-- 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__
diff --git a/libsanitizer/tsan/tsan_platform_mac.cc b/libsanitizer/tsan/tsan_platform_mac.cc
new file mode 100644 (file)
index 0000000..e22a500
--- /dev/null
@@ -0,0 +1,102 @@
+//===-- 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__
diff --git a/libsanitizer/tsan/tsan_printf.cc b/libsanitizer/tsan/tsan_printf.cc
new file mode 100644 (file)
index 0000000..982e292
--- /dev/null
@@ -0,0 +1,38 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc
new file mode 100644 (file)
index 0000000..716a20f
--- /dev/null
@@ -0,0 +1,183 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_report.h b/libsanitizer/tsan/tsan_report.h
new file mode 100644 (file)
index 0000000..9972829
--- /dev/null
@@ -0,0 +1,103 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc
new file mode 100644 (file)
index 0000000..4ff2697
--- /dev/null
@@ -0,0 +1,578 @@
+//===-- tsan_rtl.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.
+//
+// Main file (entry points) for the TSan run-time.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "tsan_defs.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_suppressions.h"
+
+volatile int __tsan_resumed = 0;
+
+extern "C" void __tsan_resume() {
+  __tsan_resumed = 1;
+}
+
+namespace __tsan {
+
+#ifndef TSAN_GO
+THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
+#endif
+static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
+
+static Context *ctx;
+Context *CTX() {
+  return ctx;
+}
+
+Context::Context()
+  : initialized()
+  , report_mtx(MutexTypeReport, StatMtxReport)
+  , nreported()
+  , nmissed_expected()
+  , thread_mtx(MutexTypeThreads, StatMtxThreads)
+  , racy_stacks(MBlockRacyStacks)
+  , racy_addresses(MBlockRacyAddresses)
+  , fired_suppressions(MBlockRacyAddresses) {
+}
+
+// The objects are allocated in TLS, so one may rely on zero-initialization.
+ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
+                         uptr stk_addr, uptr stk_size,
+                         uptr tls_addr, uptr tls_size)
+  : fast_state(tid, epoch)
+  // Do not touch these, rely on zero initialization,
+  // they may be accessed before the ctor.
+  // , fast_ignore_reads()
+  // , fast_ignore_writes()
+  // , in_rtl()
+  , shadow_stack_pos(&shadow_stack[0])
+  , tid(tid)
+  , unique_id(unique_id)
+  , stk_addr(stk_addr)
+  , stk_size(stk_size)
+  , tls_addr(tls_addr)
+  , tls_size(tls_size) {
+}
+
+ThreadContext::ThreadContext(int tid)
+  : tid(tid)
+  , unique_id()
+  , os_id()
+  , user_id()
+  , thr()
+  , status(ThreadStatusInvalid)
+  , detached()
+  , reuse_count()
+  , epoch0()
+  , epoch1()
+  , dead_info()
+  , dead_next() {
+}
+
+static void WriteMemoryProfile(char *buf, uptr buf_size, int num) {
+  uptr shadow = GetShadowMemoryConsumption();
+
+  int nthread = 0;
+  int nlivethread = 0;
+  uptr threadmem = 0;
+  {
+    Lock l(&ctx->thread_mtx);
+    for (unsigned i = 0; i < kMaxTid; i++) {
+      ThreadContext *tctx = ctx->threads[i];
+      if (tctx == 0)
+        continue;
+      nthread += 1;
+      threadmem += sizeof(ThreadContext);
+      if (tctx->status != ThreadStatusRunning)
+        continue;
+      nlivethread += 1;
+      threadmem += sizeof(ThreadState);
+    }
+  }
+
+  uptr nsync = 0;
+  uptr syncmem = CTX()->synctab.GetMemoryConsumption(&nsync);
+
+  internal_snprintf(buf, buf_size, "%d: shadow=%zuMB"
+                                   " thread=%zuMB(total=%d/live=%d)"
+                                   " sync=%zuMB(cnt=%zu)\n",
+    num,
+    shadow >> 20,
+    threadmem >> 20, nthread, nlivethread,
+    syncmem >> 20, nsync);
+}
+
+static void MemoryProfileThread(void *arg) {
+  ScopedInRtl in_rtl;
+  fd_t fd = (fd_t)(uptr)arg;
+  for (int i = 0; ; i++) {
+    InternalScopedBuffer<char> buf(4096);
+    WriteMemoryProfile(buf.data(), buf.size(), i);
+    internal_write(fd, buf.data(), internal_strlen(buf.data()));
+    SleepForSeconds(1);
+  }
+}
+
+static void InitializeMemoryProfile() {
+  if (flags()->profile_memory == 0 || flags()->profile_memory[0] == 0)
+    return;
+  InternalScopedBuffer<char> filename(4096);
+  internal_snprintf(filename.data(), filename.size(), "%s.%d",
+      flags()->profile_memory, GetPid());
+  fd_t fd = internal_open(filename.data(), true);
+  if (fd == kInvalidFd) {
+    TsanPrintf("Failed to open memory profile file '%s'\n", &filename[0]);
+    Die();
+  }
+  internal_start_thread(&MemoryProfileThread, (void*)(uptr)fd);
+}
+
+static void MemoryFlushThread(void *arg) {
+  ScopedInRtl in_rtl;
+  for (int i = 0; ; i++) {
+    SleepForMillis(flags()->flush_memory_ms);
+    FlushShadowMemory();
+  }
+}
+
+static void InitializeMemoryFlush() {
+  if (flags()->flush_memory_ms == 0)
+    return;
+  if (flags()->flush_memory_ms < 100)
+    flags()->flush_memory_ms = 100;
+  internal_start_thread(&MemoryFlushThread, 0);
+}
+
+void Initialize(ThreadState *thr) {
+  // Thread safe because done before all threads exist.
+  static bool is_initialized = false;
+  if (is_initialized)
+    return;
+  is_initialized = true;
+  // Install tool-specific callbacks in sanitizer_common.
+  SetCheckFailedCallback(TsanCheckFailed);
+
+  ScopedInRtl in_rtl;
+#ifndef TSAN_GO
+  InitializeAllocator();
+#endif
+  InitializeInterceptors();
+  const char *env = InitializePlatform();
+  InitializeMutex();
+  InitializeDynamicAnnotations();
+  ctx = new(ctx_placeholder) Context;
+  InitializeShadowMemory();
+  ctx->dead_list_size = 0;
+  ctx->dead_list_head = 0;
+  ctx->dead_list_tail = 0;
+  InitializeFlags(&ctx->flags, env);
+  InitializeSuppressions();
+#ifndef TSAN_GO
+  // Initialize external symbolizer before internal threads are started.
+  const char *external_symbolizer = flags()->external_symbolizer_path;
+  if (external_symbolizer != 0 && external_symbolizer[0] != '\0') {
+    InitializeExternalSymbolizer(external_symbolizer);
+  }
+#endif
+  InitializeMemoryProfile();
+  InitializeMemoryFlush();
+
+  if (ctx->flags.verbosity)
+    TsanPrintf("***** Running under ThreadSanitizer v2 (pid %d) *****\n",
+               GetPid());
+
+  // Initialize thread 0.
+  ctx->thread_seq = 0;
+  int tid = ThreadCreate(thr, 0, 0, true);
+  CHECK_EQ(tid, 0);
+  ThreadStart(thr, tid, GetPid());
+  CHECK_EQ(thr->in_rtl, 1);
+  ctx->initialized = true;
+
+  if (flags()->stop_on_start) {
+    TsanPrintf("ThreadSanitizer is suspended at startup (pid %d)."
+           " Call __tsan_resume().\n",
+           GetPid());
+    while (__tsan_resumed == 0);
+  }
+}
+
+int Finalize(ThreadState *thr) {
+  ScopedInRtl in_rtl;
+  Context *ctx = __tsan::ctx;
+  bool failed = false;
+
+  // Wait for pending reports.
+  ctx->report_mtx.Lock();
+  ctx->report_mtx.Unlock();
+
+  ThreadFinalize(thr);
+
+  if (ctx->nreported) {
+    failed = true;
+    TsanPrintf("ThreadSanitizer: reported %d warnings\n", ctx->nreported);
+  }
+
+  if (ctx->nmissed_expected) {
+    failed = true;
+    TsanPrintf("ThreadSanitizer: missed %d expected races\n",
+        ctx->nmissed_expected);
+  }
+
+  StatOutput(ctx->stat);
+  return failed ? flags()->exitcode : 0;
+}
+
+#ifndef TSAN_GO
+u32 CurrentStackId(ThreadState *thr, uptr pc) {
+  if (thr->shadow_stack_pos == 0)  // May happen during bootstrap.
+    return 0;
+  if (pc) {
+    thr->shadow_stack_pos[0] = pc;
+    thr->shadow_stack_pos++;
+  }
+  u32 id = StackDepotPut(thr->shadow_stack,
+                         thr->shadow_stack_pos - thr->shadow_stack);
+  if (pc)
+    thr->shadow_stack_pos--;
+  return id;
+}
+#endif
+
+void TraceSwitch(ThreadState *thr) {
+  thr->nomalloc++;
+  ScopedInRtl in_rtl;
+  Lock l(&thr->trace.mtx);
+  unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % kTraceParts;
+  TraceHeader *hdr = &thr->trace.headers[trace];
+  hdr->epoch0 = thr->fast_state.epoch();
+  hdr->stack0.ObtainCurrent(thr, 0);
+  thr->nomalloc--;
+}
+
+#ifndef TSAN_GO
+extern "C" void __tsan_trace_switch() {
+  TraceSwitch(cur_thread());
+}
+
+extern "C" void __tsan_report_race() {
+  ReportRace(cur_thread());
+}
+#endif
+
+ALWAYS_INLINE
+static Shadow LoadShadow(u64 *p) {
+  u64 raw = atomic_load((atomic_uint64_t*)p, memory_order_relaxed);
+  return Shadow(raw);
+}
+
+ALWAYS_INLINE
+static void StoreShadow(u64 *sp, u64 s) {
+  atomic_store((atomic_uint64_t*)sp, s, memory_order_relaxed);
+}
+
+ALWAYS_INLINE
+static void StoreIfNotYetStored(u64 *sp, u64 *s) {
+  StoreShadow(sp, *s);
+  *s = 0;
+}
+
+static inline void HandleRace(ThreadState *thr, u64 *shadow_mem,
+                              Shadow cur, Shadow old) {
+  thr->racy_state[0] = cur.raw();
+  thr->racy_state[1] = old.raw();
+  thr->racy_shadow_addr = shadow_mem;
+  ReportRace(thr);
+}
+
+static inline bool BothReads(Shadow s, int kAccessIsWrite) {
+  return !kAccessIsWrite && !s.is_write();
+}
+
+static inline bool OldIsRWStronger(Shadow old, int kAccessIsWrite) {
+  return old.is_write() || !kAccessIsWrite;
+}
+
+static inline bool OldIsRWWeaker(Shadow old, int kAccessIsWrite) {
+  return !old.is_write() || kAccessIsWrite;
+}
+
+static inline bool OldIsInSameSynchEpoch(Shadow old, ThreadState *thr) {
+  return old.epoch() >= thr->fast_synch_epoch;
+}
+
+static inline bool HappensBefore(Shadow old, ThreadState *thr) {
+  return thr->clock.get(old.tid()) >= old.epoch();
+}
+
+ALWAYS_INLINE
+void MemoryAccessImpl(ThreadState *thr, uptr addr,
+    int kAccessSizeLog, bool kAccessIsWrite, FastState fast_state,
+    u64 *shadow_mem, Shadow cur) {
+  StatInc(thr, StatMop);
+  StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+  StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+
+  // This potentially can live in an MMX/SSE scratch register.
+  // The required intrinsics are:
+  // __m128i _mm_move_epi64(__m128i*);
+  // _mm_storel_epi64(u64*, __m128i);
+  u64 store_word = cur.raw();
+
+  // scan all the shadow values and dispatch to 4 categories:
+  // same, replace, candidate and race (see comments below).
+  // we consider only 3 cases regarding access sizes:
+  // equal, intersect and not intersect. initially I considered
+  // larger and smaller as well, it allowed to replace some
+  // 'candidates' with 'same' or 'replace', but I think
+  // it's just not worth it (performance- and complexity-wise).
+
+  Shadow old(0);
+  if (kShadowCnt == 1) {
+    int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+  } else if (kShadowCnt == 2) {
+    int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 1;
+#include "tsan_update_shadow_word_inl.h"
+  } else if (kShadowCnt == 4) {
+    int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 1;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 2;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 3;
+#include "tsan_update_shadow_word_inl.h"
+  } else if (kShadowCnt == 8) {
+    int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 1;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 2;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 3;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 4;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 5;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 6;
+#include "tsan_update_shadow_word_inl.h"
+    idx = 7;
+#include "tsan_update_shadow_word_inl.h"
+  } else {
+    CHECK(false);
+  }
+
+  // we did not find any races and had already stored
+  // the current access info, so we are done
+  if (LIKELY(store_word == 0))
+    return;
+  // choose a random candidate slot and replace it
+  StoreShadow(shadow_mem + (cur.epoch() % kShadowCnt), store_word);
+  StatInc(thr, StatShadowReplace);
+  return;
+ RACE:
+  HandleRace(thr, shadow_mem, cur, old);
+  return;
+}
+
+ALWAYS_INLINE
+void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
+    int kAccessSizeLog, bool kAccessIsWrite) {
+  u64 *shadow_mem = (u64*)MemToShadow(addr);
+  DPrintf2("#%d: tsan::OnMemoryAccess: @%p %p size=%d"
+      " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n",
+      (int)thr->fast_state.tid(), (void*)pc, (void*)addr,
+      (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem,
+      (uptr)shadow_mem[0], (uptr)shadow_mem[1],
+      (uptr)shadow_mem[2], (uptr)shadow_mem[3]);
+#if TSAN_DEBUG
+  if (!IsAppMem(addr)) {
+    TsanPrintf("Access to non app mem %zx\n", addr);
+    DCHECK(IsAppMem(addr));
+  }
+  if (!IsShadowMem((uptr)shadow_mem)) {
+    TsanPrintf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+    DCHECK(IsShadowMem((uptr)shadow_mem));
+  }
+#endif
+
+  FastState fast_state = thr->fast_state;
+  if (fast_state.GetIgnoreBit())
+    return;
+  fast_state.IncrementEpoch();
+  thr->fast_state = fast_state;
+  Shadow cur(fast_state);
+  cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog);
+  cur.SetWrite(kAccessIsWrite);
+
+  // We must not store to the trace if we do not store to the shadow.
+  // That is, this call must be moved somewhere below.
+  TraceAddEvent(thr, fast_state.epoch(), EventTypeMop, pc);
+
+  MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, fast_state,
+      shadow_mem, cur);
+}
+
+static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size,
+                           u64 val) {
+  if (size == 0)
+    return;
+  // FIXME: fix me.
+  uptr offset = addr % kShadowCell;
+  if (offset) {
+    offset = kShadowCell - offset;
+    if (size <= offset)
+      return;
+    addr += offset;
+    size -= offset;
+  }
+  DCHECK_EQ(addr % 8, 0);
+  // If a user passes some insane arguments (memset(0)),
+  // let it just crash as usual.
+  if (!IsAppMem(addr) || !IsAppMem(addr + size - 1))
+    return;
+  (void)thr;
+  (void)pc;
+  // Some programs mmap like hundreds of GBs but actually used a small part.
+  // So, it's better to report a false positive on the memory
+  // then to hang here senselessly.
+  const uptr kMaxResetSize = 4ull*1024*1024*1024;
+  if (size > kMaxResetSize)
+    size = kMaxResetSize;
+  size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1);
+  u64 *p = (u64*)MemToShadow(addr);
+  CHECK(IsShadowMem((uptr)p));
+  CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1)));
+  // FIXME: may overwrite a part outside the region
+  for (uptr i = 0; i < size * kShadowCnt / kShadowCell;) {
+    p[i++] = val;
+    for (uptr j = 1; j < kShadowCnt; j++)
+      p[i++] = 0;
+  }
+}
+
+void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+  MemoryRangeSet(thr, pc, addr, size, 0);
+}
+
+void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+  MemoryAccessRange(thr, pc, addr, size, true);
+  Shadow s(thr->fast_state);
+  s.MarkAsFreed();
+  s.SetWrite(true);
+  s.SetAddr0AndSizeLog(0, 3);
+  MemoryRangeSet(thr, pc, addr, size, s.raw());
+}
+
+void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+  Shadow s(thr->fast_state);
+  s.SetWrite(true);
+  s.SetAddr0AndSizeLog(0, 3);
+  MemoryRangeSet(thr, pc, addr, size, s.raw());
+}
+
+void FuncEntry(ThreadState *thr, uptr pc) {
+  DCHECK_EQ(thr->in_rtl, 0);
+  StatInc(thr, StatFuncEnter);
+  DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
+  thr->fast_state.IncrementEpoch();
+  TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeFuncEnter, pc);
+
+  // Shadow stack maintenance can be replaced with
+  // stack unwinding during trace switch (which presumably must be faster).
+  DCHECK_GE(thr->shadow_stack_pos, &thr->shadow_stack[0]);
+#ifndef TSAN_GO
+  DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]);
+#else
+  if (thr->shadow_stack_pos == thr->shadow_stack_end) {
+    const int sz = thr->shadow_stack_end - thr->shadow_stack;
+    const int newsz = 2 * sz;
+    uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack,
+        newsz * sizeof(uptr));
+    internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr));
+    internal_free(thr->shadow_stack);
+    thr->shadow_stack = newstack;
+    thr->shadow_stack_pos = newstack + sz;
+    thr->shadow_stack_end = newstack + newsz;
+  }
+#endif
+  thr->shadow_stack_pos[0] = pc;
+  thr->shadow_stack_pos++;
+}
+
+void FuncExit(ThreadState *thr) {
+  DCHECK_EQ(thr->in_rtl, 0);
+  StatInc(thr, StatFuncExit);
+  DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
+  thr->fast_state.IncrementEpoch();
+  TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeFuncExit, 0);
+
+  DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]);
+#ifndef TSAN_GO
+  DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]);
+#endif
+  thr->shadow_stack_pos--;
+}
+
+void IgnoreCtl(ThreadState *thr, bool write, bool begin) {
+  DPrintf("#%d: IgnoreCtl(%d, %d)\n", thr->tid, write, begin);
+  thr->ignore_reads_and_writes += begin ? 1 : -1;
+  CHECK_GE(thr->ignore_reads_and_writes, 0);
+  if (thr->ignore_reads_and_writes)
+    thr->fast_state.SetIgnoreBit();
+  else
+    thr->fast_state.ClearIgnoreBit();
+}
+
+bool MD5Hash::operator==(const MD5Hash &other) const {
+  return hash[0] == other.hash[0] && hash[1] == other.hash[1];
+}
+
+#if TSAN_DEBUG
+void build_consistency_debug() {}
+#else
+void build_consistency_release() {}
+#endif
+
+#if TSAN_COLLECT_STATS
+void build_consistency_stats() {}
+#else
+void build_consistency_nostats() {}
+#endif
+
+#if TSAN_SHADOW_COUNT == 1
+void build_consistency_shadow1() {}
+#elif TSAN_SHADOW_COUNT == 2
+void build_consistency_shadow2() {}
+#elif TSAN_SHADOW_COUNT == 4
+void build_consistency_shadow4() {}
+#else
+void build_consistency_shadow8() {}
+#endif
+
+}  // namespace __tsan
+
+#ifndef TSAN_GO
+// Must be included in this file to make sure everything is inlined.
+#include "tsan_interface_inl.h"
+#endif
diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h
new file mode 100644 (file)
index 0000000..3df229c
--- /dev/null
@@ -0,0 +1,554 @@
+//===-- tsan_rtl.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.
+//
+// Main internal TSan header file.
+//
+// Ground rules:
+//   - C++ run-time should not be used (static CTORs, RTTI, exceptions, static
+//     function-scope locals)
+//   - All functions/classes/etc reside in namespace __tsan, except for those
+//     declared in tsan_interface.h.
+//   - Platform-specific files should be used instead of ifdefs (*).
+//   - No system headers included in header files (*).
+//   - Platform specific headres included only into platform-specific files (*).
+//
+//  (*) Except when inlining is critical for performance.
+//===----------------------------------------------------------------------===//
+
+#ifndef TSAN_RTL_H
+#define TSAN_RTL_H
+
+#include "sanitizer_common/sanitizer_common.h"
+#if __WORDSIZE == 64
+#include "sanitizer_common/sanitizer_allocator64.h"
+#else
+#include "sanitizer_common/sanitizer_allocator.h"
+#endif
+#include "tsan_clock.h"
+#include "tsan_defs.h"
+#include "tsan_flags.h"
+#include "tsan_sync.h"
+#include "tsan_trace.h"
+#include "tsan_vector.h"
+#include "tsan_report.h"
+
+namespace __tsan {
+
+// Descriptor of user's memory block.
+struct MBlock {
+  Mutex mtx;
+  uptr size;
+  u32 alloc_tid;
+  u32 alloc_stack_id;
+  SyncVar *head;
+};
+
+#ifndef TSAN_GO
+#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
+const uptr kAllocatorSpace = 0x7d0000000000ULL;
+#else
+const uptr kAllocatorSpace = 0x7d0000000000ULL;
+#endif
+const uptr kAllocatorSize  =  0x10000000000ULL;  // 1T.
+
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, sizeof(MBlock),
+    DefaultSizeClassMap> PrimaryAllocator;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator::kNumClasses,
+    PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+    SecondaryAllocator> Allocator;
+Allocator *allocator();
+#endif
+
+void TsanCheckFailed(const char *file, int line, const char *cond,
+                     u64 v1, u64 v2);
+void TsanPrintf(const char *format, ...);
+
+// FastState (from most significant bit):
+//   unused          : 1
+//   tid             : kTidBits
+//   epoch           : kClkBits
+//   unused          : -
+//   ignore_bit      : 1
+class FastState {
+ public:
+  FastState(u64 tid, u64 epoch) {
+    x_ = tid << kTidShift;
+    x_ |= epoch << kClkShift;
+    DCHECK(tid == this->tid());
+    DCHECK(epoch == this->epoch());
+  }
+
+  explicit FastState(u64 x)
+      : x_(x) {
+  }
+
+  u64 raw() const {
+    return x_;
+  }
+
+  u64 tid() const {
+    u64 res = x_ >> kTidShift;
+    return res;
+  }
+
+  u64 epoch() const {
+    u64 res = (x_ << (kTidBits + 1)) >> (64 - kClkBits);
+    return res;
+  }
+
+  void IncrementEpoch() {
+    u64 old_epoch = epoch();
+    x_ += 1 << kClkShift;
+    DCHECK_EQ(old_epoch + 1, epoch());
+    (void)old_epoch;
+  }
+
+  void SetIgnoreBit() { x_ |= kIgnoreBit; }
+  void ClearIgnoreBit() { x_ &= ~kIgnoreBit; }
+  bool GetIgnoreBit() const { return x_ & kIgnoreBit; }
+
+ private:
+  friend class Shadow;
+  static const int kTidShift = 64 - kTidBits - 1;
+  static const int kClkShift = kTidShift - kClkBits;
+  static const u64 kIgnoreBit = 1ull;
+  static const u64 kFreedBit = 1ull << 63;
+  u64 x_;
+};
+
+// Shadow (from most significant bit):
+//   freed           : 1
+//   tid             : kTidBits
+//   epoch           : kClkBits
+//   is_write        : 1
+//   size_log        : 2
+//   addr0           : 3
+class Shadow : public FastState {
+ public:
+  explicit Shadow(u64 x) : FastState(x) { }
+
+  explicit Shadow(const FastState &s) : FastState(s.x_) { }
+
+  void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) {
+    DCHECK_EQ(x_ & 31, 0);
+    DCHECK_LE(addr0, 7);
+    DCHECK_LE(kAccessSizeLog, 3);
+    x_ |= (kAccessSizeLog << 3) | addr0;
+    DCHECK_EQ(kAccessSizeLog, size_log());
+    DCHECK_EQ(addr0, this->addr0());
+  }
+
+  void SetWrite(unsigned kAccessIsWrite) {
+    DCHECK_EQ(x_ & 32, 0);
+    if (kAccessIsWrite)
+      x_ |= 32;
+    DCHECK_EQ(kAccessIsWrite, is_write());
+  }
+
+  bool IsZero() const { return x_ == 0; }
+
+  static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) {
+    u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift;
+    DCHECK_EQ(shifted_xor == 0, s1.tid() == s2.tid());
+    return shifted_xor == 0;
+  }
+
+  static inline bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) {
+    u64 masked_xor = (s1.x_ ^ s2.x_) & 31;
+    return masked_xor == 0;
+  }
+
+  static inline bool TwoRangesIntersect(Shadow s1, Shadow s2,
+      unsigned kS2AccessSize) {
+    bool res = false;
+    u64 diff = s1.addr0() - s2.addr0();
+    if ((s64)diff < 0) {  // s1.addr0 < s2.addr0  // NOLINT
+      // if (s1.addr0() + size1) > s2.addr0()) return true;
+      if (s1.size() > -diff)  res = true;
+    } else {
+      // if (s2.addr0() + kS2AccessSize > s1.addr0()) return true;
+      if (kS2AccessSize > diff) res = true;
+    }
+    DCHECK_EQ(res, TwoRangesIntersectSLOW(s1, s2));
+    DCHECK_EQ(res, TwoRangesIntersectSLOW(s2, s1));
+    return res;
+  }
+
+  // The idea behind the offset is as follows.
+  // Consider that we have 8 bool's contained within a single 8-byte block
+  // (mapped to a single shadow "cell"). Now consider that we write to the bools
+  // from a single thread (which we consider the common case).
+  // W/o offsetting each access will have to scan 4 shadow values at average
+  // to find the corresponding shadow value for the bool.
+  // With offsetting we start scanning shadow with the offset so that
+  // each access hits necessary shadow straight off (at least in an expected
+  // optimistic case).
+  // This logic works seamlessly for any layout of user data. For example,
+  // if user data is {int, short, char, char}, then accesses to the int are
+  // offsetted to 0, short - 4, 1st char - 6, 2nd char - 7. Hopefully, accesses
+  // from a single thread won't need to scan all 8 shadow values.
+  unsigned ComputeSearchOffset() {
+    return x_ & 7;
+  }
+  u64 addr0() const { return x_ & 7; }
+  u64 size() const { return 1ull << size_log(); }
+  bool is_write() const { return x_ & 32; }
+
+  // The idea behind the freed bit is as follows.
+  // When the memory is freed (or otherwise unaccessible) we write to the shadow
+  // values with tid/epoch related to the free and the freed bit set.
+  // During memory accesses processing the freed bit is considered
+  // as msb of tid. So any access races with shadow with freed bit set
+  // (it is as if write from a thread with which we never synchronized before).
+  // This allows us to detect accesses to freed memory w/o additional
+  // overheads in memory access processing and at the same time restore
+  // tid/epoch of free.
+  void MarkAsFreed() {
+     x_ |= kFreedBit;
+  }
+
+  bool GetFreedAndReset() {
+    bool res = x_ & kFreedBit;
+    x_ &= ~kFreedBit;
+    return res;
+  }
+
+ private:
+  u64 size_log() const { return (x_ >> 3) & 3; }
+
+  static bool TwoRangesIntersectSLOW(const Shadow s1, const Shadow s2) {
+    if (s1.addr0() == s2.addr0()) return true;
+    if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0())
+      return true;
+    if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0())
+      return true;
+    return false;
+  }
+};
+
+// Freed memory.
+// As if 8-byte write by thread 0xff..f at epoch 0xff..f, races with everything.
+const u64 kShadowFreed = 0xfffffffffffffff8ull;
+
+struct SignalContext;
+
+// This struct is stored in TLS.
+struct ThreadState {
+  FastState fast_state;
+  // Synch epoch represents the threads's epoch before the last synchronization
+  // action. It allows to reduce number of shadow state updates.
+  // For example, fast_synch_epoch=100, last write to addr X was at epoch=150,
+  // if we are processing write to X from the same thread at epoch=200,
+  // we do nothing, because both writes happen in the same 'synch epoch'.
+  // That is, if another memory access does not race with the former write,
+  // it does not race with the latter as well.
+  // QUESTION: can we can squeeze this into ThreadState::Fast?
+  // E.g. ThreadState::Fast is a 44-bit, 32 are taken by synch_epoch and 12 are
+  // taken by epoch between synchs.
+  // This way we can save one load from tls.
+  u64 fast_synch_epoch;
+  // This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read.
+  // We do not distinguish beteween ignoring reads and writes
+  // for better performance.
+  int ignore_reads_and_writes;
+  uptr *shadow_stack_pos;
+  u64 *racy_shadow_addr;
+  u64 racy_state[2];
+  Trace trace;
+#ifndef TSAN_GO
+  // C/C++ uses embed shadow stack of fixed size.
+  uptr shadow_stack[kShadowStackSize];
+#else
+  // Go uses satellite shadow stack with dynamic size.
+  uptr *shadow_stack;
+  uptr *shadow_stack_end;
+#endif
+  ThreadClock clock;
+#ifndef TSAN_GO
+  AllocatorCache alloc_cache;
+#endif
+  u64 stat[StatCnt];
+  const int tid;
+  const int unique_id;
+  int in_rtl;
+  bool is_alive;
+  const uptr stk_addr;
+  const uptr stk_size;
+  const uptr tls_addr;
+  const uptr tls_size;
+
+  DeadlockDetector deadlock_detector;
+
+  bool in_signal_handler;
+  SignalContext *signal_ctx;
+
+#ifndef TSAN_GO
+  u32 last_sleep_stack_id;
+  ThreadClock last_sleep_clock;
+#endif
+
+  // Set in regions of runtime that must be signal-safe and fork-safe.
+  // If set, malloc must not be called.
+  int nomalloc;
+
+  explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
+                       uptr stk_addr, uptr stk_size,
+                       uptr tls_addr, uptr tls_size);
+};
+
+Context *CTX();
+
+#ifndef TSAN_GO
+extern THREADLOCAL char cur_thread_placeholder[];
+INLINE ThreadState *cur_thread() {
+  return reinterpret_cast<ThreadState *>(&cur_thread_placeholder);
+}
+#endif
+
+enum ThreadStatus {
+  ThreadStatusInvalid,   // Non-existent thread, data is invalid.
+  ThreadStatusCreated,   // Created but not yet running.
+  ThreadStatusRunning,   // The thread is currently running.
+  ThreadStatusFinished,  // Joinable thread is finished but not yet joined.
+  ThreadStatusDead       // Joined, but some info (trace) is still alive.
+};
+
+// An info about a thread that is hold for some time after its termination.
+struct ThreadDeadInfo {
+  Trace trace;
+};
+
+struct ThreadContext {
+  const int tid;
+  int unique_id;  // Non-rolling thread id.
+  uptr os_id;  // pid
+  uptr user_id;  // Some opaque user thread id (e.g. pthread_t).
+  ThreadState *thr;
+  ThreadStatus status;
+  bool detached;
+  int reuse_count;
+  SyncClock sync;
+  // Epoch at which the thread had started.
+  // If we see an event from the thread stamped by an older epoch,
+  // the event is from a dead thread that shared tid with this thread.
+  u64 epoch0;
+  u64 epoch1;
+  StackTrace creation_stack;
+  ThreadDeadInfo *dead_info;
+  ThreadContext *dead_next;  // In dead thread list.
+
+  explicit ThreadContext(int tid);
+};
+
+struct RacyStacks {
+  MD5Hash hash[2];
+  bool operator==(const RacyStacks &other) const {
+    if (hash[0] == other.hash[0] && hash[1] == other.hash[1])
+      return true;
+    if (hash[0] == other.hash[1] && hash[1] == other.hash[0])
+      return true;
+    return false;
+  }
+};
+
+struct RacyAddress {
+  uptr addr_min;
+  uptr addr_max;
+};
+
+struct FiredSuppression {
+  ReportType type;
+  uptr pc;
+};
+
+struct Context {
+  Context();
+
+  bool initialized;
+
+  SyncTab synctab;
+
+  Mutex report_mtx;
+  int nreported;
+  int nmissed_expected;
+
+  Mutex thread_mtx;
+  unsigned thread_seq;
+  unsigned unique_thread_seq;
+  int alive_threads;
+  int max_alive_threads;
+  ThreadContext *threads[kMaxTid];
+  int dead_list_size;
+  ThreadContext* dead_list_head;
+  ThreadContext* dead_list_tail;
+
+  Vector<RacyStacks> racy_stacks;
+  Vector<RacyAddress> racy_addresses;
+  Vector<FiredSuppression> fired_suppressions;
+
+  Flags flags;
+
+  u64 stat[StatCnt];
+  u64 int_alloc_cnt[MBlockTypeCount];
+  u64 int_alloc_siz[MBlockTypeCount];
+};
+
+class ScopedInRtl {
+ public:
+  ScopedInRtl();
+  ~ScopedInRtl();
+ private:
+  ThreadState*thr_;
+  int in_rtl_;
+  int errno_;
+};
+
+class ScopedReport {
+ public:
+  explicit ScopedReport(ReportType typ);
+  ~ScopedReport();
+
+  void AddStack(const StackTrace *stack);
+  void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack);
+  void AddThread(const ThreadContext *tctx);
+  void AddMutex(const SyncVar *s);
+  void AddLocation(uptr addr, uptr size);
+  void AddSleep(u32 stack_id);
+
+  const ReportDesc *GetReport() const;
+
+ private:
+  Context *ctx_;
+  ReportDesc *rep_;
+
+  ScopedReport(const ScopedReport&);
+  void operator = (const ScopedReport&);
+};
+
+void RestoreStack(int tid, const u64 epoch, StackTrace *stk);
+
+void StatAggregate(u64 *dst, u64 *src);
+void StatOutput(u64 *stat);
+void ALWAYS_INLINE INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) {
+  if (kCollectStats)
+    thr->stat[typ] += n;
+}
+
+void InitializeShadowMemory();
+void InitializeInterceptors();
+void InitializeDynamicAnnotations();
+
+void ReportRace(ThreadState *thr);
+bool OutputReport(Context *ctx,
+                  const ScopedReport &srep,
+                  const ReportStack *suppress_stack = 0);
+bool IsFiredSuppression(Context *ctx,
+                        const ScopedReport &srep,
+                        const StackTrace &trace);
+bool IsExpectedReport(uptr addr, uptr size);
+
+#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1
+# define DPrintf TsanPrintf
+#else
+# define DPrintf(...)
+#endif
+
+#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 2
+# define DPrintf2 TsanPrintf
+#else
+# define DPrintf2(...)
+#endif
+
+u32 CurrentStackId(ThreadState *thr, uptr pc);
+void PrintCurrentStack(ThreadState *thr, uptr pc);
+
+void Initialize(ThreadState *thr);
+int Finalize(ThreadState *thr);
+
+void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
+    int kAccessSizeLog, bool kAccessIsWrite);
+void MemoryAccessImpl(ThreadState *thr, uptr addr,
+    int kAccessSizeLog, bool kAccessIsWrite, FastState fast_state,
+    u64 *shadow_mem, Shadow cur);
+void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr);
+void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
+                       uptr size, bool is_write);
+void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size);
+void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size);
+void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size);
+void IgnoreCtl(ThreadState *thr, bool write, bool begin);
+
+void FuncEntry(ThreadState *thr, uptr pc);
+void FuncExit(ThreadState *thr);
+
+int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
+void ThreadStart(ThreadState *thr, int tid, uptr os_id);
+void ThreadFinish(ThreadState *thr);
+int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
+void ThreadJoin(ThreadState *thr, uptr pc, int tid);
+void ThreadDetach(ThreadState *thr, uptr pc, int tid);
+void ThreadFinalize(ThreadState *thr);
+void ThreadFinalizerGoroutine(ThreadState *thr);
+
+void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
+                 bool rw, bool recursive, bool linker_init);
+void MutexDestroy(ThreadState *thr, uptr pc, uptr addr);
+void MutexLock(ThreadState *thr, uptr pc, uptr addr);
+void MutexUnlock(ThreadState *thr, uptr pc, uptr addr);
+void MutexReadLock(ThreadState *thr, uptr pc, uptr addr);
+void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr);
+void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
+
+void Acquire(ThreadState *thr, uptr pc, uptr addr);
+void Release(ThreadState *thr, uptr pc, uptr addr);
+void ReleaseStore(ThreadState *thr, uptr pc, uptr addr);
+void AfterSleep(ThreadState *thr, uptr pc);
+
+// The hacky call uses custom calling convention and an assembly thunk.
+// It is considerably faster that a normal call for the caller
+// if it is not executed (it is intended for slow paths from hot functions).
+// The trick is that the call preserves all registers and the compiler
+// does not treat it as a call.
+// If it does not work for you, use normal call.
+#if TSAN_DEBUG == 0
+// The caller may not create the stack frame for itself at all,
+// so we create a reserve stack frame for it (1024b must be enough).
+#define HACKY_CALL(f) \
+  __asm__ __volatile__("sub $1024, %%rsp;" \
+                       "/*.cfi_adjust_cfa_offset 1024;*/" \
+                       "call " #f "_thunk;" \
+                       "add $1024, %%rsp;" \
+                       "/*.cfi_adjust_cfa_offset -1024;*/" \
+                       ::: "memory", "cc");
+#else
+#define HACKY_CALL(f) f()
+#endif
+
+void TraceSwitch(ThreadState *thr);
+
+extern "C" void __tsan_trace_switch();
+void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, u64 epoch,
+                                        EventType typ, uptr addr) {
+  StatInc(thr, StatEvents);
+  if (UNLIKELY((epoch % kTracePartSize) == 0)) {
+    TraceSwitch(thr);
+  }
+  Event *evp = &thr->trace.events[epoch % kTraceSize];
+  Event ev = (u64)addr | ((u64)typ << 61);
+  *evp = ev;
+}
+
+}  // namespace __tsan
+
+#endif  // TSAN_RTL_H
diff --git a/libsanitizer/tsan/tsan_rtl_amd64.S b/libsanitizer/tsan/tsan_rtl_amd64.S
new file mode 100644 (file)
index 0000000..aee650d
--- /dev/null
@@ -0,0 +1,164 @@
+.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
diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc
new file mode 100644 (file)
index 0000000..098894f
--- /dev/null
@@ -0,0 +1,269 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc
new file mode 100644 (file)
index 0000000..1511913
--- /dev/null
@@ -0,0 +1,461 @@
+//===-- tsan_rtl_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 "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_suppressions.h"
+#include "tsan_symbolize.h"
+#include "tsan_report.h"
+#include "tsan_sync.h"
+#include "tsan_mman.h"
+#include "tsan_flags.h"
+
+namespace __tsan {
+
+void TsanCheckFailed(const char *file, int line, const char *cond,
+                     u64 v1, u64 v2) {
+  ScopedInRtl in_rtl;
+  TsanPrintf("FATAL: ThreadSanitizer CHECK failed: "
+             "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
+             file, line, cond, (uptr)v1, (uptr)v2);
+  Die();
+}
+
+// Can be overriden by an application/test to intercept reports.
+#ifdef TSAN_EXTERNAL_HOOKS
+bool OnReport(const ReportDesc *rep, bool suppressed);
+#else
+SANITIZER_INTERFACE_ATTRIBUTE
+bool WEAK OnReport(const ReportDesc *rep, bool suppressed) {
+  (void)rep;
+  return suppressed;
+}
+#endif
+
+static void StackStripMain(ReportStack *stack) {
+  ReportStack *last_frame = 0;
+  ReportStack *last_frame2 = 0;
+  const char *prefix = "__interceptor_";
+  uptr prefix_len = internal_strlen(prefix);
+  const char *path_prefix = flags()->strip_path_prefix;
+  uptr path_prefix_len = internal_strlen(path_prefix);
+  char *pos;
+  for (ReportStack *ent = stack; ent; ent = ent->next) {
+    if (ent->func && 0 == internal_strncmp(ent->func, prefix, prefix_len))
+      ent->func += prefix_len;
+    if (ent->file && (pos = internal_strstr(ent->file, path_prefix)))
+      ent->file = pos + path_prefix_len;
+    if (ent->file && ent->file[0] == '.' && ent->file[1] == '/')
+      ent->file += 2;
+    last_frame2 = last_frame;
+    last_frame = ent;
+  }
+
+  if (last_frame2 == 0)
+    return;
+  const char *last = last_frame->func;
+#ifndef TSAN_GO
+  const char *last2 = last_frame2->func;
+  // Strip frame above 'main'
+  if (last2 && 0 == internal_strcmp(last2, "main")) {
+    last_frame2->next = 0;
+  // Strip our internal thread start routine.
+  } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) {
+    last_frame2->next = 0;
+  // Strip global ctors init.
+  } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) {
+    last_frame2->next = 0;
+  // If both are 0, then we probably just failed to symbolize.
+  } else if (last || last2) {
+    // Ensure that we recovered stack completely. Trimmed stack
+    // can actually happen if we do not instrument some code,
+    // so it's only a debug print. However we must try hard to not miss it
+    // due to our fault.
+    DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc);
+  }
+#else
+  if (last && 0 == internal_strcmp(last, "schedunlock"))
+    last_frame2->next = 0;
+#endif
+}
+
+static ReportStack *SymbolizeStack(const StackTrace& trace) {
+  if (trace.IsEmpty())
+    return 0;
+  ReportStack *stack = 0;
+  for (uptr si = 0; si < trace.Size(); si++) {
+    // We obtain the return address, that is, address of the next instruction,
+    // so offset it by 1 byte.
+    bool is_last = (si == trace.Size() - 1);
+    ReportStack *ent = SymbolizeCode(trace.Get(si) - !is_last);
+    CHECK_NE(ent, 0);
+    ReportStack *last = ent;
+    while (last->next) {
+      last->pc += !is_last;
+      last = last->next;
+    }
+    last->pc += !is_last;
+    last->next = stack;
+    stack = ent;
+  }
+  StackStripMain(stack);
+  return stack;
+}
+
+ScopedReport::ScopedReport(ReportType typ) {
+  ctx_ = CTX();
+  void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
+  rep_ = new(mem) ReportDesc;
+  rep_->typ = typ;
+  ctx_->report_mtx.Lock();
+}
+
+ScopedReport::~ScopedReport() {
+  ctx_->report_mtx.Unlock();
+  rep_->~ReportDesc();
+  internal_free(rep_);
+}
+
+void ScopedReport::AddStack(const StackTrace *stack) {
+  ReportStack **rs = rep_->stacks.PushBack();
+  *rs = SymbolizeStack(*stack);
+}
+
+void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
+                                   const StackTrace *stack) {
+  void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
+  ReportMop *mop = new(mem) ReportMop;
+  rep_->mops.PushBack(mop);
+  mop->tid = s.tid();
+  mop->addr = addr + s.addr0();
+  mop->size = s.size();
+  mop->write = s.is_write();
+  mop->nmutex = 0;
+  mop->stack = SymbolizeStack(*stack);
+}
+
+void ScopedReport::AddThread(const ThreadContext *tctx) {
+  for (uptr i = 0; i < rep_->threads.Size(); i++) {
+    if (rep_->threads[i]->id == tctx->tid)
+      return;
+  }
+  void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread));
+  ReportThread *rt = new(mem) ReportThread();
+  rep_->threads.PushBack(rt);
+  rt->id = tctx->tid;
+  rt->pid = tctx->os_id;
+  rt->running = (tctx->status == ThreadStatusRunning);
+  rt->stack = SymbolizeStack(tctx->creation_stack);
+}
+
+#ifndef TSAN_GO
+static ThreadContext *FindThread(int unique_id) {
+  CTX()->thread_mtx.CheckLocked();
+  for (unsigned i = 0; i < kMaxTid; i++) {
+    ThreadContext *tctx = CTX()->threads[i];
+    if (tctx && tctx->unique_id == unique_id) {
+      return tctx;
+    }
+  }
+  return 0;
+}
+#endif
+
+void ScopedReport::AddMutex(const SyncVar *s) {
+  void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
+  ReportMutex *rm = new(mem) ReportMutex();
+  rep_->mutexes.PushBack(rm);
+  rm->id = 42;
+  rm->stack = SymbolizeStack(s->creation_stack);
+}
+
+void ScopedReport::AddLocation(uptr addr, uptr size) {
+  if (addr == 0)
+    return;
+#ifndef TSAN_GO
+  if (allocator()->PointerIsMine((void*)addr)) {
+    MBlock *b = user_mblock(0, (void*)addr);
+    ThreadContext *tctx = FindThread(b->alloc_tid);
+    void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
+    ReportLocation *loc = new(mem) ReportLocation();
+    rep_->locs.PushBack(loc);
+    loc->type = ReportLocationHeap;
+    loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr);
+    loc->size = b->size;
+    loc->tid = tctx ? tctx->tid : b->alloc_tid;
+    loc->name = 0;
+    loc->file = 0;
+    loc->line = 0;
+    loc->stack = 0;
+    uptr ssz = 0;
+    const uptr *stack = StackDepotGet(b->alloc_stack_id, &ssz);
+    if (stack) {
+      StackTrace trace;
+      trace.Init(stack, ssz);
+      loc->stack = SymbolizeStack(trace);
+    }
+    if (tctx)
+      AddThread(tctx);
+    return;
+  }
+#endif
+  ReportStack *symb = SymbolizeData(addr);
+  if (symb) {
+    void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
+    ReportLocation *loc = new(mem) ReportLocation();
+    rep_->locs.PushBack(loc);
+    loc->type = ReportLocationGlobal;
+    loc->addr = addr;
+    loc->size = size;
+    loc->tid = 0;
+    loc->name = symb->func;
+    loc->file = symb->file;
+    loc->line = symb->line;
+    loc->stack = 0;
+    internal_free(symb);
+    return;
+  }
+}
+
+#ifndef TSAN_GO
+void ScopedReport::AddSleep(u32 stack_id) {
+  uptr ssz = 0;
+  const uptr *stack = StackDepotGet(stack_id, &ssz);
+  if (stack) {
+    StackTrace trace;
+    trace.Init(stack, ssz);
+    rep_->sleep = SymbolizeStack(trace);
+  }
+}
+#endif
+
+const ReportDesc *ScopedReport::GetReport() const {
+  return rep_;
+}
+
+void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
+  ThreadContext *tctx = CTX()->threads[tid];
+  if (tctx == 0)
+    return;
+  Trace* trace = 0;
+  if (tctx->status == ThreadStatusRunning) {
+    CHECK(tctx->thr);
+    trace = &tctx->thr->trace;
+  } else if (tctx->status == ThreadStatusFinished
+      || tctx->status == ThreadStatusDead) {
+    if (tctx->dead_info == 0)
+      return;
+    trace = &tctx->dead_info->trace;
+  } else {
+    return;
+  }
+  Lock l(&trace->mtx);
+  const int partidx = (epoch / (kTraceSize / kTraceParts)) % kTraceParts;
+  TraceHeader* hdr = &trace->headers[partidx];
+  if (epoch < hdr->epoch0)
+    return;
+  const u64 eend = epoch % kTraceSize;
+  const u64 ebegin = eend / kTracePartSize * kTracePartSize;
+  DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
+          tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
+  InternalScopedBuffer<uptr> stack(1024);  // FIXME: de-hardcode 1024
+  for (uptr i = 0; i < hdr->stack0.Size(); i++) {
+    stack[i] = hdr->stack0.Get(i);
+    DPrintf2("  #%02lu: pc=%zx\n", i, stack[i]);
+  }
+  uptr pos = hdr->stack0.Size();
+  for (uptr i = ebegin; i <= eend; i++) {
+    Event ev = trace->events[i];
+    EventType typ = (EventType)(ev >> 61);
+    uptr pc = (uptr)(ev & 0xffffffffffffull);
+    DPrintf2("  %zu typ=%d pc=%zx\n", i, typ, pc);
+    if (typ == EventTypeMop) {
+      stack[pos] = pc;
+    } else if (typ == EventTypeFuncEnter) {
+      stack[pos++] = pc;
+    } else if (typ == EventTypeFuncExit) {
+      if (pos > 0)
+        pos--;
+    }
+    for (uptr j = 0; j <= pos; j++)
+      DPrintf2("      #%zu: %zx\n", j, stack[j]);
+  }
+  if (pos == 0 && stack[0] == 0)
+    return;
+  pos++;
+  stk->Init(stack.data(), pos);
+}
+
+static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
+    uptr addr_min, uptr addr_max) {
+  Context *ctx = CTX();
+  bool equal_stack = false;
+  RacyStacks hash = {};
+  if (flags()->suppress_equal_stacks) {
+    hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
+    hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
+    for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
+      if (hash == ctx->racy_stacks[i]) {
+        DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n");
+        equal_stack = true;
+        break;
+      }
+    }
+  }
+  bool equal_address = false;
+  RacyAddress ra0 = {addr_min, addr_max};
+  if (flags()->suppress_equal_addresses) {
+    for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
+      RacyAddress ra2 = ctx->racy_addresses[i];
+      uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
+      uptr minend = min(ra0.addr_max, ra2.addr_max);
+      if (maxbeg < minend) {
+        DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n");
+        equal_address = true;
+        break;
+      }
+    }
+  }
+  if (equal_stack || equal_address) {
+    if (!equal_stack)
+      ctx->racy_stacks.PushBack(hash);
+    if (!equal_address)
+      ctx->racy_addresses.PushBack(ra0);
+    return true;
+  }
+  return false;
+}
+
+static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
+    uptr addr_min, uptr addr_max) {
+  Context *ctx = CTX();
+  if (flags()->suppress_equal_stacks) {
+    RacyStacks hash;
+    hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
+    hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
+    ctx->racy_stacks.PushBack(hash);
+  }
+  if (flags()->suppress_equal_addresses) {
+    RacyAddress ra0 = {addr_min, addr_max};
+    ctx->racy_addresses.PushBack(ra0);
+  }
+}
+
+bool OutputReport(Context *ctx,
+                  const ScopedReport &srep,
+                  const ReportStack *suppress_stack) {
+  const ReportDesc *rep = srep.GetReport();
+  const uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack);
+  if (suppress_pc != 0) {
+    FiredSuppression supp = {srep.GetReport()->typ, suppress_pc};
+    ctx->fired_suppressions.PushBack(supp);
+  }
+  if (OnReport(rep, suppress_pc != 0))
+    return false;
+  PrintReport(rep);
+  CTX()->nreported++;
+  return true;
+}
+
+bool IsFiredSuppression(Context *ctx,
+                        const ScopedReport &srep,
+                        const StackTrace &trace) {
+  for (uptr k = 0; k < ctx->fired_suppressions.Size(); k++) {
+    if (ctx->fired_suppressions[k].type != srep.GetReport()->typ)
+      continue;
+    for (uptr j = 0; j < trace.Size(); j++) {
+      if (trace.Get(j) == ctx->fired_suppressions[k].pc)
+        return true;
+    }
+  }
+  return false;
+}
+
+void ReportRace(ThreadState *thr) {
+  ScopedInRtl in_rtl;
+
+  bool freed = false;
+  {
+    Shadow s(thr->racy_state[1]);
+    freed = s.GetFreedAndReset();
+    thr->racy_state[1] = s.raw();
+  }
+
+  uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr);
+  uptr addr_min = 0;
+  uptr addr_max = 0;
+  {
+    uptr a0 = addr + Shadow(thr->racy_state[0]).addr0();
+    uptr a1 = addr + Shadow(thr->racy_state[1]).addr0();
+    uptr e0 = a0 + Shadow(thr->racy_state[0]).size();
+    uptr e1 = a1 + Shadow(thr->racy_state[1]).size();
+    addr_min = min(a0, a1);
+    addr_max = max(e0, e1);
+    if (IsExpectedReport(addr_min, addr_max - addr_min))
+      return;
+  }
+
+  Context *ctx = CTX();
+  Lock l0(&ctx->thread_mtx);
+
+  ScopedReport rep(freed ? ReportTypeUseAfterFree : ReportTypeRace);
+  const uptr kMop = 2;
+  StackTrace traces[kMop];
+  const uptr toppc = thr->trace.events[thr->fast_state.epoch() % kTraceSize]
+      & ((1ull << 61) - 1);
+  traces[0].ObtainCurrent(thr, toppc);
+  if (IsFiredSuppression(ctx, rep, traces[0]))
+    return;
+  Shadow s2(thr->racy_state[1]);
+  RestoreStack(s2.tid(), s2.epoch(), &traces[1]);
+
+  if (HandleRacyStacks(thr, traces, addr_min, addr_max))
+    return;
+
+  for (uptr i = 0; i < kMop; i++) {
+    Shadow s(thr->racy_state[i]);
+    rep.AddMemoryAccess(addr, s, &traces[i]);
+  }
+
+  for (uptr i = 0; i < kMop; i++) {
+    FastState s(thr->racy_state[i]);
+    ThreadContext *tctx = ctx->threads[s.tid()];
+    if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1)
+      continue;
+    rep.AddThread(tctx);
+  }
+
+  rep.AddLocation(addr_min, addr_max - addr_min);
+
+#ifndef TSAN_GO
+  {  // NOLINT
+    Shadow s(thr->racy_state[1]);
+    if (s.epoch() <= thr->last_sleep_clock.get(s.tid()))
+      rep.AddSleep(thr->last_sleep_stack_id);
+  }
+#endif
+
+  if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack))
+    return;
+
+  AddRacyStacks(thr, traces, addr_min, addr_max);
+}
+
+void PrintCurrentStack(ThreadState *thr, uptr pc) {
+  StackTrace trace;
+  trace.ObtainCurrent(thr, pc);
+  PrintStack(SymbolizeStack(trace));
+}
+
+}  // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_rtl_thread.cc b/libsanitizer/tsan/tsan_rtl_thread.cc
new file mode 100644 (file)
index 0000000..0ae658c
--- /dev/null
@@ -0,0 +1,397 @@
+//===-- tsan_rtl_thread.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_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_platform.h"
+#include "tsan_report.h"
+#include "tsan_sync.h"
+
+namespace __tsan {
+
+#ifndef TSAN_GO
+const int kThreadQuarantineSize = 16;
+#else
+const int kThreadQuarantineSize = 64;
+#endif
+
+static void MaybeReportThreadLeak(ThreadContext *tctx) {
+  if (tctx->detached)
+    return;
+  if (tctx->status != ThreadStatusCreated
+      && tctx->status != ThreadStatusRunning
+      && tctx->status != ThreadStatusFinished)
+    return;
+  ScopedReport rep(ReportTypeThreadLeak);
+  rep.AddThread(tctx);
+  OutputReport(CTX(), rep);
+}
+
+void ThreadFinalize(ThreadState *thr) {
+  CHECK_GT(thr->in_rtl, 0);
+  if (!flags()->report_thread_leaks)
+    return;
+  Context *ctx = CTX();
+  Lock l(&ctx->thread_mtx);
+  for (unsigned i = 0; i < kMaxTid; i++) {
+    ThreadContext *tctx = ctx->threads[i];
+    if (tctx == 0)
+      continue;
+    MaybeReportThreadLeak(tctx);
+  }
+}
+
+static void ThreadDead(ThreadState *thr, ThreadContext *tctx) {
+  Context *ctx = CTX();
+  CHECK_GT(thr->in_rtl, 0);
+  CHECK(tctx->status == ThreadStatusRunning
+      || tctx->status == ThreadStatusFinished);
+  DPrintf("#%d: ThreadDead uid=%zu\n", thr->tid, tctx->user_id);
+  tctx->status = ThreadStatusDead;
+  tctx->user_id = 0;
+  tctx->sync.Reset();
+
+  // Put to dead list.
+  tctx->dead_next = 0;
+  if (ctx->dead_list_size == 0)
+    ctx->dead_list_head = tctx;
+  else
+    ctx->dead_list_tail->dead_next = tctx;
+  ctx->dead_list_tail = tctx;
+  ctx->dead_list_size++;
+}
+
+int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
+  CHECK_GT(thr->in_rtl, 0);
+  Context *ctx = CTX();
+  Lock l(&ctx->thread_mtx);
+  StatInc(thr, StatThreadCreate);
+  int tid = -1;
+  ThreadContext *tctx = 0;
+  if (ctx->dead_list_size > kThreadQuarantineSize
+      || ctx->thread_seq >= kMaxTid) {
+    if (ctx->dead_list_size == 0) {
+      TsanPrintf("ThreadSanitizer: %d thread limit exceeded. Dying.\n",
+                 kMaxTid);
+      Die();
+    }
+    StatInc(thr, StatThreadReuse);
+    tctx = ctx->dead_list_head;
+    ctx->dead_list_head = tctx->dead_next;
+    ctx->dead_list_size--;
+    if (ctx->dead_list_size == 0) {
+      CHECK_EQ(tctx->dead_next, 0);
+      ctx->dead_list_head = 0;
+    }
+    CHECK_EQ(tctx->status, ThreadStatusDead);
+    tctx->status = ThreadStatusInvalid;
+    tctx->reuse_count++;
+    tctx->sync.Reset();
+    tid = tctx->tid;
+    DestroyAndFree(tctx->dead_info);
+  } else {
+    StatInc(thr, StatThreadMaxTid);
+    tid = ctx->thread_seq++;
+    void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext));
+    tctx = new(mem) ThreadContext(tid);
+    ctx->threads[tid] = tctx;
+  }
+  CHECK_NE(tctx, 0);
+  CHECK_GE(tid, 0);
+  CHECK_LT(tid, kMaxTid);
+  DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid);
+  CHECK_EQ(tctx->status, ThreadStatusInvalid);
+  ctx->alive_threads++;
+  if (ctx->max_alive_threads < ctx->alive_threads) {
+    ctx->max_alive_threads++;
+    CHECK_EQ(ctx->max_alive_threads, ctx->alive_threads);
+    StatInc(thr, StatThreadMaxAlive);
+  }
+  tctx->status = ThreadStatusCreated;
+  tctx->thr = 0;
+  tctx->user_id = uid;
+  tctx->unique_id = ctx->unique_thread_seq++;
+  tctx->detached = detached;
+  if (tid) {
+    thr->fast_state.IncrementEpoch();
+    // Can't increment epoch w/o writing to the trace as well.
+    TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeMop, 0);
+    thr->clock.set(thr->tid, thr->fast_state.epoch());
+    thr->fast_synch_epoch = thr->fast_state.epoch();
+    thr->clock.release(&tctx->sync);
+    StatInc(thr, StatSyncRelease);
+
+    tctx->creation_stack.ObtainCurrent(thr, pc);
+  }
+  return tid;
+}
+
+void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
+  CHECK_GT(thr->in_rtl, 0);
+  uptr stk_addr = 0;
+  uptr stk_size = 0;
+  uptr tls_addr = 0;
+  uptr tls_size = 0;
+  GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
+
+  if (tid) {
+    if (stk_addr && stk_size) {
+      MemoryResetRange(thr, /*pc=*/ 1, stk_addr, stk_size);
+    }
+
+    if (tls_addr && tls_size) {
+      // Check that the thr object is in tls;
+      const uptr thr_beg = (uptr)thr;
+      const uptr thr_end = (uptr)thr + sizeof(*thr);
+      CHECK_GE(thr_beg, tls_addr);
+      CHECK_LE(thr_beg, tls_addr + tls_size);
+      CHECK_GE(thr_end, tls_addr);
+      CHECK_LE(thr_end, tls_addr + tls_size);
+      // Since the thr object is huge, skip it.
+      MemoryResetRange(thr, /*pc=*/ 2, tls_addr, thr_beg - tls_addr);
+      MemoryResetRange(thr, /*pc=*/ 2, thr_end, tls_addr + tls_size - thr_end);
+    }
+  }
+
+  Lock l(&CTX()->thread_mtx);
+  ThreadContext *tctx = CTX()->threads[tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(tctx->status, ThreadStatusCreated);
+  tctx->status = ThreadStatusRunning;
+  tctx->os_id = os_id;
+  tctx->epoch0 = tctx->epoch1 + 1;
+  tctx->epoch1 = (u64)-1;
+  new(thr) ThreadState(CTX(), tid, tctx->unique_id,
+      tctx->epoch0, stk_addr, stk_size,
+      tls_addr, tls_size);
+#ifdef TSAN_GO
+  // Setup dynamic shadow stack.
+  const int kInitStackSize = 8;
+  thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack,
+      kInitStackSize * sizeof(uptr));
+  thr->shadow_stack_pos = thr->shadow_stack;
+  thr->shadow_stack_end = thr->shadow_stack + kInitStackSize;
+#endif
+  tctx->thr = thr;
+  thr->fast_synch_epoch = tctx->epoch0;
+  thr->clock.set(tid, tctx->epoch0);
+  thr->clock.acquire(&tctx->sync);
+  StatInc(thr, StatSyncAcquire);
+  DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
+          "tls_addr=%zx tls_size=%zx\n",
+          tid, (uptr)tctx->epoch0, stk_addr, stk_size, tls_addr, tls_size);
+  thr->is_alive = true;
+}
+
+void ThreadFinish(ThreadState *thr) {
+  CHECK_GT(thr->in_rtl, 0);
+  StatInc(thr, StatThreadFinish);
+  // FIXME: Treat it as write.
+  if (thr->stk_addr && thr->stk_size)
+    MemoryResetRange(thr, /*pc=*/ 3, thr->stk_addr, thr->stk_size);
+  if (thr->tls_addr && thr->tls_size) {
+    const uptr thr_beg = (uptr)thr;
+    const uptr thr_end = (uptr)thr + sizeof(*thr);
+    // Since the thr object is huge, skip it.
+    MemoryResetRange(thr, /*pc=*/ 4, thr->tls_addr, thr_beg - thr->tls_addr);
+    MemoryResetRange(thr, /*pc=*/ 5,
+        thr_end, thr->tls_addr + thr->tls_size - thr_end);
+  }
+  thr->is_alive = false;
+  Context *ctx = CTX();
+  Lock l(&ctx->thread_mtx);
+  ThreadContext *tctx = ctx->threads[thr->tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(tctx->status, ThreadStatusRunning);
+  CHECK_GT(ctx->alive_threads, 0);
+  ctx->alive_threads--;
+  if (tctx->detached) {
+    ThreadDead(thr, tctx);
+  } else {
+    thr->fast_state.IncrementEpoch();
+    // Can't increment epoch w/o writing to the trace as well.
+    TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeMop, 0);
+    thr->clock.set(thr->tid, thr->fast_state.epoch());
+    thr->fast_synch_epoch = thr->fast_state.epoch();
+    thr->clock.release(&tctx->sync);
+    StatInc(thr, StatSyncRelease);
+    tctx->status = ThreadStatusFinished;
+  }
+
+  // Save from info about the thread.
+  tctx->dead_info = new(internal_alloc(MBlockDeadInfo, sizeof(ThreadDeadInfo)))
+      ThreadDeadInfo();
+  internal_memcpy(&tctx->dead_info->trace.events[0],
+      &thr->trace.events[0], sizeof(thr->trace.events));
+  for (int i = 0; i < kTraceParts; i++) {
+    tctx->dead_info->trace.headers[i].stack0.CopyFrom(
+        thr->trace.headers[i].stack0);
+  }
+  tctx->epoch1 = thr->fast_state.epoch();
+
+#ifndef TSAN_GO
+  AlloctorThreadFinish(thr);
+#endif
+  thr->~ThreadState();
+  StatAggregate(ctx->stat, thr->stat);
+  tctx->thr = 0;
+}
+
+int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
+  CHECK_GT(thr->in_rtl, 0);
+  Context *ctx = CTX();
+  Lock l(&ctx->thread_mtx);
+  int res = -1;
+  for (unsigned tid = 0; tid < kMaxTid; tid++) {
+    ThreadContext *tctx = ctx->threads[tid];
+    if (tctx != 0 && tctx->user_id == uid
+        && tctx->status != ThreadStatusInvalid) {
+      tctx->user_id = 0;
+      res = tid;
+      break;
+    }
+  }
+  DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
+  return res;
+}
+
+void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
+  CHECK_GT(thr->in_rtl, 0);
+  CHECK_GT(tid, 0);
+  CHECK_LT(tid, kMaxTid);
+  DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid);
+  Context *ctx = CTX();
+  Lock l(&ctx->thread_mtx);
+  ThreadContext *tctx = ctx->threads[tid];
+  if (tctx->status == ThreadStatusInvalid) {
+    TsanPrintf("ThreadSanitizer: join of non-existent thread\n");
+    return;
+  }
+  CHECK_EQ(tctx->detached, false);
+  CHECK_EQ(tctx->status, ThreadStatusFinished);
+  thr->clock.acquire(&tctx->sync);
+  StatInc(thr, StatSyncAcquire);
+  ThreadDead(thr, tctx);
+}
+
+void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
+  CHECK_GT(thr->in_rtl, 0);
+  CHECK_GT(tid, 0);
+  CHECK_LT(tid, kMaxTid);
+  Context *ctx = CTX();
+  Lock l(&ctx->thread_mtx);
+  ThreadContext *tctx = ctx->threads[tid];
+  if (tctx->status == ThreadStatusInvalid) {
+    TsanPrintf("ThreadSanitizer: detach of non-existent thread\n");
+    return;
+  }
+  if (tctx->status == ThreadStatusFinished) {
+    ThreadDead(thr, tctx);
+  } else {
+    tctx->detached = true;
+  }
+}
+
+void ThreadFinalizerGoroutine(ThreadState *thr) {
+  thr->clock.Disable(thr->tid);
+}
+
+void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
+                       uptr size, bool is_write) {
+  if (size == 0)
+    return;
+
+  u64 *shadow_mem = (u64*)MemToShadow(addr);
+  DPrintf2("#%d: MemoryAccessRange: @%p %p size=%d is_write=%d\n",
+      thr->tid, (void*)pc, (void*)addr,
+      (int)size, is_write);
+
+#if TSAN_DEBUG
+  if (!IsAppMem(addr)) {
+    TsanPrintf("Access to non app mem %zx\n", addr);
+    DCHECK(IsAppMem(addr));
+  }
+  if (!IsAppMem(addr + size - 1)) {
+    TsanPrintf("Access to non app mem %zx\n", addr + size - 1);
+    DCHECK(IsAppMem(addr + size - 1));
+  }
+  if (!IsShadowMem((uptr)shadow_mem)) {
+    TsanPrintf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+    DCHECK(IsShadowMem((uptr)shadow_mem));
+  }
+  if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) {
+    TsanPrintf("Bad shadow addr %p (%zx)\n",
+               shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1);
+    DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1)));
+  }
+#endif
+
+  StatInc(thr, StatMopRange);
+
+  FastState fast_state = thr->fast_state;
+  if (fast_state.GetIgnoreBit())
+    return;
+
+  fast_state.IncrementEpoch();
+  thr->fast_state = fast_state;
+  TraceAddEvent(thr, fast_state.epoch(), EventTypeMop, pc);
+
+  bool unaligned = (addr % kShadowCell) != 0;
+
+  // Handle unaligned beginning, if any.
+  for (; addr % kShadowCell && size; addr++, size--) {
+    int const kAccessSizeLog = 0;
+    Shadow cur(fast_state);
+    cur.SetWrite(is_write);
+    cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
+    MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+        shadow_mem, cur);
+  }
+  if (unaligned)
+    shadow_mem += kShadowCnt;
+  // Handle middle part, if any.
+  for (; size >= kShadowCell; addr += kShadowCell, size -= kShadowCell) {
+    int const kAccessSizeLog = 3;
+    Shadow cur(fast_state);
+    cur.SetWrite(is_write);
+    cur.SetAddr0AndSizeLog(0, kAccessSizeLog);
+    MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+        shadow_mem, cur);
+    shadow_mem += kShadowCnt;
+  }
+  // Handle ending, if any.
+  for (; size; addr++, size--) {
+    int const kAccessSizeLog = 0;
+    Shadow cur(fast_state);
+    cur.SetWrite(is_write);
+    cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
+    MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+        shadow_mem, cur);
+  }
+}
+
+void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr) {
+  MemoryAccess(thr, pc, addr, 0, 0);
+}
+
+void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr) {
+  MemoryAccess(thr, pc, addr, 0, 1);
+}
+
+void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr) {
+  MemoryAccess(thr, pc, addr, 3, 0);
+}
+
+void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr) {
+  MemoryAccess(thr, pc, addr, 3, 1);
+}
+}  // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_stat.cc b/libsanitizer/tsan/tsan_stat.cc
new file mode 100644 (file)
index 0000000..fcbc631
--- /dev/null
@@ -0,0 +1,247 @@
+//===-- tsan_stat.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_stat.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+void StatAggregate(u64 *dst, u64 *src) {
+  if (!kCollectStats)
+    return;
+  for (int i = 0; i < StatCnt; i++)
+    dst[i] += src[i];
+}
+
+void StatOutput(u64 *stat) {
+  if (!kCollectStats)
+    return;
+
+  stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero];
+
+  static const char *name[StatCnt] = {};
+  name[StatMop]                          = "Memory accesses                   ";
+  name[StatMopRead]                      = "  Including reads                 ";
+  name[StatMopWrite]                     = "            writes                ";
+  name[StatMop1]                         = "  Including size 1                ";
+  name[StatMop2]                         = "            size 2                ";
+  name[StatMop4]                         = "            size 4                ";
+  name[StatMop8]                         = "            size 8                ";
+  name[StatMopSame]                      = "  Including same                  ";
+  name[StatMopRange]                     = "  Including range                 ";
+  name[StatShadowProcessed]              = "Shadow processed                  ";
+  name[StatShadowZero]                   = "  Including empty                 ";
+  name[StatShadowNonZero]                = "  Including non empty             ";
+  name[StatShadowSameSize]               = "  Including same size             ";
+  name[StatShadowIntersect]              = "            intersect             ";
+  name[StatShadowNotIntersect]           = "            not intersect         ";
+  name[StatShadowSameThread]             = "  Including same thread           ";
+  name[StatShadowAnotherThread]          = "            another thread        ";
+  name[StatShadowReplace]                = "  Including evicted               ";
+
+  name[StatFuncEnter]                    = "Function entries                  ";
+  name[StatFuncExit]                     = "Function exits                    ";
+  name[StatEvents]                       = "Events collected                  ";
+
+  name[StatThreadCreate]                 = "Total threads created             ";
+  name[StatThreadFinish]                 = "  threads finished                ";
+  name[StatThreadReuse]                  = "  threads reused                  ";
+  name[StatThreadMaxTid]                 = "  max tid                         ";
+  name[StatThreadMaxAlive]               = "  max alive threads               ";
+
+  name[StatMutexCreate]                  = "Mutexes created                   ";
+  name[StatMutexDestroy]                 = "  destroyed                       ";
+  name[StatMutexLock]                    = "  lock                            ";
+  name[StatMutexUnlock]                  = "  unlock                          ";
+  name[StatMutexRecLock]                 = "  recursive lock                  ";
+  name[StatMutexRecUnlock]               = "  recursive unlock                ";
+  name[StatMutexReadLock]                = "  read lock                       ";
+  name[StatMutexReadUnlock]              = "  read unlock                     ";
+
+  name[StatSyncCreated]                  = "Sync objects created              ";
+  name[StatSyncDestroyed]                = "             destroyed            ";
+  name[StatSyncAcquire]                  = "             acquired             ";
+  name[StatSyncRelease]                  = "             released             ";
+
+  name[StatAtomic]                       = "Atomic operations                 ";
+  name[StatAtomicLoad]                   = "  Including load                  ";
+  name[StatAtomicStore]                  = "            store                 ";
+  name[StatAtomicExchange]               = "            exchange              ";
+  name[StatAtomicFetchAdd]               = "            fetch_add             ";
+  name[StatAtomicCAS]                    = "            compare_exchange      ";
+  name[StatAtomicFence]                  = "            fence                 ";
+  name[StatAtomicRelaxed]                = "  Including relaxed               ";
+  name[StatAtomicConsume]                = "            consume               ";
+  name[StatAtomicAcquire]                = "            acquire               ";
+  name[StatAtomicRelease]                = "            release               ";
+  name[StatAtomicAcq_Rel]                = "            acq_rel               ";
+  name[StatAtomicSeq_Cst]                = "            seq_cst               ";
+  name[StatAtomic1]                      = "  Including size 1                ";
+  name[StatAtomic2]                      = "            size 2                ";
+  name[StatAtomic4]                      = "            size 4                ";
+  name[StatAtomic8]                      = "            size 8                ";
+
+  name[StatInterceptor]                  = "Interceptors                      ";
+  name[StatInt_longjmp]                  = "  longjmp                         ";
+  name[StatInt_siglongjmp]               = "  siglongjmp                      ";
+  name[StatInt_malloc]                   = "  malloc                          ";
+  name[StatInt_calloc]                   = "  calloc                          ";
+  name[StatInt_realloc]                  = "  realloc                         ";
+  name[StatInt_free]                     = "  free                            ";
+  name[StatInt_cfree]                    = "  cfree                           ";
+  name[StatInt_mmap]                     = "  mmap                            ";
+  name[StatInt_mmap64]                   = "  mmap64                          ";
+  name[StatInt_munmap]                   = "  munmap                          ";
+  name[StatInt_memalign]                 = "  memalign                        ";
+  name[StatInt_valloc]                   = "  valloc                          ";
+  name[StatInt_pvalloc]                  = "  pvalloc                         ";
+  name[StatInt_posix_memalign]           = "  posix_memalign                  ";
+  name[StatInt__Znwm]                    = "  _Znwm                           ";
+  name[StatInt__ZnwmRKSt9nothrow_t]      = "  _ZnwmRKSt9nothrow_t             ";
+  name[StatInt__Znam]                    = "  _Znam                           ";
+  name[StatInt__ZnamRKSt9nothrow_t]      = "  _ZnamRKSt9nothrow_t             ";
+  name[StatInt__ZdlPv]                   = "  _ZdlPv                          ";
+  name[StatInt__ZdlPvRKSt9nothrow_t]     = "  _ZdlPvRKSt9nothrow_t            ";
+  name[StatInt__ZdaPv]                   = "  _ZdaPv                          ";
+  name[StatInt__ZdaPvRKSt9nothrow_t]     = "  _ZdaPvRKSt9nothrow_t            ";
+  name[StatInt_strlen]                   = "  strlen                          ";
+  name[StatInt_memset]                   = "  memset                          ";
+  name[StatInt_memcpy]                   = "  memcpy                          ";
+  name[StatInt_strcmp]                   = "  strcmp                          ";
+  name[StatInt_memchr]                   = "  memchr                          ";
+  name[StatInt_memrchr]                  = "  memrchr                         ";
+  name[StatInt_memmove]                  = "  memmove                         ";
+  name[StatInt_memcmp]                   = "  memcmp                          ";
+  name[StatInt_strchr]                   = "  strchr                          ";
+  name[StatInt_strchrnul]                = "  strchrnul                       ";
+  name[StatInt_strrchr]                  = "  strrchr                         ";
+  name[StatInt_strncmp]                  = "  strncmp                         ";
+  name[StatInt_strcpy]                   = "  strcpy                          ";
+  name[StatInt_strncpy]                  = "  strncpy                         ";
+  name[StatInt_strstr]                   = "  strstr                          ";
+  name[StatInt_atexit]                   = "  atexit                          ";
+  name[StatInt___cxa_guard_acquire]      = "  __cxa_guard_acquire             ";
+  name[StatInt___cxa_guard_release]      = "  __cxa_guard_release             ";
+  name[StatInt_pthread_create]           = "  pthread_create                  ";
+  name[StatInt_pthread_join]             = "  pthread_join                    ";
+  name[StatInt_pthread_detach]           = "  pthread_detach                  ";
+  name[StatInt_pthread_mutex_init]       = "  pthread_mutex_init              ";
+  name[StatInt_pthread_mutex_destroy]    = "  pthread_mutex_destroy           ";
+  name[StatInt_pthread_mutex_lock]       = "  pthread_mutex_lock              ";
+  name[StatInt_pthread_mutex_trylock]    = "  pthread_mutex_trylock           ";
+  name[StatInt_pthread_mutex_timedlock]  = "  pthread_mutex_timedlock         ";
+  name[StatInt_pthread_mutex_unlock]     = "  pthread_mutex_unlock            ";
+  name[StatInt_pthread_spin_init]        = "  pthread_spin_init               ";
+  name[StatInt_pthread_spin_destroy]     = "  pthread_spin_destroy            ";
+  name[StatInt_pthread_spin_lock]        = "  pthread_spin_lock               ";
+  name[StatInt_pthread_spin_trylock]     = "  pthread_spin_trylock            ";
+  name[StatInt_pthread_spin_unlock]      = "  pthread_spin_unlock             ";
+  name[StatInt_pthread_rwlock_init]      = "  pthread_rwlock_init             ";
+  name[StatInt_pthread_rwlock_destroy]   = "  pthread_rwlock_destroy          ";
+  name[StatInt_pthread_rwlock_rdlock]    = "  pthread_rwlock_rdlock           ";
+  name[StatInt_pthread_rwlock_tryrdlock] = "  pthread_rwlock_tryrdlock        ";
+  name[StatInt_pthread_rwlock_timedrdlock]
+                                         = "  pthread_rwlock_timedrdlock      ";
+  name[StatInt_pthread_rwlock_wrlock]    = "  pthread_rwlock_wrlock           ";
+  name[StatInt_pthread_rwlock_trywrlock] = "  pthread_rwlock_trywrlock        ";
+  name[StatInt_pthread_rwlock_timedwrlock]
+                                         = "  pthread_rwlock_timedwrlock      ";
+  name[StatInt_pthread_rwlock_unlock]    = "  pthread_rwlock_unlock           ";
+  name[StatInt_pthread_cond_init]        = "  pthread_cond_init               ";
+  name[StatInt_pthread_cond_destroy]     = "  pthread_cond_destroy            ";
+  name[StatInt_pthread_cond_signal]      = "  pthread_cond_signal             ";
+  name[StatInt_pthread_cond_broadcast]   = "  pthread_cond_broadcast          ";
+  name[StatInt_pthread_cond_wait]        = "  pthread_cond_wait               ";
+  name[StatInt_pthread_cond_timedwait]   = "  pthread_cond_timedwait          ";
+  name[StatInt_pthread_barrier_init]     = "  pthread_barrier_init            ";
+  name[StatInt_pthread_barrier_destroy]  = "  pthread_barrier_destroy         ";
+  name[StatInt_pthread_barrier_wait]     = "  pthread_barrier_wait            ";
+  name[StatInt_pthread_once]             = "  pthread_once                    ";
+  name[StatInt_sem_init]                 = "  sem_init                        ";
+  name[StatInt_sem_destroy]              = "  sem_destroy                     ";
+  name[StatInt_sem_wait]                 = "  sem_wait                        ";
+  name[StatInt_sem_trywait]              = "  sem_trywait                     ";
+  name[StatInt_sem_timedwait]            = "  sem_timedwait                   ";
+  name[StatInt_sem_post]                 = "  sem_post                        ";
+  name[StatInt_sem_getvalue]             = "  sem_getvalue                    ";
+  name[StatInt_read]                     = "  read                            ";
+  name[StatInt_pread]                    = "  pread                           ";
+  name[StatInt_pread64]                  = "  pread64                         ";
+  name[StatInt_readv]                    = "  readv                           ";
+  name[StatInt_preadv64]                 = "  preadv64                        ";
+  name[StatInt_write]                    = "  write                           ";
+  name[StatInt_pwrite]                   = "  pwrite                          ";
+  name[StatInt_pwrite64]                 = "  pwrite64                        ";
+  name[StatInt_writev]                   = "  writev                          ";
+  name[StatInt_pwritev64]                = "  pwritev64                       ";
+  name[StatInt_send]                     = "  send                            ";
+  name[StatInt_sendmsg]                  = "  sendmsg                         ";
+  name[StatInt_recv]                     = "  recv                            ";
+  name[StatInt_recvmsg]                  = "  recvmsg                         ";
+  name[StatInt_unlink]                   = "  unlink                          ";
+  name[StatInt_fopen]                    = "  fopen                           ";
+  name[StatInt_fread]                    = "  fread                           ";
+  name[StatInt_fwrite]                   = "  fwrite                          ";
+  name[StatInt_puts]                     = "  puts                            ";
+  name[StatInt_rmdir]                    = "  rmdir                           ";
+  name[StatInt_opendir]                  = "  opendir                         ";
+  name[StatInt_epoll_ctl]                = "  epoll_ctl                       ";
+  name[StatInt_epoll_wait]               = "  epoll_wait                      ";
+  name[StatInt_sigaction]                = "  sigaction                       ";
+
+  name[StatAnnotation]                   = "Dynamic annotations               ";
+  name[StatAnnotateHappensBefore]        = "  HappensBefore                   ";
+  name[StatAnnotateHappensAfter]         = "  HappensAfter                    ";
+  name[StatAnnotateCondVarSignal]        = "  CondVarSignal                   ";
+  name[StatAnnotateCondVarSignalAll]     = "  CondVarSignalAll                ";
+  name[StatAnnotateMutexIsNotPHB]        = "  MutexIsNotPHB                   ";
+  name[StatAnnotateCondVarWait]          = "  CondVarWait                     ";
+  name[StatAnnotateRWLockCreate]         = "  RWLockCreate                    ";
+  name[StatAnnotateRWLockDestroy]        = "  RWLockDestroy                   ";
+  name[StatAnnotateRWLockAcquired]       = "  RWLockAcquired                  ";
+  name[StatAnnotateRWLockReleased]       = "  RWLockReleased                  ";
+  name[StatAnnotateTraceMemory]          = "  TraceMemory                     ";
+  name[StatAnnotateFlushState]           = "  FlushState                      ";
+  name[StatAnnotateNewMemory]            = "  NewMemory                       ";
+  name[StatAnnotateNoOp]                 = "  NoOp                            ";
+  name[StatAnnotateFlushExpectedRaces]   = "  FlushExpectedRaces              ";
+  name[StatAnnotateEnableRaceDetection]  = "  EnableRaceDetection             ";
+  name[StatAnnotateMutexIsUsedAsCondVar] = "  MutexIsUsedAsCondVar            ";
+  name[StatAnnotatePCQGet]               = "  PCQGet                          ";
+  name[StatAnnotatePCQPut]               = "  PCQPut                          ";
+  name[StatAnnotatePCQDestroy]           = "  PCQDestroy                      ";
+  name[StatAnnotatePCQCreate]            = "  PCQCreate                       ";
+  name[StatAnnotateExpectRace]           = "  ExpectRace                      ";
+  name[StatAnnotateBenignRaceSized]      = "  BenignRaceSized                 ";
+  name[StatAnnotateBenignRace]           = "  BenignRace                      ";
+  name[StatAnnotateIgnoreReadsBegin]     = "  IgnoreReadsBegin                ";
+  name[StatAnnotateIgnoreReadsEnd]       = "  IgnoreReadsEnd                  ";
+  name[StatAnnotateIgnoreWritesBegin]    = "  IgnoreWritesBegin               ";
+  name[StatAnnotateIgnoreWritesEnd]      = "  IgnoreWritesEnd                 ";
+  name[StatAnnotatePublishMemoryRange]   = "  PublishMemoryRange              ";
+  name[StatAnnotateUnpublishMemoryRange] = "  UnpublishMemoryRange            ";
+  name[StatAnnotateThreadName]           = "  ThreadName                      ";
+
+  name[StatMtxTotal]                     = "Contentionz                       ";
+  name[StatMtxTrace]                     = "  Trace                           ";
+  name[StatMtxThreads]                   = "  Threads                         ";
+  name[StatMtxReport]                    = "  Report                          ";
+  name[StatMtxSyncVar]                   = "  SyncVar                         ";
+  name[StatMtxSyncTab]                   = "  SyncTab                         ";
+  name[StatMtxSlab]                      = "  Slab                            ";
+  name[StatMtxAtExit]                    = "  Atexit                          ";
+  name[StatMtxAnnotations]               = "  Annotations                     ";
+
+  TsanPrintf("Statistics:\n");
+  for (int i = 0; i < StatCnt; i++)
+    TsanPrintf("%s: %zu\n", name[i], (uptr)stat[i]);
+}
+
+}  // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_stat.h b/libsanitizer/tsan/tsan_stat.h
new file mode 100644 (file)
index 0000000..b194343
--- /dev/null
@@ -0,0 +1,257 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_suppressions.cc b/libsanitizer/tsan/tsan_suppressions.cc
new file mode 100644 (file)
index 0000000..9b087ad
--- /dev/null
@@ -0,0 +1,161 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_suppressions.h b/libsanitizer/tsan/tsan_suppressions.h
new file mode 100644 (file)
index 0000000..4761eaa
--- /dev/null
@@ -0,0 +1,41 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc
new file mode 100644 (file)
index 0000000..439b882
--- /dev/null
@@ -0,0 +1,83 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_symbolize.h b/libsanitizer/tsan/tsan_symbolize.h
new file mode 100644 (file)
index 0000000..6ac19ca
--- /dev/null
@@ -0,0 +1,29 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc b/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc
new file mode 100644 (file)
index 0000000..9caf091
--- /dev/null
@@ -0,0 +1,191 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_sync.cc b/libsanitizer/tsan/tsan_sync.cc
new file mode 100644 (file)
index 0000000..3bd1b35
--- /dev/null
@@ -0,0 +1,278 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_sync.h b/libsanitizer/tsan/tsan_sync.h
new file mode 100644 (file)
index 0000000..2912d2c
--- /dev/null
@@ -0,0 +1,106 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_trace.h b/libsanitizer/tsan/tsan_trace.h
new file mode 100644 (file)
index 0000000..69233a6
--- /dev/null
@@ -0,0 +1,73 @@
+//===-- 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
diff --git a/libsanitizer/tsan/tsan_update_shadow_word_inl.h b/libsanitizer/tsan/tsan_update_shadow_word_inl.h
new file mode 100644 (file)
index 0000000..ae4a318
--- /dev/null
@@ -0,0 +1,77 @@
+//===-- 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);
diff --git a/libsanitizer/tsan/tsan_vector.h b/libsanitizer/tsan/tsan_vector.h
new file mode 100644 (file)
index 0000000..d6bb707
--- /dev/null
@@ -0,0 +1,108 @@
+//===-- 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