xref: /llvm-project/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cpp (revision aa44f59abf399f81585898fb95e66518ef3591af)
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(&registry);
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