sanitizer_thread_registry_test.cc revision 2d1fdb26e458c4ddc04155c1d421bced3ba90cd0
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"
14
15#include "sanitizer_pthread_wrappers.h"
16
17#include "gtest/gtest.h"
18
19#include <vector>
20
21namespace __sanitizer {
22
23static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED);
24static LowLevelAllocator tctx_allocator;
25
26template<typename TCTX>
27static ThreadContextBase *GetThreadContext(u32 tid) {
28  BlockingMutexLock l(&tctx_allocator_lock);
29  return new(tctx_allocator) TCTX(tid);
30}
31
32static const u32 kMaxRegistryThreads = 1000;
33static const u32 kRegistryQuarantine = 2;
34
35static 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
44static bool is_detached(u32 tid) {
45  return (tid % 2 == 0);
46}
47
48static uptr get_uid(u32 tid) {
49  return tid * 2;
50}
51
52static bool HasName(ThreadContextBase *tctx, void *arg) {
53  char *name = (char*)arg;
54  return (0 == internal_strcmp(tctx->name, name));
55}
56
57static bool HasUid(ThreadContextBase *tctx, void *arg) {
58  uptr uid = (uptr)arg;
59  return (tctx->user_id == uid);
60}
61
62static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {
63  bool *arr = (bool*)arg;
64  arr[tctx->tid] = true;
65}
66
67static 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++) {
110    registry->DetachThread(i);
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);
116    registry->DetachThread(tid);
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
140TEST(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
152static const int kThreadsPerShard = 20;
153static const int kNumShards = 25;
154
155static int num_created[kNumShards + 1];
156static int num_started[kNumShards + 1];
157static int num_joined[kNumShards + 1];
158
159namespace {
160
161struct RunThreadArgs {
162  ThreadRegistry *registry;
163  uptr shard;  // started from 1.
164};
165
166class 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
185void *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
200static 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;
209    PTHREAD_CREATE(&threads[i], 0, RunThread, &args[i]);
210  }
211  for (int i = 0; i < kNumShards; i++) {
212    PTHREAD_JOIN(threads[i], 0);
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
226TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
227  ThreadRegistry registry(GetThreadContext<TestThreadContext>,
228                          kThreadsPerShard * kNumShards + 1, 10);
229  ThreadedTestRegistry(&registry);
230}
231
232}  // namespace __sanitizer
233