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