diff options
Diffstat (limited to 'lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc')
-rw-r--r-- | lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc | 230 |
1 files changed, 230 insertions, 0 deletions
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(®istry); +} + +} // namespace __sanitizer |