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 "sanitizer_pthread_wrappers.h"
15 
16 #include "gtest/gtest.h"
17 
18 #include <vector>
19 
20 namespace __sanitizer {
21 
22 static Mutex tctx_allocator_lock;
23 static LowLevelAllocator tctx_allocator;
24 
25 template<typename TCTX>
GetThreadContext(u32 tid)26 static ThreadContextBase *GetThreadContext(u32 tid) {
27   Lock l(&tctx_allocator_lock);
28   return new(tctx_allocator) TCTX(tid);
29 }
30 
31 static const u32 kMaxRegistryThreads = 1000;
32 static const u32 kRegistryQuarantine = 2;
33 
CheckThreadQuantity(ThreadRegistry * registry,uptr exp_total,uptr exp_running,uptr exp_alive)34 static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total,
35                                 uptr exp_running, uptr exp_alive) {
36   uptr total, running, alive;
37   registry->GetNumberOfThreads(&total, &running, &alive);
38   EXPECT_EQ(exp_total, total);
39   EXPECT_EQ(exp_running, running);
40   EXPECT_EQ(exp_alive, alive);
41 }
42 
is_detached(u32 tid)43 static bool is_detached(u32 tid) {
44   return (tid % 2 == 0);
45 }
46 
get_uid(u32 tid)47 static uptr get_uid(u32 tid) {
48   return tid * 2;
49 }
50 
HasName(ThreadContextBase * tctx,void * arg)51 static bool HasName(ThreadContextBase *tctx, void *arg) {
52   char *name = (char*)arg;
53   return (0 == internal_strcmp(tctx->name, name));
54 }
55 
HasUid(ThreadContextBase * tctx,void * arg)56 static bool HasUid(ThreadContextBase *tctx, void *arg) {
57   uptr uid = (uptr)arg;
58   return (tctx->user_id == uid);
59 }
60 
MarkUidAsPresent(ThreadContextBase * tctx,void * arg)61 static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {
62   bool *arr = (bool*)arg;
63   arr[tctx->tid] = true;
64 }
65 
TestRegistry(ThreadRegistry * registry,bool has_quarantine)66 static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) {
67   // Create and start a main thread.
68   EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0));
69   registry->StartThread(0, 0, ThreadType::Regular, 0);
70   // Create a bunch of threads.
71   for (u32 i = 1; i <= 10; i++) {
72     EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
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));
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   // Detach and finish and join remaining threads.
106   for (u32 i = 6; i <= 10; i++) {
107     registry->DetachThread(i, 0);
108     registry->FinishThread(i);
109   }
110   for (u32 i = 0; i < new_tids.size(); i++) {
111     u32 tid = new_tids[i];
112     registry->StartThread(tid, 0, ThreadType::Regular, 0);
113     registry->DetachThread(tid, 0);
114     registry->FinishThread(tid);
115   }
116   CheckThreadQuantity(registry, exp_total, 1, 1);
117   // Test methods that require the caller to hold a ThreadRegistryLock.
118   bool has_tid[16];
119   internal_memset(&has_tid[0], 0, sizeof(has_tid));
120   {
121     ThreadRegistryLock l(registry);
122     registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
123   }
124   for (u32 i = 0; i < exp_total; i++) {
125     EXPECT_TRUE(has_tid[i]);
126   }
127   {
128     ThreadRegistryLock l(registry);
129     registry->CheckLocked();
130     ThreadContextBase *main_thread = registry->GetThreadLocked(0);
131     EXPECT_EQ(main_thread, registry->FindThreadContextLocked(
132         HasUid, (void*)get_uid(0)));
133   }
134   EXPECT_EQ(11U, registry->GetMaxAliveThreads());
135 }
136 
TEST(SanitizerCommon,ThreadRegistryTest)137 TEST(SanitizerCommon, ThreadRegistryTest) {
138   ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
139                                      kMaxRegistryThreads, kRegistryQuarantine,
140                                      0);
141   TestRegistry(&quarantine_registry, true);
142 
143   ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
144                                         kMaxRegistryThreads,
145                                         kMaxRegistryThreads, 0);
146   TestRegistry(&no_quarantine_registry, false);
147 }
148 
149 static const int kThreadsPerShard = 20;
150 static const int kNumShards = 25;
151 
152 static int num_created[kNumShards + 1];
153 static int num_started[kNumShards + 1];
154 static int num_joined[kNumShards + 1];
155 
156 namespace {
157 
158 struct RunThreadArgs {
159   ThreadRegistry *registry;
160   uptr shard;  // started from 1.
161 };
162 
163 class TestThreadContext final : public ThreadContextBase {
164  public:
TestThreadContext(int tid)165   explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}
OnJoined(void * arg)166   void OnJoined(void *arg) {
167     uptr shard = (uptr)arg;
168     num_joined[shard]++;
169   }
OnStarted(void * arg)170   void OnStarted(void *arg) {
171     uptr shard = (uptr)arg;
172     num_started[shard]++;
173   }
OnCreated(void * arg)174   void OnCreated(void *arg) {
175     uptr shard = (uptr)arg;
176     num_created[shard]++;
177   }
178 };
179 
180 }  // namespace
181 
RunThread(void * arg)182 void *RunThread(void *arg) {
183   RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);
184   std::vector<int> tids;
185   for (int i = 0; i < kThreadsPerShard; i++)
186     tids.push_back(
187         args->registry->CreateThread(0, false, 0, (void*)args->shard));
188   for (int i = 0; i < kThreadsPerShard; i++)
189     args->registry->StartThread(tids[i], 0, ThreadType::Regular,
190         (void*)args->shard);
191   for (int i = 0; i < kThreadsPerShard; i++)
192     args->registry->FinishThread(tids[i]);
193   for (int i = 0; i < kThreadsPerShard; i++)
194     args->registry->JoinThread(tids[i], (void*)args->shard);
195   return 0;
196 }
197 
ThreadedTestRegistry(ThreadRegistry * registry)198 static void ThreadedTestRegistry(ThreadRegistry *registry) {
199   // Create and start a main thread.
200   EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0));
201   registry->StartThread(0, 0, ThreadType::Regular, 0);
202   pthread_t threads[kNumShards];
203   RunThreadArgs args[kNumShards];
204   for (int i = 0; i < kNumShards; i++) {
205     args[i].registry = registry;
206     args[i].shard = i + 1;
207     PTHREAD_CREATE(&threads[i], 0, RunThread, &args[i]);
208   }
209   for (int i = 0; i < kNumShards; i++) {
210     PTHREAD_JOIN(threads[i], 0);
211   }
212   // Check that each thread created/started/joined correct amount
213   // of "threads" in thread_registry.
214   EXPECT_EQ(1, num_created[0]);
215   EXPECT_EQ(1, num_started[0]);
216   EXPECT_EQ(0, num_joined[0]);
217   for (int i = 1; i <= kNumShards; i++) {
218     EXPECT_EQ(kThreadsPerShard, num_created[i]);
219     EXPECT_EQ(kThreadsPerShard, num_started[i]);
220     EXPECT_EQ(kThreadsPerShard, num_joined[i]);
221   }
222 }
223 
TEST(SanitizerCommon,ThreadRegistryThreadedTest)224 TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
225   memset(&num_created, 0, sizeof(num_created));
226   memset(&num_started, 0, sizeof(num_created));
227   memset(&num_joined, 0, sizeof(num_created));
228 
229   ThreadRegistry registry(GetThreadContext<TestThreadContext>,
230                           kThreadsPerShard * kNumShards + 1, 10, 0);
231   ThreadedTestRegistry(&registry);
232 }
233 
234 }  // namespace __sanitizer
235