| Index: base/debug/activity_tracker_unittest.cc
|
| diff --git a/base/debug/activity_tracker_unittest.cc b/base/debug/activity_tracker_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..75d5d3cdeca53f3eb47e91a43931c374c948e9f0
|
| --- /dev/null
|
| +++ b/base/debug/activity_tracker_unittest.cc
|
| @@ -0,0 +1,252 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/debug/activity_tracker.h"
|
| +
|
| +#include <memory>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/files/file.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/files/memory_mapped_file.h"
|
| +#include "base/files/scoped_temp_dir.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/pending_task.h"
|
| +#include "base/synchronization/condition_variable.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/synchronization/spin_wait.h"
|
| +#include "base/threading/simple_thread.h"
|
| +#include "base/time/time.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace base {
|
| +namespace debug {
|
| +
|
| +namespace {
|
| +
|
| +class TestActivityTracker : public ThreadActivityTracker {
|
| + public:
|
| + TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size)
|
| + : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size),
|
| + mem_segment_(std::move(memory)) {}
|
| +
|
| + ~TestActivityTracker() override {}
|
| +
|
| + private:
|
| + std::unique_ptr<char[]> mem_segment_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +class ActivityTrackerTest : public testing::Test {
|
| + public:
|
| + const int kMemorySize = 1 << 10; // 1KiB
|
| + const int kStackSize = 256;
|
| +
|
| + using StackEntry = ThreadActivityTracker::StackEntry;
|
| + using StackEntryData = ThreadActivityTracker::StackEntryData;
|
| +
|
| + ActivityTrackerTest() {}
|
| +
|
| + ~ActivityTrackerTest() override {
|
| + GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
|
| + if (global_tracker) {
|
| + global_tracker->ReleaseTrackerForCurrentThreadForTesting();
|
| + delete global_tracker;
|
| + }
|
| + }
|
| +
|
| + std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() {
|
| + std::unique_ptr<char[]> memory(new char[kStackSize]);
|
| + return WrapUnique(new TestActivityTracker(std::move(memory), kStackSize));
|
| + }
|
| +
|
| + size_t GetGlobalActiveTrackerCount() {
|
| + GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
|
| + if (!global_tracker)
|
| + return 0;
|
| + return global_tracker->thread_tracker_count_.load(
|
| + std::memory_order_relaxed);
|
| + }
|
| +
|
| + size_t GetGlobalInactiveTrackerCount() {
|
| + GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
|
| + if (!global_tracker)
|
| + return 0;
|
| + return global_tracker->available_memories_count_.load(
|
| + std::memory_order_relaxed);
|
| + }
|
| +
|
| + static void DoNothing() {}
|
| +};
|
| +
|
| +TEST_F(ActivityTrackerTest, PushPopTest) {
|
| + std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker();
|
| + std::vector<ThreadActivityTracker::StackEntry> stack;
|
| + ThreadActivityAnalyzer analyzer(tracker.get());
|
| +
|
| + ASSERT_EQ(0U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(0U, stack.size());
|
| +
|
| + char source1;
|
| + tracker->PushActivity(&source1, ThreadActivityTracker::ACT_TASK,
|
| + StackEntryData::ForTask(11));
|
| + ASSERT_EQ(1U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(1U, stack.size());
|
| + EXPECT_NE(0, stack[0].time_ticks);
|
| + EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type);
|
| + EXPECT_EQ(reinterpret_cast<uintptr_t>(&source1), stack[0].source_address);
|
| + EXPECT_EQ(11U, stack[0].data.task.sequence_id);
|
| +
|
| + char source2;
|
| + char lock2;
|
| + tracker->PushActivity(&source2, ThreadActivityTracker::ACT_LOCK,
|
| + StackEntryData::ForLock(&lock2));
|
| + ASSERT_EQ(2U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(2U, stack.size());
|
| + EXPECT_LE(stack[0].time_ticks, stack[1].time_ticks);
|
| + EXPECT_EQ(ThreadActivityTracker::ACT_LOCK, stack[1].activity_type);
|
| + EXPECT_EQ(reinterpret_cast<uintptr_t>(&source2), stack[1].source_address);
|
| + EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2),
|
| + stack[1].data.lock.lock_address);
|
| +
|
| + tracker->PopActivity(&source2);
|
| + ASSERT_EQ(1U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(1U, stack.size());
|
| + EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type);
|
| + EXPECT_EQ(reinterpret_cast<uintptr_t>(&source1), stack[0].source_address);
|
| + EXPECT_EQ(11U, stack[0].data.task.sequence_id);
|
| +
|
| + tracker->PopActivity(&source1);
|
| + ASSERT_EQ(0U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(0U, stack.size());
|
| +}
|
| +
|
| +TEST_F(ActivityTrackerTest, ScopedTaskTest) {
|
| + GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3);
|
| +
|
| + ThreadActivityTracker* tracker =
|
| + GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
|
| + std::vector<ThreadActivityTracker::StackEntry> stack;
|
| + ThreadActivityAnalyzer analyzer(tracker);
|
| +
|
| + ASSERT_EQ(0U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(0U, stack.size());
|
| +
|
| + {
|
| + PendingTask task1(FROM_HERE, base::Bind(&DoNothing));
|
| + ScopedTaskActivity activity1(task1);
|
| +
|
| + ASSERT_EQ(1U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(1U, stack.size());
|
| + EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type);
|
| +
|
| + {
|
| + PendingTask task2(FROM_HERE, base::Bind(&DoNothing));
|
| + ScopedTaskActivity activity2(task2);
|
| +
|
| + ASSERT_EQ(2U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(2U, stack.size());
|
| + EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[1].activity_type);
|
| + }
|
| +
|
| + ASSERT_EQ(1U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(1U, stack.size());
|
| + EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type);
|
| + }
|
| +
|
| + ASSERT_EQ(0U, analyzer.SnapshotStack(&stack));
|
| + ASSERT_EQ(0U, stack.size());
|
| +}
|
| +
|
| +class SimpleActivityThread : public SimpleThread {
|
| + public:
|
| + SimpleActivityThread(const std::string& name,
|
| + const void* source,
|
| + ThreadActivityTracker::ActivityType activity,
|
| + const ThreadActivityTracker::StackEntryData& data)
|
| + : SimpleThread(name, Options()),
|
| + source_(source),
|
| + activity_(activity),
|
| + data_(data),
|
| + exit_condition_(&lock_) {}
|
| +
|
| + ~SimpleActivityThread() override {}
|
| +
|
| + void Run() override {
|
| + GlobalActivityTracker::Get()
|
| + ->GetOrCreateTrackerForCurrentThread()
|
| + ->PushActivity(source_, activity_, data_);
|
| +
|
| + {
|
| + AutoLock auto_lock(lock_);
|
| + ready_ = true;
|
| + while (!exit_)
|
| + exit_condition_.Wait();
|
| + }
|
| +
|
| + GlobalActivityTracker::Get()
|
| + ->GetOrCreateTrackerForCurrentThread()
|
| + ->PopActivity(source_);
|
| + }
|
| +
|
| + void Exit() {
|
| + AutoLock auto_lock(lock_);
|
| + exit_ = true;
|
| + exit_condition_.Signal();
|
| + }
|
| +
|
| + void WaitReady() {
|
| + SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_);
|
| + }
|
| +
|
| + private:
|
| + const void* source_;
|
| + ThreadActivityTracker::ActivityType activity_;
|
| + ThreadActivityTracker::StackEntryData data_;
|
| +
|
| + bool ready_ = false;
|
| + bool exit_ = false;
|
| + Lock lock_;
|
| + ConditionVariable exit_condition_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread);
|
| +};
|
| +
|
| +TEST_F(ActivityTrackerTest, ThreadDeathTest) {
|
| + GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3);
|
| + GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
|
| + const size_t starting_active = GetGlobalActiveTrackerCount();
|
| + const size_t starting_inactive = GetGlobalInactiveTrackerCount();
|
| +
|
| + SimpleActivityThread t1("t1", nullptr, ThreadActivityTracker::ACT_TASK,
|
| + StackEntryData::ForTask(11));
|
| + t1.Start();
|
| + t1.WaitReady();
|
| + EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
|
| + EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
|
| +
|
| + t1.Exit();
|
| + t1.Join();
|
| + EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
|
| + EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
|
| +
|
| + // Start another thread and ensure it re-uses the existing memory.
|
| +
|
| + SimpleActivityThread t2("t2", nullptr, ThreadActivityTracker::ACT_TASK,
|
| + StackEntryData::ForTask(22));
|
| + t2.Start();
|
| + t2.WaitReady();
|
| + EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
|
| + EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
|
| +
|
| + t2.Exit();
|
| + t2.Join();
|
| + EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
|
| + EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
|
| +}
|
| +
|
| +} // namespace debug
|
| +} // namespace base
|
|
|