OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/debug/activity_tracker.h" |
| 6 |
| 7 #include <memory> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/files/file.h" |
| 11 #include "base/files/file_util.h" |
| 12 #include "base/files/memory_mapped_file.h" |
| 13 #include "base/files/scoped_temp_dir.h" |
| 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/pending_task.h" |
| 16 #include "base/synchronization/condition_variable.h" |
| 17 #include "base/synchronization/lock.h" |
| 18 #include "base/synchronization/spin_wait.h" |
| 19 #include "base/threading/simple_thread.h" |
| 20 #include "base/time/time.h" |
| 21 #include "testing/gtest/include/gtest/gtest.h" |
| 22 |
| 23 namespace base { |
| 24 namespace debug { |
| 25 |
| 26 namespace { |
| 27 |
| 28 class TestActivityTracker : public ThreadActivityTracker { |
| 29 public: |
| 30 TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size) |
| 31 : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size), |
| 32 mem_segment_(std::move(memory)) {} |
| 33 |
| 34 ~TestActivityTracker() override {} |
| 35 |
| 36 private: |
| 37 std::unique_ptr<char[]> mem_segment_; |
| 38 }; |
| 39 |
| 40 } // namespace |
| 41 |
| 42 |
| 43 class ActivityTrackerTest : public testing::Test { |
| 44 public: |
| 45 const int kMemorySize = 1 << 10; // 1KiB |
| 46 const int kStackSize = 256; |
| 47 |
| 48 using StackEntry = ThreadActivityTracker::StackEntry; |
| 49 using StackEntryData = ThreadActivityTracker::StackEntryData; |
| 50 |
| 51 ActivityTrackerTest() {} |
| 52 |
| 53 ~ActivityTrackerTest() override { |
| 54 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); |
| 55 if (global_tracker) { |
| 56 global_tracker->ReleaseTrackerForCurrentThreadForTesting(); |
| 57 delete global_tracker; |
| 58 } |
| 59 } |
| 60 |
| 61 std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() { |
| 62 std::unique_ptr<char[]> memory(new char[kStackSize]); |
| 63 return WrapUnique(new TestActivityTracker(std::move(memory), kStackSize)); |
| 64 } |
| 65 |
| 66 size_t GetGlobalActiveTrackerCount() { |
| 67 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); |
| 68 if (!global_tracker) |
| 69 return 0; |
| 70 return global_tracker->thread_tracker_count_.load( |
| 71 std::memory_order_relaxed); |
| 72 } |
| 73 |
| 74 size_t GetGlobalInactiveTrackerCount() { |
| 75 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); |
| 76 if (!global_tracker) |
| 77 return 0; |
| 78 return global_tracker->available_memories_count_.load( |
| 79 std::memory_order_relaxed); |
| 80 } |
| 81 |
| 82 static void DoNothing() {} |
| 83 }; |
| 84 |
| 85 TEST_F(ActivityTrackerTest, PushPopTest) { |
| 86 std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker(); |
| 87 std::vector<ThreadActivityTracker::StackEntry> stack; |
| 88 ThreadActivityAnalyzer analyzer(tracker.get()); |
| 89 |
| 90 ASSERT_EQ(0U, analyzer.SnapshotStack(&stack)); |
| 91 ASSERT_EQ(0U, stack.size()); |
| 92 |
| 93 char source1; |
| 94 tracker->PushActivity(&source1, ThreadActivityTracker::ACT_TASK, |
| 95 StackEntryData::ForTask(11)); |
| 96 ASSERT_EQ(1U, analyzer.SnapshotStack(&stack)); |
| 97 ASSERT_EQ(1U, stack.size()); |
| 98 EXPECT_NE(0, stack[0].time_ticks); |
| 99 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type); |
| 100 EXPECT_EQ(reinterpret_cast<uintptr_t>(&source1), stack[0].source_address); |
| 101 EXPECT_EQ(11U, stack[0].data.task.sequence_id); |
| 102 |
| 103 char source2; |
| 104 char lock2; |
| 105 tracker->PushActivity(&source2, ThreadActivityTracker::ACT_LOCK, |
| 106 StackEntryData::ForLock(&lock2)); |
| 107 ASSERT_EQ(2U, analyzer.SnapshotStack(&stack)); |
| 108 ASSERT_EQ(2U, stack.size()); |
| 109 EXPECT_LE(stack[0].time_ticks, stack[1].time_ticks); |
| 110 EXPECT_EQ(ThreadActivityTracker::ACT_LOCK, stack[1].activity_type); |
| 111 EXPECT_EQ(reinterpret_cast<uintptr_t>(&source2), stack[1].source_address); |
| 112 EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2), |
| 113 stack[1].data.lock.lock_address); |
| 114 |
| 115 tracker->PopActivity(&source2); |
| 116 ASSERT_EQ(1U, analyzer.SnapshotStack(&stack)); |
| 117 ASSERT_EQ(1U, stack.size()); |
| 118 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type); |
| 119 EXPECT_EQ(reinterpret_cast<uintptr_t>(&source1), stack[0].source_address); |
| 120 EXPECT_EQ(11U, stack[0].data.task.sequence_id); |
| 121 |
| 122 tracker->PopActivity(&source1); |
| 123 ASSERT_EQ(0U, analyzer.SnapshotStack(&stack)); |
| 124 ASSERT_EQ(0U, stack.size()); |
| 125 } |
| 126 |
| 127 TEST_F(ActivityTrackerTest, ScopedTaskTest) { |
| 128 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); |
| 129 |
| 130 ThreadActivityTracker* tracker = |
| 131 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); |
| 132 std::vector<ThreadActivityTracker::StackEntry> stack; |
| 133 ThreadActivityAnalyzer analyzer(tracker); |
| 134 |
| 135 ASSERT_EQ(0U, analyzer.SnapshotStack(&stack)); |
| 136 ASSERT_EQ(0U, stack.size()); |
| 137 |
| 138 { |
| 139 PendingTask task1(FROM_HERE, base::Bind(&DoNothing)); |
| 140 ScopedTaskActivity activity1(task1); |
| 141 |
| 142 ASSERT_EQ(1U, analyzer.SnapshotStack(&stack)); |
| 143 ASSERT_EQ(1U, stack.size()); |
| 144 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type); |
| 145 |
| 146 { |
| 147 PendingTask task2(FROM_HERE, base::Bind(&DoNothing)); |
| 148 ScopedTaskActivity activity2(task2); |
| 149 |
| 150 ASSERT_EQ(2U, analyzer.SnapshotStack(&stack)); |
| 151 ASSERT_EQ(2U, stack.size()); |
| 152 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[1].activity_type); |
| 153 } |
| 154 |
| 155 ASSERT_EQ(1U, analyzer.SnapshotStack(&stack)); |
| 156 ASSERT_EQ(1U, stack.size()); |
| 157 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, stack[0].activity_type); |
| 158 } |
| 159 |
| 160 ASSERT_EQ(0U, analyzer.SnapshotStack(&stack)); |
| 161 ASSERT_EQ(0U, stack.size()); |
| 162 } |
| 163 |
| 164 class SimpleActivityThread : public SimpleThread { |
| 165 public: |
| 166 SimpleActivityThread(const std::string& name, |
| 167 const void* source, |
| 168 ThreadActivityTracker::ActivityType activity, |
| 169 const ThreadActivityTracker::StackEntryData& data) |
| 170 : SimpleThread(name, Options()), |
| 171 source_(source), |
| 172 activity_(activity), |
| 173 data_(data), |
| 174 exit_condition_(&lock_) {} |
| 175 |
| 176 ~SimpleActivityThread() override {} |
| 177 |
| 178 void Run() override { |
| 179 GlobalActivityTracker::Get() |
| 180 ->GetOrCreateTrackerForCurrentThread() |
| 181 ->PushActivity(source_, activity_, data_); |
| 182 |
| 183 { |
| 184 AutoLock auto_lock(lock_); |
| 185 ready_ = true; |
| 186 while (!exit_) |
| 187 exit_condition_.Wait(); |
| 188 } |
| 189 |
| 190 GlobalActivityTracker::Get() |
| 191 ->GetOrCreateTrackerForCurrentThread() |
| 192 ->PopActivity(source_); |
| 193 } |
| 194 |
| 195 void Exit() { |
| 196 AutoLock auto_lock(lock_); |
| 197 exit_ = true; |
| 198 exit_condition_.Signal(); |
| 199 } |
| 200 |
| 201 void WaitReady() { |
| 202 SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_); |
| 203 } |
| 204 |
| 205 private: |
| 206 const void* source_; |
| 207 ThreadActivityTracker::ActivityType activity_; |
| 208 ThreadActivityTracker::StackEntryData data_; |
| 209 |
| 210 bool ready_ = false; |
| 211 bool exit_ = false; |
| 212 Lock lock_; |
| 213 ConditionVariable exit_condition_; |
| 214 |
| 215 DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread); |
| 216 }; |
| 217 |
| 218 TEST_F(ActivityTrackerTest, ThreadDeathTest) { |
| 219 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); |
| 220 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); |
| 221 const size_t starting_active = GetGlobalActiveTrackerCount(); |
| 222 const size_t starting_inactive = GetGlobalInactiveTrackerCount(); |
| 223 |
| 224 SimpleActivityThread t1("t1", nullptr, ThreadActivityTracker::ACT_TASK, |
| 225 StackEntryData::ForTask(11)); |
| 226 t1.Start(); |
| 227 t1.WaitReady(); |
| 228 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); |
| 229 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); |
| 230 |
| 231 t1.Exit(); |
| 232 t1.Join(); |
| 233 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); |
| 234 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); |
| 235 |
| 236 // Start another thread and ensure it re-uses the existing memory. |
| 237 |
| 238 SimpleActivityThread t2("t2", nullptr, ThreadActivityTracker::ACT_TASK, |
| 239 StackEntryData::ForTask(22)); |
| 240 t2.Start(); |
| 241 t2.WaitReady(); |
| 242 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); |
| 243 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); |
| 244 |
| 245 t2.Exit(); |
| 246 t2.Join(); |
| 247 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); |
| 248 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); |
| 249 } |
| 250 |
| 251 } // namespace debug |
| 252 } // namespace base |
OLD | NEW |