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:
parent
32b4b7f53e
commit
cd0be65c26
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
35
libsanitizer/configure
vendored
35
libsanitizer/configure
vendored
@ -604,6 +604,8 @@ ac_subst_vars='am__EXEEXT_FALSE
|
||||
am__EXEEXT_TRUE
|
||||
LTLIBOBJS
|
||||
LIBOBJS
|
||||
MULTISUBDIR32_FALSE
|
||||
MULTISUBDIR32_TRUE
|
||||
enable_static
|
||||
enable_shared
|
||||
CXXCPP
|
||||
@ -10898,7 +10900,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 10901 "configure"
|
||||
#line 10903 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@ -11004,7 +11006,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 11007 "configure"
|
||||
#line 11009 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@ -14266,6 +14268,14 @@ if test "${multilib}" = "yes"; then
|
||||
else
|
||||
multilib_arg=
|
||||
fi
|
||||
if test "x$with_multisubdir" = "x32"; then
|
||||
MULTISUBDIR32_TRUE=
|
||||
MULTISUBDIR32_FALSE='#'
|
||||
else
|
||||
MULTISUBDIR32_TRUE='#'
|
||||
MULTISUBDIR32_FALSE=
|
||||
fi
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile"
|
||||
|
||||
@ -14273,6 +14283,11 @@ ac_config_files="$ac_config_files Makefile"
|
||||
ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile asan/Makefile"
|
||||
|
||||
|
||||
if test "x$with_multisubdir" != "x32"; then
|
||||
ac_config_files="$ac_config_files tsan/Makefile"
|
||||
|
||||
fi
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
# tests run on this system so they can be shared between configure
|
||||
@ -14434,6 +14449,10 @@ if test -z "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then
|
||||
as_fn_error "conditional \"am__fastdepCCAS\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${MULTISUBDIR32_TRUE}" && test -z "${MULTISUBDIR32_FALSE}"; then
|
||||
as_fn_error "conditional \"MULTISUBDIR32\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
|
||||
: ${CONFIG_STATUS=./config.status}
|
||||
ac_write_fail=0
|
||||
@ -15388,6 +15407,7 @@ do
|
||||
"interception/Makefile") CONFIG_FILES="$CONFIG_FILES interception/Makefile" ;;
|
||||
"sanitizer_common/Makefile") CONFIG_FILES="$CONFIG_FILES sanitizer_common/Makefile" ;;
|
||||
"asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
|
||||
"tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
|
||||
|
||||
*) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
|
||||
esac
|
||||
@ -16752,6 +16772,17 @@ _EOF
|
||||
. ${multi_basedir}/config-ml.in
|
||||
{ ml_norecursion=; unset ml_norecursion;}
|
||||
;;
|
||||
"tsan/Makefile":F) cat > vpsed$$ << \_EOF
|
||||
s!`test -f '$<' || echo '$(srcdir)/'`!!
|
||||
_EOF
|
||||
sed -f vpsed$$ $ac_file > tmp$$
|
||||
mv tmp$$ $ac_file
|
||||
rm vpsed$$
|
||||
echo 'MULTISUBDIR =' >> $ac_file
|
||||
ml_norecursion=yes
|
||||
. ${multi_basedir}/config-ml.in
|
||||
{ ml_norecursion=; unset ml_norecursion;}
|
||||
;;
|
||||
|
||||
esac
|
||||
done # for ac_tag
|
||||
|
@ -72,6 +72,7 @@ if test "${multilib}" = "yes"; then
|
||||
else
|
||||
multilib_arg=
|
||||
fi
|
||||
AM_CONDITIONAL(MULTISUBDIR32, [test "x$with_multisubdir" = "x32"])
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
@ -88,4 +89,19 @@ _EOF
|
||||
AS_UNSET([ml_norecursion])
|
||||
])
|
||||
|
||||
if test "x$with_multisubdir" != "x32"; then
|
||||
AC_CONFIG_FILES(AC_FOREACH([DIR], [tsan], [DIR/Makefile ]),
|
||||
[cat > vpsed$$ << \_EOF
|
||||
s!`test -f '$<' || echo '$(srcdir)/'`!!
|
||||
_EOF
|
||||
sed -f vpsed$$ $ac_file > tmp$$
|
||||
mv tmp$$ $ac_file
|
||||
rm vpsed$$
|
||||
echo 'MULTISUBDIR =' >> $ac_file
|
||||
ml_norecursion=yes
|
||||
. ${multi_basedir}/config-ml.in
|
||||
AS_UNSET([ml_norecursion])
|
||||
])
|
||||
fi
|
||||
|
||||
AC_OUTPUT
|
||||
|
80
libsanitizer/tsan/Makefile.am
Normal file
80
libsanitizer/tsan/Makefile.am
Normal 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=
|
||||
|
||||
## ################################################################
|
||||
|
641
libsanitizer/tsan/Makefile.in
Normal file
641
libsanitizer/tsan/Makefile.in
Normal 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:
|
6
libsanitizer/tsan/libtool-version
Normal file
6
libsanitizer/tsan/libtool-version
Normal 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
|
116
libsanitizer/tsan/tsan_clock.cc
Normal file
116
libsanitizer/tsan/tsan_clock.cc
Normal 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
|
80
libsanitizer/tsan/tsan_clock.h
Normal file
80
libsanitizer/tsan/tsan_clock.h
Normal 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
|
139
libsanitizer/tsan/tsan_defs.h
Normal file
139
libsanitizer/tsan/tsan_defs.h
Normal 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
|
80
libsanitizer/tsan/tsan_flags.cc
Normal file
80
libsanitizer/tsan/tsan_flags.cc
Normal 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
|
71
libsanitizer/tsan/tsan_flags.h
Normal file
71
libsanitizer/tsan/tsan_flags.h
Normal 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
|
1515
libsanitizer/tsan/tsan_interceptors.cc
Normal file
1515
libsanitizer/tsan/tsan_interceptors.cc
Normal file
File diff suppressed because it is too large
Load Diff
52
libsanitizer/tsan/tsan_interceptors.h
Normal file
52
libsanitizer/tsan/tsan_interceptors.h
Normal 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
|
40
libsanitizer/tsan/tsan_interface.cc
Normal file
40
libsanitizer/tsan/tsan_interface.cc
Normal 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);
|
||||
}
|
50
libsanitizer/tsan/tsan_interface.h
Normal file
50
libsanitizer/tsan/tsan_interface.h
Normal 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
|
402
libsanitizer/tsan/tsan_interface_ann.cc
Normal file
402
libsanitizer/tsan/tsan_interface_ann.cc
Normal 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"
|
29
libsanitizer/tsan/tsan_interface_ann.h
Normal file
29
libsanitizer/tsan/tsan_interface_ann.h
Normal 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
|
368
libsanitizer/tsan/tsan_interface_atomic.cc
Normal file
368
libsanitizer/tsan/tsan_interface_atomic.cc
Normal 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) {
|
||||
}
|
173
libsanitizer/tsan/tsan_interface_atomic.h
Normal file
173
libsanitizer/tsan/tsan_interface_atomic.h
Normal 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
|
63
libsanitizer/tsan/tsan_interface_inl.h
Normal file
63
libsanitizer/tsan/tsan_interface_inl.h
Normal 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());
|
||||
}
|
243
libsanitizer/tsan/tsan_md5.cc
Normal file
243
libsanitizer/tsan/tsan_md5.cc
Normal 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;
|
||||
}
|
||||
}
|
159
libsanitizer/tsan/tsan_mman.cc
Normal file
159
libsanitizer/tsan/tsan_mman.cc
Normal 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
|
77
libsanitizer/tsan/tsan_mman.h
Normal file
77
libsanitizer/tsan/tsan_mman.h
Normal 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
|
261
libsanitizer/tsan/tsan_mutex.cc
Normal file
261
libsanitizer/tsan/tsan_mutex.cc
Normal 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
|
78
libsanitizer/tsan/tsan_mutex.h
Normal file
78
libsanitizer/tsan/tsan_mutex.h
Normal 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
|
100
libsanitizer/tsan/tsan_platform.h
Normal file
100
libsanitizer/tsan/tsan_platform.h
Normal 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
|
274
libsanitizer/tsan/tsan_platform_linux.cc
Normal file
274
libsanitizer/tsan/tsan_platform_linux.cc
Normal 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__
|
102
libsanitizer/tsan/tsan_platform_mac.cc
Normal file
102
libsanitizer/tsan/tsan_platform_mac.cc
Normal 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__
|
38
libsanitizer/tsan/tsan_printf.cc
Normal file
38
libsanitizer/tsan/tsan_printf.cc
Normal 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
|
183
libsanitizer/tsan/tsan_report.cc
Normal file
183
libsanitizer/tsan/tsan_report.cc
Normal 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
|
103
libsanitizer/tsan/tsan_report.h
Normal file
103
libsanitizer/tsan/tsan_report.h
Normal 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
|
578
libsanitizer/tsan/tsan_rtl.cc
Normal file
578
libsanitizer/tsan/tsan_rtl.cc
Normal 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
|
554
libsanitizer/tsan/tsan_rtl.h
Normal file
554
libsanitizer/tsan/tsan_rtl.h
Normal 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
|
164
libsanitizer/tsan/tsan_rtl_amd64.S
Normal file
164
libsanitizer/tsan/tsan_rtl_amd64.S
Normal 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
|
269
libsanitizer/tsan/tsan_rtl_mutex.cc
Normal file
269
libsanitizer/tsan/tsan_rtl_mutex.cc
Normal 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
|
461
libsanitizer/tsan/tsan_rtl_report.cc
Normal file
461
libsanitizer/tsan/tsan_rtl_report.cc
Normal 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
|
397
libsanitizer/tsan/tsan_rtl_thread.cc
Normal file
397
libsanitizer/tsan/tsan_rtl_thread.cc
Normal 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
|
247
libsanitizer/tsan/tsan_stat.cc
Normal file
247
libsanitizer/tsan/tsan_stat.cc
Normal 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
|
257
libsanitizer/tsan/tsan_stat.h
Normal file
257
libsanitizer/tsan/tsan_stat.h
Normal 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
|
161
libsanitizer/tsan/tsan_suppressions.cc
Normal file
161
libsanitizer/tsan/tsan_suppressions.cc
Normal 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
|
41
libsanitizer/tsan/tsan_suppressions.h
Normal file
41
libsanitizer/tsan/tsan_suppressions.h
Normal 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
|
83
libsanitizer/tsan/tsan_symbolize.cc
Normal file
83
libsanitizer/tsan/tsan_symbolize.cc
Normal 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
|
29
libsanitizer/tsan/tsan_symbolize.h
Normal file
29
libsanitizer/tsan/tsan_symbolize.h
Normal 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
|
191
libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc
Normal file
191
libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc
Normal 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
|
278
libsanitizer/tsan/tsan_sync.cc
Normal file
278
libsanitizer/tsan/tsan_sync.cc
Normal 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
|
106
libsanitizer/tsan/tsan_sync.h
Normal file
106
libsanitizer/tsan/tsan_sync.h
Normal 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
|
73
libsanitizer/tsan/tsan_trace.h
Normal file
73
libsanitizer/tsan/tsan_trace.h
Normal 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
|
77
libsanitizer/tsan/tsan_update_shadow_word_inl.h
Normal file
77
libsanitizer/tsan/tsan_update_shadow_word_inl.h
Normal 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);
|
108
libsanitizer/tsan/tsan_vector.h
Normal file
108
libsanitizer/tsan/tsan_vector.h
Normal 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
|
Loading…
Reference in New Issue
Block a user