forked from OSchip/llvm-project
230 lines
7.2 KiB
C++
230 lines
7.2 KiB
C++
//===-- sanitizer_thread_registry_test.cc ---------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file is a part of shared sanitizer runtime.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "sanitizer_common/sanitizer_thread_registry.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <vector>
|
|
|
|
namespace __sanitizer {
|
|
|
|
static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED);
|
|
static LowLevelAllocator tctx_allocator;
|
|
|
|
template<typename TCTX>
|
|
static ThreadContextBase *GetThreadContext(u32 tid) {
|
|
BlockingMutexLock l(&tctx_allocator_lock);
|
|
return new(tctx_allocator) TCTX(tid);
|
|
}
|
|
|
|
static const u32 kMaxRegistryThreads = 1000;
|
|
static const u32 kRegistryQuarantine = 2;
|
|
|
|
static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total,
|
|
uptr exp_running, uptr exp_alive) {
|
|
uptr total, running, alive;
|
|
registry->GetNumberOfThreads(&total, &running, &alive);
|
|
EXPECT_EQ(exp_total, total);
|
|
EXPECT_EQ(exp_running, running);
|
|
EXPECT_EQ(exp_alive, alive);
|
|
}
|
|
|
|
static bool is_detached(u32 tid) {
|
|
return (tid % 2 == 0);
|
|
}
|
|
|
|
static uptr get_uid(u32 tid) {
|
|
return tid * 2;
|
|
}
|
|
|
|
static bool HasName(ThreadContextBase *tctx, void *arg) {
|
|
char *name = (char*)arg;
|
|
return (tctx->name && 0 == internal_strcmp(tctx->name, name));
|
|
}
|
|
|
|
static bool HasUid(ThreadContextBase *tctx, void *arg) {
|
|
uptr uid = (uptr)arg;
|
|
return (tctx->user_id == uid);
|
|
}
|
|
|
|
static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {
|
|
bool *arr = (bool*)arg;
|
|
arr[tctx->tid] = true;
|
|
}
|
|
|
|
static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) {
|
|
// Create and start a main thread.
|
|
EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0));
|
|
registry->StartThread(0, 0, 0);
|
|
// Create a bunch of threads.
|
|
for (u32 i = 1; i <= 10; i++) {
|
|
EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
|
|
}
|
|
CheckThreadQuantity(registry, 11, 1, 11);
|
|
// Start some of them.
|
|
for (u32 i = 1; i <= 5; i++) {
|
|
registry->StartThread(i, 0, 0);
|
|
}
|
|
CheckThreadQuantity(registry, 11, 6, 11);
|
|
// Finish, create and start more threads.
|
|
for (u32 i = 1; i <= 5; i++) {
|
|
registry->FinishThread(i);
|
|
if (!is_detached(i))
|
|
registry->JoinThread(i, 0);
|
|
}
|
|
for (u32 i = 6; i <= 10; i++) {
|
|
registry->StartThread(i, 0, 0);
|
|
}
|
|
std::vector<u32> new_tids;
|
|
for (u32 i = 11; i <= 15; i++) {
|
|
new_tids.push_back(
|
|
registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
|
|
}
|
|
ASSERT_LE(kRegistryQuarantine, 5U);
|
|
u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0);
|
|
CheckThreadQuantity(registry, exp_total, 6, 11);
|
|
// Test SetThreadName and FindThread.
|
|
registry->SetThreadName(6, "six");
|
|
registry->SetThreadName(7, "seven");
|
|
EXPECT_EQ(7U, registry->FindThread(HasName, (void*)"seven"));
|
|
EXPECT_EQ(ThreadRegistry::kUnknownTid,
|
|
registry->FindThread(HasName, (void*)"none"));
|
|
EXPECT_EQ(0U, registry->FindThread(HasUid, (void*)get_uid(0)));
|
|
EXPECT_EQ(10U, registry->FindThread(HasUid, (void*)get_uid(10)));
|
|
EXPECT_EQ(ThreadRegistry::kUnknownTid,
|
|
registry->FindThread(HasUid, (void*)0x1234));
|
|
// Detach and finish and join remaining threads.
|
|
for (u32 i = 6; i <= 10; i++) {
|
|
registry->DetachThread(i);
|
|
registry->FinishThread(i);
|
|
}
|
|
for (u32 i = 0; i < new_tids.size(); i++) {
|
|
u32 tid = new_tids[i];
|
|
registry->StartThread(tid, 0, 0);
|
|
registry->DetachThread(tid);
|
|
registry->FinishThread(tid);
|
|
}
|
|
CheckThreadQuantity(registry, exp_total, 1, 1);
|
|
// Test methods that require the caller to hold a ThreadRegistryLock.
|
|
bool has_tid[16];
|
|
internal_memset(&has_tid[0], 0, sizeof(has_tid));
|
|
{
|
|
ThreadRegistryLock l(registry);
|
|
registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
|
|
}
|
|
for (u32 i = 0; i < exp_total; i++) {
|
|
EXPECT_TRUE(has_tid[i]);
|
|
}
|
|
{
|
|
ThreadRegistryLock l(registry);
|
|
registry->CheckLocked();
|
|
ThreadContextBase *main_thread = registry->GetThreadLocked(0);
|
|
EXPECT_EQ(main_thread, registry->FindThreadContextLocked(
|
|
HasUid, (void*)get_uid(0)));
|
|
}
|
|
EXPECT_EQ(11U, registry->GetMaxAliveThreads());
|
|
}
|
|
|
|
TEST(SanitizerCommon, ThreadRegistryTest) {
|
|
ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
|
|
kMaxRegistryThreads,
|
|
kRegistryQuarantine);
|
|
TestRegistry(&quarantine_registry, true);
|
|
|
|
ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
|
|
kMaxRegistryThreads,
|
|
kMaxRegistryThreads);
|
|
TestRegistry(&no_quarantine_registry, false);
|
|
}
|
|
|
|
static const int kThreadsPerShard = 20;
|
|
static const int kNumShards = 25;
|
|
|
|
static int num_created[kNumShards + 1];
|
|
static int num_started[kNumShards + 1];
|
|
static int num_joined[kNumShards + 1];
|
|
|
|
namespace {
|
|
|
|
struct RunThreadArgs {
|
|
ThreadRegistry *registry;
|
|
uptr shard; // started from 1.
|
|
};
|
|
|
|
class TestThreadContext : public ThreadContextBase {
|
|
public:
|
|
explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}
|
|
void OnJoined(void *arg) {
|
|
uptr shard = (uptr)arg;
|
|
num_joined[shard]++;
|
|
}
|
|
void OnStarted(void *arg) {
|
|
uptr shard = (uptr)arg;
|
|
num_started[shard]++;
|
|
}
|
|
void OnCreated(void *arg) {
|
|
uptr shard = (uptr)arg;
|
|
num_created[shard]++;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
void *RunThread(void *arg) {
|
|
RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);
|
|
std::vector<int> tids;
|
|
for (int i = 0; i < kThreadsPerShard; i++)
|
|
tids.push_back(
|
|
args->registry->CreateThread(0, false, 0, (void*)args->shard));
|
|
for (int i = 0; i < kThreadsPerShard; i++)
|
|
args->registry->StartThread(tids[i], 0, (void*)args->shard);
|
|
for (int i = 0; i < kThreadsPerShard; i++)
|
|
args->registry->FinishThread(tids[i]);
|
|
for (int i = 0; i < kThreadsPerShard; i++)
|
|
args->registry->JoinThread(tids[i], (void*)args->shard);
|
|
return 0;
|
|
}
|
|
|
|
static void ThreadedTestRegistry(ThreadRegistry *registry) {
|
|
// Create and start a main thread.
|
|
EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0));
|
|
registry->StartThread(0, 0, 0);
|
|
pthread_t threads[kNumShards];
|
|
RunThreadArgs args[kNumShards];
|
|
for (int i = 0; i < kNumShards; i++) {
|
|
args[i].registry = registry;
|
|
args[i].shard = i + 1;
|
|
pthread_create(&threads[i], 0, RunThread, &args[i]);
|
|
}
|
|
for (int i = 0; i < kNumShards; i++) {
|
|
pthread_join(threads[i], 0);
|
|
}
|
|
// Check that each thread created/started/joined correct amount
|
|
// of "threads" in thread_registry.
|
|
EXPECT_EQ(1, num_created[0]);
|
|
EXPECT_EQ(1, num_started[0]);
|
|
EXPECT_EQ(0, num_joined[0]);
|
|
for (int i = 1; i <= kNumShards; i++) {
|
|
EXPECT_EQ(kThreadsPerShard, num_created[i]);
|
|
EXPECT_EQ(kThreadsPerShard, num_started[i]);
|
|
EXPECT_EQ(kThreadsPerShard, num_joined[i]);
|
|
}
|
|
}
|
|
|
|
TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
|
|
ThreadRegistry registry(GetThreadContext<TestThreadContext>,
|
|
kThreadsPerShard * kNumShards + 1, 10);
|
|
ThreadedTestRegistry(®istry);
|
|
}
|
|
|
|
} // namespace __sanitizer
|