Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2831)

Unified Diff: base/debug/activity_tracker_unittest.cc

Issue 1980743002: Track thread activities in order to diagnose hangs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@readwrite-mmf
Patch Set: track locks and waitable events Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698