1 //===-- sanitizer_thread_registry_test.cc ---------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a part of shared sanitizer runtime.
11 //
12 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_thread_registry.h"
14 #include "gtest/gtest.h"
15
16 #include <vector>
17
18 namespace __sanitizer {
19
20 static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED);
21 static LowLevelAllocator tctx_allocator;
22
23 template<typename TCTX>
GetThreadContext(u32 tid)24 static ThreadContextBase *GetThreadContext(u32 tid) {
25 BlockingMutexLock l(&tctx_allocator_lock);
26 void *mem = tctx_allocator.Allocate(sizeof(TCTX));
27 return new(mem) TCTX(tid);
28 }
29
30 static const u32 kMaxRegistryThreads = 1000;
31 static const u32 kRegistryQuarantine = 2;
32
CheckThreadQuantity(ThreadRegistry * registry,uptr exp_total,uptr exp_running,uptr exp_alive)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
is_detached(u32 tid)42 static bool is_detached(u32 tid) {
43 return (tid % 2 == 0);
44 }
45
get_uid(u32 tid)46 static uptr get_uid(u32 tid) {
47 return tid * 2;
48 }
49
HasName(ThreadContextBase * tctx,void * arg)50 static bool HasName(ThreadContextBase *tctx, void *arg) {
51 char *name = (char*)arg;
52 return (tctx->name && 0 == internal_strcmp(tctx->name, name));
53 }
54
HasUid(ThreadContextBase * tctx,void * arg)55 static bool HasUid(ThreadContextBase *tctx, void *arg) {
56 uptr uid = (uptr)arg;
57 return (tctx->user_id == uid);
58 }
59
MarkUidAsPresent(ThreadContextBase * tctx,void * arg)60 static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {
61 bool *arr = (bool*)arg;
62 arr[tctx->tid] = true;
63 }
64
TestRegistry(ThreadRegistry * registry,bool has_quarantine)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));
68 registry->StartThread(0, 0, 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), 0, 0));
72 }
73 CheckThreadQuantity(registry, 11, 1, 11);
74 // Start some of them.
75 for (u32 i = 1; i <= 5; i++) {
76 registry->StartThread(i, 0, 0);
77 }
78 CheckThreadQuantity(registry, 11, 6, 11);
79 // Finish, create and start more threads.
80 for (u32 i = 1; i <= 5; i++) {
81 registry->FinishThread(i);
82 if (!is_detached(i))
83 registry->JoinThread(i, 0);
84 }
85 for (u32 i = 6; i <= 10; i++) {
86 registry->StartThread(i, 0, 0);
87 }
88 std::vector<u32> new_tids;
89 for (u32 i = 11; i <= 15; i++) {
90 new_tids.push_back(
91 registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
92 }
93 ASSERT_LE(kRegistryQuarantine, 5U);
94 u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0);
95 CheckThreadQuantity(registry, exp_total, 6, 11);
96 // Test SetThreadName and FindThread.
97 registry->SetThreadName(6, "six");
98 registry->SetThreadName(7, "seven");
99 EXPECT_EQ(7U, registry->FindThread(HasName, (void*)"seven"));
100 EXPECT_EQ(ThreadRegistry::kUnknownTid,
101 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(ThreadRegistry::kUnknownTid,
105 registry->FindThread(HasUid, (void*)0x1234));
106 // Detach and finish and join remaining threads.
107 for (u32 i = 6; i <= 10; i++) {
108 registry->DetachThread(i);
109 registry->FinishThread(i);
110 }
111 for (u32 i = 0; i < new_tids.size(); i++) {
112 u32 tid = new_tids[i];
113 registry->StartThread(tid, 0, 0);
114 registry->DetachThread(tid);
115 registry->FinishThread(tid);
116 }
117 CheckThreadQuantity(registry, exp_total, 1, 1);
118 // Test methods that require the caller to hold a ThreadRegistryLock.
119 bool has_tid[16];
120 internal_memset(&has_tid[0], 0, sizeof(has_tid));
121 {
122 ThreadRegistryLock l(registry);
123 registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
124 }
125 for (u32 i = 0; i < exp_total; i++) {
126 EXPECT_TRUE(has_tid[i]);
127 }
128 {
129 ThreadRegistryLock l(registry);
130 registry->CheckLocked();
131 ThreadContextBase *main_thread = registry->GetThreadLocked(0);
132 EXPECT_EQ(main_thread, registry->FindThreadContextLocked(
133 HasUid, (void*)get_uid(0)));
134 }
135 EXPECT_EQ(11U, registry->GetMaxAliveThreads());
136 }
137
TEST(SanitizerCommon,ThreadRegistryTest)138 TEST(SanitizerCommon, ThreadRegistryTest) {
139 ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
140 kMaxRegistryThreads,
141 kRegistryQuarantine);
142 TestRegistry(&quarantine_registry, true);
143
144 ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
145 kMaxRegistryThreads,
146 kMaxRegistryThreads);
147 TestRegistry(&no_quarantine_registry, false);
148 }
149
150 static const int kThreadsPerShard = 20;
151 static const int kNumShards = 25;
152
153 static int num_created[kNumShards + 1];
154 static int num_started[kNumShards + 1];
155 static int num_joined[kNumShards + 1];
156
157 namespace {
158
159 struct RunThreadArgs {
160 ThreadRegistry *registry;
161 uptr shard; // started from 1.
162 };
163
164 class TestThreadContext : public ThreadContextBase {
165 public:
TestThreadContext(int tid)166 explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}
OnJoined(void * arg)167 void OnJoined(void *arg) {
168 uptr shard = (uptr)arg;
169 num_joined[shard]++;
170 }
OnStarted(void * arg)171 void OnStarted(void *arg) {
172 uptr shard = (uptr)arg;
173 num_started[shard]++;
174 }
OnCreated(void * arg)175 void OnCreated(void *arg) {
176 uptr shard = (uptr)arg;
177 num_created[shard]++;
178 }
179 };
180
181 } // namespace
182
RunThread(void * arg)183 void *RunThread(void *arg) {
184 RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);
185 std::vector<int> tids;
186 for (int i = 0; i < kThreadsPerShard; i++)
187 tids.push_back(
188 args->registry->CreateThread(0, false, 0, (void*)args->shard));
189 for (int i = 0; i < kThreadsPerShard; i++)
190 args->registry->StartThread(tids[i], 0, (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, 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 ThreadRegistry registry(GetThreadContext<TestThreadContext>,
226 kThreadsPerShard * kNumShards + 1, 10);
227 ThreadedTestRegistry(®istry);
228 }
229
230 } // namespace __sanitizer
231