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"
14 #include "sanitizer_pthread_wrappers.h"
16 #include "gtest/gtest.h"
20 namespace __sanitizer
{
22 static Mutex tctx_allocator_lock
;
23 static LowLevelAllocator tctx_allocator
;
25 template<typename TCTX
>
26 static ThreadContextBase
*GetThreadContext(u32 tid
) {
27 Lock
l(&tctx_allocator_lock
);
28 return new(tctx_allocator
) TCTX(tid
);
31 static const u32 kMaxRegistryThreads
= 1000;
32 static const u32 kRegistryQuarantine
= 2;
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
);
43 static bool is_detached(u32 tid
) {
44 return (tid
% 2 == 0);
47 static uptr
get_uid(u32 tid
) {
51 static bool HasName(ThreadContextBase
*tctx
, void *arg
) {
52 char *name
= (char*)arg
;
53 return (0 == internal_strcmp(tctx
->name
, name
));
56 static bool HasUid(ThreadContextBase
*tctx
, void *arg
) {
58 return (tctx
->user_id
== uid
);
61 static void MarkUidAsPresent(ThreadContextBase
*tctx
, void *arg
) {
62 bool *arr
= (bool*)arg
;
63 arr
[tctx
->tid
] = true;
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));
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);
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
);
84 registry
->JoinThread(i
, 0);
86 for (u32 i
= 6; i
<= 10; i
++) {
87 registry
->StartThread(i
, 0, ThreadType::Regular
, 0);
89 std::vector
<u32
> new_tids
;
90 for (u32 i
= 11; i
<= 15; i
++) {
92 registry
->CreateThread(get_uid(i
), is_detached(i
), 0, 0));
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
);
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
);
116 CheckThreadQuantity(registry
, exp_total
, 1, 1);
117 // Test methods that require the caller to hold a ThreadRegistryLock.
119 internal_memset(&has_tid
[0], 0, sizeof(has_tid
));
121 ThreadRegistryLock
l(registry
);
122 registry
->RunCallbackForEachThreadLocked(MarkUidAsPresent
, &has_tid
[0]);
124 for (u32 i
= 0; i
< exp_total
; i
++) {
125 EXPECT_TRUE(has_tid
[i
]);
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)));
134 EXPECT_EQ(11U, registry
->GetMaxAliveThreads());
137 TEST(SanitizerCommon
, ThreadRegistryTest
) {
138 ThreadRegistry
quarantine_registry(GetThreadContext
<ThreadContextBase
>,
139 kMaxRegistryThreads
, kRegistryQuarantine
,
141 TestRegistry(&quarantine_registry
, true);
143 ThreadRegistry
no_quarantine_registry(GetThreadContext
<ThreadContextBase
>,
145 kMaxRegistryThreads
, 0);
146 TestRegistry(&no_quarantine_registry
, false);
149 static const int kThreadsPerShard
= 20;
150 static const int kNumShards
= 25;
152 static int num_created
[kNumShards
+ 1];
153 static int num_started
[kNumShards
+ 1];
154 static int num_joined
[kNumShards
+ 1];
158 struct RunThreadArgs
{
159 ThreadRegistry
*registry
;
160 uptr shard
; // started from 1.
163 class TestThreadContext final
: public ThreadContextBase
{
165 explicit TestThreadContext(int tid
) : ThreadContextBase(tid
) {}
166 void OnJoined(void *arg
) {
167 uptr shard
= (uptr
)arg
;
170 void OnStarted(void *arg
) {
171 uptr shard
= (uptr
)arg
;
172 num_started
[shard
]++;
174 void OnCreated(void *arg
) {
175 uptr shard
= (uptr
)arg
;
176 num_created
[shard
]++;
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
++)
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
,
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
);
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
]);
209 for (int i
= 0; i
< kNumShards
; i
++) {
210 PTHREAD_JOIN(threads
[i
], 0);
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
]);
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
));
229 ThreadRegistry
registry(GetThreadContext
<TestThreadContext
>,
230 kThreadsPerShard
* kNumShards
+ 1, 10, 0);
231 ThreadedTestRegistry(®istry
);
234 } // namespace __sanitizer