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; // 1MiB |
| 46 const int kStackSize = 1 << 10; // 1KiB |
| 47 |
| 48 using Activity = ThreadActivityTracker::Activity; |
| 49 using ActivityData = ThreadActivityTracker::ActivityData; |
| 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 ThreadActivityTracker::ActivitySnapshot snapshot; |
| 88 |
| 89 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 90 ASSERT_EQ(0U, snapshot.activity_stack_depth); |
| 91 ASSERT_EQ(0U, snapshot.activity_stack.size()); |
| 92 |
| 93 char origin1; |
| 94 tracker->PushActivity(&origin1, ThreadActivityTracker::ACT_TASK, |
| 95 ActivityData::ForTask(11)); |
| 96 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 97 ASSERT_EQ(1U, snapshot.activity_stack_depth); |
| 98 ASSERT_EQ(1U, snapshot.activity_stack.size()); |
| 99 EXPECT_NE(0, snapshot.activity_stack[0].time_internal); |
| 100 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, |
| 101 snapshot.activity_stack[0].activity_type); |
| 102 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1), |
| 103 snapshot.activity_stack[0].origin_address); |
| 104 EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); |
| 105 |
| 106 char origin2; |
| 107 char lock2; |
| 108 tracker->PushActivity(&origin2, ThreadActivityTracker::ACT_LOCK, |
| 109 ActivityData::ForLock(&lock2)); |
| 110 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 111 ASSERT_EQ(2U, snapshot.activity_stack_depth); |
| 112 ASSERT_EQ(2U, snapshot.activity_stack.size()); |
| 113 EXPECT_LE(snapshot.activity_stack[0].time_internal, |
| 114 snapshot.activity_stack[1].time_internal); |
| 115 EXPECT_EQ(ThreadActivityTracker::ACT_LOCK, |
| 116 snapshot.activity_stack[1].activity_type); |
| 117 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin2), |
| 118 snapshot.activity_stack[1].origin_address); |
| 119 EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2), |
| 120 snapshot.activity_stack[1].data.lock.lock_address); |
| 121 |
| 122 tracker->PopActivity(); |
| 123 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 124 ASSERT_EQ(1U, snapshot.activity_stack_depth); |
| 125 ASSERT_EQ(1U, snapshot.activity_stack.size()); |
| 126 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, |
| 127 snapshot.activity_stack[0].activity_type); |
| 128 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1), |
| 129 snapshot.activity_stack[0].origin_address); |
| 130 EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); |
| 131 |
| 132 tracker->PopActivity(); |
| 133 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 134 ASSERT_EQ(0U, snapshot.activity_stack_depth); |
| 135 ASSERT_EQ(0U, snapshot.activity_stack.size()); |
| 136 } |
| 137 |
| 138 TEST_F(ActivityTrackerTest, ScopedTaskTest) { |
| 139 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); |
| 140 |
| 141 ThreadActivityTracker* tracker = |
| 142 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); |
| 143 ThreadActivityTracker::ActivitySnapshot snapshot; |
| 144 |
| 145 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 146 ASSERT_EQ(0U, snapshot.activity_stack_depth); |
| 147 ASSERT_EQ(0U, snapshot.activity_stack.size()); |
| 148 |
| 149 { |
| 150 PendingTask task1(FROM_HERE, base::Bind(&DoNothing)); |
| 151 ScopedTaskRunActivity activity1(task1); |
| 152 |
| 153 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 154 ASSERT_EQ(1U, snapshot.activity_stack_depth); |
| 155 ASSERT_EQ(1U, snapshot.activity_stack.size()); |
| 156 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, |
| 157 snapshot.activity_stack[0].activity_type); |
| 158 |
| 159 { |
| 160 PendingTask task2(FROM_HERE, base::Bind(&DoNothing)); |
| 161 ScopedTaskRunActivity activity2(task2); |
| 162 |
| 163 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 164 ASSERT_EQ(2U, snapshot.activity_stack_depth); |
| 165 ASSERT_EQ(2U, snapshot.activity_stack.size()); |
| 166 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, |
| 167 snapshot.activity_stack[1].activity_type); |
| 168 } |
| 169 |
| 170 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 171 ASSERT_EQ(1U, snapshot.activity_stack_depth); |
| 172 ASSERT_EQ(1U, snapshot.activity_stack.size()); |
| 173 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, |
| 174 snapshot.activity_stack[0].activity_type); |
| 175 } |
| 176 |
| 177 ASSERT_TRUE(tracker->Snapshot(&snapshot)); |
| 178 ASSERT_EQ(0U, snapshot.activity_stack_depth); |
| 179 ASSERT_EQ(0U, snapshot.activity_stack.size()); |
| 180 } |
| 181 |
| 182 TEST_F(ActivityTrackerTest, CreateWithFileTest) { |
| 183 const char temp_name[] = "CreateWithFileTest"; |
| 184 ScopedTempDir temp_dir; |
| 185 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 186 FilePath temp_file = temp_dir.path().AppendASCII(temp_name); |
| 187 const size_t temp_size = 64 << 10; // 64 KiB |
| 188 |
| 189 // Create a global tracker on a new file. |
| 190 ASSERT_FALSE(PathExists(temp_file)); |
| 191 GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "foo", 3); |
| 192 GlobalActivityTracker* global = GlobalActivityTracker::Get(); |
| 193 EXPECT_EQ(std::string("foo"), global->allocator()->Name()); |
| 194 global->ReleaseTrackerForCurrentThreadForTesting(); |
| 195 delete global; |
| 196 |
| 197 // Create a global tracker over an existing file, replacing it. If the |
| 198 // replacement doesn't work, the name will remain as it was first created. |
| 199 ASSERT_TRUE(PathExists(temp_file)); |
| 200 GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "bar", 3); |
| 201 global = GlobalActivityTracker::Get(); |
| 202 EXPECT_EQ(std::string("bar"), global->allocator()->Name()); |
| 203 global->ReleaseTrackerForCurrentThreadForTesting(); |
| 204 delete global; |
| 205 } |
| 206 |
| 207 |
| 208 // GlobalActivityTracker tests below. |
| 209 |
| 210 class SimpleActivityThread : public SimpleThread { |
| 211 public: |
| 212 SimpleActivityThread(const std::string& name, |
| 213 const void* origin, |
| 214 ThreadActivityTracker::ActivityType activity, |
| 215 const ThreadActivityTracker::ActivityData& data) |
| 216 : SimpleThread(name, Options()), |
| 217 origin_(origin), |
| 218 activity_(activity), |
| 219 data_(data), |
| 220 exit_condition_(&lock_) {} |
| 221 |
| 222 ~SimpleActivityThread() override {} |
| 223 |
| 224 void Run() override { |
| 225 GlobalActivityTracker::Get() |
| 226 ->GetOrCreateTrackerForCurrentThread() |
| 227 ->PushActivity(origin_, activity_, data_); |
| 228 |
| 229 { |
| 230 AutoLock auto_lock(lock_); |
| 231 ready_ = true; |
| 232 while (!exit_) |
| 233 exit_condition_.Wait(); |
| 234 } |
| 235 |
| 236 GlobalActivityTracker::Get() |
| 237 ->GetOrCreateTrackerForCurrentThread() |
| 238 ->PopActivity(); |
| 239 } |
| 240 |
| 241 void Exit() { |
| 242 AutoLock auto_lock(lock_); |
| 243 exit_ = true; |
| 244 exit_condition_.Signal(); |
| 245 } |
| 246 |
| 247 void WaitReady() { |
| 248 SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_); |
| 249 } |
| 250 |
| 251 private: |
| 252 const void* origin_; |
| 253 ThreadActivityTracker::ActivityType activity_; |
| 254 ThreadActivityTracker::ActivityData data_; |
| 255 |
| 256 bool ready_ = false; |
| 257 bool exit_ = false; |
| 258 Lock lock_; |
| 259 ConditionVariable exit_condition_; |
| 260 |
| 261 DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread); |
| 262 }; |
| 263 |
| 264 TEST_F(ActivityTrackerTest, ThreadDeathTest) { |
| 265 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); |
| 266 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); |
| 267 const size_t starting_active = GetGlobalActiveTrackerCount(); |
| 268 const size_t starting_inactive = GetGlobalInactiveTrackerCount(); |
| 269 |
| 270 SimpleActivityThread t1("t1", nullptr, ThreadActivityTracker::ACT_TASK, |
| 271 ThreadActivityTracker::ActivityData::ForTask(11)); |
| 272 t1.Start(); |
| 273 t1.WaitReady(); |
| 274 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); |
| 275 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); |
| 276 |
| 277 t1.Exit(); |
| 278 t1.Join(); |
| 279 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); |
| 280 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); |
| 281 |
| 282 // Start another thread and ensure it re-uses the existing memory. |
| 283 |
| 284 SimpleActivityThread t2("t2", nullptr, ThreadActivityTracker::ACT_TASK, |
| 285 ThreadActivityTracker::ActivityData::ForTask(22)); |
| 286 t2.Start(); |
| 287 t2.WaitReady(); |
| 288 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); |
| 289 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); |
| 290 |
| 291 t2.Exit(); |
| 292 t2.Join(); |
| 293 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); |
| 294 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); |
| 295 } |
| 296 |
| 297 } // namespace debug |
| 298 } // namespace base |
OLD | NEW |