summaryrefslogtreecommitdiff
path: root/lib/sanitizer_common/tests
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sanitizer_common/tests')
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt49
-rw-r--r--lib/sanitizer_common/tests/lit.cfg5
-rw-r--r--lib/sanitizer_common/tests/lit.site.cfg.in9
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator_test.cc300
-rw-r--r--lib/sanitizer_common/tests/sanitizer_atomic_test.cc55
-rw-r--r--lib/sanitizer_common/tests/sanitizer_common_test.cc66
-rw-r--r--lib/sanitizer_common/tests/sanitizer_flags_test.cc20
-rw-r--r--lib/sanitizer_common/tests/sanitizer_libc_test.cc75
-rw-r--r--lib/sanitizer_common/tests/sanitizer_linux_test.cc253
-rw-r--r--lib/sanitizer_common/tests/sanitizer_mutex_test.cc7
-rw-r--r--lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc137
-rw-r--r--lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc96
-rw-r--r--lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc194
-rw-r--r--lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc53
-rw-r--r--lib/sanitizer_common/tests/sanitizer_test_utils.h14
-rw-r--r--lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc230
16 files changed, 1475 insertions, 88 deletions
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index f83a89cbe37c7..25e57507ad148 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -2,15 +2,20 @@ include(CompilerRTCompile)
set(SANITIZER_UNITTESTS
sanitizer_allocator_test.cc
+ sanitizer_atomic_test.cc
sanitizer_common_test.cc
sanitizer_flags_test.cc
sanitizer_libc_test.cc
+ sanitizer_linux_test.cc
sanitizer_list_test.cc
sanitizer_mutex_test.cc
sanitizer_printf_test.cc
sanitizer_scanf_interceptor_test.cc
sanitizer_stackdepot_test.cc
+ sanitizer_stacktrace_test.cc
+ sanitizer_stoptheworld_test.cc
sanitizer_test_main.cc
+ sanitizer_thread_registry_test.cc
)
set(SANITIZER_TEST_HEADERS)
@@ -18,6 +23,18 @@ foreach(header ${SANITIZER_HEADERS})
list(APPEND SANITIZER_TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header})
endforeach()
+set(SANITIZER_TEST_CFLAGS_COMMON
+ ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/include
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common
+ -DGTEST_HAS_RTTI=0
+ -O2 -g -fno-rtti
+ -Wall -Werror -Werror=sign-compare)
+
+set(SANITIZER_TEST_LINK_FLAGS_COMMON
+ -lstdc++ -ldl)
+
include_directories(..)
include_directories(../..)
@@ -49,18 +66,12 @@ macro(add_sanitizer_tests_for_arch arch)
get_target_flags_for_arch(${arch} TARGET_FLAGS)
set(SANITIZER_TEST_SOURCES ${SANITIZER_UNITTESTS}
${COMPILER_RT_GTEST_SOURCE})
- set(SANITIZER_TEST_CFLAGS ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
- -I${COMPILER_RT_SOURCE_DIR}/include
- -I${COMPILER_RT_SOURCE_DIR}/lib
- -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common
- -O2 -g ${TARGET_FLAGS})
- set(SANITIZER_TEST_LINK_FLAGS -lstdc++ -lpthread ${TARGET_FLAGS})
set(SANITIZER_TEST_OBJECTS)
foreach(source ${SANITIZER_TEST_SOURCES})
get_filename_component(basename ${source} NAME)
set(output_obj "${basename}.${arch}.o")
clang_compile(${output_obj} ${source}
- CFLAGS ${SANITIZER_TEST_CFLAGS}
+ CFLAGS ${SANITIZER_TEST_CFLAGS_COMMON} ${TARGET_FLAGS}
DEPS gtest ${SANITIZER_RUNTIME_LIBRARIES}
${SANITIZER_TEST_HEADERS})
list(APPEND SANITIZER_TEST_OBJECTS ${output_obj})
@@ -73,7 +84,8 @@ macro(add_sanitizer_tests_for_arch arch)
OBJECTS ${SANITIZER_TEST_OBJECTS}
${SANITIZER_COMMON_LIB_NAME}
DEPS ${SANITIZER_TEST_OBJECTS} ${SANITIZER_COMMON_LIB}
- LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS})
+ LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS_COMMON}
+ -lpthread ${TARGET_FLAGS})
endmacro()
if(COMPILER_RT_CAN_EXECUTE_TESTS)
@@ -85,11 +97,13 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
else()
if(CAN_TARGET_x86_64)
add_sanitizer_common_lib("RTSanitizerCommon.test.x86_64"
- $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>)
+ $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.x86_64>)
endif()
if(CAN_TARGET_i386)
add_sanitizer_common_lib("RTSanitizerCommon.test.i386"
- $<TARGET_OBJECTS:RTSanitizerCommon.i386>)
+ $<TARGET_OBJECTS:RTSanitizerCommon.i386>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.i386>)
endif()
endif()
if(CAN_TARGET_x86_64)
@@ -118,21 +132,14 @@ if(ANDROID)
add_executable(SanitizerTest
${SANITIZER_UNITTESTS}
${COMPILER_RT_GTEST_SOURCE}
- $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>
- )
+ $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>)
set_target_compile_flags(SanitizerTest
${SANITIZER_COMMON_CFLAGS}
- ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
- -I${COMPILER_RT_SOURCE_DIR}/include
- -I${COMPILER_RT_SOURCE_DIR}/lib
- -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common
- -O2 -g
- )
+ ${SANITIZER_TEST_CFLAGS_COMMON})
# Setup correct output directory and link flags.
- get_unittest_directory(OUTPUT_DIR)
set_target_properties(SanitizerTest PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
- set_target_link_flags(SanitizerTest ${SANITIZER_TEST_LINK_FLAGS})
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ set_target_link_flags(SanitizerTest ${SANITIZER_TEST_LINK_FLAGS_COMMON})
# Add unit test to test suite.
add_dependencies(SanitizerUnitTests SanitizerTest)
endif()
diff --git a/lib/sanitizer_common/tests/lit.cfg b/lib/sanitizer_common/tests/lit.cfg
index d774753985ac9..303d56c910790 100644
--- a/lib/sanitizer_common/tests/lit.cfg
+++ b/lib/sanitizer_common/tests/lit.cfg
@@ -11,9 +11,8 @@ def get_required_attr(config, attr_name):
return attr_value
# Setup attributes common for all compiler-rt projects.
-llvm_src_root = get_required_attr(config, 'llvm_src_root')
-compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects",
- "compiler-rt", "lib",
+compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root')
+compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib",
"lit.common.unit.cfg")
lit.load_config(config, compiler_rt_lit_unit_cfg)
diff --git a/lib/sanitizer_common/tests/lit.site.cfg.in b/lib/sanitizer_common/tests/lit.site.cfg.in
index bb9a28d6a6cb7..50485aa16ec2f 100644
--- a/lib/sanitizer_common/tests/lit.site.cfg.in
+++ b/lib/sanitizer_common/tests/lit.site.cfg.in
@@ -1,9 +1,16 @@
## Autogenerated by LLVM/Clang configuration.
# Do not edit!
-config.build_type = "@CMAKE_BUILD_TYPE@"
config.llvm_obj_root = "@LLVM_BINARY_DIR@"
config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@"
+config.llvm_build_mode = "@LLVM_BUILD_MODE@"
+
+try:
+ config.llvm_build_mode = config.llvm_build_mode % lit.params
+except KeyError,e:
+ key, = e.args
+ lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))
# Let the main config do the real work.
lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
index d67f4636ef4f7..de949ca7defec 100644
--- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
@@ -22,6 +22,7 @@
#include <pthread.h>
#include <algorithm>
#include <vector>
+#include <set>
// Too slow for debug build
#if TSAN_DEBUG == 0
@@ -40,8 +41,16 @@ typedef SizeClassAllocator64<
static const u64 kAddressSpaceSize = 1ULL << 32;
#endif
+static const uptr kRegionSizeLog = FIRST_32_SECOND_64(20, 24);
+static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog;
+
typedef SizeClassAllocator32<
- 0, kAddressSpaceSize, 16, CompactSizeClassMap> Allocator32Compact;
+ 0, kAddressSpaceSize,
+ /*kMetadataSize*/16,
+ CompactSizeClassMap,
+ kRegionSizeLog,
+ FlatByteMap<kFlatByteMapSize> >
+ Allocator32Compact;
template <class SizeClassMap>
void TestSizeClassMap() {
@@ -63,7 +72,8 @@ void TestSizeClassAllocator() {
Allocator *a = new Allocator;
a->Init();
SizeClassAllocatorLocalCache<Allocator> cache;
- cache.Init();
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000,
50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000};
@@ -77,7 +87,7 @@ void TestSizeClassAllocator() {
uptr size = sizes[s];
if (!a->CanAllocate(size, 1)) continue;
// printf("s = %ld\n", size);
- uptr n_iter = std::max((uptr)6, 10000000 / size);
+ uptr n_iter = std::max((uptr)6, 8000000 / size);
// fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);
for (uptr i = 0; i < n_iter; i++) {
uptr class_id0 = Allocator::SizeClassMapT::ClassID(size);
@@ -114,6 +124,12 @@ void TestSizeClassAllocator() {
CHECK_EQ(last_total_allocated, total_allocated);
}
+ // Check that GetBlockBegin never crashes.
+ for (uptr x = 0, step = kAddressSpaceSize / 100000;
+ x < kAddressSpaceSize - step; x += step)
+ if (a->PointerIsMine(reinterpret_cast<void *>(x)))
+ Ident(a->GetBlockBegin(reinterpret_cast<void *>(x)));
+
a->TestOnlyUnmap();
delete a;
}
@@ -137,25 +153,28 @@ void SizeClassAllocatorMetadataStress() {
Allocator *a = new Allocator;
a->Init();
SizeClassAllocatorLocalCache<Allocator> cache;
- cache.Init();
- static volatile void *sink;
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
- const uptr kNumAllocs = 10000;
+ const uptr kNumAllocs = 1 << 13;
void *allocated[kNumAllocs];
+ void *meta[kNumAllocs];
for (uptr i = 0; i < kNumAllocs; i++) {
void *x = cache.Allocate(a, 1 + i % 50);
allocated[i] = x;
+ meta[i] = a->GetMetaData(x);
}
// Get Metadata kNumAllocs^2 times.
for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) {
- sink = a->GetMetaData(allocated[i % kNumAllocs]);
+ uptr idx = i % kNumAllocs;
+ void *m = a->GetMetaData(allocated[idx]);
+ EXPECT_EQ(m, meta[idx]);
}
for (uptr i = 0; i < kNumAllocs; i++) {
cache.Deallocate(a, 1 + i % 50, allocated[i]);
}
a->TestOnlyUnmap();
- (void)sink;
delete a;
}
@@ -167,11 +186,47 @@ TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) {
TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) {
SizeClassAllocatorMetadataStress<Allocator64Compact>();
}
-#endif
+#endif // SANITIZER_WORDSIZE == 64
TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) {
SizeClassAllocatorMetadataStress<Allocator32Compact>();
}
+template <class Allocator>
+void SizeClassAllocatorGetBlockBeginStress() {
+ Allocator *a = new Allocator;
+ a->Init();
+ SizeClassAllocatorLocalCache<Allocator> cache;
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
+
+ uptr max_size_class = Allocator::kNumClasses - 1;
+ uptr size = Allocator::SizeClassMapT::Size(max_size_class);
+ u64 G8 = 1ULL << 33;
+ // Make sure we correctly compute GetBlockBegin() w/o overflow.
+ for (size_t i = 0; i <= G8 / size; i++) {
+ void *x = cache.Allocate(a, max_size_class);
+ void *beg = a->GetBlockBegin(x);
+ // if ((i & (i - 1)) == 0)
+ // fprintf(stderr, "[%zd] %p %p\n", i, x, beg);
+ EXPECT_EQ(x, beg);
+ }
+
+ a->TestOnlyUnmap();
+ delete a;
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, SizeClassAllocator64GetBlockBegin) {
+ SizeClassAllocatorGetBlockBeginStress<Allocator64>();
+}
+TEST(SanitizerCommon, SizeClassAllocator64CompactGetBlockBegin) {
+ SizeClassAllocatorGetBlockBeginStress<Allocator64Compact>();
+}
+TEST(SanitizerCommon, SizeClassAllocator32CompactGetBlockBegin) {
+ SizeClassAllocatorGetBlockBeginStress<Allocator32Compact>();
+}
+#endif // SANITIZER_WORDSIZE == 64
+
struct TestMapUnmapCallback {
static int map_count, unmap_count;
void OnMap(uptr p, uptr size) const { map_count++; }
@@ -191,8 +246,11 @@ TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) {
a->Init();
EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state.
SizeClassAllocatorLocalCache<Allocator64WithCallBack> cache;
- cache.Init();
- a->AllocateBatch(&cache, 64);
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
+ AllocatorStats stats;
+ stats.Init();
+ a->AllocateBatch(&stats, &cache, 32);
EXPECT_EQ(TestMapUnmapCallback::map_count, 3); // State + alloc + metadata.
a->TestOnlyUnmap();
EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing.
@@ -204,17 +262,25 @@ TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) {
TestMapUnmapCallback::map_count = 0;
TestMapUnmapCallback::unmap_count = 0;
typedef SizeClassAllocator32<
- 0, kAddressSpaceSize, 16, CompactSizeClassMap,
- TestMapUnmapCallback> Allocator32WithCallBack;
+ 0, kAddressSpaceSize,
+ /*kMetadataSize*/16,
+ CompactSizeClassMap,
+ kRegionSizeLog,
+ FlatByteMap<kFlatByteMapSize>,
+ TestMapUnmapCallback>
+ Allocator32WithCallBack;
Allocator32WithCallBack *a = new Allocator32WithCallBack;
a->Init();
- EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state.
+ EXPECT_EQ(TestMapUnmapCallback::map_count, 0);
SizeClassAllocatorLocalCache<Allocator32WithCallBack> cache;
- cache.Init();
- a->AllocateBatch(&cache, 64);
- EXPECT_EQ(TestMapUnmapCallback::map_count, 2); // alloc.
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
+ AllocatorStats stats;
+ stats.Init();
+ a->AllocateBatch(&stats, &cache, 32);
+ EXPECT_EQ(TestMapUnmapCallback::map_count, 1);
a->TestOnlyUnmap();
- EXPECT_EQ(TestMapUnmapCallback::unmap_count, 2); // The whole thing + alloc.
+ EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1);
delete a;
// fprintf(stderr, "Map: %d Unmap: %d\n",
// TestMapUnmapCallback::map_count,
@@ -226,9 +292,11 @@ TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) {
TestMapUnmapCallback::unmap_count = 0;
LargeMmapAllocator<TestMapUnmapCallback> a;
a.Init();
- void *x = a.Allocate(1 << 20, 1);
+ AllocatorStats stats;
+ stats.Init();
+ void *x = a.Allocate(&stats, 1 << 20, 1);
EXPECT_EQ(TestMapUnmapCallback::map_count, 1);
- a.Deallocate(x);
+ a.Deallocate(&stats, x);
EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1);
}
@@ -237,9 +305,12 @@ void FailInAssertionOnOOM() {
Allocator a;
a.Init();
SizeClassAllocatorLocalCache<Allocator> cache;
- cache.Init();
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
+ AllocatorStats stats;
+ stats.Init();
for (int i = 0; i < 1000000; i++) {
- a.AllocateBatch(&cache, 64);
+ a.AllocateBatch(&stats, &cache, 52);
}
a.TestOnlyUnmap();
@@ -254,13 +325,15 @@ TEST(SanitizerCommon, SizeClassAllocator64Overflow) {
TEST(SanitizerCommon, LargeMmapAllocator) {
LargeMmapAllocator<> a;
a.Init();
+ AllocatorStats stats;
+ stats.Init();
static const int kNumAllocs = 1000;
char *allocated[kNumAllocs];
static const uptr size = 4000;
// Allocate some.
for (int i = 0; i < kNumAllocs; i++) {
- allocated[i] = (char *)a.Allocate(size, 1);
+ allocated[i] = (char *)a.Allocate(&stats, size, 1);
CHECK(a.PointerIsMine(allocated[i]));
}
// Deallocate all.
@@ -268,14 +341,14 @@ TEST(SanitizerCommon, LargeMmapAllocator) {
for (int i = 0; i < kNumAllocs; i++) {
char *p = allocated[i];
CHECK(a.PointerIsMine(p));
- a.Deallocate(p);
+ a.Deallocate(&stats, p);
}
// Check that non left.
CHECK_EQ(a.TotalMemoryUsed(), 0);
// Allocate some more, also add metadata.
for (int i = 0; i < kNumAllocs; i++) {
- char *x = (char *)a.Allocate(size, 1);
+ char *x = (char *)a.Allocate(&stats, size, 1);
CHECK_GE(a.GetActuallyAllocatedSize(x), size);
uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x));
*meta = i;
@@ -294,7 +367,7 @@ TEST(SanitizerCommon, LargeMmapAllocator) {
uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(p));
CHECK_EQ(*meta, idx);
CHECK(a.PointerIsMine(p));
- a.Deallocate(p);
+ a.Deallocate(&stats, p);
}
CHECK_EQ(a.TotalMemoryUsed(), 0);
@@ -304,7 +377,7 @@ TEST(SanitizerCommon, LargeMmapAllocator) {
const uptr kNumAlignedAllocs = 100;
for (uptr i = 0; i < kNumAlignedAllocs; i++) {
uptr size = ((i % 10) + 1) * 4096;
- char *p = allocated[i] = (char *)a.Allocate(size, alignment);
+ char *p = allocated[i] = (char *)a.Allocate(&stats, size, alignment);
CHECK_EQ(p, a.GetBlockBegin(p));
CHECK_EQ(p, a.GetBlockBegin(p + size - 1));
CHECK_EQ(p, a.GetBlockBegin(p + size / 2));
@@ -312,9 +385,17 @@ TEST(SanitizerCommon, LargeMmapAllocator) {
p[0] = p[size - 1] = 0;
}
for (uptr i = 0; i < kNumAlignedAllocs; i++) {
- a.Deallocate(allocated[i]);
+ a.Deallocate(&stats, allocated[i]);
}
}
+
+ // Regression test for boundary condition in GetBlockBegin().
+ uptr page_size = GetPageSizeCached();
+ char *p = (char *)a.Allocate(&stats, page_size, 1);
+ CHECK_EQ(p, a.GetBlockBegin(p));
+ CHECK_EQ(p, (char *)a.GetBlockBegin(p + page_size - 1));
+ CHECK_NE(p, (char *)a.GetBlockBegin(p + page_size));
+ a.Deallocate(&stats, p);
}
template
@@ -327,7 +408,8 @@ void TestCombinedAllocator() {
a->Init();
AllocatorCache cache;
- cache.Init();
+ memset(&cache, 0, sizeof(cache));
+ a->InitCache(&cache);
EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0);
EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0);
@@ -363,6 +445,7 @@ void TestCombinedAllocator() {
allocated.clear();
a->SwallowCache(&cache);
}
+ a->DestroyCache(&cache);
a->TestOnlyUnmap();
}
@@ -388,14 +471,13 @@ TEST(SanitizerCommon, CombinedAllocator32Compact) {
template <class AllocatorCache>
void TestSizeClassAllocatorLocalCache() {
- static AllocatorCache static_allocator_cache;
- static_allocator_cache.Init();
AllocatorCache cache;
typedef typename AllocatorCache::Allocator Allocator;
Allocator *a = new Allocator();
a->Init();
- cache.Init();
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
const uptr kNumAllocs = 10000;
const int kNumIter = 100;
@@ -466,6 +548,42 @@ TEST(SanitizerCommon, AllocatorLeakTest) {
a.TestOnlyUnmap();
}
+
+// Struct which is allocated to pass info to new threads. The new thread frees
+// it.
+struct NewThreadParams {
+ AllocatorCache *thread_cache;
+ AllocatorCache::Allocator *allocator;
+ uptr class_id;
+};
+
+// Called in a new thread. Just frees its argument.
+static void *DeallocNewThreadWorker(void *arg) {
+ NewThreadParams *params = reinterpret_cast<NewThreadParams*>(arg);
+ params->thread_cache->Deallocate(params->allocator, params->class_id, params);
+ return NULL;
+}
+
+// The allocator cache is supposed to be POD and zero initialized. We should be
+// able to call Deallocate on a zeroed cache, and it will self-initialize.
+TEST(Allocator, AllocatorCacheDeallocNewThread) {
+ AllocatorCache::Allocator allocator;
+ allocator.Init();
+ AllocatorCache main_cache;
+ AllocatorCache child_cache;
+ memset(&main_cache, 0, sizeof(main_cache));
+ memset(&child_cache, 0, sizeof(child_cache));
+
+ uptr class_id = DefaultSizeClassMap::ClassID(sizeof(NewThreadParams));
+ NewThreadParams *params = reinterpret_cast<NewThreadParams*>(
+ main_cache.Allocate(&allocator, class_id));
+ params->thread_cache = &child_cache;
+ params->allocator = &allocator;
+ params->class_id = class_id;
+ pthread_t t;
+ EXPECT_EQ(0, pthread_create(&t, 0, DeallocNewThreadWorker, params));
+ EXPECT_EQ(0, pthread_join(t, 0));
+}
#endif
TEST(Allocator, Basic) {
@@ -507,4 +625,122 @@ TEST(Allocator, ScopedBuffer) {
}
}
+class IterationTestCallback {
+ public:
+ explicit IterationTestCallback(std::set<void *> *chunks)
+ : chunks_(chunks) {}
+ void operator()(void *chunk) const {
+ chunks_->insert(chunk);
+ }
+ private:
+ std::set<void *> *chunks_;
+};
+
+template <class Allocator>
+void TestSizeClassAllocatorIteration() {
+ Allocator *a = new Allocator;
+ a->Init();
+ SizeClassAllocatorLocalCache<Allocator> cache;
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
+
+ static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000,
+ 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000};
+
+ std::vector<void *> allocated;
+
+ // Allocate a bunch of chunks.
+ for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) {
+ uptr size = sizes[s];
+ if (!a->CanAllocate(size, 1)) continue;
+ // printf("s = %ld\n", size);
+ uptr n_iter = std::max((uptr)6, 80000 / size);
+ // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);
+ for (uptr j = 0; j < n_iter; j++) {
+ uptr class_id0 = Allocator::SizeClassMapT::ClassID(size);
+ void *x = cache.Allocate(a, class_id0);
+ allocated.push_back(x);
+ }
+ }
+
+ std::set<void *> reported_chunks;
+ IterationTestCallback callback(&reported_chunks);
+ a->ForceLock();
+ a->ForEachChunk(callback);
+ a->ForceUnlock();
+
+ for (uptr i = 0; i < allocated.size(); i++) {
+ // Don't use EXPECT_NE. Reporting the first mismatch is enough.
+ ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end());
+ }
+
+ a->TestOnlyUnmap();
+ delete a;
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, SizeClassAllocator64Iteration) {
+ TestSizeClassAllocatorIteration<Allocator64>();
+}
+#endif
+
+TEST(SanitizerCommon, SizeClassAllocator32Iteration) {
+ TestSizeClassAllocatorIteration<Allocator32Compact>();
+}
+
+TEST(SanitizerCommon, LargeMmapAllocatorIteration) {
+ LargeMmapAllocator<> a;
+ a.Init();
+ AllocatorStats stats;
+ stats.Init();
+
+ static const uptr kNumAllocs = 1000;
+ char *allocated[kNumAllocs];
+ static const uptr size = 40;
+ // Allocate some.
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ allocated[i] = (char *)a.Allocate(&stats, size, 1);
+ }
+
+ std::set<void *> reported_chunks;
+ IterationTestCallback callback(&reported_chunks);
+ a.ForceLock();
+ a.ForEachChunk(callback);
+ a.ForceUnlock();
+
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ // Don't use EXPECT_NE. Reporting the first mismatch is enough.
+ ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end());
+ }
+}
+
+#if SANITIZER_WORDSIZE == 64
+// Regression test for out-of-memory condition in PopulateFreeList().
+TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) {
+ // In a world where regions are small and chunks are huge...
+ typedef SizeClassMap<63, 128, 16> SpecialSizeClassMap;
+ typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0,
+ SpecialSizeClassMap> SpecialAllocator64;
+ const uptr kRegionSize =
+ kAllocatorSize / SpecialSizeClassMap::kNumClassesRounded;
+ SpecialAllocator64 *a = new SpecialAllocator64;
+ a->Init();
+ SizeClassAllocatorLocalCache<SpecialAllocator64> cache;
+ memset(&cache, 0, sizeof(cache));
+ cache.Init(0);
+
+ // ...one man is on a mission to overflow a region with a series of
+ // successive allocations.
+ const uptr kClassID = 107;
+ const uptr kAllocationSize = DefaultSizeClassMap::Size(kClassID);
+ ASSERT_LT(2 * kAllocationSize, kRegionSize);
+ ASSERT_GT(3 * kAllocationSize, kRegionSize);
+ cache.Allocate(a, kClassID);
+ EXPECT_DEATH(cache.Allocate(a, kClassID) && cache.Allocate(a, kClassID),
+ "The process has exhausted");
+ a->TestOnlyUnmap();
+ delete a;
+}
+#endif
+
#endif // #if TSAN_DEBUG==0
diff --git a/lib/sanitizer_common/tests/sanitizer_atomic_test.cc b/lib/sanitizer_common/tests/sanitizer_atomic_test.cc
new file mode 100644
index 0000000000000..a4a97c43e00f6
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_atomic_test.cc
@@ -0,0 +1,55 @@
+//===-- sanitizer_atomic_test.cc ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "gtest/gtest.h"
+
+namespace __sanitizer {
+
+// Clang crashes while compiling this test for Android:
+// http://llvm.org/bugs/show_bug.cgi?id=15587
+#if !SANITIZER_ANDROID
+template<typename T>
+void CheckAtomicCompareExchange() {
+ typedef typename T::Type Type;
+ {
+ Type old_val = 42;
+ Type new_val = 24;
+ Type var = old_val;
+ EXPECT_TRUE(atomic_compare_exchange_strong((T*)&var, &old_val, new_val,
+ memory_order_relaxed));
+ EXPECT_FALSE(atomic_compare_exchange_strong((T*)&var, &old_val, new_val,
+ memory_order_relaxed));
+ EXPECT_EQ(new_val, old_val);
+ }
+ {
+ Type old_val = 42;
+ Type new_val = 24;
+ Type var = old_val;
+ EXPECT_TRUE(atomic_compare_exchange_weak((T*)&var, &old_val, new_val,
+ memory_order_relaxed));
+ EXPECT_FALSE(atomic_compare_exchange_weak((T*)&var, &old_val, new_val,
+ memory_order_relaxed));
+ EXPECT_EQ(new_val, old_val);
+ }
+}
+
+TEST(SanitizerCommon, AtomicCompareExchangeTest) {
+ CheckAtomicCompareExchange<atomic_uint8_t>();
+ CheckAtomicCompareExchange<atomic_uint16_t>();
+ CheckAtomicCompareExchange<atomic_uint32_t>();
+ CheckAtomicCompareExchange<atomic_uint64_t>();
+ CheckAtomicCompareExchange<atomic_uintptr_t>();
+}
+#endif //!SANITIZER_ANDROID
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc
index 01d8b5a87c012..424c279d4adab 100644
--- a/lib/sanitizer_common/tests/sanitizer_common_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_common_test.cc
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_platform.h"
#include "gtest/gtest.h"
namespace __sanitizer {
@@ -79,7 +80,7 @@ TEST(SanitizerCommon, MmapAlignedOrDie) {
}
}
-#ifdef __linux__
+#if SANITIZER_LINUX
TEST(SanitizerCommon, SanitizerSetThreadName) {
const char *names[] = {
"0123456789012",
@@ -96,4 +97,65 @@ TEST(SanitizerCommon, SanitizerSetThreadName) {
}
#endif
-} // namespace sanitizer
+TEST(SanitizerCommon, InternalVector) {
+ InternalVector<uptr> vector(1);
+ for (uptr i = 0; i < 100; i++) {
+ EXPECT_EQ(i, vector.size());
+ vector.push_back(i);
+ }
+ for (uptr i = 0; i < 100; i++) {
+ EXPECT_EQ(i, vector[i]);
+ }
+ for (int i = 99; i >= 0; i--) {
+ EXPECT_EQ((uptr)i, vector.back());
+ vector.pop_back();
+ EXPECT_EQ((uptr)i, vector.size());
+ }
+}
+
+void TestThreadInfo(bool main) {
+ uptr stk_addr = 0;
+ uptr stk_size = 0;
+ uptr tls_addr = 0;
+ uptr tls_size = 0;
+ GetThreadStackAndTls(main, &stk_addr, &stk_size, &tls_addr, &tls_size);
+
+ int stack_var;
+ EXPECT_NE(stk_addr, (uptr)0);
+ EXPECT_NE(stk_size, (uptr)0);
+ EXPECT_GT((uptr)&stack_var, stk_addr);
+ EXPECT_LT((uptr)&stack_var, stk_addr + stk_size);
+
+#if SANITIZER_LINUX && defined(__x86_64__)
+ static __thread int thread_var;
+ EXPECT_NE(tls_addr, (uptr)0);
+ EXPECT_NE(tls_size, (uptr)0);
+ EXPECT_GT((uptr)&thread_var, tls_addr);
+ EXPECT_LT((uptr)&thread_var, tls_addr + tls_size);
+
+ // Ensure that tls and stack do not intersect.
+ uptr tls_end = tls_addr + tls_size;
+ EXPECT_TRUE(tls_addr < stk_addr || tls_addr >= stk_addr + stk_size);
+ EXPECT_TRUE(tls_end < stk_addr || tls_end >= stk_addr + stk_size);
+ EXPECT_TRUE((tls_addr < stk_addr) == (tls_end < stk_addr));
+#endif
+}
+
+static void *WorkerThread(void *arg) {
+ TestThreadInfo(false);
+ return 0;
+}
+
+TEST(SanitizerCommon, ThreadStackTlsMain) {
+ InitTlsSize();
+ TestThreadInfo(true);
+}
+
+TEST(SanitizerCommon, ThreadStackTlsWorker) {
+ InitTlsSize();
+ pthread_t t;
+ pthread_create(&t, 0, WorkerThread, 0);
+ pthread_join(t, 0);
+}
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/lib/sanitizer_common/tests/sanitizer_flags_test.cc
index c0589f4d2e900..cd3cac11bc807 100644
--- a/lib/sanitizer_common/tests/sanitizer_flags_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_flags_test.cc
@@ -32,7 +32,7 @@ static void TestStrFlag(const char *start_value, const char *env,
const char *final_value) {
const char *flag = start_value;
ParseFlag(env, &flag, kFlagName);
- EXPECT_EQ(internal_strcmp(final_value, flag), 0);
+ EXPECT_EQ(0, internal_strcmp(final_value, flag));
}
TEST(SanitizerCommon, BooleanFlags) {
@@ -63,6 +63,24 @@ TEST(SanitizerCommon, StrFlags) {
TestStrFlag("", "--flag_name='abc zxc'", "abc zxc");
TestStrFlag("", "--flag_name='abc zxcc'", "abc zxcc");
TestStrFlag("", "--flag_name=\"abc qwe\" asd", "abc qwe");
+ TestStrFlag("", "other_flag_name=zzz", "");
+}
+
+static void TestTwoFlags(const char *env, bool expected_flag1,
+ const char *expected_flag2) {
+ bool flag1 = !expected_flag1;
+ const char *flag2 = "";
+ ParseFlag(env, &flag1, "flag1");
+ ParseFlag(env, &flag2, "flag2");
+ EXPECT_EQ(expected_flag1, flag1);
+ EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2));
+}
+
+TEST(SanitizerCommon, MultipleFlags) {
+ TestTwoFlags("flag1=1 flag2='zzz'", true, "zzz");
+ TestTwoFlags("flag2='qxx' flag1=0", false, "qxx");
+ TestTwoFlags("flag1=false:flag2='zzz'", false, "zzz");
+ TestTwoFlags("flag2=qxx:flag1=yes", true, "qxx");
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
index b9d8414e0cbf9..39c29d3573274 100644
--- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
@@ -9,9 +9,18 @@
// Tests for sanitizer_libc.h.
//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_platform.h"
#include "gtest/gtest.h"
+#if SANITIZER_LINUX || SANITIZER_MAC
+# define SANITIZER_TEST_HAS_STAT_H 1
+# include <sys/stat.h>
+#else
+# define SANITIZER_TEST_HAS_STAT_H 0
+#endif
+
// A regression test for internal_memmove() implementation.
TEST(SanitizerCommon, InternalMemmoveRegression) {
char src[] = "Hello World";
@@ -40,3 +49,69 @@ TEST(SanitizerCommon, mem_is_zero) {
}
delete [] x;
}
+
+struct stat_and_more {
+ struct stat st;
+ unsigned char z;
+};
+
+TEST(SanitizerCommon, FileOps) {
+ const char *str1 = "qwerty";
+ uptr len1 = internal_strlen(str1);
+ const char *str2 = "zxcv";
+ uptr len2 = internal_strlen(str2);
+
+ u32 uid = GetUid();
+ char temp_filename[128];
+#if SANITIZER_ANDROID
+ // I don't know a way to query temp directory location on Android without
+ // going through Java interfaces. The code below is not ideal, but should
+ // work. May require "adb root", but it is needed for almost any use of ASan
+ // on Android already.
+ internal_snprintf(temp_filename, sizeof(temp_filename),
+ "%s/sanitizer_common.tmp.%d",
+ GetEnv("EXTERNAL_STORAGE"), uid);
+#else
+ internal_snprintf(temp_filename, sizeof(temp_filename),
+ "/tmp/sanitizer_common.tmp.%d", uid);
+#endif
+ uptr openrv = OpenFile(temp_filename, true);
+ EXPECT_FALSE(internal_iserror(openrv));
+ fd_t fd = openrv;
+ EXPECT_EQ(len1, internal_write(fd, str1, len1));
+ EXPECT_EQ(len2, internal_write(fd, str2, len2));
+ internal_close(fd);
+
+ openrv = OpenFile(temp_filename, false);
+ EXPECT_FALSE(internal_iserror(openrv));
+ fd = openrv;
+ uptr fsize = internal_filesize(fd);
+ EXPECT_EQ(len1 + len2, fsize);
+
+#if SANITIZER_TEST_HAS_STAT_H
+ struct stat st1, st2, st3;
+ EXPECT_EQ(0u, internal_stat(temp_filename, &st1));
+ EXPECT_EQ(0u, internal_lstat(temp_filename, &st2));
+ EXPECT_EQ(0u, internal_fstat(fd, &st3));
+ EXPECT_EQ(fsize, (uptr)st3.st_size);
+
+ // Verify that internal_fstat does not write beyond the end of the supplied
+ // buffer.
+ struct stat_and_more sam;
+ memset(&sam, 0xAB, sizeof(sam));
+ EXPECT_EQ(0u, internal_fstat(fd, &sam.st));
+ EXPECT_EQ(0xAB, sam.z);
+ EXPECT_NE(0xAB, sam.st.st_size);
+ EXPECT_NE(0, sam.st.st_size);
+#endif
+
+ char buf[64] = {};
+ EXPECT_EQ(len1, internal_read(fd, buf, len1));
+ EXPECT_EQ(0, internal_memcmp(buf, str1, len1));
+ EXPECT_EQ((char)0, buf[len1 + 1]);
+ internal_memset(buf, 0, len1);
+ EXPECT_EQ(len2, internal_read(fd, buf, len2));
+ EXPECT_EQ(0, internal_memcmp(buf, str2, len2));
+ internal_close(fd);
+}
+
diff --git a/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/lib/sanitizer_common/tests/sanitizer_linux_test.cc
new file mode 100644
index 0000000000000..b18aeb030acff
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_linux_test.cc
@@ -0,0 +1,253 @@
+//===-- sanitizer_linux_test.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for sanitizer_linux.h
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include "sanitizer_common/sanitizer_linux.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "gtest/gtest.h"
+
+#ifdef __x86_64__
+#include <asm/prctl.h>
+#endif
+#include <pthread.h>
+#include <sched.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <vector>
+
+#ifdef __x86_64__
+extern "C" int arch_prctl(int code, __sanitizer::uptr *addr);
+#endif
+
+namespace __sanitizer {
+
+struct TidReporterArgument {
+ TidReporterArgument() {
+ pthread_mutex_init(&terminate_thread_mutex, NULL);
+ pthread_mutex_init(&tid_reported_mutex, NULL);
+ pthread_cond_init(&terminate_thread_cond, NULL);
+ pthread_cond_init(&tid_reported_cond, NULL);
+ terminate_thread = false;
+ }
+
+ ~TidReporterArgument() {
+ pthread_mutex_destroy(&terminate_thread_mutex);
+ pthread_mutex_destroy(&tid_reported_mutex);
+ pthread_cond_destroy(&terminate_thread_cond);
+ pthread_cond_destroy(&tid_reported_cond);
+ }
+
+ pid_t reported_tid;
+ // For signaling to spawned threads that they should terminate.
+ pthread_cond_t terminate_thread_cond;
+ pthread_mutex_t terminate_thread_mutex;
+ bool terminate_thread;
+ // For signaling to main thread that a child thread has reported its tid.
+ pthread_cond_t tid_reported_cond;
+ pthread_mutex_t tid_reported_mutex;
+
+ private:
+ // Disallow evil constructors
+ TidReporterArgument(const TidReporterArgument &);
+ void operator=(const TidReporterArgument &);
+};
+
+class ThreadListerTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ pthread_t pthread_id;
+ pid_t tid;
+ for (uptr i = 0; i < kThreadCount; i++) {
+ SpawnTidReporter(&pthread_id, &tid);
+ pthread_ids_.push_back(pthread_id);
+ tids_.push_back(tid);
+ }
+ }
+
+ virtual void TearDown() {
+ pthread_mutex_lock(&thread_arg.terminate_thread_mutex);
+ thread_arg.terminate_thread = true;
+ pthread_cond_broadcast(&thread_arg.terminate_thread_cond);
+ pthread_mutex_unlock(&thread_arg.terminate_thread_mutex);
+ for (uptr i = 0; i < pthread_ids_.size(); i++)
+ pthread_join(pthread_ids_[i], NULL);
+ }
+
+ void SpawnTidReporter(pthread_t *pthread_id, pid_t *tid);
+
+ static const uptr kThreadCount = 20;
+
+ std::vector<pthread_t> pthread_ids_;
+ std::vector<pid_t> tids_;
+
+ TidReporterArgument thread_arg;
+};
+
+// Writes its TID once to reported_tid and waits until signaled to terminate.
+void *TidReporterThread(void *argument) {
+ TidReporterArgument *arg = reinterpret_cast<TidReporterArgument *>(argument);
+ pthread_mutex_lock(&arg->tid_reported_mutex);
+ arg->reported_tid = GetTid();
+ pthread_cond_broadcast(&arg->tid_reported_cond);
+ pthread_mutex_unlock(&arg->tid_reported_mutex);
+
+ pthread_mutex_lock(&arg->terminate_thread_mutex);
+ while (!arg->terminate_thread)
+ pthread_cond_wait(&arg->terminate_thread_cond,
+ &arg->terminate_thread_mutex);
+ pthread_mutex_unlock(&arg->terminate_thread_mutex);
+ return NULL;
+}
+
+void ThreadListerTest::SpawnTidReporter(pthread_t *pthread_id,
+ pid_t *tid) {
+ pthread_mutex_lock(&thread_arg.tid_reported_mutex);
+ thread_arg.reported_tid = -1;
+ ASSERT_EQ(0, pthread_create(pthread_id, NULL,
+ TidReporterThread,
+ &thread_arg));
+ while (thread_arg.reported_tid == -1)
+ pthread_cond_wait(&thread_arg.tid_reported_cond,
+ &thread_arg.tid_reported_mutex);
+ pthread_mutex_unlock(&thread_arg.tid_reported_mutex);
+ *tid = thread_arg.reported_tid;
+}
+
+static std::vector<pid_t> ReadTidsToVector(ThreadLister *thread_lister) {
+ std::vector<pid_t> listed_tids;
+ pid_t tid;
+ while ((tid = thread_lister->GetNextTID()) >= 0)
+ listed_tids.push_back(tid);
+ EXPECT_FALSE(thread_lister->error());
+ return listed_tids;
+}
+
+static bool Includes(std::vector<pid_t> first, std::vector<pid_t> second) {
+ std::sort(first.begin(), first.end());
+ std::sort(second.begin(), second.end());
+ return std::includes(first.begin(), first.end(),
+ second.begin(), second.end());
+}
+
+static bool HasElement(std::vector<pid_t> vector, pid_t element) {
+ return std::find(vector.begin(), vector.end(), element) != vector.end();
+}
+
+// ThreadLister's output should include the current thread's TID and the TID of
+// every thread we spawned.
+TEST_F(ThreadListerTest, ThreadListerSeesAllSpawnedThreads) {
+ pid_t self_tid = GetTid();
+ ThreadLister thread_lister(getpid());
+ std::vector<pid_t> listed_tids = ReadTidsToVector(&thread_lister);
+ ASSERT_TRUE(HasElement(listed_tids, self_tid));
+ ASSERT_TRUE(Includes(listed_tids, tids_));
+}
+
+// Calling Reset() should not cause ThreadLister to forget any threads it's
+// supposed to know about.
+TEST_F(ThreadListerTest, ResetDoesNotForgetThreads) {
+ ThreadLister thread_lister(getpid());
+
+ // Run the loop body twice, because Reset() might behave differently if called
+ // on a freshly created object.
+ for (uptr i = 0; i < 2; i++) {
+ thread_lister.Reset();
+ std::vector<pid_t> listed_tids = ReadTidsToVector(&thread_lister);
+ ASSERT_TRUE(Includes(listed_tids, tids_));
+ }
+}
+
+// If new threads have spawned during ThreadLister object's lifetime, calling
+// Reset() should cause ThreadLister to recognize their existence.
+TEST_F(ThreadListerTest, ResetMakesNewThreadsKnown) {
+ ThreadLister thread_lister(getpid());
+ std::vector<pid_t> threads_before_extra = ReadTidsToVector(&thread_lister);
+
+ pthread_t extra_pthread_id;
+ pid_t extra_tid;
+ SpawnTidReporter(&extra_pthread_id, &extra_tid);
+ // Register the new thread so it gets terminated in TearDown().
+ pthread_ids_.push_back(extra_pthread_id);
+
+ // It would be very bizarre if the new TID had been listed before we even
+ // spawned that thread, but it would also cause a false success in this test,
+ // so better check for that.
+ ASSERT_FALSE(HasElement(threads_before_extra, extra_tid));
+
+ thread_lister.Reset();
+
+ std::vector<pid_t> threads_after_extra = ReadTidsToVector(&thread_lister);
+ ASSERT_TRUE(HasElement(threads_after_extra, extra_tid));
+}
+
+TEST(SanitizerCommon, SetEnvTest) {
+ const char kEnvName[] = "ENV_FOO";
+ SetEnv(kEnvName, "value");
+ EXPECT_STREQ("value", getenv(kEnvName));
+ unsetenv(kEnvName);
+ EXPECT_EQ(0, getenv(kEnvName));
+}
+
+#ifdef __x86_64__
+// libpthread puts the thread descriptor (%fs:0x0) at the end of stack space.
+void *thread_descriptor_test_func(void *arg) {
+ uptr fs;
+ arch_prctl(ARCH_GET_FS, &fs);
+ pthread_attr_t attr;
+ pthread_getattr_np(pthread_self(), &attr);
+ void *stackaddr;
+ uptr stacksize;
+ pthread_attr_getstack(&attr, &stackaddr, &stacksize);
+ return (void *)((uptr)stackaddr + stacksize - fs);
+}
+
+TEST(SanitizerLinux, ThreadDescriptorSize) {
+ pthread_t tid;
+ void *result;
+ pthread_create(&tid, 0, thread_descriptor_test_func, 0);
+ ASSERT_EQ(0, pthread_join(tid, &result));
+ EXPECT_EQ((uptr)result, ThreadDescriptorSize());
+}
+#endif
+
+TEST(SanitizerCommon, LibraryNameIs) {
+ EXPECT_FALSE(LibraryNameIs("", ""));
+
+ char full_name[256];
+ const char *paths[] = { "", "/", "/path/to/" };
+ const char *suffixes[] = { "", "-linux", ".1.2", "-linux.1.2" };
+ const char *base_names[] = { "lib", "lib.0", "lib-i386" };
+ const char *wrong_names[] = { "", "lib.9", "lib-x86_64" };
+ for (uptr i = 0; i < ARRAY_SIZE(paths); i++)
+ for (uptr j = 0; j < ARRAY_SIZE(suffixes); j++) {
+ for (uptr k = 0; k < ARRAY_SIZE(base_names); k++) {
+ internal_snprintf(full_name, ARRAY_SIZE(full_name), "%s%s%s.so",
+ paths[i], base_names[k], suffixes[j]);
+ EXPECT_TRUE(LibraryNameIs(full_name, base_names[k]))
+ << "Full name " << full_name
+ << " doesn't match base name " << base_names[k];
+ for (uptr m = 0; m < ARRAY_SIZE(wrong_names); m++)
+ EXPECT_FALSE(LibraryNameIs(full_name, wrong_names[m]))
+ << "Full name " << full_name
+ << " matches base name " << wrong_names[m];
+ }
+ }
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX
diff --git a/lib/sanitizer_common/tests/sanitizer_mutex_test.cc b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc
index 6bb2ae29a188c..1dc9bef207104 100644
--- a/lib/sanitizer_common/tests/sanitizer_mutex_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc
@@ -92,6 +92,12 @@ static void *try_thread(void *param) {
return 0;
}
+template<typename MutexType>
+static void check_locked(MutexType *mtx) {
+ GenericScopedLock<MutexType> l(mtx);
+ mtx->CheckLocked();
+}
+
TEST(SanitizerCommon, SpinMutex) {
SpinMutex mtx;
mtx.Init();
@@ -123,6 +129,7 @@ TEST(SanitizerCommon, BlockingMutex) {
pthread_create(&threads[i], 0, lock_thread<BlockingMutex>, &data);
for (int i = 0; i < kThreads; i++)
pthread_join(threads[i], 0);
+ check_locked(mtx);
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc b/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc
index 00b260479da98..1df2bcfd4bec7 100644
--- a/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc
@@ -19,45 +19,72 @@
using namespace __sanitizer;
-#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
((std::vector<unsigned> *)ctx)->push_back(size)
#include "sanitizer_common/sanitizer_common_interceptors_scanf.inc"
-static void testScanf2(void *ctx, const char *format, ...) {
+static const char scanf_buf[] = "Test string.";
+static size_t scanf_buf_size = sizeof(scanf_buf);
+static const unsigned SCANF_ARGS_MAX = 16;
+
+static void testScanf3(void *ctx, int result, bool allowGnuMalloc,
+ const char *format, ...) {
va_list ap;
va_start(ap, format);
- scanf_common(ctx, format, ap);
+ scanf_common(ctx, result, allowGnuMalloc, format, ap);
va_end(ap);
}
-static void testScanf(const char *format, unsigned n, ...) {
+static void testScanf2(const char *format, int scanf_result,
+ bool allowGnuMalloc, unsigned n,
+ va_list expected_sizes) {
std::vector<unsigned> scanf_sizes;
// 16 args should be enough.
- testScanf2((void *)&scanf_sizes, format,
- (void*)0, (void*)0, (void*)0, (void*)0,
- (void*)0, (void*)0, (void*)0, (void*)0,
- (void*)0, (void*)0, (void*)0, (void*)0,
- (void*)0, (void*)0, (void*)0, (void*)0);
- ASSERT_EQ(n, scanf_sizes.size()) <<
- "Unexpected number of format arguments: '" << format << "'";
+ testScanf3((void *)&scanf_sizes, scanf_result, allowGnuMalloc, format,
+ scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf,
+ scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf,
+ scanf_buf, scanf_buf, scanf_buf, scanf_buf);
+ ASSERT_EQ(n, scanf_sizes.size()) << "Unexpected number of format arguments: '"
+ << format << "'";
+ for (unsigned i = 0; i < n; ++i)
+ EXPECT_EQ(va_arg(expected_sizes, unsigned), scanf_sizes[i])
+ << "Unexpect write size for argument " << i << ", format string '"
+ << format << "'";
+}
+
+static void testScanf(const char *format, unsigned n, ...) {
va_list ap;
va_start(ap, n);
- for (unsigned i = 0; i < n; ++i)
- EXPECT_EQ(va_arg(ap, unsigned), scanf_sizes[i]) <<
- "Unexpect write size for argument " << i << ", format string '" <<
- format << "'";
+ testScanf2(format, SCANF_ARGS_MAX, /* allowGnuMalloc */ true, n, ap);
+ va_end(ap);
+}
+
+static void testScanfPartial(const char *format, int scanf_result, unsigned n,
+ ...) {
+ va_list ap;
+ va_start(ap, n);
+ testScanf2(format, scanf_result, /* allowGnuMalloc */ true, n, ap);
+ va_end(ap);
+}
+
+static void testScanfNoGnuMalloc(const char *format, unsigned n, ...) {
+ va_list ap;
+ va_start(ap, n);
+ testScanf2(format, SCANF_ARGS_MAX, /* allowGnuMalloc */ false, n, ap);
va_end(ap);
}
TEST(SanitizerCommonInterceptors, Scanf) {
- const unsigned I = sizeof(int); // NOLINT
- const unsigned L = sizeof(long); // NOLINT
- const unsigned LL = sizeof(long long); // NOLINT
- const unsigned S = sizeof(short); // NOLINT
- const unsigned C = sizeof(char); // NOLINT
- const unsigned D = sizeof(double); // NOLINT
- const unsigned F = sizeof(float); // NOLINT
+ const unsigned I = sizeof(int); // NOLINT
+ const unsigned L = sizeof(long); // NOLINT
+ const unsigned LL = sizeof(long long); // NOLINT
+ const unsigned S = sizeof(short); // NOLINT
+ const unsigned C = sizeof(char); // NOLINT
+ const unsigned D = sizeof(double); // NOLINT
+ const unsigned LD = sizeof(long double); // NOLINT
+ const unsigned F = sizeof(float); // NOLINT
+ const unsigned P = sizeof(char *); // NOLINT
testScanf("%d", 1, I);
testScanf("%d%d%d", 3, I, I, I);
@@ -65,6 +92,7 @@ TEST(SanitizerCommonInterceptors, Scanf) {
testScanf("%ld", 1, L);
testScanf("%llu", 1, LL);
testScanf("a %hd%hhx", 2, S, C);
+ testScanf("%c", 1, C);
testScanf("%%", 0);
testScanf("a%%", 0);
@@ -79,7 +107,72 @@ TEST(SanitizerCommonInterceptors, Scanf) {
testScanf("%nf", 1, I);
testScanf("%10s", 1, 11);
+ testScanf("%10c", 1, 10);
testScanf("%%10s", 0);
testScanf("%*10s", 0);
testScanf("%*d", 0);
+
+ testScanf("%4d%8f%c", 3, I, F, C);
+ testScanf("%s%d", 2, scanf_buf_size, I);
+ testScanf("%[abc]", 1, scanf_buf_size);
+ testScanf("%4[bcdef]", 1, 5);
+ testScanf("%[]]", 1, scanf_buf_size);
+ testScanf("%8[^]%d0-9-]%c", 2, 9, C);
+
+ testScanf("%*[^:]%n:%d:%1[ ]%n", 4, I, I, 2, I);
+
+ testScanf("%*d%u", 1, I);
+
+ testScanf("%c%d", 2, C, I);
+ testScanf("%A%lf", 2, F, D);
+
+ testScanf("%ms %Lf", 2, P, LD);
+ testScanf("s%Las", 1, LD);
+ testScanf("%ar", 1, F);
+
+ // In the cases with std::min below the format spec can be interpreted as
+ // either floating-something, or (GNU extension) callee-allocated string.
+ // Our conservative implementation reports one of the two possibilities with
+ // the least store range.
+ testScanf("%a[", 0);
+ testScanf("%a[]", 0);
+ testScanf("%a[]]", 1, std::min(F, P));
+ testScanf("%a[abc]", 1, std::min(F, P));
+ testScanf("%a[^abc]", 1, std::min(F, P));
+ testScanf("%a[ab%c] %d", 0);
+ testScanf("%a[^ab%c] %d", 0);
+ testScanf("%as", 1, std::min(F, P));
+ testScanf("%aS", 1, std::min(F, P));
+ testScanf("%a13S", 1, std::min(F, P));
+ testScanf("%alS", 1, std::min(F, P));
+
+ testScanfNoGnuMalloc("s%Las", 1, LD);
+ testScanfNoGnuMalloc("%ar", 1, F);
+ testScanfNoGnuMalloc("%a[", 1, F);
+ testScanfNoGnuMalloc("%a[]", 1, F);
+ testScanfNoGnuMalloc("%a[]]", 1, F);
+ testScanfNoGnuMalloc("%a[abc]", 1, F);
+ testScanfNoGnuMalloc("%a[^abc]", 1, F);
+ testScanfNoGnuMalloc("%a[ab%c] %d", 3, F, C, I);
+ testScanfNoGnuMalloc("%a[^ab%c] %d", 3, F, C, I);
+ testScanfNoGnuMalloc("%as", 1, F);
+ testScanfNoGnuMalloc("%aS", 1, F);
+ testScanfNoGnuMalloc("%a13S", 1, F);
+ testScanfNoGnuMalloc("%alS", 1, F);
+
+ testScanf("%5$d", 0);
+ testScanf("%md", 0);
+ testScanf("%m10s", 0);
+
+ testScanfPartial("%d%d%d%d //1\n", 1, 1, I);
+ testScanfPartial("%d%d%d%d //2\n", 2, 2, I, I);
+ testScanfPartial("%d%d%d%d //3\n", 3, 3, I, I, I);
+ testScanfPartial("%d%d%d%d //4\n", 4, 4, I, I, I, I);
+
+ testScanfPartial("%d%n%n%d //1\n", 1, 1, I);
+ testScanfPartial("%d%n%n%d //2\n", 2, 4, I, I, I, I);
+
+ testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, scanf_buf_size);
+ testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, scanf_buf_size,
+ scanf_buf_size);
}
diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
new file mode 100644
index 0000000000000..3d352cb97a5e3
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
@@ -0,0 +1,96 @@
+//===-- sanitizer_stacktrace_test.cc --------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "gtest/gtest.h"
+
+namespace __sanitizer {
+
+class FastUnwindTest : public ::testing::Test {
+ protected:
+ virtual void SetUp();
+
+ uptr fake_stack[10];
+ uptr start_pc;
+ uptr fake_top;
+ uptr fake_bottom;
+ StackTrace trace;
+};
+
+static uptr PC(uptr idx) {
+ return (1<<20) + idx;
+}
+
+void FastUnwindTest::SetUp() {
+ // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have
+ // even indices.
+ for (uptr i = 0; i+1 < ARRAY_SIZE(fake_stack); i += 2) {
+ fake_stack[i] = (uptr)&fake_stack[i+2]; // fp
+ fake_stack[i+1] = PC(i + 1); // retaddr
+ }
+ // Mark the last fp as zero to terminate the stack trace.
+ fake_stack[RoundDownTo(ARRAY_SIZE(fake_stack) - 1, 2)] = 0;
+
+ // Top is two slots past the end because FastUnwindStack subtracts two.
+ fake_top = (uptr)&fake_stack[ARRAY_SIZE(fake_stack) + 2];
+ // Bottom is one slot before the start because FastUnwindStack uses >.
+ fake_bottom = (uptr)&fake_stack[-1];
+ start_pc = PC(0);
+
+ // This is common setup done by __asan::GetStackTrace().
+ trace.size = 0;
+ trace.max_size = ARRAY_SIZE(fake_stack);
+ trace.trace[0] = start_pc;
+}
+
+TEST_F(FastUnwindTest, Basic) {
+ trace.FastUnwindStack(start_pc, (uptr)&fake_stack[0],
+ fake_top, fake_bottom);
+ // Should get all on-stack retaddrs and start_pc.
+ EXPECT_EQ(6U, trace.size);
+ EXPECT_EQ(start_pc, trace.trace[0]);
+ for (uptr i = 1; i <= 5; i++) {
+ EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
+ }
+}
+
+// From: http://code.google.com/p/address-sanitizer/issues/detail?id=162
+TEST_F(FastUnwindTest, FramePointerLoop) {
+ // Make one fp point to itself.
+ fake_stack[4] = (uptr)&fake_stack[4];
+ trace.FastUnwindStack(start_pc, (uptr)&fake_stack[0],
+ fake_top, fake_bottom);
+ // Should get all on-stack retaddrs up to the 4th slot and start_pc.
+ EXPECT_EQ(4U, trace.size);
+ EXPECT_EQ(start_pc, trace.trace[0]);
+ for (uptr i = 1; i <= 3; i++) {
+ EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
+ }
+}
+
+TEST_F(FastUnwindTest, MisalignedFramePointer) {
+ // Make one fp misaligned.
+ fake_stack[4] += 3;
+ trace.FastUnwindStack(start_pc, (uptr)&fake_stack[0],
+ fake_top, fake_bottom);
+ // Should get all on-stack retaddrs up to the 4th slot and start_pc.
+ EXPECT_EQ(4U, trace.size);
+ EXPECT_EQ(start_pc, trace.trace[0]);
+ for (uptr i = 1; i < 4U; i++) {
+ EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
+ }
+}
+
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
new file mode 100644
index 0000000000000..a5f8516df575d
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
@@ -0,0 +1,194 @@
+//===-- sanitizer_stoptheworld_test.cc ------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for sanitizer_stoptheworld.h
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "gtest/gtest.h"
+
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <pthread.h>
+#include <sched.h>
+
+namespace __sanitizer {
+
+static pthread_mutex_t incrementer_thread_exit_mutex;
+
+struct CallbackArgument {
+ volatile int counter;
+ volatile bool threads_stopped;
+ volatile bool callback_executed;
+ CallbackArgument()
+ : counter(0),
+ threads_stopped(false),
+ callback_executed(false) {}
+};
+
+void *IncrementerThread(void *argument) {
+ CallbackArgument *callback_argument = (CallbackArgument *)argument;
+ while (true) {
+ __sync_fetch_and_add(&callback_argument->counter, 1);
+ if (pthread_mutex_trylock(&incrementer_thread_exit_mutex) == 0) {
+ pthread_mutex_unlock(&incrementer_thread_exit_mutex);
+ return NULL;
+ } else {
+ sched_yield();
+ }
+ }
+}
+
+// This callback checks that IncrementerThread is suspended at the time of its
+// execution.
+void Callback(const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ CallbackArgument *callback_argument = (CallbackArgument *)argument;
+ callback_argument->callback_executed = true;
+ int counter_at_init = __sync_fetch_and_add(&callback_argument->counter, 0);
+ for (uptr i = 0; i < 1000; i++) {
+ sched_yield();
+ if (__sync_fetch_and_add(&callback_argument->counter, 0) !=
+ counter_at_init) {
+ callback_argument->threads_stopped = false;
+ return;
+ }
+ }
+ callback_argument->threads_stopped = true;
+}
+
+TEST(StopTheWorld, SuspendThreadsSimple) {
+ pthread_mutex_init(&incrementer_thread_exit_mutex, NULL);
+ CallbackArgument argument;
+ pthread_t thread_id;
+ int pthread_create_result;
+ pthread_mutex_lock(&incrementer_thread_exit_mutex);
+ pthread_create_result = pthread_create(&thread_id, NULL, IncrementerThread,
+ &argument);
+ ASSERT_EQ(0, pthread_create_result);
+ StopTheWorld(&Callback, &argument);
+ pthread_mutex_unlock(&incrementer_thread_exit_mutex);
+ EXPECT_TRUE(argument.callback_executed);
+ EXPECT_TRUE(argument.threads_stopped);
+ // argument is on stack, so we have to wait for the incrementer thread to
+ // terminate before we can return from this function.
+ ASSERT_EQ(0, pthread_join(thread_id, NULL));
+ pthread_mutex_destroy(&incrementer_thread_exit_mutex);
+}
+
+// A more comprehensive test where we spawn a bunch of threads while executing
+// StopTheWorld in parallel.
+static const uptr kThreadCount = 50;
+static const uptr kStopWorldAfter = 10; // let this many threads spawn first
+
+static pthread_mutex_t advanced_incrementer_thread_exit_mutex;
+
+struct AdvancedCallbackArgument {
+ volatile uptr thread_index;
+ volatile int counters[kThreadCount];
+ pthread_t thread_ids[kThreadCount];
+ volatile bool threads_stopped;
+ volatile bool callback_executed;
+ volatile bool fatal_error;
+ AdvancedCallbackArgument()
+ : thread_index(0),
+ threads_stopped(false),
+ callback_executed(false),
+ fatal_error(false) {}
+};
+
+void *AdvancedIncrementerThread(void *argument) {
+ AdvancedCallbackArgument *callback_argument =
+ (AdvancedCallbackArgument *)argument;
+ uptr this_thread_index = __sync_fetch_and_add(
+ &callback_argument->thread_index, 1);
+ // Spawn the next thread.
+ int pthread_create_result;
+ if (this_thread_index + 1 < kThreadCount) {
+ pthread_create_result =
+ pthread_create(&callback_argument->thread_ids[this_thread_index + 1],
+ NULL, AdvancedIncrementerThread, argument);
+ // Cannot use ASSERT_EQ in non-void-returning functions. If there's a
+ // problem, defer failing to the main thread.
+ if (pthread_create_result != 0) {
+ callback_argument->fatal_error = true;
+ __sync_fetch_and_add(&callback_argument->thread_index,
+ kThreadCount - callback_argument->thread_index);
+ }
+ }
+ // Do the actual work.
+ while (true) {
+ __sync_fetch_and_add(&callback_argument->counters[this_thread_index], 1);
+ if (pthread_mutex_trylock(&advanced_incrementer_thread_exit_mutex) == 0) {
+ pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex);
+ return NULL;
+ } else {
+ sched_yield();
+ }
+ }
+}
+
+void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ AdvancedCallbackArgument *callback_argument =
+ (AdvancedCallbackArgument *)argument;
+ callback_argument->callback_executed = true;
+
+ int counters_at_init[kThreadCount];
+ for (uptr j = 0; j < kThreadCount; j++)
+ counters_at_init[j] = __sync_fetch_and_add(&callback_argument->counters[j],
+ 0);
+ for (uptr i = 0; i < 10; i++) {
+ sched_yield();
+ for (uptr j = 0; j < kThreadCount; j++)
+ if (__sync_fetch_and_add(&callback_argument->counters[j], 0) !=
+ counters_at_init[j]) {
+ callback_argument->threads_stopped = false;
+ return;
+ }
+ }
+ callback_argument->threads_stopped = true;
+}
+
+TEST(StopTheWorld, SuspendThreadsAdvanced) {
+ pthread_mutex_init(&advanced_incrementer_thread_exit_mutex, NULL);
+ AdvancedCallbackArgument argument;
+
+ pthread_mutex_lock(&advanced_incrementer_thread_exit_mutex);
+ int pthread_create_result;
+ pthread_create_result = pthread_create(&argument.thread_ids[0], NULL,
+ AdvancedIncrementerThread,
+ &argument);
+ ASSERT_EQ(0, pthread_create_result);
+ // Wait for several threads to spawn before proceeding.
+ while (__sync_fetch_and_add(&argument.thread_index, 0) < kStopWorldAfter)
+ sched_yield();
+ StopTheWorld(&AdvancedCallback, &argument);
+ EXPECT_TRUE(argument.callback_executed);
+ EXPECT_TRUE(argument.threads_stopped);
+
+ // Wait for all threads to spawn before we start terminating them.
+ while (__sync_fetch_and_add(&argument.thread_index, 0) < kThreadCount)
+ sched_yield();
+ ASSERT_FALSE(argument.fatal_error); // a pthread_create has failed
+ // Signal the threads to terminate.
+ pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex);
+ for (uptr i = 0; i < kThreadCount; i++)
+ ASSERT_EQ(0, pthread_join(argument.thread_ids[i], NULL));
+ pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX
diff --git a/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc b/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc
new file mode 100644
index 0000000000000..d8be2afb19e9c
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc
@@ -0,0 +1,53 @@
+//===-- sanitizer_stoptheworld_testlib.cc ---------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Dynamic library to test StopTheWorld functionality.
+// When loaded with LD_PRELOAD, it will periodically suspend all threads.
+//===----------------------------------------------------------------------===//
+/* Usage:
+clang++ -fno-exceptions -g -fPIC -I. \
+ sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc \
+ sanitizer_common/sanitizer_*.cc -shared -lpthread -o teststoptheworld.so
+LD_PRELOAD=`pwd`/teststoptheworld.so /your/app
+*/
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+
+namespace {
+const uptr kSuspendDuration = 3;
+const uptr kRunDuration = 3;
+
+void Callback(const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ sleep(kSuspendDuration);
+}
+
+void *SuspenderThread(void *argument) {
+ while (true) {
+ sleep(kRunDuration);
+ StopTheWorld(Callback, NULL);
+ }
+ return NULL;
+}
+
+__attribute__((constructor)) void StopTheWorldTestLibConstructor(void) {
+ pthread_t thread_id;
+ pthread_create(&thread_id, NULL, SuspenderThread, NULL);
+}
+} // namespace
+
+#endif // SANITIZER_LINUX
diff --git a/lib/sanitizer_common/tests/sanitizer_test_utils.h b/lib/sanitizer_common/tests/sanitizer_test_utils.h
index 6129ea8a5370a..a770d0fbd39ec 100644
--- a/lib/sanitizer_common/tests/sanitizer_test_utils.h
+++ b/lib/sanitizer_common/tests/sanitizer_test_utils.h
@@ -36,12 +36,14 @@ typedef __int64 int64_t;
#define __has_feature(x) 0
#endif
-#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
-# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
- __attribute__((no_address_safety_analysis))
-#else
-# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
-#endif
+#ifndef ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
+# if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
+ __attribute__((no_sanitize_address))
+# else
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
+# endif
+#endif // ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
#if __LP64__ || defined(_WIN64)
# define SANITIZER_WORDSIZE 64
diff --git a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
new file mode 100644
index 0000000000000..e080403fb56c9
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
@@ -0,0 +1,230 @@
+//===-- sanitizer_thread_registry_test.cc ---------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of shared sanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_thread_registry.h"
+#include "gtest/gtest.h"
+
+#include <vector>
+
+namespace __sanitizer {
+
+static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED);
+static LowLevelAllocator tctx_allocator;
+
+template<typename TCTX>
+static ThreadContextBase *GetThreadContext(u32 tid) {
+ BlockingMutexLock l(&tctx_allocator_lock);
+ void *mem = tctx_allocator.Allocate(sizeof(TCTX));
+ return new(mem) TCTX(tid);
+}
+
+static const u32 kMaxRegistryThreads = 1000;
+static const u32 kRegistryQuarantine = 2;
+
+static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total,
+ uptr exp_running, uptr exp_alive) {
+ uptr total, running, alive;
+ registry->GetNumberOfThreads(&total, &running, &alive);
+ EXPECT_EQ(exp_total, total);
+ EXPECT_EQ(exp_running, running);
+ EXPECT_EQ(exp_alive, alive);
+}
+
+static bool is_detached(u32 tid) {
+ return (tid % 2 == 0);
+}
+
+static uptr get_uid(u32 tid) {
+ return tid * 2;
+}
+
+static bool HasName(ThreadContextBase *tctx, void *arg) {
+ char *name = (char*)arg;
+ return (tctx->name && 0 == internal_strcmp(tctx->name, name));
+}
+
+static bool HasUid(ThreadContextBase *tctx, void *arg) {
+ uptr uid = (uptr)arg;
+ return (tctx->user_id == uid);
+}
+
+static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {
+ bool *arr = (bool*)arg;
+ arr[tctx->tid] = true;
+}
+
+static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) {
+ // Create and start a main thread.
+ EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0));
+ registry->StartThread(0, 0, 0);
+ // Create a bunch of threads.
+ for (u32 i = 1; i <= 10; i++) {
+ EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
+ }
+ CheckThreadQuantity(registry, 11, 1, 11);
+ // Start some of them.
+ for (u32 i = 1; i <= 5; i++) {
+ registry->StartThread(i, 0, 0);
+ }
+ CheckThreadQuantity(registry, 11, 6, 11);
+ // Finish, create and start more threads.
+ for (u32 i = 1; i <= 5; i++) {
+ registry->FinishThread(i);
+ if (!is_detached(i))
+ registry->JoinThread(i, 0);
+ }
+ for (u32 i = 6; i <= 10; i++) {
+ registry->StartThread(i, 0, 0);
+ }
+ std::vector<u32> new_tids;
+ for (u32 i = 11; i <= 15; i++) {
+ new_tids.push_back(
+ registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
+ }
+ ASSERT_LE(kRegistryQuarantine, 5U);
+ u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0);
+ CheckThreadQuantity(registry, exp_total, 6, 11);
+ // Test SetThreadName and FindThread.
+ registry->SetThreadName(6, "six");
+ registry->SetThreadName(7, "seven");
+ EXPECT_EQ(7U, registry->FindThread(HasName, (void*)"seven"));
+ EXPECT_EQ(ThreadRegistry::kUnknownTid,
+ registry->FindThread(HasName, (void*)"none"));
+ EXPECT_EQ(0U, registry->FindThread(HasUid, (void*)get_uid(0)));
+ EXPECT_EQ(10U, registry->FindThread(HasUid, (void*)get_uid(10)));
+ EXPECT_EQ(ThreadRegistry::kUnknownTid,
+ registry->FindThread(HasUid, (void*)0x1234));
+ // Detach and finish and join remaining threads.
+ for (u32 i = 6; i <= 10; i++) {
+ registry->DetachThread(i);
+ registry->FinishThread(i);
+ }
+ for (u32 i = 0; i < new_tids.size(); i++) {
+ u32 tid = new_tids[i];
+ registry->StartThread(tid, 0, 0);
+ registry->DetachThread(tid);
+ registry->FinishThread(tid);
+ }
+ CheckThreadQuantity(registry, exp_total, 1, 1);
+ // Test methods that require the caller to hold a ThreadRegistryLock.
+ bool has_tid[16];
+ internal_memset(&has_tid[0], 0, sizeof(has_tid));
+ {
+ ThreadRegistryLock l(registry);
+ registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
+ }
+ for (u32 i = 0; i < exp_total; i++) {
+ EXPECT_TRUE(has_tid[i]);
+ }
+ {
+ ThreadRegistryLock l(registry);
+ registry->CheckLocked();
+ ThreadContextBase *main_thread = registry->GetThreadLocked(0);
+ EXPECT_EQ(main_thread, registry->FindThreadContextLocked(
+ HasUid, (void*)get_uid(0)));
+ }
+ EXPECT_EQ(11U, registry->GetMaxAliveThreads());
+}
+
+TEST(SanitizerCommon, ThreadRegistryTest) {
+ ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
+ kMaxRegistryThreads,
+ kRegistryQuarantine);
+ TestRegistry(&quarantine_registry, true);
+
+ ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
+ kMaxRegistryThreads,
+ kMaxRegistryThreads);
+ TestRegistry(&no_quarantine_registry, false);
+}
+
+static const int kThreadsPerShard = 20;
+static const int kNumShards = 25;
+
+static int num_created[kNumShards + 1];
+static int num_started[kNumShards + 1];
+static int num_joined[kNumShards + 1];
+
+namespace {
+
+struct RunThreadArgs {
+ ThreadRegistry *registry;
+ uptr shard; // started from 1.
+};
+
+class TestThreadContext : public ThreadContextBase {
+ public:
+ explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}
+ void OnJoined(void *arg) {
+ uptr shard = (uptr)arg;
+ num_joined[shard]++;
+ }
+ void OnStarted(void *arg) {
+ uptr shard = (uptr)arg;
+ num_started[shard]++;
+ }
+ void OnCreated(void *arg) {
+ uptr shard = (uptr)arg;
+ num_created[shard]++;
+ }
+};
+
+} // namespace
+
+void *RunThread(void *arg) {
+ RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);
+ std::vector<int> tids;
+ for (int i = 0; i < kThreadsPerShard; i++)
+ tids.push_back(
+ args->registry->CreateThread(0, false, 0, (void*)args->shard));
+ for (int i = 0; i < kThreadsPerShard; i++)
+ args->registry->StartThread(tids[i], 0, (void*)args->shard);
+ for (int i = 0; i < kThreadsPerShard; i++)
+ args->registry->FinishThread(tids[i]);
+ for (int i = 0; i < kThreadsPerShard; i++)
+ args->registry->JoinThread(tids[i], (void*)args->shard);
+ return 0;
+}
+
+static void ThreadedTestRegistry(ThreadRegistry *registry) {
+ // Create and start a main thread.
+ EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0));
+ registry->StartThread(0, 0, 0);
+ pthread_t threads[kNumShards];
+ RunThreadArgs args[kNumShards];
+ for (int i = 0; i < kNumShards; i++) {
+ args[i].registry = registry;
+ args[i].shard = i + 1;
+ pthread_create(&threads[i], 0, RunThread, &args[i]);
+ }
+ for (int i = 0; i < kNumShards; i++) {
+ pthread_join(threads[i], 0);
+ }
+ // Check that each thread created/started/joined correct amount
+ // of "threads" in thread_registry.
+ EXPECT_EQ(1, num_created[0]);
+ EXPECT_EQ(1, num_started[0]);
+ EXPECT_EQ(0, num_joined[0]);
+ for (int i = 1; i <= kNumShards; i++) {
+ EXPECT_EQ(kThreadsPerShard, num_created[i]);
+ EXPECT_EQ(kThreadsPerShard, num_started[i]);
+ EXPECT_EQ(kThreadsPerShard, num_joined[i]);
+ }
+}
+
+TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
+ ThreadRegistry registry(GetThreadContext<TestThreadContext>,
+ kThreadsPerShard * kNumShards + 1, 10);
+ ThreadedTestRegistry(&registry);
+}
+
+} // namespace __sanitizer