303 lines · cpp
1//===-- sanitizer_thread_registry_test.cpp --------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of shared sanitizer runtime.10//11//===----------------------------------------------------------------------===//12#include "sanitizer_common/sanitizer_thread_registry.h"13 14#include <iostream>15#include <vector>16 17#include "gmock/gmock.h"18#include "gtest/gtest.h"19#include "sanitizer_common/sanitizer_common.h"20#include "sanitizer_common/sanitizer_stackdepot.h"21#include "sanitizer_common/sanitizer_stacktrace.h"22#include "sanitizer_common/sanitizer_thread_history.h"23#include "sanitizer_pthread_wrappers.h"24 25using testing::HasSubstr;26 27namespace __sanitizer {28 29static Mutex tctx_allocator_lock;30static LowLevelAllocator tctx_allocator;31 32template<typename TCTX>33static ThreadContextBase *GetThreadContext(u32 tid) {34 Lock l(&tctx_allocator_lock);35 return new(tctx_allocator) TCTX(tid);36}37 38static const u32 kMaxRegistryThreads = 1000;39static const u32 kRegistryQuarantine = 2;40 41static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total,42 uptr exp_running, uptr exp_alive) {43 uptr total, running, alive;44 registry->GetNumberOfThreads(&total, &running, &alive);45 EXPECT_EQ(exp_total, total);46 EXPECT_EQ(exp_running, running);47 EXPECT_EQ(exp_alive, alive);48}49 50static bool is_detached(u32 tid) {51 return (tid % 2 == 0);52}53 54static uptr get_uid(u32 tid) {55 return tid * 2;56}57 58static bool HasName(ThreadContextBase *tctx, void *arg) {59 char *name = (char*)arg;60 return (0 == internal_strcmp(tctx->name, name));61}62 63static bool HasUid(ThreadContextBase *tctx, void *arg) {64 uptr uid = (uptr)arg;65 return (tctx->user_id == uid);66}67 68static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {69 bool *arr = (bool*)arg;70 arr[tctx->tid] = true;71}72 73static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) {74 // Create and start a main thread.75 EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0, nullptr));76 registry->StartThread(0, 0, ThreadType::Regular, 0);77 // Create a bunch of threads.78 for (u32 i = 1; i <= 10; i++) {79 EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 100 + i,80 200 + i, nullptr));81 }82 CheckThreadQuantity(registry, 11, 1, 11);83 // Start some of them.84 for (u32 i = 1; i <= 5; i++) {85 registry->StartThread(i, 0, ThreadType::Regular, 0);86 }87 CheckThreadQuantity(registry, 11, 6, 11);88 // Finish, create and start more threads.89 for (u32 i = 1; i <= 5; i++) {90 registry->FinishThread(i);91 if (!is_detached(i))92 registry->JoinThread(i, 0);93 }94 for (u32 i = 6; i <= 10; i++) {95 registry->StartThread(i, 0, ThreadType::Regular, 0);96 }97 std::vector<u32> new_tids;98 for (u32 i = 11; i <= 15; i++) {99 new_tids.push_back(100 registry->CreateThread(get_uid(i), is_detached(i), 0, 0, nullptr));101 }102 ASSERT_LE(kRegistryQuarantine, 5U);103 u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0);104 CheckThreadQuantity(registry, exp_total, 6, 11);105 // Test SetThreadName and FindThread.106 registry->SetThreadName(6, "six");107 registry->SetThreadName(7, "seven");108 EXPECT_EQ(7U, registry->FindThread(HasName, (void *)"seven"));109 EXPECT_EQ(kInvalidTid, registry->FindThread(HasName, (void *)"none"));110 EXPECT_EQ(0U, registry->FindThread(HasUid, (void *)get_uid(0)));111 EXPECT_EQ(10U, registry->FindThread(HasUid, (void *)get_uid(10)));112 EXPECT_EQ(kInvalidTid, registry->FindThread(HasUid, (void *)0x1234));113 EXPECT_EQ(7U,114 registry->FindThread([](ThreadContextBase *tctx,115 void *) { return tctx->parent_tid == 107; },116 nullptr));117 EXPECT_EQ(8U,118 registry->FindThread([](ThreadContextBase *tctx,119 void *) { return tctx->stack_id == 208; },120 nullptr));121 // Detach and finish and join remaining threads.122 for (u32 i = 6; i <= 10; i++) {123 registry->DetachThread(i, 0);124 registry->FinishThread(i);125 }126 for (u32 i = 0; i < new_tids.size(); i++) {127 u32 tid = new_tids[i];128 registry->StartThread(tid, 0, ThreadType::Regular, 0);129 registry->DetachThread(tid, 0);130 registry->FinishThread(tid);131 }132 CheckThreadQuantity(registry, exp_total, 1, 1);133 // Test methods that require the caller to hold a ThreadRegistryLock.134 bool has_tid[16];135 internal_memset(&has_tid[0], 0, sizeof(has_tid));136 {137 ThreadRegistryLock l(registry);138 registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);139 }140 for (u32 i = 0; i < exp_total; i++) {141 EXPECT_TRUE(has_tid[i]);142 }143 {144 ThreadRegistryLock l(registry);145 registry->CheckLocked();146 ThreadContextBase *main_thread = registry->GetThreadLocked(0);147 EXPECT_EQ(main_thread, registry->FindThreadContextLocked(148 HasUid, (void*)get_uid(0)));149 }150 EXPECT_EQ(11U, registry->GetMaxAliveThreads());151}152 153TEST(SanitizerCommon, ThreadRegistryTest) {154 ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,155 kMaxRegistryThreads, kRegistryQuarantine,156 0);157 TestRegistry(&quarantine_registry, true);158 159 ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,160 kMaxRegistryThreads,161 kMaxRegistryThreads, 0);162 TestRegistry(&no_quarantine_registry, false);163}164 165static const int kThreadsPerShard = 20;166static const int kNumShards = 25;167 168static int num_created[kNumShards + 1];169static int num_started[kNumShards + 1];170static int num_joined[kNumShards + 1];171 172namespace {173 174struct RunThreadArgs {175 ThreadRegistry *registry;176 uptr shard; // started from 1.177};178 179class TestThreadContext final : public ThreadContextBase {180 public:181 explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}182 void OnJoined(void *arg) {183 uptr shard = (uptr)arg;184 num_joined[shard]++;185 }186 void OnStarted(void *arg) {187 uptr shard = (uptr)arg;188 num_started[shard]++;189 }190 void OnCreated(void *arg) {191 uptr shard = (uptr)arg;192 num_created[shard]++;193 }194};195 196} // namespace197 198void *RunThread(void *arg) {199 RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);200 std::vector<int> tids;201 for (int i = 0; i < kThreadsPerShard; i++)202 tids.push_back(203 args->registry->CreateThread(0, false, 0, (void*)args->shard));204 for (int i = 0; i < kThreadsPerShard; i++)205 args->registry->StartThread(tids[i], 0, ThreadType::Regular,206 (void*)args->shard);207 for (int i = 0; i < kThreadsPerShard; i++)208 args->registry->FinishThread(tids[i]);209 for (int i = 0; i < kThreadsPerShard; i++)210 args->registry->JoinThread(tids[i], (void*)args->shard);211 return 0;212}213 214static void ThreadedTestRegistry(ThreadRegistry *registry) {215 // Create and start a main thread.216 EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0));217 registry->StartThread(0, 0, ThreadType::Regular, 0);218 pthread_t threads[kNumShards];219 RunThreadArgs args[kNumShards];220 for (int i = 0; i < kNumShards; i++) {221 args[i].registry = registry;222 args[i].shard = i + 1;223 PTHREAD_CREATE(&threads[i], 0, RunThread, &args[i]);224 }225 for (int i = 0; i < kNumShards; i++) {226 PTHREAD_JOIN(threads[i], 0);227 }228 // Check that each thread created/started/joined correct amount229 // of "threads" in thread_registry.230 EXPECT_EQ(1, num_created[0]);231 EXPECT_EQ(1, num_started[0]);232 EXPECT_EQ(0, num_joined[0]);233 for (int i = 1; i <= kNumShards; i++) {234 EXPECT_EQ(kThreadsPerShard, num_created[i]);235 EXPECT_EQ(kThreadsPerShard, num_started[i]);236 EXPECT_EQ(kThreadsPerShard, num_joined[i]);237 }238}239 240TEST(SanitizerCommon, ThreadRegistryThreadedTest) {241 memset(&num_created, 0, sizeof(num_created));242 memset(&num_started, 0, sizeof(num_created));243 memset(&num_joined, 0, sizeof(num_created));244 245 ThreadRegistry registry(GetThreadContext<TestThreadContext>,246 kThreadsPerShard * kNumShards + 1, 10, 0);247 ThreadedTestRegistry(®istry);248}249 250TEST(SanitizerCommon, PrintThreadHistory) {251 ThreadRegistry registry(GetThreadContext<TestThreadContext>,252 kThreadsPerShard * kNumShards + 1, 10, 0);253 254 UNINITIALIZED BufferedStackTrace stack1;255 stack1.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false,256 /*max_depth=*/1);257 258 UNINITIALIZED BufferedStackTrace stack2;259 stack2.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false,260 /*max_depth=*/1);261 262 EXPECT_EQ(0U, registry.CreateThread(0, true, -1, 0, nullptr));263 for (int i = 0; i < 5; i++) {264 registry.CreateThread(0, true, 0, StackDepotPut(stack1), nullptr);265 registry.CreateThread(0, true, 0, StackDepotPut(stack2), nullptr);266 }267 268 InternalScopedString out;269 PrintThreadHistory(registry, out);270 271 std::string substrings[] = {272 "Thread T0/0 was created by T-1",273 "<empty stack>",274 "",275 "Thread T1/0 was created by T0/0",276 "Thread T3/0 was created by T0/0",277 "Thread T5/0 was created by T0/0",278 "Thread T7/0 was created by T0/0",279 "Thread T9/0 was created by T0/0",280 "#0 0x",281 "",282 "Thread T2/0 was created by T0/0",283 "Thread T4/0 was created by T0/0",284 "Thread T6/0 was created by T0/0",285 "Thread T8/0 was created by T0/0",286 "Thread T10/0 was created by T0/0",287 "#0 0x",288 "",289 };290 291 std::stringstream ss(out.data());292 std::string line;293 294 for (auto substr : substrings) {295 std::getline(ss, line);296 EXPECT_THAT(line, HasSubstr(substr)) << line;297 }298 299 EXPECT_FALSE(std::getline(ss, line)) << "Unmatched line: " << line;300}301 302} // namespace __sanitizer303