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 <iostream> 15 #include <vector> 16 17 #include "gmock/gmock.h" 18 #include "gtest/gtest.h" 19 #include "sanitizer_common/sanitizer_common.h" 20 #include "sanitizer_common/sanitizer_stackdepot.h" 21 #include "sanitizer_common/sanitizer_stacktrace.h" 22 #include "sanitizer_common/sanitizer_thread_history.h" 23 #include "sanitizer_pthread_wrappers.h" 24 25 using testing::HasSubstr; 26 27 namespace __sanitizer { 28 29 static Mutex tctx_allocator_lock; 30 static LowLevelAllocator tctx_allocator; 31 32 template<typename TCTX> 33 static ThreadContextBase *GetThreadContext(u32 tid) { 34 Lock l(&tctx_allocator_lock); 35 return new(tctx_allocator) TCTX(tid); 36 } 37 38 static const u32 kMaxRegistryThreads = 1000; 39 static const u32 kRegistryQuarantine = 2; 40 41 static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total, 42 uptr exp_running, uptr exp_alive) { 43 uptr total, running, alive; 44 registry->GetNumberOfThreads(&total, &running, &alive); 45 EXPECT_EQ(exp_total, total); 46 EXPECT_EQ(exp_running, running); 47 EXPECT_EQ(exp_alive, alive); 48 } 49 50 static bool is_detached(u32 tid) { 51 return (tid % 2 == 0); 52 } 53 54 static uptr get_uid(u32 tid) { 55 return tid * 2; 56 } 57 58 static bool HasName(ThreadContextBase *tctx, void *arg) { 59 char *name = (char*)arg; 60 return (0 == internal_strcmp(tctx->name, name)); 61 } 62 63 static bool HasUid(ThreadContextBase *tctx, void *arg) { 64 uptr uid = (uptr)arg; 65 return (tctx->user_id == uid); 66 } 67 68 static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) { 69 bool *arr = (bool*)arg; 70 arr[tctx->tid] = true; 71 } 72 73 static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) { 74 // Create and start a main thread. 75 EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0, nullptr)); 76 registry->StartThread(0, 0, ThreadType::Regular, 0); 77 // Create a bunch of threads. 78 for (u32 i = 1; i <= 10; i++) { 79 EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 100 + i, 80 200 + i, nullptr)); 81 } 82 CheckThreadQuantity(registry, 11, 1, 11); 83 // Start some of them. 84 for (u32 i = 1; i <= 5; i++) { 85 registry->StartThread(i, 0, ThreadType::Regular, 0); 86 } 87 CheckThreadQuantity(registry, 11, 6, 11); 88 // Finish, create and start more threads. 89 for (u32 i = 1; i <= 5; i++) { 90 registry->FinishThread(i); 91 if (!is_detached(i)) 92 registry->JoinThread(i, 0); 93 } 94 for (u32 i = 6; i <= 10; i++) { 95 registry->StartThread(i, 0, ThreadType::Regular, 0); 96 } 97 std::vector<u32> new_tids; 98 for (u32 i = 11; i <= 15; i++) { 99 new_tids.push_back( 100 registry->CreateThread(get_uid(i), is_detached(i), 0, 0, nullptr)); 101 } 102 ASSERT_LE(kRegistryQuarantine, 5U); 103 u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0); 104 CheckThreadQuantity(registry, exp_total, 6, 11); 105 // Test SetThreadName and FindThread. 106 registry->SetThreadName(6, "six"); 107 registry->SetThreadName(7, "seven"); 108 EXPECT_EQ(7U, registry->FindThread(HasName, (void *)"seven")); 109 EXPECT_EQ(kInvalidTid, registry->FindThread(HasName, (void *)"none")); 110 EXPECT_EQ(0U, registry->FindThread(HasUid, (void *)get_uid(0))); 111 EXPECT_EQ(10U, registry->FindThread(HasUid, (void *)get_uid(10))); 112 EXPECT_EQ(kInvalidTid, registry->FindThread(HasUid, (void *)0x1234)); 113 EXPECT_EQ(7U, 114 registry->FindThread([](ThreadContextBase *tctx, 115 void *) { return tctx->parent_tid == 107; }, 116 nullptr)); 117 EXPECT_EQ(8U, 118 registry->FindThread([](ThreadContextBase *tctx, 119 void *) { return tctx->stack_id == 208; }, 120 nullptr)); 121 // Detach and finish and join remaining threads. 122 for (u32 i = 6; i <= 10; i++) { 123 registry->DetachThread(i, 0); 124 registry->FinishThread(i); 125 } 126 for (u32 i = 0; i < new_tids.size(); i++) { 127 u32 tid = new_tids[i]; 128 registry->StartThread(tid, 0, ThreadType::Regular, 0); 129 registry->DetachThread(tid, 0); 130 registry->FinishThread(tid); 131 } 132 CheckThreadQuantity(registry, exp_total, 1, 1); 133 // Test methods that require the caller to hold a ThreadRegistryLock. 134 bool has_tid[16]; 135 internal_memset(&has_tid[0], 0, sizeof(has_tid)); 136 { 137 ThreadRegistryLock l(registry); 138 registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]); 139 } 140 for (u32 i = 0; i < exp_total; i++) { 141 EXPECT_TRUE(has_tid[i]); 142 } 143 { 144 ThreadRegistryLock l(registry); 145 registry->CheckLocked(); 146 ThreadContextBase *main_thread = registry->GetThreadLocked(0); 147 EXPECT_EQ(main_thread, registry->FindThreadContextLocked( 148 HasUid, (void*)get_uid(0))); 149 } 150 EXPECT_EQ(11U, registry->GetMaxAliveThreads()); 151 } 152 153 TEST(SanitizerCommon, ThreadRegistryTest) { 154 ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>, 155 kMaxRegistryThreads, kRegistryQuarantine, 156 0); 157 TestRegistry(&quarantine_registry, true); 158 159 ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>, 160 kMaxRegistryThreads, 161 kMaxRegistryThreads, 0); 162 TestRegistry(&no_quarantine_registry, false); 163 } 164 165 static const int kThreadsPerShard = 20; 166 static const int kNumShards = 25; 167 168 static int num_created[kNumShards + 1]; 169 static int num_started[kNumShards + 1]; 170 static int num_joined[kNumShards + 1]; 171 172 namespace { 173 174 struct RunThreadArgs { 175 ThreadRegistry *registry; 176 uptr shard; // started from 1. 177 }; 178 179 class TestThreadContext final : public ThreadContextBase { 180 public: 181 explicit TestThreadContext(int tid) : ThreadContextBase(tid) {} 182 void OnJoined(void *arg) { 183 uptr shard = (uptr)arg; 184 num_joined[shard]++; 185 } 186 void OnStarted(void *arg) { 187 uptr shard = (uptr)arg; 188 num_started[shard]++; 189 } 190 void OnCreated(void *arg) { 191 uptr shard = (uptr)arg; 192 num_created[shard]++; 193 } 194 }; 195 196 } // namespace 197 198 void *RunThread(void *arg) { 199 RunThreadArgs *args = static_cast<RunThreadArgs*>(arg); 200 std::vector<int> tids; 201 for (int i = 0; i < kThreadsPerShard; i++) 202 tids.push_back( 203 args->registry->CreateThread(0, false, 0, (void*)args->shard)); 204 for (int i = 0; i < kThreadsPerShard; i++) 205 args->registry->StartThread(tids[i], 0, ThreadType::Regular, 206 (void*)args->shard); 207 for (int i = 0; i < kThreadsPerShard; i++) 208 args->registry->FinishThread(tids[i]); 209 for (int i = 0; i < kThreadsPerShard; i++) 210 args->registry->JoinThread(tids[i], (void*)args->shard); 211 return 0; 212 } 213 214 static void ThreadedTestRegistry(ThreadRegistry *registry) { 215 // Create and start a main thread. 216 EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0)); 217 registry->StartThread(0, 0, ThreadType::Regular, 0); 218 pthread_t threads[kNumShards]; 219 RunThreadArgs args[kNumShards]; 220 for (int i = 0; i < kNumShards; i++) { 221 args[i].registry = registry; 222 args[i].shard = i + 1; 223 PTHREAD_CREATE(&threads[i], 0, RunThread, &args[i]); 224 } 225 for (int i = 0; i < kNumShards; i++) { 226 PTHREAD_JOIN(threads[i], 0); 227 } 228 // Check that each thread created/started/joined correct amount 229 // of "threads" in thread_registry. 230 EXPECT_EQ(1, num_created[0]); 231 EXPECT_EQ(1, num_started[0]); 232 EXPECT_EQ(0, num_joined[0]); 233 for (int i = 1; i <= kNumShards; i++) { 234 EXPECT_EQ(kThreadsPerShard, num_created[i]); 235 EXPECT_EQ(kThreadsPerShard, num_started[i]); 236 EXPECT_EQ(kThreadsPerShard, num_joined[i]); 237 } 238 } 239 240 TEST(SanitizerCommon, ThreadRegistryThreadedTest) { 241 memset(&num_created, 0, sizeof(num_created)); 242 memset(&num_started, 0, sizeof(num_created)); 243 memset(&num_joined, 0, sizeof(num_created)); 244 245 ThreadRegistry registry(GetThreadContext<TestThreadContext>, 246 kThreadsPerShard * kNumShards + 1, 10, 0); 247 ThreadedTestRegistry(®istry); 248 } 249 250 TEST(SanitizerCommon, PrintThreadHistory) { 251 ThreadRegistry registry(GetThreadContext<TestThreadContext>, 252 kThreadsPerShard * kNumShards + 1, 10, 0); 253 254 UNINITIALIZED BufferedStackTrace stack1; 255 stack1.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false, 256 /*max_depth=*/1); 257 258 UNINITIALIZED BufferedStackTrace stack2; 259 stack2.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false, 260 /*max_depth=*/1); 261 262 EXPECT_EQ(0U, registry.CreateThread(0, true, -1, 0, nullptr)); 263 for (int i = 0; i < 5; i++) { 264 registry.CreateThread(0, true, 0, StackDepotPut(stack1), nullptr); 265 registry.CreateThread(0, true, 0, StackDepotPut(stack2), nullptr); 266 } 267 268 InternalScopedString out; 269 PrintThreadHistory(registry, out); 270 271 std::string substrings[] = { 272 "Thread T0/0 was created by T-1", 273 "<empty stack>", 274 "", 275 "Thread T1/0 was created by T0/0", 276 "Thread T3/0 was created by T0/0", 277 "Thread T5/0 was created by T0/0", 278 "Thread T7/0 was created by T0/0", 279 "Thread T9/0 was created by T0/0", 280 "#0 0x", 281 "", 282 "Thread T2/0 was created by T0/0", 283 "Thread T4/0 was created by T0/0", 284 "Thread T6/0 was created by T0/0", 285 "Thread T8/0 was created by T0/0", 286 "Thread T10/0 was created by T0/0", 287 "#0 0x", 288 "", 289 }; 290 291 std::stringstream ss(out.data()); 292 std::string line; 293 294 for (auto substr : substrings) { 295 std::getline(ss, line); 296 EXPECT_THAT(line, HasSubstr(substr)) << line; 297 } 298 299 EXPECT_FALSE(std::getline(ss, line)) << "Unmatched line: " << line; 300 } 301 302 } // namespace __sanitizer 303