1 //===-- sanitizer_thread_registry_test.cpp --------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of shared sanitizer runtime.
11 //===----------------------------------------------------------------------===//
12 #include "sanitizer_common/sanitizer_thread_registry.h"
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"
25 using testing::HasSubstr
;
27 namespace __sanitizer
{
29 static Mutex tctx_allocator_lock
;
30 static LowLevelAllocator tctx_allocator
;
32 template<typename TCTX
>
33 static ThreadContextBase
*GetThreadContext(u32 tid
) {
34 Lock
l(&tctx_allocator_lock
);
35 return new(tctx_allocator
) TCTX(tid
);
38 static const u32 kMaxRegistryThreads
= 1000;
39 static const u32 kRegistryQuarantine
= 2;
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
);
50 static bool is_detached(u32 tid
) {
51 return (tid
% 2 == 0);
54 static uptr
get_uid(u32 tid
) {
58 static bool HasName(ThreadContextBase
*tctx
, void *arg
) {
59 char *name
= (char*)arg
;
60 return (0 == internal_strcmp(tctx
->name
, name
));
63 static bool HasUid(ThreadContextBase
*tctx
, void *arg
) {
65 return (tctx
->user_id
== uid
);
68 static void MarkUidAsPresent(ThreadContextBase
*tctx
, void *arg
) {
69 bool *arr
= (bool*)arg
;
70 arr
[tctx
->tid
] = true;
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
,
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);
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
);
92 registry
->JoinThread(i
, 0);
94 for (u32 i
= 6; i
<= 10; i
++) {
95 registry
->StartThread(i
, 0, ThreadType::Regular
, 0);
97 std::vector
<u32
> new_tids
;
98 for (u32 i
= 11; i
<= 15; i
++) {
100 registry
->CreateThread(get_uid(i
), is_detached(i
), 0, 0, nullptr));
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));
114 registry
->FindThread([](ThreadContextBase
*tctx
,
115 void *) { return tctx
->parent_tid
== 107; },
118 registry
->FindThread([](ThreadContextBase
*tctx
,
119 void *) { return tctx
->stack_id
== 208; },
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
);
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
);
132 CheckThreadQuantity(registry
, exp_total
, 1, 1);
133 // Test methods that require the caller to hold a ThreadRegistryLock.
135 internal_memset(&has_tid
[0], 0, sizeof(has_tid
));
137 ThreadRegistryLock
l(registry
);
138 registry
->RunCallbackForEachThreadLocked(MarkUidAsPresent
, &has_tid
[0]);
140 for (u32 i
= 0; i
< exp_total
; i
++) {
141 EXPECT_TRUE(has_tid
[i
]);
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)));
150 EXPECT_EQ(11U, registry
->GetMaxAliveThreads());
153 TEST(SanitizerCommon
, ThreadRegistryTest
) {
154 ThreadRegistry
quarantine_registry(GetThreadContext
<ThreadContextBase
>,
155 kMaxRegistryThreads
, kRegistryQuarantine
,
157 TestRegistry(&quarantine_registry
, true);
159 ThreadRegistry
no_quarantine_registry(GetThreadContext
<ThreadContextBase
>,
161 kMaxRegistryThreads
, 0);
162 TestRegistry(&no_quarantine_registry
, false);
165 static const int kThreadsPerShard
= 20;
166 static const int kNumShards
= 25;
168 static int num_created
[kNumShards
+ 1];
169 static int num_started
[kNumShards
+ 1];
170 static int num_joined
[kNumShards
+ 1];
174 struct RunThreadArgs
{
175 ThreadRegistry
*registry
;
176 uptr shard
; // started from 1.
179 class TestThreadContext final
: public ThreadContextBase
{
181 explicit TestThreadContext(int tid
) : ThreadContextBase(tid
) {}
182 void OnJoined(void *arg
) {
183 uptr shard
= (uptr
)arg
;
186 void OnStarted(void *arg
) {
187 uptr shard
= (uptr
)arg
;
188 num_started
[shard
]++;
190 void OnCreated(void *arg
) {
191 uptr shard
= (uptr
)arg
;
192 num_created
[shard
]++;
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
++)
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
,
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
);
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
]);
225 for (int i
= 0; i
< kNumShards
; i
++) {
226 PTHREAD_JOIN(threads
[i
], 0);
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
]);
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
));
245 ThreadRegistry
registry(GetThreadContext
<TestThreadContext
>,
246 kThreadsPerShard
* kNumShards
+ 1, 10, 0);
247 ThreadedTestRegistry(®istry
);
250 TEST(SanitizerCommon
, PrintThreadHistory
) {
251 ThreadRegistry
registry(GetThreadContext
<TestThreadContext
>,
252 kThreadsPerShard
* kNumShards
+ 1, 10, 0);
254 UNINITIALIZED BufferedStackTrace stack1
;
255 stack1
.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false,
258 UNINITIALIZED BufferedStackTrace stack2
;
259 stack2
.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false,
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);
268 InternalScopedString out
;
269 PrintThreadHistory(registry
, out
);
271 std::string substrings
[] = {
272 "Thread T0/0 was created by T-1",
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",
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",
291 std::stringstream
ss(out
.data());
294 for (auto substr
: substrings
) {
295 std::getline(ss
, line
);
296 EXPECT_THAT(line
, HasSubstr(substr
)) << line
;
299 EXPECT_FALSE(std::getline(ss
, line
)) << "Unmatched line: " << line
;
302 } // namespace __sanitizer