sanitizer_thread_registry_test.cc revision 6d429cda69b7ccc007ee368a73a6040c6b125afb
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#include "gtest/gtest.h"
15
16#include <vector>
17
18namespace __sanitizer {
19
20static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED);
21static LowLevelAllocator tctx_allocator;
22
23template<typename TCTX>
24static ThreadContextBase *GetThreadContext(u32 tid) {
25  BlockingMutexLock l(&tctx_allocator_lock);
26  void *mem = tctx_allocator.Allocate(sizeof(TCTX));
27  return new(mem) 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, &running, &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(int tid) {
43  return (tid % 2 == 0);
44}
45
46static uptr get_uid(int tid) {
47  return tid * 2;
48}
49
50static bool HasName(ThreadContextBase *tctx, void *arg) {
51  char *name = (char*)arg;
52  return (tctx->name && 0 == internal_strcmp(tctx->name, 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(0, registry->CreateThread(get_uid(0), true, -1, 0));
68  registry->StartThread(0, 0, 0);
69  // Create a bunch of threads.
70  for (int i = 1; i <= 10; i++) {
71    EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
72  }
73  CheckThreadQuantity(registry, 11, 1, 11);
74  // Start some of them.
75  for (int i = 1; i <= 5; i++) {
76    registry->StartThread(i, 0, 0);
77  }
78  CheckThreadQuantity(registry, 11, 6, 11);
79  // Finish, create and start more threads.
80  for (int i = 1; i <= 5; i++) {
81    registry->FinishThread(i);
82    if (!is_detached(i))
83      registry->JoinThread(i, 0);
84  }
85  for (int i = 6; i <= 10; i++) {
86    registry->StartThread(i, 0, 0);
87  }
88  std::vector<int> new_tids;
89  for (int 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, 5);
94  int exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine  : 0);
95  CheckThreadQuantity(registry, exp_total, 6, 11);
96  // Test SetThreadName and FindThread.
97  registry->SetThreadName(6, "six");
98  registry->SetThreadName(7, "seven");
99  EXPECT_EQ(7, registry->FindThread(HasName, (void*)"seven"));
100  EXPECT_EQ(-1, registry->FindThread(HasName, (void*)"none"));
101  EXPECT_EQ(0, registry->FindThread(HasUid, (void*)get_uid(0)));
102  EXPECT_EQ(10, registry->FindThread(HasUid, (void*)get_uid(10)));
103  EXPECT_EQ(-1, registry->FindThread(HasUid, (void*)0x1234));
104  // Detach and finish and join remaining threads.
105  for (int i = 6; i <= 10; i++) {
106    registry->DetachThread(i);
107    registry->FinishThread(i);
108  }
109  for (int i = 0; i < new_tids.size(); i++) {
110    int tid = new_tids[i];
111    registry->StartThread(tid, 0, 0);
112    registry->DetachThread(tid);
113    registry->FinishThread(tid);
114  }
115  CheckThreadQuantity(registry, exp_total, 1, 1);
116  // Test methods that require the caller to hold a ThreadRegistryLock.
117  bool has_tid[16];
118  internal_memset(&has_tid[0], 0, sizeof(has_tid));
119  {
120    ThreadRegistryLock l(registry);
121    registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
122  }
123  for (int 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(0);
130    EXPECT_EQ(main_thread, registry->FindThreadContextLocked(
131        HasUid, (void*)get_uid(0)));
132  }
133  EXPECT_EQ(11, registry->GetMaxAliveThreads());
134}
135
136TEST(SanitizerCommon, ThreadRegistryTest) {
137  ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
138                                     kMaxRegistryThreads,
139                                     kRegistryQuarantine);
140  TestRegistry(&quarantine_registry, true);
141
142  ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
143                                        kMaxRegistryThreads,
144                                        kMaxRegistryThreads);
145  TestRegistry(&no_quarantine_registry, 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 : 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, (void*)args->shard);
189  for (int i = 0; i < kThreadsPerShard; i++)
190    args->registry->FinishThread(tids[i]);
191  for (int i = 0; i < kThreadsPerShard; i++)
192    args->registry->JoinThread(tids[i], (void*)args->shard);
193  return 0;
194}
195
196static void ThreadedTestRegistry(ThreadRegistry *registry) {
197  // Create and start a main thread.
198  EXPECT_EQ(0, registry->CreateThread(0, true, -1, 0));
199  registry->StartThread(0, 0, 0);
200  pthread_t threads[kNumShards];
201  RunThreadArgs args[kNumShards];
202  for (int i = 0; i < kNumShards; i++) {
203    args[i].registry = registry;
204    args[i].shard = i + 1;
205    pthread_create(&threads[i], 0, RunThread, &args[i]);
206  }
207  for (int i = 0; i < kNumShards; i++) {
208    pthread_join(threads[i], 0);
209  }
210  // Check that each thread created/started/joined correct amount
211  // of "threads" in thread_registry.
212  EXPECT_EQ(1, num_created[0]);
213  EXPECT_EQ(1, num_started[0]);
214  EXPECT_EQ(0, num_joined[0]);
215  for (int i = 1; i <= kNumShards; i++) {
216    EXPECT_EQ(kThreadsPerShard, num_created[i]);
217    EXPECT_EQ(kThreadsPerShard, num_started[i]);
218    EXPECT_EQ(kThreadsPerShard, num_joined[i]);
219  }
220}
221
222TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
223  ThreadRegistry registry(GetThreadContext<TestThreadContext>,
224                          kThreadsPerShard * kNumShards + 1, 10);
225  ThreadedTestRegistry(&registry);
226}
227
228}  // namespace __sanitizer
229