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

source code of compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cpp