sanitizer_thread_registry_test.cpp revision 1.1.1.3
1112158Sdas//===-- sanitizer_thread_registry_test.cpp --------------------------------===//
2112158Sdas//
3112158Sdas// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4112158Sdas// See https://llvm.org/LICENSE.txt for license information.
5112158Sdas// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6112158Sdas//
7112158Sdas//===----------------------------------------------------------------------===//
8112158Sdas//
9112158Sdas// This file is a part of shared sanitizer runtime.
10112158Sdas//
11112158Sdas//===----------------------------------------------------------------------===//
12112158Sdas#include "sanitizer_common/sanitizer_thread_registry.h"
13112158Sdas
14112158Sdas#include "sanitizer_pthread_wrappers.h"
15112158Sdas
16112158Sdas#include "gtest/gtest.h"
17112158Sdas
18112158Sdas#include <vector>
19112158Sdas
20112158Sdasnamespace __sanitizer {
21112158Sdas
22112158Sdasstatic Mutex tctx_allocator_lock;
23112158Sdasstatic LowLevelAllocator tctx_allocator;
24112158Sdas
25112158Sdastemplate<typename TCTX>
26112158Sdasstatic ThreadContextBase *GetThreadContext(u32 tid) {
27112158Sdas  Lock l(&tctx_allocator_lock);
28112158Sdas  return new(tctx_allocator) TCTX(tid);
29165743Sdas}
30165743Sdas
31112158Sdasstatic const u32 kMaxRegistryThreads = 1000;
32112158Sdasstatic const u32 kRegistryQuarantine = 2;
33112158Sdas
34112158Sdasstatic void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total,
35112158Sdas                                uptr exp_running, uptr exp_alive) {
36112158Sdas  uptr total, running, alive;
37112158Sdas  registry->GetNumberOfThreads(&total, &running, &alive);
38112158Sdas  EXPECT_EQ(exp_total, total);
39112158Sdas  EXPECT_EQ(exp_running, running);
40112158Sdas  EXPECT_EQ(exp_alive, alive);
41112158Sdas}
42112158Sdas
43112158Sdasstatic bool is_detached(u32 tid) {
44112158Sdas  return (tid % 2 == 0);
45112158Sdas}
46112158Sdas
47112158Sdasstatic uptr get_uid(u32 tid) {
48112158Sdas  return tid * 2;
49112158Sdas}
50112158Sdas
51112158Sdasstatic bool HasName(ThreadContextBase *tctx, void *arg) {
52112158Sdas  char *name = (char*)arg;
53112158Sdas  return (0 == internal_strcmp(tctx->name, name));
54112158Sdas}
55112158Sdas
56112158Sdasstatic bool HasUid(ThreadContextBase *tctx, void *arg) {
57165743Sdas  uptr uid = (uptr)arg;
58112158Sdas  return (tctx->user_id == uid);
59112158Sdas}
60112158Sdas
61112158Sdasstatic void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {
62112158Sdas  bool *arr = (bool*)arg;
63112158Sdas  arr[tctx->tid] = true;
64112158Sdas}
65112158Sdas
66112158Sdasstatic void TestRegistry(ThreadRegistry *registry, bool has_quarantine) {
67112158Sdas  // Create and start a main thread.
68112158Sdas  EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0));
69112158Sdas  registry->StartThread(0, 0, ThreadType::Regular, 0);
70112158Sdas  // Create a bunch of threads.
71112158Sdas  for (u32 i = 1; i <= 10; i++) {
72112158Sdas    EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
73112158Sdas  }
74112158Sdas  CheckThreadQuantity(registry, 11, 1, 11);
75112158Sdas  // Start some of them.
76112158Sdas  for (u32 i = 1; i <= 5; i++) {
77112158Sdas    registry->StartThread(i, 0, ThreadType::Regular, 0);
78112158Sdas  }
79112158Sdas  CheckThreadQuantity(registry, 11, 6, 11);
80112158Sdas  // Finish, create and start more threads.
81112158Sdas  for (u32 i = 1; i <= 5; i++) {
82112158Sdas    registry->FinishThread(i);
83112158Sdas    if (!is_detached(i))
84165743Sdas      registry->JoinThread(i, 0);
85165743Sdas  }
86165743Sdas  for (u32 i = 6; i <= 10; i++) {
87112158Sdas    registry->StartThread(i, 0, ThreadType::Regular, 0);
88112158Sdas  }
89112158Sdas  std::vector<u32> new_tids;
90112158Sdas  for (u32 i = 11; i <= 15; i++) {
91112158Sdas    new_tids.push_back(
92        registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
93  }
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);
109  }
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);
115  }
116  CheckThreadQuantity(registry, exp_total, 1, 1);
117  // Test methods that require the caller to hold a ThreadRegistryLock.
118  bool has_tid[16];
119  internal_memset(&has_tid[0], 0, sizeof(has_tid));
120  {
121    ThreadRegistryLock l(registry);
122    registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
123  }
124  for (u32 i = 0; i < exp_total; i++) {
125    EXPECT_TRUE(has_tid[i]);
126  }
127  {
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)));
133  }
134  EXPECT_EQ(11U, registry->GetMaxAliveThreads());
135}
136
137TEST(SanitizerCommon, ThreadRegistryTest) {
138  ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
139                                     kMaxRegistryThreads, kRegistryQuarantine,
140                                     0);
141  TestRegistry(&quarantine_registry, true);
142
143  ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
144                                        kMaxRegistryThreads,
145                                        kMaxRegistryThreads, 0);
146  TestRegistry(&no_quarantine_registry, false);
147}
148
149static const int kThreadsPerShard = 20;
150static const int kNumShards = 25;
151
152static int num_created[kNumShards + 1];
153static int num_started[kNumShards + 1];
154static int num_joined[kNumShards + 1];
155
156namespace {
157
158struct RunThreadArgs {
159  ThreadRegistry *registry;
160  uptr shard;  // started from 1.
161};
162
163class TestThreadContext final : public ThreadContextBase {
164 public:
165  explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}
166  void OnJoined(void *arg) {
167    uptr shard = (uptr)arg;
168    num_joined[shard]++;
169  }
170  void OnStarted(void *arg) {
171    uptr shard = (uptr)arg;
172    num_started[shard]++;
173  }
174  void OnCreated(void *arg) {
175    uptr shard = (uptr)arg;
176    num_created[shard]++;
177  }
178};
179
180}  // namespace
181
182void *RunThread(void *arg) {
183  RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);
184  std::vector<int> tids;
185  for (int i = 0; i < kThreadsPerShard; i++)
186    tids.push_back(
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,
190        (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, 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]);
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  memset(&num_created, 0, sizeof(num_created));
226  memset(&num_started, 0, sizeof(num_created));
227  memset(&num_joined, 0, sizeof(num_created));
228
229  ThreadRegistry registry(GetThreadContext<TestThreadContext>,
230                          kThreadsPerShard * kNumShards + 1, 10, 0);
231  ThreadedTestRegistry(&registry);
232}
233
234}  // namespace __sanitizer
235