1 //===-- sanitizer_thread_registry_test.cpp --------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file is a part of shared sanitizer runtime. 10 // 11 //===----------------------------------------------------------------------===// 12 #include "sanitizer_common/sanitizer_thread_registry.h" 13 14 #include <vector> 15 16 #include "gtest/gtest.h" 17 #include "sanitizer_pthread_wrappers.h" 18 19 namespace __sanitizer { 20 21 static Mutex tctx_allocator_lock; 22 static LowLevelAllocator tctx_allocator; 23 24 template<typename TCTX> 25 static ThreadContextBase *GetThreadContext(u32 tid) { 26 Lock l(&tctx_allocator_lock); 27 return new(tctx_allocator) TCTX(tid); 28 } 29 30 static const u32 kMaxRegistryThreads = 1000; 31 static const u32 kRegistryQuarantine = 2; 32 33 static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total, 34 uptr exp_running, uptr exp_alive) { 35 uptr total, running, alive; 36 registry->GetNumberOfThreads(&total, &running, &alive); 37 EXPECT_EQ(exp_total, total); 38 EXPECT_EQ(exp_running, running); 39 EXPECT_EQ(exp_alive, alive); 40 } 41 42 static bool is_detached(u32 tid) { 43 return (tid % 2 == 0); 44 } 45 46 static uptr get_uid(u32 tid) { 47 return tid * 2; 48 } 49 50 static bool HasName(ThreadContextBase *tctx, void *arg) { 51 char *name = (char*)arg; 52 return (0 == internal_strcmp(tctx->name, name)); 53 } 54 55 static bool HasUid(ThreadContextBase *tctx, void *arg) { 56 uptr uid = (uptr)arg; 57 return (tctx->user_id == uid); 58 } 59 60 static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) { 61 bool *arr = (bool*)arg; 62 arr[tctx->tid] = true; 63 } 64 65 static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) { 66 // Create and start a main thread. 67 EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0, nullptr)); 68 registry->StartThread(0, 0, ThreadType::Regular, 0); 69 // Create a bunch of threads. 70 for (u32 i = 1; i <= 10; i++) { 71 EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 100 + i, 72 200 + i, nullptr)); 73 } 74 CheckThreadQuantity(registry, 11, 1, 11); 75 // Start some of them. 76 for (u32 i = 1; i <= 5; i++) { 77 registry->StartThread(i, 0, ThreadType::Regular, 0); 78 } 79 CheckThreadQuantity(registry, 11, 6, 11); 80 // Finish, create and start more threads. 81 for (u32 i = 1; i <= 5; i++) { 82 registry->FinishThread(i); 83 if (!is_detached(i)) 84 registry->JoinThread(i, 0); 85 } 86 for (u32 i = 6; i <= 10; i++) { 87 registry->StartThread(i, 0, ThreadType::Regular, 0); 88 } 89 std::vector<u32> new_tids; 90 for (u32 i = 11; i <= 15; i++) { 91 new_tids.push_back( 92 registry->CreateThread(get_uid(i), is_detached(i), 0, 0, nullptr)); 93 } 94 ASSERT_LE(kRegistryQuarantine, 5U); 95 u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0); 96 CheckThreadQuantity(registry, exp_total, 6, 11); 97 // Test SetThreadName and FindThread. 98 registry->SetThreadName(6, "six"); 99 registry->SetThreadName(7, "seven"); 100 EXPECT_EQ(7U, registry->FindThread(HasName, (void *)"seven")); 101 EXPECT_EQ(kInvalidTid, registry->FindThread(HasName, (void *)"none")); 102 EXPECT_EQ(0U, registry->FindThread(HasUid, (void *)get_uid(0))); 103 EXPECT_EQ(10U, registry->FindThread(HasUid, (void *)get_uid(10))); 104 EXPECT_EQ(kInvalidTid, registry->FindThread(HasUid, (void *)0x1234)); 105 EXPECT_EQ(7U, 106 registry->FindThread([](ThreadContextBase *tctx, 107 void *) { return tctx->parent_tid == 107; }, 108 nullptr)); 109 EXPECT_EQ(8U, 110 registry->FindThread([](ThreadContextBase *tctx, 111 void *) { return tctx->stack_id == 208; }, 112 nullptr)); 113 // Detach and finish and join remaining threads. 114 for (u32 i = 6; i <= 10; i++) { 115 registry->DetachThread(i, 0); 116 registry->FinishThread(i); 117 } 118 for (u32 i = 0; i < new_tids.size(); i++) { 119 u32 tid = new_tids[i]; 120 registry->StartThread(tid, 0, ThreadType::Regular, 0); 121 registry->DetachThread(tid, 0); 122 registry->FinishThread(tid); 123 } 124 CheckThreadQuantity(registry, exp_total, 1, 1); 125 // Test methods that require the caller to hold a ThreadRegistryLock. 126 bool has_tid[16]; 127 internal_memset(&has_tid[0], 0, sizeof(has_tid)); 128 { 129 ThreadRegistryLock l(registry); 130 registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]); 131 } 132 for (u32 i = 0; i < exp_total; i++) { 133 EXPECT_TRUE(has_tid[i]); 134 } 135 { 136 ThreadRegistryLock l(registry); 137 registry->CheckLocked(); 138 ThreadContextBase *main_thread = registry->GetThreadLocked(0); 139 EXPECT_EQ(main_thread, registry->FindThreadContextLocked( 140 HasUid, (void*)get_uid(0))); 141 } 142 EXPECT_EQ(11U, registry->GetMaxAliveThreads()); 143 } 144 145 TEST(SanitizerCommon, ThreadRegistryTest) { 146 ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>, 147 kMaxRegistryThreads, kRegistryQuarantine, 148 0); 149 TestRegistry(&quarantine_registry, true); 150 151 ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>, 152 kMaxRegistryThreads, 153 kMaxRegistryThreads, 0); 154 TestRegistry(&no_quarantine_registry, false); 155 } 156 157 static const int kThreadsPerShard = 20; 158 static const int kNumShards = 25; 159 160 static int num_created[kNumShards + 1]; 161 static int num_started[kNumShards + 1]; 162 static int num_joined[kNumShards + 1]; 163 164 namespace { 165 166 struct RunThreadArgs { 167 ThreadRegistry *registry; 168 uptr shard; // started from 1. 169 }; 170 171 class TestThreadContext final : public ThreadContextBase { 172 public: 173 explicit TestThreadContext(int tid) : ThreadContextBase(tid) {} 174 void OnJoined(void *arg) { 175 uptr shard = (uptr)arg; 176 num_joined[shard]++; 177 } 178 void OnStarted(void *arg) { 179 uptr shard = (uptr)arg; 180 num_started[shard]++; 181 } 182 void OnCreated(void *arg) { 183 uptr shard = (uptr)arg; 184 num_created[shard]++; 185 } 186 }; 187 188 } // namespace 189 190 void *RunThread(void *arg) { 191 RunThreadArgs *args = static_cast<RunThreadArgs*>(arg); 192 std::vector<int> tids; 193 for (int i = 0; i < kThreadsPerShard; i++) 194 tids.push_back( 195 args->registry->CreateThread(0, false, 0, (void*)args->shard)); 196 for (int i = 0; i < kThreadsPerShard; i++) 197 args->registry->StartThread(tids[i], 0, ThreadType::Regular, 198 (void*)args->shard); 199 for (int i = 0; i < kThreadsPerShard; i++) 200 args->registry->FinishThread(tids[i]); 201 for (int i = 0; i < kThreadsPerShard; i++) 202 args->registry->JoinThread(tids[i], (void*)args->shard); 203 return 0; 204 } 205 206 static void ThreadedTestRegistry(ThreadRegistry *registry) { 207 // Create and start a main thread. 208 EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0)); 209 registry->StartThread(0, 0, ThreadType::Regular, 0); 210 pthread_t threads[kNumShards]; 211 RunThreadArgs args[kNumShards]; 212 for (int i = 0; i < kNumShards; i++) { 213 args[i].registry = registry; 214 args[i].shard = i + 1; 215 PTHREAD_CREATE(&threads[i], 0, RunThread, &args[i]); 216 } 217 for (int i = 0; i < kNumShards; i++) { 218 PTHREAD_JOIN(threads[i], 0); 219 } 220 // Check that each thread created/started/joined correct amount 221 // of "threads" in thread_registry. 222 EXPECT_EQ(1, num_created[0]); 223 EXPECT_EQ(1, num_started[0]); 224 EXPECT_EQ(0, num_joined[0]); 225 for (int i = 1; i <= kNumShards; i++) { 226 EXPECT_EQ(kThreadsPerShard, num_created[i]); 227 EXPECT_EQ(kThreadsPerShard, num_started[i]); 228 EXPECT_EQ(kThreadsPerShard, num_joined[i]); 229 } 230 } 231 232 TEST(SanitizerCommon, ThreadRegistryThreadedTest) { 233 memset(&num_created, 0, sizeof(num_created)); 234 memset(&num_started, 0, sizeof(num_created)); 235 memset(&num_joined, 0, sizeof(num_created)); 236 237 ThreadRegistry registry(GetThreadContext<TestThreadContext>, 238 kThreadsPerShard * kNumShards + 1, 10, 0); 239 ThreadedTestRegistry(®istry); 240 } 241 242 } // namespace __sanitizer 243