diff options
Diffstat (limited to 'lib/sanitizer_common/tests')
25 files changed, 1863 insertions, 222 deletions
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt index 5b66917b05b0..bb7a399b0ec6 100644 --- a/lib/sanitizer_common/tests/CMakeLists.txt +++ b/lib/sanitizer_common/tests/CMakeLists.txt @@ -1,10 +1,16 @@ include(CompilerRTCompile) +clang_compiler_add_cxx_check() + set(SANITIZER_UNITTESTS sanitizer_allocator_test.cc sanitizer_atomic_test.cc + sanitizer_bitvector_test.cc + sanitizer_bvgraph_test.cc sanitizer_common_test.cc + sanitizer_deadlock_detector_test.cc sanitizer_flags_test.cc + sanitizer_format_interceptor_test.cc sanitizer_ioctl_test.cc sanitizer_libc_test.cc sanitizer_linux_test.cc @@ -13,8 +19,9 @@ set(SANITIZER_UNITTESTS sanitizer_nolibc_test.cc sanitizer_posix_test.cc sanitizer_printf_test.cc - sanitizer_scanf_interceptor_test.cc + sanitizer_procmaps_test.cc sanitizer_stackdepot_test.cc + sanitizer_stacktrace_printer_test.cc sanitizer_stacktrace_test.cc sanitizer_stoptheworld_test.cc sanitizer_suppressions_test.cc @@ -22,22 +29,47 @@ set(SANITIZER_UNITTESTS sanitizer_thread_registry_test.cc) set(SANITIZER_TEST_HEADERS + sanitizer_pthread_wrappers.h + sanitizer_test_config.h sanitizer_test_utils.h) 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} + ${COMPILER_RT_TEST_CFLAGS} + ${COMPILER_RT_GTEST_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) + -fno-rtti + -O2 + -Werror=sign-compare + -Wno-non-virtual-dtor) + +# -gline-tables-only must be enough for these tests, so use it if possible. +if(COMPILER_RT_TEST_COMPILER_ID MATCHES "Clang") + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -gline-tables-only) +else() + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -g) +endif() + +if(NOT MSVC) + list(APPEND SANITIZER_TEST_LINK_FLAGS_COMMON --driver-mode=g++) +endif() + +set(SANITIZER_TEST_LINK_LIBS) +append_list_if(ANDROID log SANITIZER_TEST_LINK_LIBS) +# NDK r10 requires -latomic almost always. +append_list_if(ANDROID atomic SANITIZER_TEST_LINK_LIBS) -set(SANITIZER_TEST_LINK_FLAGS_COMMON - -lstdc++ -ldl) +append_list_if(COMPILER_RT_HAS_LIBDL -ldl SANITIZER_TEST_LINK_FLAGS_COMMON) +append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread SANITIZER_TEST_LINK_FLAGS_COMMON) +# x86_64 FreeBSD 9.2 additionally requires libc++ to build the tests. Also, +# 'libm' shall be specified explicitly to build i386 tests. +if(CMAKE_SYSTEM MATCHES "FreeBSD-9.2-RELEASE") + list(APPEND SANITIZER_TEST_LINK_FLAGS_COMMON "-lc++ -lm") +endif() include_directories(..) include_directories(../..) @@ -57,7 +89,11 @@ function(get_sanitizer_common_lib_for_arch arch lib lib_name) set(tgt_name "RTSanitizerCommon.test.${arch}") endif() set(${lib} "${tgt_name}" PARENT_SCOPE) - set(${lib_name} "lib${tgt_name}.a" PARENT_SCOPE) + if(NOT MSVC) + set(${lib_name} "lib${tgt_name}.a" PARENT_SCOPE) + else() + set(${lib_name} "${tgt_name}.lib" PARENT_SCOPE) + endif() endfunction() # Sanitizer_common unit tests testsuite. @@ -70,14 +106,17 @@ 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_COMPILE_DEPS ${SANITIZER_TEST_HEADERS}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND SANITIZER_TEST_COMPILE_DEPS gtest) + endif() 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_COMMON} ${TARGET_FLAGS} - DEPS gtest ${SANITIZER_RUNTIME_LIBRARIES} - ${SANITIZER_TEST_HEADERS}) + DEPS ${SANITIZER_TEST_COMPILE_DEPS}) list(APPEND SANITIZER_TEST_OBJECTS ${output_obj}) endforeach() get_sanitizer_common_lib_for_arch(${arch} SANITIZER_COMMON_LIB @@ -89,7 +128,7 @@ macro(add_sanitizer_tests_for_arch arch) ${SANITIZER_COMMON_LIB_NAME} DEPS ${SANITIZER_TEST_OBJECTS} ${SANITIZER_COMMON_LIB} LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS_COMMON} - -lpthread ${TARGET_FLAGS}) + ${TARGET_FLAGS}) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64") # Test that the libc-independent part of sanitizer_common is indeed @@ -98,7 +137,7 @@ macro(add_sanitizer_tests_for_arch arch) clang_compile(sanitizer_nolibc_test_main.${arch}.o sanitizer_nolibc_test_main.cc CFLAGS ${SANITIZER_TEST_CFLAGS_COMMON} ${TARGET_FLAGS} - DEPS ${SANITIZER_RUNTIME_LIBRARIES} ${SANITIZER_TEST_HEADERS}) + DEPS ${SANITIZER_TEST_COMPILE_DEPS}) add_compiler_rt_test(SanitizerUnitTests "Sanitizer-${arch}-Test-Nolibc" OBJECTS sanitizer_nolibc_test_main.${arch}.o -Wl,-whole-archive @@ -110,7 +149,7 @@ macro(add_sanitizer_tests_for_arch arch) endif() endmacro() -if(COMPILER_RT_CAN_EXECUTE_TESTS) +if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID) # We use just-built clang to build sanitizer_common unittests, so we must # be sure that produced binaries would work. if(APPLE) @@ -136,34 +175,24 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS) if(CAN_TARGET_i386) add_sanitizer_tests_for_arch(i386) endif() - - # Run unittests as a part of lit testsuite. - configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) - - add_lit_testsuite(check-sanitizer "Running sanitizer library unittests" - ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS SanitizerUnitTests - ) - set_target_properties(check-sanitizer PROPERTIES FOLDER "Sanitizer unittests") endif() if(ANDROID) - # We assume that unit tests on Android are built in a build - # tree with fresh Clang as a host compiler. - add_executable(SanitizerTest - ${SANITIZER_UNITTESTS} - ${COMPILER_RT_GTEST_SOURCE} - $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>) - set_target_compile_flags(SanitizerTest - ${SANITIZER_COMMON_CFLAGS} - ${SANITIZER_TEST_CFLAGS_COMMON}) - # Setup correct output directory and link flags. - set_target_properties(SanitizerTest PROPERTIES - 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) + foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH}) + add_executable(SanitizerTest + ${SANITIZER_UNITTESTS} + ${COMPILER_RT_GTEST_SOURCE} + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>) + set_target_compile_flags(SanitizerTest + ${SANITIZER_COMMON_CFLAGS} + ${SANITIZER_TEST_CFLAGS_COMMON}) + # Setup correct output directory and link flags. + set_target_properties(SanitizerTest PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + set_target_link_flags(SanitizerTest ${SANITIZER_TEST_LINK_FLAGS_COMMON}) + target_link_libraries(SanitizerTest ${SANITIZER_TEST_LINK_LIBS}) + # Add unit test to test suite. + add_dependencies(SanitizerUnitTests SanitizerTest) + endforeach() endif() diff --git a/lib/sanitizer_common/tests/lit.site.cfg.in b/lib/sanitizer_common/tests/lit.site.cfg.in deleted file mode 100644 index 5ceb9e4c5c28..000000000000 --- a/lib/sanitizer_common/tests/lit.site.cfg.in +++ /dev/null @@ -1,14 +0,0 @@ -## Autogenerated by LLVM/Clang configuration. -# Do not edit! - -# Load common config for all compiler-rt unit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") - -# Setup config name. -config.name = 'SanitizerCommon-Unit' - -# Setup test source and exec root. For unit tests, we define -# it as build directory with sanitizer_common tests. -config.test_exec_root = os.path.join("@COMPILER_RT_BINARY_DIR@", "lib", - "sanitizer_common", "tests") -config.test_source_root = config.test_exec_root diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc index d92a07fe4c2d..f61d58dea7d9 100644 --- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc @@ -17,11 +17,11 @@ #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_test_utils.h" +#include "sanitizer_pthread_wrappers.h" #include "gtest/gtest.h" #include <stdlib.h> -#include <pthread.h> #include <algorithm> #include <vector> #include <set> @@ -328,6 +328,7 @@ TEST(SanitizerCommon, SizeClassAllocator64Overflow) { } #endif +#if !defined(_WIN32) // FIXME: This currently fails on Windows. TEST(SanitizerCommon, LargeMmapAllocator) { LargeMmapAllocator<> a; a.Init(); @@ -403,6 +404,7 @@ TEST(SanitizerCommon, LargeMmapAllocator) { CHECK_NE(p, (char *)a.GetBlockBegin(p + page_size)); a.Deallocate(&stats, p); } +#endif template <class PrimaryAllocator, class SecondaryAllocator, class AllocatorCache> @@ -477,11 +479,13 @@ TEST(SanitizerCommon, CombinedAllocator64Compact) { } #endif +#if !defined(_WIN32) // FIXME: This currently fails on Windows. TEST(SanitizerCommon, CombinedAllocator32Compact) { TestCombinedAllocator<Allocator32Compact, LargeMmapAllocator<>, SizeClassAllocatorLocalCache<Allocator32Compact> > (); } +#endif template <class AllocatorCache> void TestSizeClassAllocatorLocalCache() { @@ -553,8 +557,8 @@ TEST(SanitizerCommon, AllocatorLeakTest) { uptr total_used_memory = 0; for (int i = 0; i < 100; i++) { pthread_t t; - EXPECT_EQ(0, pthread_create(&t, 0, AllocatorLeakTestWorker, &a)); - EXPECT_EQ(0, pthread_join(t, 0)); + PTHREAD_CREATE(&t, 0, AllocatorLeakTestWorker, &a); + PTHREAD_JOIN(t, 0); if (i == 0) total_used_memory = a.TotalMemoryUsed(); EXPECT_EQ(a.TotalMemoryUsed(), total_used_memory); @@ -595,8 +599,8 @@ TEST(Allocator, AllocatorCacheDeallocNewThread) { 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)); + PTHREAD_CREATE(&t, 0, DeallocNewThreadWorker, params); + PTHREAD_JOIN(t, 0); } #endif @@ -625,9 +629,9 @@ TEST(Allocator, Stress) { } } -TEST(Allocator, InternalAllocFailure) { - EXPECT_DEATH(Ident(InternalAlloc(10 << 20)), - "Unexpected mmap in InternalAllocator!"); +TEST(Allocator, LargeAlloc) { + void *p = InternalAlloc(10 << 20); + InternalFree(p); } TEST(Allocator, ScopedBuffer) { @@ -794,4 +798,65 @@ TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { } #endif +TEST(SanitizerCommon, TwoLevelByteMap) { + const u64 kSize1 = 1 << 6, kSize2 = 1 << 12; + const u64 n = kSize1 * kSize2; + TwoLevelByteMap<kSize1, kSize2> m; + m.TestOnlyInit(); + for (u64 i = 0; i < n; i += 7) { + m.set(i, (i % 100) + 1); + } + for (u64 j = 0; j < n; j++) { + if (j % 7) + EXPECT_EQ(m[j], 0); + else + EXPECT_EQ(m[j], (j % 100) + 1); + } + + m.TestOnlyUnmap(); +} + + +typedef TwoLevelByteMap<1 << 12, 1 << 13, TestMapUnmapCallback> TestByteMap; + +struct TestByteMapParam { + TestByteMap *m; + size_t shard; + size_t num_shards; +}; + +void *TwoLevelByteMapUserThread(void *param) { + TestByteMapParam *p = (TestByteMapParam*)param; + for (size_t i = p->shard; i < p->m->size(); i += p->num_shards) { + size_t val = (i % 100) + 1; + p->m->set(i, val); + EXPECT_EQ((*p->m)[i], val); + } + return 0; +} + +TEST(SanitizerCommon, ThreadedTwoLevelByteMap) { + TestByteMap m; + m.TestOnlyInit(); + TestMapUnmapCallback::map_count = 0; + TestMapUnmapCallback::unmap_count = 0; + static const int kNumThreads = 4; + pthread_t t[kNumThreads]; + TestByteMapParam p[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) { + p[i].m = &m; + p[i].shard = i; + p[i].num_shards = kNumThreads; + PTHREAD_CREATE(&t[i], 0, TwoLevelByteMapUserThread, &p[i]); + } + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_JOIN(t[i], 0); + } + EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); + EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, 0UL); + m.TestOnlyUnmap(); + EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); + EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, m.size1()); +} + #endif // #if TSAN_DEBUG==0 diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc b/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc index f6a944f68f5e..0cc3b9ba6944 100644 --- a/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc +++ b/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc @@ -156,7 +156,7 @@ void *operator new(size_t size) ALIAS("malloc"); void *operator new[](size_t size) ALIAS("malloc"); void *operator new(size_t size, std::nothrow_t const&) ALIAS("malloc"); void *operator new[](size_t size, std::nothrow_t const&) ALIAS("malloc"); -void operator delete(void *ptr) ALIAS("free"); -void operator delete[](void *ptr) ALIAS("free"); +void operator delete(void *ptr) throw() ALIAS("free"); +void operator delete[](void *ptr) throw() ALIAS("free"); void operator delete(void *ptr, std::nothrow_t const&) ALIAS("free"); void operator delete[](void *ptr, std::nothrow_t const&) ALIAS("free"); diff --git a/lib/sanitizer_common/tests/sanitizer_atomic_test.cc b/lib/sanitizer_common/tests/sanitizer_atomic_test.cc index a4a97c43e00f..56bcd35c826c 100644 --- a/lib/sanitizer_common/tests/sanitizer_atomic_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_atomic_test.cc @@ -15,6 +15,79 @@ namespace __sanitizer { +template<typename T> +struct ValAndMagic { + typename T::Type magic0; + T a; + typename T::Type magic1; + + static ValAndMagic<T> *sink; +}; + +template<typename T> +ValAndMagic<T> *ValAndMagic<T>::sink; + +template<typename T, memory_order load_mo, memory_order store_mo> +void CheckStoreLoad() { + typedef typename T::Type Type; + ValAndMagic<T> val; + // Prevent the compiler from scalarizing the struct. + ValAndMagic<T>::sink = &val; + // Ensure that surrounding memory is not overwritten. + val.magic0 = val.magic1 = (Type)-3; + for (u64 i = 0; i < 100; i++) { + // Generate a value that occupies all bytes of the variable. + u64 v = i; + v |= v << 8; + v |= v << 16; + v |= v << 32; + val.a.val_dont_use = (Type)v; + EXPECT_EQ(atomic_load(&val.a, load_mo), (Type)v); + val.a.val_dont_use = (Type)-1; + atomic_store(&val.a, (Type)v, store_mo); + EXPECT_EQ(val.a.val_dont_use, (Type)v); + } + EXPECT_EQ(val.magic0, (Type)-3); + EXPECT_EQ(val.magic1, (Type)-3); +} + +TEST(SanitizerCommon, AtomicStoreLoad) { + CheckStoreLoad<atomic_uint8_t, memory_order_relaxed, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint8_t, memory_order_consume, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint8_t, memory_order_acquire, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint8_t, memory_order_relaxed, memory_order_release>(); + CheckStoreLoad<atomic_uint8_t, memory_order_seq_cst, memory_order_seq_cst>(); + + CheckStoreLoad<atomic_uint16_t, memory_order_relaxed, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint16_t, memory_order_consume, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint16_t, memory_order_acquire, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint16_t, memory_order_relaxed, memory_order_release>(); + CheckStoreLoad<atomic_uint16_t, memory_order_seq_cst, memory_order_seq_cst>(); + + CheckStoreLoad<atomic_uint32_t, memory_order_relaxed, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint32_t, memory_order_consume, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint32_t, memory_order_acquire, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint32_t, memory_order_relaxed, memory_order_release>(); + CheckStoreLoad<atomic_uint32_t, memory_order_seq_cst, memory_order_seq_cst>(); + + CheckStoreLoad<atomic_uint64_t, memory_order_relaxed, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint64_t, memory_order_consume, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint64_t, memory_order_acquire, memory_order_relaxed>(); + CheckStoreLoad<atomic_uint64_t, memory_order_relaxed, memory_order_release>(); + CheckStoreLoad<atomic_uint64_t, memory_order_seq_cst, memory_order_seq_cst>(); + + CheckStoreLoad<atomic_uintptr_t, memory_order_relaxed, memory_order_relaxed> + (); + CheckStoreLoad<atomic_uintptr_t, memory_order_consume, memory_order_relaxed> + (); + CheckStoreLoad<atomic_uintptr_t, memory_order_acquire, memory_order_relaxed> + (); + CheckStoreLoad<atomic_uintptr_t, memory_order_relaxed, memory_order_release> + (); + CheckStoreLoad<atomic_uintptr_t, memory_order_seq_cst, memory_order_seq_cst> + (); +} + // Clang crashes while compiling this test for Android: // http://llvm.org/bugs/show_bug.cgi?id=15587 #if !SANITIZER_ANDROID diff --git a/lib/sanitizer_common/tests/sanitizer_bitvector_test.cc b/lib/sanitizer_common/tests/sanitizer_bitvector_test.cc new file mode 100644 index 000000000000..706b4c58968e --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_bitvector_test.cc @@ -0,0 +1,176 @@ +//===-- sanitizer_bitvector_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 Sanitizer runtime. +// Tests for sanitizer_bitvector.h. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_bitvector.h" + +#include "sanitizer_test_utils.h" + +#include "gtest/gtest.h" + +#include <algorithm> +#include <vector> +#include <set> + +using namespace __sanitizer; +using namespace std; + + +// Check the 'bv' == 's' and that the indexes go in increasing order. +// Also check the BV::Iterator +template <class BV> +static void CheckBV(const BV &bv, const set<uptr> &s) { + BV t; + t.copyFrom(bv); + set<uptr> t_s(s); + uptr last_idx = bv.size(); + uptr count = 0; + for (typename BV::Iterator it(bv); it.hasNext();) { + uptr idx = it.next(); + count++; + if (last_idx != bv.size()) + EXPECT_LT(last_idx, idx); + last_idx = idx; + EXPECT_TRUE(s.count(idx)); + } + EXPECT_EQ(count, s.size()); + + last_idx = bv.size(); + while (!t.empty()) { + uptr idx = t.getAndClearFirstOne(); + if (last_idx != bv.size()) + EXPECT_LT(last_idx, idx); + last_idx = idx; + EXPECT_TRUE(t_s.erase(idx)); + } + EXPECT_TRUE(t_s.empty()); +} + +template <class BV> +void Print(const BV &bv) { + BV t; + t.copyFrom(bv); + while (!t.empty()) { + uptr idx = t.getAndClearFirstOne(); + fprintf(stderr, "%zd ", idx); + } + fprintf(stderr, "\n"); +} + +void Print(const set<uptr> &s) { + for (set<uptr>::iterator it = s.begin(); it != s.end(); ++it) { + fprintf(stderr, "%zd ", *it); + } + fprintf(stderr, "\n"); +} + +template <class BV> +void TestBitVector(uptr expected_size) { + BV bv, bv1, t_bv; + EXPECT_EQ(expected_size, BV::kSize); + bv.clear(); + EXPECT_TRUE(bv.empty()); + bv.setBit(5); + EXPECT_FALSE(bv.empty()); + EXPECT_FALSE(bv.getBit(4)); + EXPECT_FALSE(bv.getBit(6)); + EXPECT_TRUE(bv.getBit(5)); + bv.clearBit(5); + EXPECT_FALSE(bv.getBit(5)); + + // test random bits + bv.clear(); + set<uptr> s; + for (uptr it = 0; it < 1000; it++) { + uptr bit = ((uptr)my_rand() % bv.size()); + EXPECT_EQ(bv.getBit(bit), s.count(bit) == 1); + switch (my_rand() % 2) { + case 0: + EXPECT_EQ(bv.setBit(bit), s.insert(bit).second); + break; + case 1: + size_t old_size = s.size(); + s.erase(bit); + EXPECT_EQ(bv.clearBit(bit), old_size > s.size()); + break; + } + EXPECT_EQ(bv.getBit(bit), s.count(bit) == 1); + } + + vector<uptr>bits(bv.size()); + // Test setUnion, setIntersection, setDifference, + // intersectsWith, and getAndClearFirstOne. + for (uptr it = 0; it < 30; it++) { + // iota + for (size_t j = 0; j < bits.size(); j++) bits[j] = j; + random_shuffle(bits.begin(), bits.end()); + set<uptr> s, s1, t_s; + bv.clear(); + bv1.clear(); + uptr n_bits = ((uptr)my_rand() % bv.size()) + 1; + uptr n_bits1 = (uptr)my_rand() % (bv.size() / 2); + EXPECT_TRUE(n_bits > 0 && n_bits <= bv.size()); + EXPECT_TRUE(n_bits1 < bv.size() / 2); + for (uptr i = 0; i < n_bits; i++) { + bv.setBit(bits[i]); + s.insert(bits[i]); + } + CheckBV(bv, s); + for (uptr i = 0; i < n_bits1; i++) { + bv1.setBit(bits[bv.size() / 2 + i]); + s1.insert(bits[bv.size() / 2 + i]); + } + CheckBV(bv1, s1); + + vector<uptr> vec; + set_intersection(s.begin(), s.end(), s1.begin(), s1.end(), + back_insert_iterator<vector<uptr> >(vec)); + EXPECT_EQ(bv.intersectsWith(bv1), !vec.empty()); + + // setUnion + t_s = s; + t_bv.copyFrom(bv); + t_s.insert(s1.begin(), s1.end()); + EXPECT_EQ(t_bv.setUnion(bv1), s.size() != t_s.size()); + CheckBV(t_bv, t_s); + + // setIntersection + t_s = set<uptr>(vec.begin(), vec.end()); + t_bv.copyFrom(bv); + EXPECT_EQ(t_bv.setIntersection(bv1), s.size() != t_s.size()); + CheckBV(t_bv, t_s); + + // setDifference + vec.clear(); + set_difference(s.begin(), s.end(), s1.begin(), s1.end(), + back_insert_iterator<vector<uptr> >(vec)); + t_s = set<uptr>(vec.begin(), vec.end()); + t_bv.copyFrom(bv); + EXPECT_EQ(t_bv.setDifference(bv1), s.size() != t_s.size()); + CheckBV(t_bv, t_s); + } +} + +TEST(SanitizerCommon, BasicBitVector) { + TestBitVector<BasicBitVector<u8> >(8); + TestBitVector<BasicBitVector<u16> >(16); + TestBitVector<BasicBitVector<> >(SANITIZER_WORDSIZE); +} + +TEST(SanitizerCommon, TwoLevelBitVector) { + uptr ws = SANITIZER_WORDSIZE; + TestBitVector<TwoLevelBitVector<1, BasicBitVector<u8> > >(8 * 8); + TestBitVector<TwoLevelBitVector<> >(ws * ws); + TestBitVector<TwoLevelBitVector<2> >(ws * ws * 2); + TestBitVector<TwoLevelBitVector<3> >(ws * ws * 3); + TestBitVector<TwoLevelBitVector<3, BasicBitVector<u16> > >(16 * 16 * 3); +} diff --git a/lib/sanitizer_common/tests/sanitizer_bvgraph_test.cc b/lib/sanitizer_common/tests/sanitizer_bvgraph_test.cc new file mode 100644 index 000000000000..3b39f8dd734a --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_bvgraph_test.cc @@ -0,0 +1,339 @@ +//===-- sanitizer_bvgraph_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 Sanitizer runtime. +// Tests for sanitizer_bvgraph.h. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_bvgraph.h" + +#include "sanitizer_test_utils.h" + +#include "gtest/gtest.h" + +#include <algorithm> +#include <vector> +#include <set> + +using namespace __sanitizer; +using namespace std; + +typedef BasicBitVector<u8> BV1; +typedef BasicBitVector<> BV2; +typedef TwoLevelBitVector<> BV3; +typedef TwoLevelBitVector<3, BasicBitVector<u8> > BV4; + +template<class G> +void PrintGraph(const G &g) { + for (uptr i = 0; i < g.size(); i++) { + for (uptr j = 0; j < g.size(); j++) { + fprintf(stderr, "%d", g.hasEdge(i, j)); + } + fprintf(stderr, "\n"); + } +} + + +class SimpleGraph { + public: + void clear() { s_.clear(); } + bool addEdge(uptr from, uptr to) { + return s_.insert(idx(from, to)).second; + } + bool removeEdge(uptr from, uptr to) { + return s_.erase(idx(from, to)); + } + template <class G> + void checkSameAs(G *g) { + for (set<uptr>::iterator it = s_.begin(); it != s_.end(); ++it) { + uptr from = *it >> 16; + uptr to = *it & ((1 << 16) - 1); + EXPECT_TRUE(g->removeEdge(from, to)); + } + EXPECT_TRUE(g->empty()); + } + private: + uptr idx(uptr from, uptr to) { + CHECK_LE(from|to, 1 << 16); + return (from << 16) + to; + } + set<uptr> s_; +}; + +template <class BV> +void BasicTest() { + BVGraph<BV> g; + g.clear(); + BV target; + SimpleGraph s_g; + set<uptr> s; + set<uptr> s_target; + int num_reachable = 0; + for (int it = 0; it < 1000; it++) { + target.clear(); + s_target.clear(); + for (int t = 0; t < 4; t++) { + uptr idx = (uptr)my_rand() % g.size(); + EXPECT_EQ(target.setBit(idx), s_target.insert(idx).second); + } + uptr from = my_rand() % g.size(); + uptr to = my_rand() % g.size(); + EXPECT_EQ(g.addEdge(from, to), s_g.addEdge(from, to)); + EXPECT_TRUE(g.hasEdge(from, to)); + for (int i = 0; i < 10; i++) { + from = my_rand() % g.size(); + bool is_reachable = g.isReachable(from, target); + if (is_reachable) { + uptr path[BV::kSize]; + uptr len; + for (len = 1; len < BV::kSize; len++) { + if (g.findPath(from, target, path, len) == len) + break; + } + EXPECT_LT(len, BV::kSize); + EXPECT_TRUE(target.getBit(path[len - 1])); + // fprintf(stderr, "reachable: %zd; path %zd {%zd %zd %zd}\n", + // from, len, path[0], path[1], path[2]); + num_reachable++; + } + } + } + EXPECT_GT(num_reachable, 0); +} + +TEST(BVGraph, BasicTest) { + BasicTest<BV1>(); + BasicTest<BV2>(); + BasicTest<BV3>(); + BasicTest<BV4>(); +} + +template <class BV> +void RemoveEdges() { + SimpleGraph s_g; + BVGraph<BV> g; + g.clear(); + BV bv; + set<uptr> s; + for (int it = 0; it < 100; it++) { + s.clear(); + bv.clear(); + s_g.clear(); + g.clear(); + for (uptr j = 0; j < g.size() * 2; j++) { + uptr from = my_rand() % g.size(); + uptr to = my_rand() % g.size(); + EXPECT_EQ(g.addEdge(from, to), s_g.addEdge(from, to)); + } + for (uptr j = 0; j < 5; j++) { + uptr idx = my_rand() % g.size(); + s.insert(idx); + bv.setBit(idx); + } + + if (it % 2) { + g.removeEdgesFrom(bv); + for (set<uptr>::iterator from = s.begin(); from != s.end(); ++from) { + for (uptr to = 0; to < g.size(); to++) + s_g.removeEdge(*from, to); + } + } else { + g.removeEdgesTo(bv); + for (set<uptr>::iterator to = s.begin(); to != s.end(); ++to) { + for (uptr from = 0; from < g.size(); from++) + s_g.removeEdge(from, *to); + } + } + s_g.checkSameAs(&g); + } +} + +TEST(BVGraph, RemoveEdges) { + RemoveEdges<BV1>(); + RemoveEdges<BV2>(); + RemoveEdges<BV3>(); + RemoveEdges<BV4>(); +} + +template <class BV> +void Test_isReachable() { + uptr path[5]; + BVGraph<BV> g; + g.clear(); + BV target; + target.clear(); + uptr t0 = 0; + uptr t1 = g.size() - 1; + target.setBit(t0); + target.setBit(t1); + + uptr f0 = 1; + uptr f1 = 2; + uptr f2 = g.size() / 2; + uptr f3 = g.size() - 2; + + EXPECT_FALSE(g.isReachable(f0, target)); + EXPECT_FALSE(g.isReachable(f1, target)); + EXPECT_FALSE(g.isReachable(f2, target)); + EXPECT_FALSE(g.isReachable(f3, target)); + + g.addEdge(f0, f1); + g.addEdge(f1, f2); + g.addEdge(f2, f3); + EXPECT_FALSE(g.isReachable(f0, target)); + EXPECT_FALSE(g.isReachable(f1, target)); + EXPECT_FALSE(g.isReachable(f2, target)); + EXPECT_FALSE(g.isReachable(f3, target)); + + g.addEdge(f1, t0); + EXPECT_TRUE(g.isReachable(f0, target)); + EXPECT_TRUE(g.isReachable(f1, target)); + EXPECT_FALSE(g.isReachable(f2, target)); + EXPECT_FALSE(g.isReachable(f3, target)); + EXPECT_EQ(g.findPath(f0, target, path, ARRAY_SIZE(path)), 3U); + EXPECT_EQ(path[0], f0); + EXPECT_EQ(path[1], f1); + EXPECT_EQ(path[2], t0); + EXPECT_EQ(g.findPath(f1, target, path, ARRAY_SIZE(path)), 2U); + EXPECT_EQ(path[0], f1); + EXPECT_EQ(path[1], t0); + + g.addEdge(f3, t1); + EXPECT_TRUE(g.isReachable(f0, target)); + EXPECT_TRUE(g.isReachable(f1, target)); + EXPECT_TRUE(g.isReachable(f2, target)); + EXPECT_TRUE(g.isReachable(f3, target)); +} + +TEST(BVGraph, isReachable) { + Test_isReachable<BV1>(); + Test_isReachable<BV2>(); + Test_isReachable<BV3>(); + Test_isReachable<BV4>(); +} + +template <class BV> +void LongCycle() { + BVGraph<BV> g; + g.clear(); + vector<uptr> path_vec(g.size()); + uptr *path = path_vec.data(); + uptr start = 5; + for (uptr i = start; i < g.size() - 1; i++) { + g.addEdge(i, i + 1); + for (uptr j = 0; j < start; j++) + g.addEdge(i, j); + } + // Bad graph that looks like this: + // 00000000000000 + // 00000000000000 + // 00000000000000 + // 00000000000000 + // 00000000000000 + // 11111010000000 + // 11111001000000 + // 11111000100000 + // 11111000010000 + // 11111000001000 + // 11111000000100 + // 11111000000010 + // 11111000000001 + // if (g.size() <= 64) PrintGraph(g); + BV target; + for (uptr i = start + 1; i < g.size(); i += 11) { + // if ((i & (i - 1)) == 0) fprintf(stderr, "Path: : %zd\n", i); + target.clear(); + target.setBit(i); + EXPECT_TRUE(g.isReachable(start, target)); + EXPECT_EQ(g.findPath(start, target, path, g.size()), i - start + 1); + } +} + +TEST(BVGraph, LongCycle) { + LongCycle<BV1>(); + LongCycle<BV2>(); + LongCycle<BV3>(); + LongCycle<BV4>(); +} + +template <class BV> +void ShortestPath() { + uptr path[8]; + BVGraph<BV> g; + g.clear(); + BV t7; + t7.clear(); + t7.setBit(7); + // 1=>2=>3=>4=>5=>6=>7 + // 1=>7 + g.addEdge(1, 2); + g.addEdge(2, 3); + g.addEdge(3, 4); + g.addEdge(4, 5); + g.addEdge(5, 6); + g.addEdge(6, 7); + g.addEdge(1, 7); + EXPECT_TRUE(g.isReachable(1, t7)); + // No path of length 1. + EXPECT_EQ(0U, g.findPath(1, t7, path, 1)); + // Trying to find a path of len 2..6 gives path of len 2. + EXPECT_EQ(2U, g.findPath(1, t7, path, 2)); + EXPECT_EQ(2U, g.findPath(1, t7, path, 3)); + EXPECT_EQ(2U, g.findPath(1, t7, path, 4)); + EXPECT_EQ(2U, g.findPath(1, t7, path, 5)); + EXPECT_EQ(2U, g.findPath(1, t7, path, 6)); + // Trying to find a path of len 7 gives path of len 7, because this is DFS. + EXPECT_EQ(7U, g.findPath(1, t7, path, 7)); + // But findShortestPath will find the shortest path. + EXPECT_EQ(2U, g.findShortestPath(1, t7, path, 2)); + EXPECT_EQ(2U, g.findShortestPath(1, t7, path, 7)); +} + +TEST(BVGraph, ShortestPath) { + ShortestPath<BV1>(); + ShortestPath<BV2>(); + ShortestPath<BV3>(); + ShortestPath<BV4>(); +} + +template <class BV> +void RunAddEdgesTest() { + BVGraph<BV> g; + BV from; + const int kMaxEdges = 10; + uptr added_edges[kMaxEdges]; + g.clear(); + from.clear(); + EXPECT_EQ(0U, g.addEdges(from, 0, added_edges, kMaxEdges)); + EXPECT_EQ(0U, g.addEdges(from, 1, added_edges, kMaxEdges)); + from.setBit(0); + EXPECT_EQ(1U, g.addEdges(from, 1, added_edges, kMaxEdges)); + EXPECT_EQ(0U, added_edges[0]); + EXPECT_EQ(0U, g.addEdges(from, 1, added_edges, kMaxEdges)); + + from.clear(); + from.setBit(1); + EXPECT_EQ(1U, g.addEdges(from, 4, added_edges, kMaxEdges)); + EXPECT_TRUE(g.hasEdge(1, 4)); + EXPECT_FALSE(g.hasEdge(1, 5)); + EXPECT_EQ(1U, added_edges[0]); + from.setBit(2); + from.setBit(3); + EXPECT_EQ(2U, g.addEdges(from, 4, added_edges, kMaxEdges)); + EXPECT_TRUE(g.hasEdge(2, 4)); + EXPECT_FALSE(g.hasEdge(2, 5)); + EXPECT_TRUE(g.hasEdge(3, 4)); + EXPECT_FALSE(g.hasEdge(3, 5)); + EXPECT_EQ(2U, added_edges[0]); + EXPECT_EQ(3U, added_edges[1]); +} + +TEST(BVGraph, AddEdgesTest) { + RunAddEdgesTest<BV2>(); +} diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc index 608f90487a7b..e08a38c82450 100644 --- a/lib/sanitizer_common/tests/sanitizer_common_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_common_test.cc @@ -15,6 +15,9 @@ #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_platform.h" + +#include "sanitizer_pthread_wrappers.h" + #include "gtest/gtest.h" namespace __sanitizer { @@ -113,6 +116,9 @@ TEST(SanitizerCommon, InternalMmapVector) { vector.pop_back(); EXPECT_EQ((uptr)i, vector.size()); } + InternalMmapVector<uptr> empty_vector(0); + CHECK_GT(empty_vector.capacity(), 0U); + CHECK_EQ(0U, empty_vector.size()); } void TestThreadInfo(bool main) { @@ -156,8 +162,8 @@ TEST(SanitizerCommon, ThreadStackTlsMain) { TEST(SanitizerCommon, ThreadStackTlsWorker) { InitTlsSize(); pthread_t t; - pthread_create(&t, 0, WorkerThread, 0); - pthread_join(t, 0); + PTHREAD_CREATE(&t, 0, WorkerThread, 0); + PTHREAD_JOIN(t, 0); } bool UptrLess(uptr a, uptr b) { @@ -220,40 +226,4 @@ TEST(SanitizerCommon, InternalScopedString) { EXPECT_STREQ("012345678", str.data()); } -TEST(SanitizerCommon, PrintSourceLocation) { - InternalScopedString str(128); - PrintSourceLocation(&str, "/dir/file.cc", 10, 5); - EXPECT_STREQ("/dir/file.cc:10:5", str.data()); - - str.clear(); - PrintSourceLocation(&str, "/dir/file.cc", 11, 0); - EXPECT_STREQ("/dir/file.cc:11", str.data()); - - str.clear(); - PrintSourceLocation(&str, "/dir/file.cc", 0, 0); - EXPECT_STREQ("/dir/file.cc", str.data()); - - // Check that we strip file prefix if necessary. - const char *old_strip_path_prefix = common_flags()->strip_path_prefix; - common_flags()->strip_path_prefix = "/dir/"; - str.clear(); - PrintSourceLocation(&str, "/dir/file.cc", 10, 5); - EXPECT_STREQ("file.cc:10:5", str.data()); - common_flags()->strip_path_prefix = old_strip_path_prefix; -} - -TEST(SanitizerCommon, PrintModuleAndOffset) { - InternalScopedString str(128); - PrintModuleAndOffset(&str, "/dir/exe", 0x123); - EXPECT_STREQ("(/dir/exe+0x123)", str.data()); - - // Check that we strip file prefix if necessary. - const char *old_strip_path_prefix = common_flags()->strip_path_prefix; - common_flags()->strip_path_prefix = "/dir/"; - str.clear(); - PrintModuleAndOffset(&str, "/dir/exe", 0x123); - EXPECT_STREQ("(exe+0x123)", str.data()); - common_flags()->strip_path_prefix = old_strip_path_prefix; -} - } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc b/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc new file mode 100644 index 000000000000..8c8363353507 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc @@ -0,0 +1,496 @@ +//===-- sanitizer_deadlock_detector_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 Sanitizer runtime. +// Tests for sanitizer_deadlock_detector.h +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_deadlock_detector.h" + +#include "sanitizer_test_utils.h" + +#include "gtest/gtest.h" + +#include <algorithm> +#include <vector> +#include <set> + +using namespace __sanitizer; +using namespace std; + +typedef BasicBitVector<u8> BV1; +typedef BasicBitVector<> BV2; +typedef TwoLevelBitVector<> BV3; +typedef TwoLevelBitVector<3, BasicBitVector<u8> > BV4; + +// Poor man's unique_ptr. +template<class BV> +struct ScopedDD { + ScopedDD() { + dp = new DeadlockDetector<BV>; + dp->clear(); + dtls.clear(); + } + ~ScopedDD() { delete dp; } + DeadlockDetector<BV> *dp; + DeadlockDetectorTLS<BV> dtls; +}; + +template <class BV> +void RunBasicTest() { + uptr path[10]; + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + set<uptr> s; + for (size_t i = 0; i < d.size() * 3; i++) { + uptr node = d.newNode(0); + EXPECT_TRUE(s.insert(node).second); + } + + d.clear(); + s.clear(); + // Add size() nodes. + for (size_t i = 0; i < d.size(); i++) { + uptr node = d.newNode(0); + EXPECT_TRUE(s.insert(node).second); + } + // Remove all nodes. + for (set<uptr>::iterator it = s.begin(); it != s.end(); ++it) + d.removeNode(*it); + // The nodes should be reused. + for (size_t i = 0; i < d.size(); i++) { + uptr node = d.newNode(0); + EXPECT_FALSE(s.insert(node).second); + } + + // Cycle: n1->n2->n1 + { + d.clear(); + dtls.clear(); + uptr n1 = d.newNode(1); + uptr n2 = d.newNode(2); + EXPECT_FALSE(d.onLock(&dtls, n1)); + EXPECT_FALSE(d.onLock(&dtls, n2)); + d.onUnlock(&dtls, n2); + d.onUnlock(&dtls, n1); + + EXPECT_FALSE(d.onLock(&dtls, n2)); + EXPECT_EQ(0U, d.findPathToLock(&dtls, n1, path, 1)); + EXPECT_EQ(2U, d.findPathToLock(&dtls, n1, path, 10)); + EXPECT_EQ(2U, d.findPathToLock(&dtls, n1, path, 2)); + EXPECT_TRUE(d.onLock(&dtls, n1)); + EXPECT_EQ(path[0], n1); + EXPECT_EQ(path[1], n2); + EXPECT_EQ(d.getData(n1), 1U); + EXPECT_EQ(d.getData(n2), 2U); + d.onUnlock(&dtls, n1); + d.onUnlock(&dtls, n2); + } + + // Cycle: n1->n2->n3->n1 + { + d.clear(); + dtls.clear(); + uptr n1 = d.newNode(1); + uptr n2 = d.newNode(2); + uptr n3 = d.newNode(3); + + EXPECT_FALSE(d.onLock(&dtls, n1)); + EXPECT_FALSE(d.onLock(&dtls, n2)); + d.onUnlock(&dtls, n2); + d.onUnlock(&dtls, n1); + + EXPECT_FALSE(d.onLock(&dtls, n2)); + EXPECT_FALSE(d.onLock(&dtls, n3)); + d.onUnlock(&dtls, n3); + d.onUnlock(&dtls, n2); + + EXPECT_FALSE(d.onLock(&dtls, n3)); + EXPECT_EQ(0U, d.findPathToLock(&dtls, n1, path, 2)); + EXPECT_EQ(3U, d.findPathToLock(&dtls, n1, path, 10)); + EXPECT_TRUE(d.onLock(&dtls, n1)); + EXPECT_EQ(path[0], n1); + EXPECT_EQ(path[1], n2); + EXPECT_EQ(path[2], n3); + EXPECT_EQ(d.getData(n1), 1U); + EXPECT_EQ(d.getData(n2), 2U); + EXPECT_EQ(d.getData(n3), 3U); + d.onUnlock(&dtls, n1); + d.onUnlock(&dtls, n3); + } +} + +TEST(DeadlockDetector, BasicTest) { + RunBasicTest<BV1>(); + RunBasicTest<BV2>(); + RunBasicTest<BV3>(); + RunBasicTest<BV4>(); +} + +template <class BV> +void RunRemoveNodeTest() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + + uptr l0 = d.newNode(0); + uptr l1 = d.newNode(1); + uptr l2 = d.newNode(2); + uptr l3 = d.newNode(3); + uptr l4 = d.newNode(4); + uptr l5 = d.newNode(5); + + // l0=>l1=>l2 + d.onLock(&dtls, l0); + d.onLock(&dtls, l1); + d.onLock(&dtls, l2); + d.onUnlock(&dtls, l1); + d.onUnlock(&dtls, l0); + d.onUnlock(&dtls, l2); + // l3=>l4=>l5 + d.onLock(&dtls, l3); + d.onLock(&dtls, l4); + d.onLock(&dtls, l5); + d.onUnlock(&dtls, l4); + d.onUnlock(&dtls, l3); + d.onUnlock(&dtls, l5); + + set<uptr> locks; + locks.insert(l0); + locks.insert(l1); + locks.insert(l2); + locks.insert(l3); + locks.insert(l4); + locks.insert(l5); + for (uptr i = 6; i < d.size(); i++) { + uptr lt = d.newNode(i); + locks.insert(lt); + d.onLock(&dtls, lt); + d.onUnlock(&dtls, lt); + d.removeNode(lt); + } + EXPECT_EQ(locks.size(), d.size()); + // l2=>l0 + EXPECT_FALSE(d.onLock(&dtls, l2)); + EXPECT_TRUE(d.onLock(&dtls, l0)); + d.onUnlock(&dtls, l2); + d.onUnlock(&dtls, l0); + // l4=>l3 + EXPECT_FALSE(d.onLock(&dtls, l4)); + EXPECT_TRUE(d.onLock(&dtls, l3)); + d.onUnlock(&dtls, l4); + d.onUnlock(&dtls, l3); + + EXPECT_EQ(d.size(), d.testOnlyGetEpoch()); + + d.removeNode(l2); + d.removeNode(l3); + locks.clear(); + // make sure no edges from or to l0,l1,l4,l5 left. + for (uptr i = 4; i < d.size(); i++) { + uptr lt = d.newNode(i); + locks.insert(lt); + uptr a, b; + // l0 => lt? + a = l0; b = lt; + EXPECT_FALSE(d.onLock(&dtls, a)); + EXPECT_FALSE(d.onLock(&dtls, b)); + d.onUnlock(&dtls, a); + d.onUnlock(&dtls, b); + // l1 => lt? + a = l1; b = lt; + EXPECT_FALSE(d.onLock(&dtls, a)); + EXPECT_FALSE(d.onLock(&dtls, b)); + d.onUnlock(&dtls, a); + d.onUnlock(&dtls, b); + // lt => l4? + a = lt; b = l4; + EXPECT_FALSE(d.onLock(&dtls, a)); + EXPECT_FALSE(d.onLock(&dtls, b)); + d.onUnlock(&dtls, a); + d.onUnlock(&dtls, b); + // lt => l5? + a = lt; b = l5; + EXPECT_FALSE(d.onLock(&dtls, a)); + EXPECT_FALSE(d.onLock(&dtls, b)); + d.onUnlock(&dtls, a); + d.onUnlock(&dtls, b); + + d.removeNode(lt); + } + // Still the same epoch. + EXPECT_EQ(d.size(), d.testOnlyGetEpoch()); + EXPECT_EQ(locks.size(), d.size() - 4); + // l2 and l3 should have ben reused. + EXPECT_EQ(locks.count(l2), 1U); + EXPECT_EQ(locks.count(l3), 1U); +} + +TEST(DeadlockDetector, RemoveNodeTest) { + RunRemoveNodeTest<BV1>(); + RunRemoveNodeTest<BV2>(); + RunRemoveNodeTest<BV3>(); + RunRemoveNodeTest<BV4>(); +} + +template <class BV> +void RunMultipleEpochsTest() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + + set<uptr> locks; + for (uptr i = 0; i < d.size(); i++) { + EXPECT_TRUE(locks.insert(d.newNode(i)).second); + } + EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); + for (uptr i = 0; i < d.size(); i++) { + EXPECT_TRUE(locks.insert(d.newNode(i)).second); + EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); + } + locks.clear(); + + uptr l0 = d.newNode(0); + uptr l1 = d.newNode(0); + d.onLock(&dtls, l0); + d.onLock(&dtls, l1); + d.onUnlock(&dtls, l0); + EXPECT_EQ(d.testOnlyGetEpoch(), 3 * d.size()); + for (uptr i = 0; i < d.size(); i++) { + EXPECT_TRUE(locks.insert(d.newNode(i)).second); + } + EXPECT_EQ(d.testOnlyGetEpoch(), 4 * d.size()); + +#if TSAN_DEBUG == 0 + // EXPECT_DEATH clones a thread with 4K stack, + // which is overflown by tsan memory accesses functions in debug mode. + + // Can not handle the locks from the previous epoch. + // The caller should update the lock id. + EXPECT_DEATH(d.onLock(&dtls, l0), "CHECK failed.*current_epoch_"); +#endif +} + +TEST(DeadlockDetector, MultipleEpochsTest) { + RunMultipleEpochsTest<BV1>(); + RunMultipleEpochsTest<BV2>(); + RunMultipleEpochsTest<BV3>(); + RunMultipleEpochsTest<BV4>(); +} + +template <class BV> +void RunCorrectEpochFlush() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + vector<uptr> locks1; + for (uptr i = 0; i < d.size(); i++) + locks1.push_back(d.newNode(i)); + EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); + d.onLock(&dtls, locks1[3]); + d.onLock(&dtls, locks1[4]); + d.onLock(&dtls, locks1[5]); + + // We have a new epoch, old locks in dtls will have to be forgotten. + uptr l0 = d.newNode(0); + EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); + uptr l1 = d.newNode(0); + EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); + d.onLock(&dtls, l0); + d.onLock(&dtls, l1); + EXPECT_TRUE(d.testOnlyHasEdgeRaw(0, 1)); + EXPECT_FALSE(d.testOnlyHasEdgeRaw(1, 0)); + EXPECT_FALSE(d.testOnlyHasEdgeRaw(3, 0)); + EXPECT_FALSE(d.testOnlyHasEdgeRaw(4, 0)); + EXPECT_FALSE(d.testOnlyHasEdgeRaw(5, 0)); +} + +TEST(DeadlockDetector, CorrectEpochFlush) { + RunCorrectEpochFlush<BV1>(); + RunCorrectEpochFlush<BV2>(); +} + +template <class BV> +void RunTryLockTest() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + + uptr l0 = d.newNode(0); + uptr l1 = d.newNode(0); + uptr l2 = d.newNode(0); + EXPECT_FALSE(d.onLock(&dtls, l0)); + EXPECT_FALSE(d.onTryLock(&dtls, l1)); + EXPECT_FALSE(d.onLock(&dtls, l2)); + EXPECT_TRUE(d.isHeld(&dtls, l0)); + EXPECT_TRUE(d.isHeld(&dtls, l1)); + EXPECT_TRUE(d.isHeld(&dtls, l2)); + EXPECT_FALSE(d.testOnlyHasEdge(l0, l1)); + EXPECT_TRUE(d.testOnlyHasEdge(l1, l2)); + d.onUnlock(&dtls, l0); + d.onUnlock(&dtls, l1); + d.onUnlock(&dtls, l2); +} + +TEST(DeadlockDetector, TryLockTest) { + RunTryLockTest<BV1>(); + RunTryLockTest<BV2>(); +} + +template <class BV> +void RunOnFirstLockTest() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + + uptr l0 = d.newNode(0); + uptr l1 = d.newNode(0); + EXPECT_FALSE(d.onFirstLock(&dtls, l0)); // dtls has old epoch. + d.onLock(&dtls, l0); + d.onUnlock(&dtls, l0); + + EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Ok, same ecpoch, first lock. + EXPECT_FALSE(d.onFirstLock(&dtls, l1)); // Second lock. + d.onLock(&dtls, l1); + d.onUnlock(&dtls, l1); + d.onUnlock(&dtls, l0); + + EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Ok + d.onUnlock(&dtls, l0); + + vector<uptr> locks1; + for (uptr i = 0; i < d.size(); i++) + locks1.push_back(d.newNode(i)); + + EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Epoch has changed, but not in dtls. + + uptr l3 = d.newNode(0); + d.onLock(&dtls, l3); + d.onUnlock(&dtls, l3); + + EXPECT_FALSE(d.onFirstLock(&dtls, l0)); // Epoch has changed in dtls. +} + +TEST(DeadlockDetector, onFirstLockTest) { + RunOnFirstLockTest<BV2>(); +} + +template <class BV> +void RunRecusriveLockTest() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + + uptr l0 = d.newNode(0); + uptr l1 = d.newNode(0); + uptr l2 = d.newNode(0); + uptr l3 = d.newNode(0); + + EXPECT_FALSE(d.onLock(&dtls, l0)); + EXPECT_FALSE(d.onLock(&dtls, l1)); + EXPECT_FALSE(d.onLock(&dtls, l0)); // Recurisve. + EXPECT_FALSE(d.onLock(&dtls, l2)); + d.onUnlock(&dtls, l0); + EXPECT_FALSE(d.onLock(&dtls, l3)); + d.onUnlock(&dtls, l0); + d.onUnlock(&dtls, l1); + d.onUnlock(&dtls, l2); + d.onUnlock(&dtls, l3); + EXPECT_TRUE(d.testOnlyHasEdge(l0, l1)); + EXPECT_TRUE(d.testOnlyHasEdge(l0, l2)); + EXPECT_TRUE(d.testOnlyHasEdge(l0, l3)); +} + +TEST(DeadlockDetector, RecusriveLockTest) { + RunRecusriveLockTest<BV2>(); +} + +template <class BV> +void RunLockContextTest() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + + uptr l0 = d.newNode(0); + uptr l1 = d.newNode(0); + uptr l2 = d.newNode(0); + uptr l3 = d.newNode(0); + uptr l4 = d.newNode(0); + EXPECT_FALSE(d.onLock(&dtls, l0, 10)); + EXPECT_FALSE(d.onLock(&dtls, l1, 11)); + EXPECT_FALSE(d.onLock(&dtls, l2, 12)); + EXPECT_FALSE(d.onLock(&dtls, l3, 13)); + EXPECT_EQ(10U, d.findLockContext(&dtls, l0)); + EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); + EXPECT_EQ(12U, d.findLockContext(&dtls, l2)); + EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); + d.onUnlock(&dtls, l0); + EXPECT_EQ(0U, d.findLockContext(&dtls, l0)); + EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); + EXPECT_EQ(12U, d.findLockContext(&dtls, l2)); + EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); + d.onUnlock(&dtls, l2); + EXPECT_EQ(0U, d.findLockContext(&dtls, l0)); + EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); + EXPECT_EQ(0U, d.findLockContext(&dtls, l2)); + EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); + + EXPECT_FALSE(d.onLock(&dtls, l4, 14)); + EXPECT_EQ(14U, d.findLockContext(&dtls, l4)); +} + +TEST(DeadlockDetector, LockContextTest) { + RunLockContextTest<BV2>(); +} + +template <class BV> +void RunRemoveEdgesTest() { + ScopedDD<BV> sdd; + DeadlockDetector<BV> &d = *sdd.dp; + DeadlockDetectorTLS<BV> &dtls = sdd.dtls; + vector<uptr> node(BV::kSize); + u32 stk_from = 0, stk_to = 0; + int unique_tid = 0; + for (size_t i = 0; i < BV::kSize; i++) + node[i] = d.newNode(0); + + for (size_t i = 0; i < BV::kSize; i++) + EXPECT_FALSE(d.onLock(&dtls, node[i], i + 1)); + for (size_t i = 0; i < BV::kSize; i++) { + for (uptr j = i + 1; j < BV::kSize; j++) { + EXPECT_TRUE( + d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); + EXPECT_EQ(stk_from, i + 1); + EXPECT_EQ(stk_to, j + 1); + } + } + EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); + // Remove and re-create half of the nodes. + for (uptr i = 1; i < BV::kSize; i += 2) + d.removeNode(node[i]); + for (uptr i = 1; i < BV::kSize; i += 2) + node[i] = d.newNode(0); + EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); + // The edges from or to the removed nodes should be gone. + for (size_t i = 0; i < BV::kSize; i++) { + for (uptr j = i + 1; j < BV::kSize; j++) { + if ((i % 2) || (j % 2)) + EXPECT_FALSE( + d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); + else + EXPECT_TRUE( + d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); + } + } +} + +TEST(DeadlockDetector, RemoveEdgesTest) { + RunRemoveEdgesTest<BV1>(); +} diff --git a/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/lib/sanitizer_common/tests/sanitizer_flags_test.cc index cd3cac11bc80..1055f5d24d6b 100644 --- a/lib/sanitizer_common/tests/sanitizer_flags_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_flags_test.cc @@ -24,14 +24,14 @@ static const char kFlagName[] = "flag_name"; template <typename T> static void TestFlag(T start_value, const char *env, T final_value) { T flag = start_value; - ParseFlag(env, &flag, kFlagName); + ParseFlag(env, &flag, kFlagName, "flag description"); EXPECT_EQ(final_value, flag); } static void TestStrFlag(const char *start_value, const char *env, const char *final_value) { const char *flag = start_value; - ParseFlag(env, &flag, kFlagName); + ParseFlag(env, &flag, kFlagName, "flag description"); EXPECT_EQ(0, internal_strcmp(final_value, flag)); } @@ -49,7 +49,7 @@ TEST(SanitizerCommon, BooleanFlags) { TEST(SanitizerCommon, IntFlags) { TestFlag(-11, 0, -11); - TestFlag(-11, "flag_name", 0); + TestFlag(-11, "flag_name", -11); TestFlag(-11, "--flag_name=", 0); TestFlag(-11, "--flag_name=42", 42); TestFlag(-11, "--flag_name=-42", -42); @@ -57,7 +57,7 @@ TEST(SanitizerCommon, IntFlags) { TEST(SanitizerCommon, StrFlags) { TestStrFlag("zzz", 0, "zzz"); - TestStrFlag("zzz", "flag_name", ""); + TestStrFlag("zzz", "flag_name", "zzz"); TestStrFlag("zzz", "--flag_name=", ""); TestStrFlag("", "--flag_name=abc", "abc"); TestStrFlag("", "--flag_name='abc zxc'", "abc zxc"); @@ -70,8 +70,8 @@ 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"); + ParseFlag(env, &flag1, "flag1", "flag1 description"); + ParseFlag(env, &flag2, "flag2", "flag2 description"); EXPECT_EQ(expected_flag1, flag1); EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2)); } diff --git a/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc b/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cc index e0354062508f..13918aff1009 100644 --- a/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cc @@ -1,4 +1,4 @@ -//===-- sanitizer_scanf_interceptor_test.cc -------------------------------===// +//===-- sanitizer_format_interceptor_test.cc ------------------------------===// // // The LLVM Compiler Infrastructure // @@ -10,22 +10,59 @@ // Tests for *scanf interceptors implementation in sanitizer_common. // //===----------------------------------------------------------------------===// +#include <algorithm> #include <vector> #include "interception/interception.h" #include "sanitizer_test_utils.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_common.h" #include "gtest/gtest.h" using namespace __sanitizer; +#define COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) \ + do { \ + ((std::vector<unsigned> *)ctx)->push_back(size); \ + ptr = ptr; \ + } while (0) + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) + #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - ((std::vector<unsigned> *)ctx)->push_back(size) + COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) + +#define SANITIZER_INTERCEPT_PRINTF 1 +#include "sanitizer_common/sanitizer_common_interceptors_format.inc" + +static const unsigned I = sizeof(int); +static const unsigned L = sizeof(long); +static const unsigned LL = sizeof(long long); +static const unsigned S = sizeof(short); +static const unsigned C = sizeof(char); +static const unsigned LC = sizeof(wchar_t); +static const unsigned D = sizeof(double); +static const unsigned LD = sizeof(long double); +static const unsigned F = sizeof(float); +static const unsigned P = sizeof(char *); + +static void verifyFormatResults(const char *format, unsigned n, + const std::vector<unsigned> &computed_sizes, + va_list expected_sizes) { + // "+ 1" because of format string + ASSERT_EQ(n + 1, + computed_sizes.size()) << "Unexpected number of format arguments: '" + << format << "'"; + for (unsigned i = 0; i < n; ++i) + EXPECT_EQ(va_arg(expected_sizes, unsigned), computed_sizes[i + 1]) + << "Unexpect write size for argument " << i << ", format string '" + << format << "'"; +} -#include "sanitizer_common/sanitizer_common_interceptors_scanf.inc" +static const char test_buf[] = "Test string."; +static const size_t test_buf_size = sizeof(test_buf); -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, @@ -42,15 +79,10 @@ static void testScanf2(const char *format, int scanf_result, std::vector<unsigned> scanf_sizes; // 16 args should be enough. 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 << "'"; + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf); + verifyFormatResults(format, n, scanf_sizes, expected_sizes); } static void testScanf(const char *format, unsigned n, ...) { @@ -76,23 +108,15 @@ static void testScanfNoGnuMalloc(const char *format, unsigned n, ...) { } 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 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); testScanf("ab%u%dc", 2, I, I); testScanf("%ld", 1, L); testScanf("%llu", 1, LL); + testScanf("%qd", 1, LL); testScanf("a %hd%hhx", 2, S, C); testScanf("%c", 1, C); + testScanf("%lc", 1, LC); testScanf("%%", 0); testScanf("a%%", 0); @@ -108,15 +132,17 @@ TEST(SanitizerCommonInterceptors, Scanf) { testScanf("%10s", 1, 11); testScanf("%10c", 1, 10); + testScanf("%10ls", 1, 11 * LC); + testScanf("%10lc", 1, 10 * LC); 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("%s%d", 2, test_buf_size, I); + testScanf("%[abc]", 1, test_buf_size); testScanf("%4[bcdef]", 1, 5); - testScanf("%[]]", 1, scanf_buf_size); + testScanf("%[]]", 1, test_buf_size); testScanf("%8[^]%d0-9-]%c", 2, 9, C); testScanf("%*[^:]%n:%d:%1[ ]%n", 4, I, I, 2, I); @@ -172,7 +198,62 @@ TEST(SanitizerCommonInterceptors, Scanf) { testScanfPartial("%d%n%n%d //1\n", 1, 3, I, I, 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); + testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, test_buf_size); + testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, test_buf_size, + test_buf_size); +} + +static void testPrintf3(void *ctx, const char *format, ...) { + va_list ap; + va_start(ap, format); + printf_common(ctx, format, ap); + va_end(ap); +} + +static void testPrintf2(const char *format, unsigned n, + va_list expected_sizes) { + std::vector<unsigned> printf_sizes; + // 16 args should be enough. + testPrintf3((void *)&printf_sizes, format, + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf); + verifyFormatResults(format, n, printf_sizes, expected_sizes); +} + +static void testPrintf(const char *format, unsigned n, ...) { + va_list ap; + va_start(ap, n); + testPrintf2(format, n, ap); + va_end(ap); +} + +TEST(SanitizerCommonInterceptors, Printf) { + // Only test functionality which differs from scanf + + // Indexed arguments + testPrintf("%5$d", 0); + testPrintf("%.*5$d", 0); + + // errno + testPrintf("%0-m", 0); + + // Dynamic width + testPrintf("%*n", 1, I); + testPrintf("%*.10n", 1, I); + + // Precision + testPrintf("%10.10n", 1, I); + testPrintf("%.3s", 1, 3); + testPrintf("%.20s", 1, test_buf_size); + + // Dynamic precision + testPrintf("%.*n", 1, I); + testPrintf("%10.*n", 1, I); + + // Dynamic precision for strings is not implemented yet. + testPrintf("%.*s", 1, 0); + + // Checks for wide-character strings are not implemented yet. + testPrintf("%ls", 1, 0); } diff --git a/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc b/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc index 154d9860b79f..22fa5228dfbd 100644 --- a/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc @@ -47,6 +47,7 @@ static struct IoctlInit { // Avoid unused function warnings. (void)&ioctl_common_pre; (void)&ioctl_common_post; + (void)&ioctl_decode; } } ioctl_static_initializer; @@ -74,4 +75,29 @@ TEST(SanitizerIoctl, Fixup) { EXPECT_EQ(EVIOCGKEY(0), desc->req); } +// Test decoding KVM ioctl numbers. +TEST(SanitizerIoctl, KVM_GET_MP_STATE) { + ioctl_desc desc; + bool res = ioctl_decode(0x8004ae98U, &desc); + EXPECT_TRUE(res); + EXPECT_EQ(ioctl_desc::WRITE, desc.type); + EXPECT_EQ(4U, desc.size); +} + +TEST(SanitizerIoctl, KVM_GET_LAPIC) { + ioctl_desc desc; + bool res = ioctl_decode(0x8400ae8eU, &desc); + EXPECT_TRUE(res); + EXPECT_EQ(ioctl_desc::WRITE, desc.type); + EXPECT_EQ(1024U, desc.size); +} + +TEST(SanitizerIoctl, KVM_GET_MSR_INDEX_LIST) { + ioctl_desc desc; + bool res = ioctl_decode(0xc004ae02U, &desc); + EXPECT_TRUE(res); + EXPECT_EQ(ioctl_desc::READWRITE, desc.type); + EXPECT_EQ(4U, desc.size); +} + #endif // SANITIZER_LINUX diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc index c4f3d8033c2d..660710d5bb7e 100644 --- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc @@ -55,6 +55,8 @@ struct stat_and_more { unsigned char z; }; +// FIXME: File manipulations are not yet supported on Windows +#if !defined(_WIN32) TEST(SanitizerCommon, FileOps) { const char *str1 = "qwerty"; uptr len1 = internal_strlen(str1); @@ -114,6 +116,7 @@ TEST(SanitizerCommon, FileOps) { EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); internal_close(fd); } +#endif TEST(SanitizerCommon, InternalStrFunctions) { const char *haystack = "haystack"; diff --git a/lib/sanitizer_common/tests/sanitizer_mutex_test.cc b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc index 06f742b41796..d14e7c2fba21 100644 --- a/lib/sanitizer_common/tests/sanitizer_mutex_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc @@ -12,6 +12,9 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_common.h" + +#include "sanitizer_pthread_wrappers.h" + #include "gtest/gtest.h" #include <string.h> @@ -103,9 +106,9 @@ TEST(SanitizerCommon, SpinMutex) { TestData<SpinMutex> data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, lock_thread<SpinMutex>, &data); + PTHREAD_CREATE(&threads[i], 0, lock_thread<SpinMutex>, &data); for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); + PTHREAD_JOIN(threads[i], 0); } TEST(SanitizerCommon, SpinMutexTry) { @@ -114,9 +117,9 @@ TEST(SanitizerCommon, SpinMutexTry) { TestData<SpinMutex> data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, try_thread<SpinMutex>, &data); + PTHREAD_CREATE(&threads[i], 0, try_thread<SpinMutex>, &data); for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); + PTHREAD_JOIN(threads[i], 0); } TEST(SanitizerCommon, BlockingMutex) { @@ -125,9 +128,9 @@ TEST(SanitizerCommon, BlockingMutex) { TestData<BlockingMutex> data(mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, lock_thread<BlockingMutex>, &data); + PTHREAD_CREATE(&threads[i], 0, lock_thread<BlockingMutex>, &data); for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); + PTHREAD_JOIN(threads[i], 0); check_locked(mtx); } diff --git a/lib/sanitizer_common/tests/sanitizer_posix_test.cc b/lib/sanitizer_common/tests/sanitizer_posix_test.cc index 035899c83022..56ce416141fa 100644 --- a/lib/sanitizer_common/tests/sanitizer_posix_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_posix_test.cc @@ -18,6 +18,7 @@ #include "gtest/gtest.h" #include <pthread.h> +#include <sys/mman.h> namespace __sanitizer { @@ -57,6 +58,23 @@ TEST(SanitizerCommon, PthreadDestructorIterations) { EXPECT_FALSE(destructor_executed); } +TEST(SanitizerCommon, IsAccessibleMemoryRange) { + const int page_size = GetPageSize(); + uptr mem = (uptr)mmap(0, 3 * page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + // Protect the middle page. + mprotect((void *)(mem + page_size), page_size, PROT_NONE); + EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size - 1)); + EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size)); + EXPECT_FALSE(IsAccessibleMemoryRange(mem, page_size + 1)); + EXPECT_TRUE(IsAccessibleMemoryRange(mem + page_size - 1, 1)); + EXPECT_FALSE(IsAccessibleMemoryRange(mem + page_size - 1, 2)); + EXPECT_FALSE(IsAccessibleMemoryRange(mem + 2 * page_size - 1, 1)); + EXPECT_TRUE(IsAccessibleMemoryRange(mem + 2 * page_size, page_size)); + EXPECT_FALSE(IsAccessibleMemoryRange(mem, 3 * page_size)); + EXPECT_FALSE(IsAccessibleMemoryRange(0x0, 2)); +} + } // namespace __sanitizer #endif // SANITIZER_POSIX diff --git a/lib/sanitizer_common/tests/sanitizer_printf_test.cc b/lib/sanitizer_common/tests/sanitizer_printf_test.cc index 2c478cc74286..d0b46ac94ff2 100644 --- a/lib/sanitizer_common/tests/sanitizer_printf_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_printf_test.cc @@ -103,6 +103,11 @@ TEST(Printf, OverflowPtr) { EXPECT_EQ(buf[9], 0); } +#if defined(_WIN32) +// Oh well, MSVS headers don't define snprintf. +# define snprintf _snprintf +#endif + template<typename T> static void TestAgainstLibc(const char *fmt, T arg1, T arg2) { char buf[1024]; @@ -115,12 +120,14 @@ static void TestAgainstLibc(const char *fmt, T arg1, T arg2) { TEST(Printf, MinMax) { TestAgainstLibc<int>("%d-%d", INT_MIN, INT_MAX); // NOLINT - TestAgainstLibc<long>("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT TestAgainstLibc<unsigned>("%u-%u", 0, UINT_MAX); // NOLINT - TestAgainstLibc<unsigned long>("%zu-%zu", 0, ULONG_MAX); // NOLINT TestAgainstLibc<unsigned>("%x-%x", 0, UINT_MAX); // NOLINT +#if !defined(_WIN32) + // %z* format doesn't seem to be supported by MSVS. + TestAgainstLibc<long>("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT + TestAgainstLibc<unsigned long>("%zu-%zu", 0, ULONG_MAX); // NOLINT TestAgainstLibc<unsigned long>("%zx-%zx", 0, ULONG_MAX); // NOLINT - Report("%zd\n", LONG_MIN); +#endif } TEST(Printf, Padding) { @@ -136,4 +143,14 @@ TEST(Printf, Padding) { TestAgainstLibc<int>("%03d - %03d", -12, -1234); } +TEST(Printf, Precision) { + char buf[1024]; + uptr len = internal_snprintf(buf, sizeof(buf), "%.*s", 3, "12345"); + EXPECT_EQ(3U, len); + EXPECT_STREQ("123", buf); + len = internal_snprintf(buf, sizeof(buf), "%.*s", 6, "12345"); + EXPECT_EQ(5U, len); + EXPECT_STREQ("12345", buf); +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc index d16e2eebf98e..495b726dcc45 100644 --- a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc @@ -10,21 +10,48 @@ // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// +#if !defined(_WIN32) // There are no /proc/maps on Windows. + #include "sanitizer_common/sanitizer_procmaps.h" -//#include "sanitizer_common/sanitizer_internal_defs.h" -//#include "sanitizer_common/sanitizer_libc.h" #include "gtest/gtest.h" +#include <stdlib.h> + +static void noop() {} +extern const char *argv0; + namespace __sanitizer { -#ifdef SANITIZER_LINUX -TEST(ProcMaps, CodeRange) { +#if SANITIZER_LINUX && !SANITIZER_ANDROID +TEST(MemoryMappingLayout, CodeRange) { uptr start, end; bool res = GetCodeRangeForFile("[vdso]", &start, &end); EXPECT_EQ(res, true); - EXPECT_GT(start, (uptr)0); + EXPECT_GT(start, 0U); EXPECT_LT(start, end); } #endif +TEST(MemoryMappingLayout, DumpListOfModules) { + const char *last_slash = strrchr(argv0, '/'); + const char *binary_name = last_slash ? last_slash + 1 : argv0; + MemoryMappingLayout memory_mapping(false); + const uptr kMaxModules = 100; + LoadedModule *modules = + (LoadedModule *)malloc(kMaxModules * sizeof(LoadedModule)); + uptr n_modules = memory_mapping.DumpListOfModules(modules, kMaxModules, 0); + EXPECT_GT(n_modules, 0U); + bool found = false; + for (uptr i = 0; i < n_modules; ++i) { + if (modules[i].containsAddress((uptr)&noop)) { + // Verify that the module name is sane. + if (strstr(modules[i].full_name(), binary_name) != 0) + found = true; + } + } + EXPECT_TRUE(found); + free(modules); +} + } // namespace __sanitizer +#endif // !defined(_WIN32) diff --git a/lib/sanitizer_common/tests/sanitizer_pthread_wrappers.h b/lib/sanitizer_common/tests/sanitizer_pthread_wrappers.h new file mode 100644 index 000000000000..47b0f97de840 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_pthread_wrappers.h @@ -0,0 +1,66 @@ +//===-- sanitizer_pthread_wrappers.h ----------------------------*- C++ -*-===// +// +// 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 *Sanitizer runtime. +// It provides handy wrappers for thread manipulation, that: +// a) assert on any failure rather than returning an error code +// b) defines pthread-like interface on platforms where where <pthread.h> +// is not supplied by default. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_PTHREAD_WRAPPERS_H +#define SANITIZER_PTHREAD_WRAPPERS_H + +#include "sanitizer_test_utils.h" + +#if !defined(_WIN32) +# include <pthread.h> +// Simply forward the arguments and check that the pthread functions succeed. +# define PTHREAD_CREATE(a, b, c, d) ASSERT_EQ(0, pthread_create(a, b, c, d)) +# define PTHREAD_JOIN(a, b) ASSERT_EQ(0, pthread_join(a, b)) +#else +typedef HANDLE pthread_t; + +struct PthreadHelperCreateThreadInfo { + void *(*start_routine)(void *); + void *arg; +}; + +inline DWORD WINAPI PthreadHelperThreadProc(void *arg) { + PthreadHelperCreateThreadInfo *start_data = + reinterpret_cast<PthreadHelperCreateThreadInfo*>(arg); + void *ret = (start_data->start_routine)(start_data->arg); + delete start_data; + return (DWORD)ret; +} + +inline void PTHREAD_CREATE(pthread_t *thread, void *attr, + void *(*start_routine)(void *), void *arg) { + ASSERT_EQ(0, attr) << "Thread attributes are not supported yet."; + PthreadHelperCreateThreadInfo *data = new PthreadHelperCreateThreadInfo; + data->start_routine = start_routine; + data->arg = arg; + *thread = CreateThread(0, 0, PthreadHelperThreadProc, data, 0, 0); + ASSERT_NE(nullptr, *thread) << "Failed to create a thread."; +} + +inline void PTHREAD_JOIN(pthread_t thread, void **value_ptr) { + ASSERT_EQ(0, value_ptr) << "Nonzero value_ptr is not supported yet."; + ASSERT_EQ(WAIT_OBJECT_0, WaitForSingleObject(thread, INFINITE)); + ASSERT_NE(0, CloseHandle(thread)); +} + +inline void pthread_exit(void *retval) { + ASSERT_EQ(0, retval) << "Nonzero retval is not supported yet."; + ExitThread((DWORD)retval); +} +#endif // _WIN32 + +#endif // SANITIZER_PTHREAD_WRAPPERS_H diff --git a/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc index 5c075d53ebff..513432fac214 100644 --- a/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc @@ -18,74 +18,75 @@ namespace __sanitizer { TEST(SanitizerCommon, StackDepotBasic) { - uptr s1[] = {1, 2, 3, 4, 5}; - u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1)); - uptr sz1 = 0; - const uptr *sp1 = StackDepotGet(i1, &sz1); - EXPECT_NE(sp1, (uptr*)0); - EXPECT_EQ(sz1, ARRAY_SIZE(s1)); - EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0); + uptr array[] = {1, 2, 3, 4, 5}; + StackTrace s1(array, ARRAY_SIZE(array)); + u32 i1 = StackDepotPut(s1); + StackTrace stack = StackDepotGet(i1); + EXPECT_NE(stack.trace, (uptr*)0); + EXPECT_EQ(ARRAY_SIZE(array), stack.size); + EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array))); } TEST(SanitizerCommon, StackDepotAbsent) { - uptr sz1 = 0; - const uptr *sp1 = StackDepotGet((1 << 30) - 1, &sz1); - EXPECT_EQ(sp1, (uptr*)0); + StackTrace stack = StackDepotGet((1 << 30) - 1); + EXPECT_EQ((uptr*)0, stack.trace); } TEST(SanitizerCommon, StackDepotEmptyStack) { - u32 i1 = StackDepotPut(0, 0); - uptr sz1 = 0; - const uptr *sp1 = StackDepotGet(i1, &sz1); - EXPECT_EQ(sp1, (uptr*)0); + u32 i1 = StackDepotPut(StackTrace()); + StackTrace stack = StackDepotGet(i1); + EXPECT_EQ((uptr*)0, stack.trace); } TEST(SanitizerCommon, StackDepotZeroId) { - uptr sz1 = 0; - const uptr *sp1 = StackDepotGet(0, &sz1); - EXPECT_EQ(sp1, (uptr*)0); + StackTrace stack = StackDepotGet(0); + EXPECT_EQ((uptr*)0, stack.trace); } TEST(SanitizerCommon, StackDepotSame) { - uptr s1[] = {1, 2, 3, 4, 6}; - u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1)); - u32 i2 = StackDepotPut(s1, ARRAY_SIZE(s1)); + uptr array[] = {1, 2, 3, 4, 6}; + StackTrace s1(array, ARRAY_SIZE(array)); + u32 i1 = StackDepotPut(s1); + u32 i2 = StackDepotPut(s1); EXPECT_EQ(i1, i2); - uptr sz1 = 0; - const uptr *sp1 = StackDepotGet(i1, &sz1); - EXPECT_NE(sp1, (uptr*)0); - EXPECT_EQ(sz1, ARRAY_SIZE(s1)); - EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0); + StackTrace stack = StackDepotGet(i1); + EXPECT_NE(stack.trace, (uptr*)0); + EXPECT_EQ(ARRAY_SIZE(array), stack.size); + EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array))); } TEST(SanitizerCommon, StackDepotSeveral) { - uptr s1[] = {1, 2, 3, 4, 7}; - u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1)); - uptr s2[] = {1, 2, 3, 4, 8, 9}; - u32 i2 = StackDepotPut(s2, ARRAY_SIZE(s2)); + uptr array1[] = {1, 2, 3, 4, 7}; + StackTrace s1(array1, ARRAY_SIZE(array1)); + u32 i1 = StackDepotPut(s1); + uptr array2[] = {1, 2, 3, 4, 8, 9}; + StackTrace s2(array2, ARRAY_SIZE(array2)); + u32 i2 = StackDepotPut(s2); EXPECT_NE(i1, i2); } TEST(SanitizerCommon, StackDepotReverseMap) { - uptr s1[] = {1, 2, 3, 4, 5}; - uptr s2[] = {7, 1, 3, 0}; - uptr s3[] = {10, 2, 5, 3}; - uptr s4[] = {1, 3, 2, 5}; + uptr array1[] = {1, 2, 3, 4, 5}; + uptr array2[] = {7, 1, 3, 0}; + uptr array3[] = {10, 2, 5, 3}; + uptr array4[] = {1, 3, 2, 5}; u32 ids[4] = {0}; - ids[0] = StackDepotPut(s1, ARRAY_SIZE(s1)); - ids[1] = StackDepotPut(s2, ARRAY_SIZE(s2)); - ids[2] = StackDepotPut(s3, ARRAY_SIZE(s3)); - ids[3] = StackDepotPut(s4, ARRAY_SIZE(s4)); + StackTrace s1(array1, ARRAY_SIZE(array1)); + StackTrace s2(array2, ARRAY_SIZE(array2)); + StackTrace s3(array3, ARRAY_SIZE(array3)); + StackTrace s4(array4, ARRAY_SIZE(array4)); + ids[0] = StackDepotPut(s1); + ids[1] = StackDepotPut(s2); + ids[2] = StackDepotPut(s3); + ids[3] = StackDepotPut(s4); StackDepotReverseMap map; for (uptr i = 0; i < 4; i++) { - uptr sz_depot, sz_map; - const uptr *sp_depot, *sp_map; - sp_depot = StackDepotGet(ids[i], &sz_depot); - sp_map = map.Get(ids[i], &sz_map); - EXPECT_EQ(sz_depot, sz_map); - EXPECT_EQ(sp_depot, sp_map); + StackTrace stack = StackDepotGet(ids[i]); + StackTrace from_map = map.Get(ids[i]); + EXPECT_EQ(stack.size, from_map.size); + EXPECT_EQ(stack.trace, from_map.trace); } } diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc new file mode 100644 index 000000000000..cc9a9edbbb46 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc @@ -0,0 +1,122 @@ +//===-- sanitizer_common_printer_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 sanitizer_common test suite. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_stacktrace_printer.h" + +#include "gtest/gtest.h" + +namespace __sanitizer { + +TEST(SanitizerStacktracePrinter, RenderSourceLocation) { + InternalScopedString str(128); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, ""); + EXPECT_STREQ("/dir/file.cc:10:5", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 11, 0, ""); + EXPECT_STREQ("/dir/file.cc:11", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 0, 0, ""); + EXPECT_STREQ("/dir/file.cc", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "/dir/"); + EXPECT_STREQ("file.cc:10:5", str.data()); +} + +TEST(SanitizerStacktracePrinter, RenderModuleLocation) { + InternalScopedString str(128); + RenderModuleLocation(&str, "/dir/exe", 0x123, ""); + EXPECT_STREQ("(/dir/exe+0x123)", str.data()); + + // Check that we strip file prefix if necessary. + str.clear(); + RenderModuleLocation(&str, "/dir/exe", 0x123, "/dir/"); + EXPECT_STREQ("(exe+0x123)", str.data()); +} + +TEST(SanitizerStacktracePrinter, RenderFrame) { + int frame_no = 42; + AddressInfo info; + info.address = 0x400000; + info.module = internal_strdup("/path/to/my/module"); + info.module_offset = 0x200; + info.function = internal_strdup("function_foo"); + info.function_offset = 0x100; + info.file = internal_strdup("/path/to/my/source"); + info.line = 10; + info.column = 5; + InternalScopedString str(256); + + // Dump all the AddressInfo fields. + RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o " + "Function:%f FunctionOffset:%q Source:%s Line:%l " + "Column:%c", + frame_no, info, "/path/to/", "function_"); + EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 " + "Function:foo FunctionOffset:0x100 Source:my/source Line:10 " + "Column:5", + str.data()); + info.Clear(); + str.clear(); + + // Test special format specifiers. + info.address = 0x400000; + RenderFrame(&str, "%M", frame_no, info); + EXPECT_NE(nullptr, internal_strstr(str.data(), "400000")); + str.clear(); + + RenderFrame(&str, "%L", frame_no, info); + EXPECT_STREQ("(<unknown module>)", str.data()); + str.clear(); + + info.module = internal_strdup("/path/to/module"); + info.module_offset = 0x200; + RenderFrame(&str, "%M", frame_no, info); + EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x")); + EXPECT_NE(nullptr, internal_strstr(str.data(), "200")); + str.clear(); + + RenderFrame(&str, "%L", frame_no, info); + EXPECT_STREQ("(/path/to/module+0x200)", str.data()); + str.clear(); + + info.function = internal_strdup("my_function"); + RenderFrame(&str, "%F", frame_no, info); + EXPECT_STREQ("in my_function", str.data()); + str.clear(); + + info.function_offset = 0x100; + RenderFrame(&str, "%F %S", frame_no, info); + EXPECT_STREQ("in my_function+0x100 <null>", str.data()); + str.clear(); + + info.file = internal_strdup("my_file"); + RenderFrame(&str, "%F %S", frame_no, info); + EXPECT_STREQ("in my_function my_file", str.data()); + str.clear(); + + info.line = 10; + RenderFrame(&str, "%F %S", frame_no, info); + EXPECT_STREQ("in my_function my_file:10", str.data()); + str.clear(); + + info.column = 5; + RenderFrame(&str, "%S %L", frame_no, info); + EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data()); + str.clear(); + + info.Clear(); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc index 2b842cd8134a..ac820c25a0f8 100644 --- a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc @@ -20,19 +20,22 @@ namespace __sanitizer { class FastUnwindTest : public ::testing::Test { protected: virtual void SetUp(); + virtual void TearDown(); bool TryFastUnwind(uptr max_depth) { if (!StackTrace::WillUseFastUnwind(true)) return false; - trace.Unwind(max_depth, start_pc, (uptr)&fake_stack[0], fake_top, + trace.Unwind(max_depth, start_pc, (uptr)&fake_stack[0], 0, fake_top, fake_bottom, true); return true; } - uptr fake_stack[10]; + void *mapping; + uptr *fake_stack; + const uptr fake_stack_size = 10; uptr start_pc; uptr fake_top; uptr fake_bottom; - StackTrace trace; + BufferedStackTrace trace; }; static uptr PC(uptr idx) { @@ -40,22 +43,34 @@ static uptr PC(uptr idx) { } void FastUnwindTest::SetUp() { + size_t ps = GetPageSize(); + mapping = MmapOrDie(2 * ps, "FastUnwindTest"); + Mprotect((uptr)mapping, ps); + + // Unwinder may peek 1 word down from the starting FP. + fake_stack = (uptr *)((uptr)mapping + ps + sizeof(uptr)); + // 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) { + for (uptr i = 0; i + 1 < fake_stack_size; 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; + // Mark the last fp point back up to terminate the stack trace. + fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uptr)&fake_stack[0]; // Top is two slots past the end because FastUnwindStack subtracts two. - fake_top = (uptr)&fake_stack[ARRAY_SIZE(fake_stack) + 2]; + fake_top = (uptr)&fake_stack[fake_stack_size + 2]; // Bottom is one slot before the start because FastUnwindStack uses >. - fake_bottom = (uptr)&fake_stack[-1]; + fake_bottom = (uptr)mapping; start_pc = PC(0); } +void FastUnwindTest::TearDown() { + size_t ps = GetPageSize(); + UnmapOrDie(mapping, 2 * ps); +} + TEST_F(FastUnwindTest, Basic) { if (!TryFastUnwind(kStackTraceMax)) return; @@ -102,4 +117,38 @@ TEST_F(FastUnwindTest, OneFrameStackTrace) { EXPECT_EQ((uptr)&fake_stack[0], trace.top_frame_bp); } +TEST_F(FastUnwindTest, ZeroFramesStackTrace) { + if (!TryFastUnwind(0)) + return; + EXPECT_EQ(0U, trace.size); + EXPECT_EQ(0U, trace.top_frame_bp); +} + +TEST_F(FastUnwindTest, FPBelowPrevFP) { + // The next FP points to unreadable memory inside the stack limits, but below + // current FP. + fake_stack[0] = (uptr)&fake_stack[-50]; + fake_stack[1] = PC(1); + if (!TryFastUnwind(3)) + return; + EXPECT_EQ(2U, trace.size); + EXPECT_EQ(PC(0), trace.trace[0]); + EXPECT_EQ(PC(1), trace.trace[1]); +} + +TEST(SlowUnwindTest, ShortStackTrace) { + if (StackTrace::WillUseFastUnwind(false)) + return; + BufferedStackTrace stack; + uptr pc = StackTrace::GetCurrentPc(); + uptr bp = GET_CURRENT_FRAME(); + stack.Unwind(0, pc, bp, 0, 0, 0, false); + EXPECT_EQ(0U, stack.size); + EXPECT_EQ(0U, stack.top_frame_bp); + stack.Unwind(1, pc, bp, 0, 0, 0, false); + EXPECT_EQ(1U, stack.size); + EXPECT_EQ(pc, stack.trace[0]); + EXPECT_EQ(bp, stack.top_frame_bp); +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc index ea8741d4f01a..0699243283df 100644 --- a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -66,9 +66,21 @@ TEST(Suppressions, TypeStrings) { CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib), - "called_from_lib")); + "called_from_lib")); + CHECK( + !internal_strcmp(SuppressionTypeString(SuppressionDeadlock), "deadlock")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionVptrCheck), + "vptr_check")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionInterceptorName), + "interceptor_name")); + CHECK( + !internal_strcmp(SuppressionTypeString(SuppressionInterceptorViaFunction), + "interceptor_via_fun")); + CHECK( + !internal_strcmp(SuppressionTypeString(SuppressionInterceptorViaLibrary), + "interceptor_via_lib")); // Ensure this test is up-to-date when suppression types are added. - CHECK_EQ(SuppressionTypeCount, 7); + CHECK_EQ(12, SuppressionTypeCount); } class SuppressionContextTest : public ::testing::Test { @@ -149,4 +161,14 @@ TEST_F(SuppressionContextTest, ParseType) { EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); } +TEST_F(SuppressionContextTest, HasSuppressionType) { + ctx_->Parse( + "race:foo\n" + "thread:bar\n"); + EXPECT_TRUE(ctx_->HasSuppressionType(SuppressionRace)); + EXPECT_TRUE(ctx_->HasSuppressionType(SuppressionThread)); + EXPECT_FALSE(ctx_->HasSuppressionType(SuppressionMutex)); + EXPECT_FALSE(ctx_->HasSuppressionType(SuppressionSignal)); +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_test_config.h b/lib/sanitizer_common/tests/sanitizer_test_config.h new file mode 100644 index 000000000000..bdf614606d6a --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_test_config.h @@ -0,0 +1,30 @@ +//===-- sanitizer_test_config.h ---------------------------------*- C++ -*-===// +// +// 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 *Sanitizer runtime. +// +//===----------------------------------------------------------------------===// +#if !defined(INCLUDED_FROM_SANITIZER_TEST_UTILS_H) +# error "This file should be included into sanitizer_test_utils.h only" +#endif + +#ifndef SANITIZER_TEST_CONFIG_H +#define SANITIZER_TEST_CONFIG_H + +#include <vector> +#include <string> +#include <map> + +#if SANITIZER_USE_DEJAGNU_GTEST +# include "dejagnu-gtest.h" +#else +# include "gtest/gtest.h" +#endif + +#endif // SANITIZER_TEST_CONFIG_H diff --git a/lib/sanitizer_common/tests/sanitizer_test_utils.h b/lib/sanitizer_common/tests/sanitizer_test_utils.h index 17adb2647656..64db37f341d3 100644 --- a/lib/sanitizer_common/tests/sanitizer_test_utils.h +++ b/lib/sanitizer_common/tests/sanitizer_test_utils.h @@ -16,21 +16,34 @@ #define SANITIZER_TEST_UTILS_H #if defined(_WIN32) -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -typedef __int8 int8_t; -typedef __int16 int16_t; -typedef __int32 int32_t; -typedef __int64 int64_t; +// <windows.h> should always be the first include on Windows. +# include <windows.h> +// MSVS headers define max/min as macros, so std::max/min gets crazy. +# undef max +# undef min +#endif + +#if !defined(SANITIZER_EXTERNAL_TEST_CONFIG) +# define INCLUDED_FROM_SANITIZER_TEST_UTILS_H +# include "sanitizer_test_config.h" +# undef INCLUDED_FROM_SANITIZER_TEST_UTILS_H +#endif + +#include <stdint.h> + +#if defined(_MSC_VER) # define NOINLINE __declspec(noinline) -# define USED -#else // defined(_WIN32) +#else // defined(_MSC_VER) # define NOINLINE __attribute__((noinline)) +#endif // defined(_MSC_VER) + +#if !defined(_MSC_VER) || defined(__clang__) +# define UNUSED __attribute__((unused)) # define USED __attribute__((used)) -#include <stdint.h> -#endif // defined(_WIN32) +#else +# define UNUSED +# define USED +#endif #if !defined(__has_feature) #define __has_feature(x) 0 @@ -53,7 +66,9 @@ typedef __int64 int64_t; // Make the compiler thinks that something is going on there. inline void break_optimization(void *arg) { +#if !defined(_WIN32) || defined(__clang__) __asm__ __volatile__("" : : "r" (arg) : "memory"); +#endif } // This function returns its parameter but in such a way that compiler @@ -78,5 +93,29 @@ static inline uint32_t my_rand() { return my_rand_r(&global_seed); } +// Set availability of platform-specific functions. + +#if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) +# define SANITIZER_TEST_HAS_POSIX_MEMALIGN 1 +#else +# define SANITIZER_TEST_HAS_POSIX_MEMALIGN 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && \ + !defined(__ANDROID__) && !defined(_WIN32) +# define SANITIZER_TEST_HAS_MEMALIGN 1 +# define SANITIZER_TEST_HAS_PVALLOC 1 +# define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 1 +#else +# define SANITIZER_TEST_HAS_MEMALIGN 0 +# define SANITIZER_TEST_HAS_PVALLOC 0 +# define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 0 +#endif + +#if !defined(__APPLE__) +# define SANITIZER_TEST_HAS_STRNLEN 1 +#else +# define SANITIZER_TEST_HAS_STRNLEN 0 +#endif #endif // SANITIZER_TEST_UTILS_H diff --git a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc index ddc8dba5d3e0..58c627a704ff 100644 --- a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc @@ -11,6 +11,9 @@ // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_thread_registry.h" + +#include "sanitizer_pthread_wrappers.h" + #include "gtest/gtest.h" #include <vector> @@ -48,7 +51,7 @@ static uptr get_uid(u32 tid) { static bool HasName(ThreadContextBase *tctx, void *arg) { char *name = (char*)arg; - return (tctx->name && 0 == internal_strcmp(tctx->name, name)); + return (0 == internal_strcmp(tctx->name, name)); } static bool HasUid(ThreadContextBase *tctx, void *arg) { @@ -104,13 +107,13 @@ static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) { registry->FindThread(HasUid, (void*)0x1234)); // Detach and finish and join remaining threads. for (u32 i = 6; i <= 10; i++) { - registry->DetachThread(i); + registry->DetachThread(i, 0); 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->DetachThread(tid, 0); registry->FinishThread(tid); } CheckThreadQuantity(registry, exp_total, 1, 1); @@ -203,10 +206,10 @@ static void ThreadedTestRegistry(ThreadRegistry *registry) { for (int i = 0; i < kNumShards; i++) { args[i].registry = registry; args[i].shard = i + 1; - pthread_create(&threads[i], 0, RunThread, &args[i]); + PTHREAD_CREATE(&threads[i], 0, RunThread, &args[i]); } for (int i = 0; i < kNumShards; i++) { - pthread_join(threads[i], 0); + PTHREAD_JOIN(threads[i], 0); } // Check that each thread created/started/joined correct amount // of "threads" in thread_registry. |