sanitizer_thread_registry_test.cc revision 827d4ef8b76760ef8c58612d916b8f53081bf368
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(u32 tid) {
43  return (tid % 2 == 0);
44}
45
46static uptr get_uid(u32 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(0U, registry->CreateThread(get_uid(0), true, -1, 0));
68  registry->StartThread(0, 0, 0);
69  // Create a bunch of threads.
70  for (u32 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 (u32 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 (u32 i = 1; i <= 5; i++) {
81    registry->FinishThread(i);
82    if (!is_detached(i))
83      registry->JoinThread(i, 0);
84  }
85  for (u32 i = 6; i <= 10; i++) {
86    registry->StartThread(i, 0, 0);
87  }
88  std::vector<u32> new_tids;
89  for (u32 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, 5U);
94  u32 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(7U, registry->FindThread(HasName, (void*)"seven"));
100  EXPECT_EQ(ThreadRegistry::kUnknownTid,
101            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(ThreadRegistry::kUnknownTid,
105            registry->FindThread(HasUid, (void*)0x1234));
106  // Detach and finish and join remaining threads.
107  for (u32 i = 6; i <= 10; i++) {
108    registry->DetachThread(i);
109    registry->FinishThread(i);
110  }
111  for (u32 i = 0; i < new_tids.size(); i++) {
112    u32 tid = new_tids[i];
113    registry->StartThread(tid, 0, 0);
114    registry->DetachThread(tid);
115    registry->FinishThread(tid);
116  }
117  CheckThreadQuantity(registry, exp_total, 1, 1);
118  // Test methods that require the caller to hold a ThreadRegistryLock.
119  bool has_tid[16];
120  internal_memset(&has_tid[0], 0, sizeof(has_tid));
121  {
122    ThreadRegistryLock l(registry);
123    registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
124  }
125  for (u32 i = 0; i < exp_total; i++) {
126    EXPECT_TRUE(has_tid[i]);
127  }
128  {
129    ThreadRegistryLock l(registry);
130    registry->CheckLocked();
131    ThreadContextBase *main_thread = registry->GetThreadLocked(0);
132    EXPECT_EQ(main_thread, registry->FindThreadContextLocked(
133        HasUid, (void*)get_uid(0)));
134  }
135  EXPECT_EQ(11U, registry->GetMaxAliveThreads());
136}
137
138TEST(SanitizerCommon, ThreadRegistryTest) {
139  ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
140                                     kMaxRegistryThreads,
141                                     kRegistryQuarantine);
142  TestRegistry(&quarantine_registry, true);
143
144  ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
145                                        kMaxRegistryThreads,
146                                        kMaxRegistryThreads);
147  TestRegistry(&no_quarantine_registry, false);
148}
149
150static const int kThreadsPerShard = 20;
151static const int kNumShards = 25;
152
153static int num_created[kNumShards + 1];
154static int num_started[kNumShards + 1];
155static int num_joined[kNumShards + 1];
156
157namespace {
158
159struct RunThreadArgs {
160  ThreadRegistry *registry;
161  uptr shard;  // started from 1.
162};
163
164class TestThreadContext : public ThreadContextBase {
165 public:
166  explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}
167  void OnJoined(void *arg) {
168    uptr shard = (uptr)arg;
169    num_joined[shard]++;
170  }
171  void OnStarted(void *arg) {
172    uptr shard = (uptr)arg;
173    num_started[shard]++;
174  }
175  void OnCreated(void *arg) {
176    uptr shard = (uptr)arg;
177    num_created[shard]++;
178  }
179};
180
181}  // namespace
182
183void *RunThread(void *arg) {
184  RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);
185  std::vector<int> tids;
186  for (int i = 0; i < kThreadsPerShard; i++)
187    tids.push_back(
188        args->registry->CreateThread(0, false, 0, (void*)args->shard));
189  for (int i = 0; i < kThreadsPerShard; i++)
190    args->registry->StartThread(tids[i], 0, (void*)args->shard);
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);
195  return 0;
196}
197
198static 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, 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]);
208  }
209  for (int i = 0; i < kNumShards; i++) {
210    pthread_join(threads[i], 0);
211  }
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]);
221  }
222}
223
224TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
225  ThreadRegistry registry(GetThreadContext<TestThreadContext>,
226                          kThreadsPerShard * kNumShards + 1, 10);
227  ThreadedTestRegistry(&registry);
228}
229
230}  // namespace __sanitizer
231