//===-- 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 "sanitizer_pthread_wrappers.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); return new(tctx_allocator) 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 (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, 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, 0); 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) { memset(&num_created, 0, sizeof(num_created)); memset(&num_started, 0, sizeof(num_created)); memset(&num_joined, 0, sizeof(num_created)); ThreadRegistry registry(GetThreadContext<TestThreadContext>, kThreadsPerShard * kNumShards + 1, 10); ThreadedTestRegistry(®istry); } } // namespace __sanitizer