tsan: New directory.

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

From-SVN: r193737
This commit is contained in:
Wei Mi 2012-11-22 22:03:11 +00:00 committed by Wei Mi
parent 32b4b7f53e
commit cd0be65c26
51 changed files with 9661 additions and 4 deletions

View 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.

View 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

View 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

View File

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

View 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

View File

@ -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=
## ################################################################

View File

@ -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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -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"

View File

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

View File

@ -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) {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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__

View File

@ -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__

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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