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/rand_util.h" 17 #include "base/synchronization/condition_variable.h" 18 #include "base/synchronization/lock.h" 19 #include "base/synchronization/spin_wait.h" 20 #include "base/threading/platform_thread.h" 21 #include "base/threading/simple_thread.h" 22 #include "base/time/time.h" 23 #include "testing/gtest/include/gtest/gtest.h" 24 25 namespace base { 26 namespace debug { 27 28 namespace { 29 30 class TestActivityTracker : public ThreadActivityTracker { 31 public: 32 TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size) 33 : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size), 34 mem_segment_(std::move(memory)) {} 35 36 ~TestActivityTracker() override {} 37 38 private: 39 std::unique_ptr<char[]> mem_segment_; 40 }; 41 42 } // namespace 43 44 45 class ActivityTrackerTest : public testing::Test { 46 public: 47 const int kMemorySize = 1 << 20; // 1MiB 48 const int kStackSize = 1 << 10; // 1KiB 49 50 using ActivityId = ThreadActivityTracker::ActivityId; 51 52 ActivityTrackerTest() {} 53 54 ~ActivityTrackerTest() override { 55 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); 56 if (global_tracker) { 57 global_tracker->ReleaseTrackerForCurrentThreadForTesting(); 58 delete global_tracker; 59 } 60 } 61 62 std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() { 63 std::unique_ptr<char[]> memory(new char[kStackSize]); 64 return MakeUnique<TestActivityTracker>(std::move(memory), kStackSize); 65 } 66 67 size_t GetGlobalActiveTrackerCount() { 68 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); 69 if (!global_tracker) 70 return 0; 71 return global_tracker->thread_tracker_count_.load( 72 std::memory_order_relaxed); 73 } 74 75 size_t GetGlobalInactiveTrackerCount() { 76 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); 77 if (!global_tracker) 78 return 0; 79 base::AutoLock autolock(global_tracker->thread_tracker_allocator_lock_); 80 return global_tracker->thread_tracker_allocator_.cache_used(); 81 } 82 83 size_t GetGlobalUserDataMemoryCacheUsed() { 84 return GlobalActivityTracker::Get()->user_data_allocator_.cache_used(); 85 } 86 87 void HandleProcessExit(int64_t id, 88 int64_t stamp, 89 int code, 90 GlobalActivityTracker::ProcessPhase phase, 91 std::string&& command, 92 ActivityUserData::Snapshot&& data) { 93 exit_id = id; 94 exit_stamp = stamp; 95 exit_code = code; 96 exit_phase = phase; 97 exit_command = std::move(command); 98 exit_data = std::move(data); 99 } 100 101 static void DoNothing() {} 102 103 int64_t exit_id = 0; 104 int64_t exit_stamp; 105 int exit_code; 106 GlobalActivityTracker::ProcessPhase exit_phase; 107 std::string exit_command; 108 ActivityUserData::Snapshot exit_data; 109 }; 110 111 TEST_F(ActivityTrackerTest, UserDataTest) { 112 char buffer[256]; 113 memset(buffer, 0, sizeof(buffer)); 114 ActivityUserData data(buffer, sizeof(buffer)); 115 size_t space = sizeof(buffer) - sizeof(ActivityUserData::MemoryHeader); 116 ASSERT_EQ(space, data.available_); 117 118 data.SetInt("foo", 1); 119 space -= 24; 120 ASSERT_EQ(space, data.available_); 121 122 data.SetUint("b", 1U); // Small names fit beside header in a word. 123 space -= 16; 124 ASSERT_EQ(space, data.available_); 125 126 data.Set("c", buffer, 10); 127 space -= 24; 128 ASSERT_EQ(space, data.available_); 129 130 data.SetString("dear john", "it's been fun"); 131 space -= 32; 132 ASSERT_EQ(space, data.available_); 133 134 data.Set("c", buffer, 20); 135 ASSERT_EQ(space, data.available_); 136 137 data.SetString("dear john", "but we're done together"); 138 ASSERT_EQ(space, data.available_); 139 140 data.SetString("dear john", "bye"); 141 ASSERT_EQ(space, data.available_); 142 143 data.SetChar("d", 'x'); 144 space -= 8; 145 ASSERT_EQ(space, data.available_); 146 147 data.SetBool("ee", true); 148 space -= 16; 149 ASSERT_EQ(space, data.available_); 150 151 data.SetString("f", ""); 152 space -= 8; 153 ASSERT_EQ(space, data.available_); 154 } 155 156 TEST_F(ActivityTrackerTest, PushPopTest) { 157 std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker(); 158 ThreadActivityTracker::Snapshot snapshot; 159 160 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 161 ASSERT_EQ(0U, snapshot.activity_stack_depth); 162 ASSERT_EQ(0U, snapshot.activity_stack.size()); 163 164 char origin1; 165 ActivityId id1 = tracker->PushActivity(&origin1, Activity::ACT_TASK, 166 ActivityData::ForTask(11)); 167 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 168 ASSERT_EQ(1U, snapshot.activity_stack_depth); 169 ASSERT_EQ(1U, snapshot.activity_stack.size()); 170 EXPECT_NE(0, snapshot.activity_stack[0].time_internal); 171 EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); 172 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1), 173 snapshot.activity_stack[0].origin_address); 174 EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); 175 176 char origin2; 177 char lock2; 178 ActivityId id2 = tracker->PushActivity(&origin2, Activity::ACT_LOCK, 179 ActivityData::ForLock(&lock2)); 180 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 181 ASSERT_EQ(2U, snapshot.activity_stack_depth); 182 ASSERT_EQ(2U, snapshot.activity_stack.size()); 183 EXPECT_LE(snapshot.activity_stack[0].time_internal, 184 snapshot.activity_stack[1].time_internal); 185 EXPECT_EQ(Activity::ACT_LOCK, snapshot.activity_stack[1].activity_type); 186 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin2), 187 snapshot.activity_stack[1].origin_address); 188 EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2), 189 snapshot.activity_stack[1].data.lock.lock_address); 190 191 tracker->PopActivity(id2); 192 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 193 ASSERT_EQ(1U, snapshot.activity_stack_depth); 194 ASSERT_EQ(1U, snapshot.activity_stack.size()); 195 EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); 196 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1), 197 snapshot.activity_stack[0].origin_address); 198 EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); 199 200 tracker->PopActivity(id1); 201 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 202 ASSERT_EQ(0U, snapshot.activity_stack_depth); 203 ASSERT_EQ(0U, snapshot.activity_stack.size()); 204 } 205 206 TEST_F(ActivityTrackerTest, ScopedTaskTest) { 207 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); 208 209 ThreadActivityTracker* tracker = 210 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); 211 ThreadActivityTracker::Snapshot snapshot; 212 ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed()); 213 214 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 215 ASSERT_EQ(0U, snapshot.activity_stack_depth); 216 ASSERT_EQ(0U, snapshot.activity_stack.size()); 217 218 { 219 PendingTask task1(FROM_HERE, base::Bind(&DoNothing)); 220 ScopedTaskRunActivity activity1(task1); 221 ActivityUserData& user_data1 = activity1.user_data(); 222 (void)user_data1; // Tell compiler it's been used. 223 224 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 225 ASSERT_EQ(1U, snapshot.activity_stack_depth); 226 ASSERT_EQ(1U, snapshot.activity_stack.size()); 227 EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); 228 229 { 230 PendingTask task2(FROM_HERE, base::Bind(&DoNothing)); 231 ScopedTaskRunActivity activity2(task2); 232 ActivityUserData& user_data2 = activity2.user_data(); 233 (void)user_data2; // Tell compiler it's been used. 234 235 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 236 ASSERT_EQ(2U, snapshot.activity_stack_depth); 237 ASSERT_EQ(2U, snapshot.activity_stack.size()); 238 EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[1].activity_type); 239 } 240 241 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 242 ASSERT_EQ(1U, snapshot.activity_stack_depth); 243 ASSERT_EQ(1U, snapshot.activity_stack.size()); 244 EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); 245 } 246 247 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 248 ASSERT_EQ(0U, snapshot.activity_stack_depth); 249 ASSERT_EQ(0U, snapshot.activity_stack.size()); 250 ASSERT_EQ(2U, GetGlobalUserDataMemoryCacheUsed()); 251 } 252 253 TEST_F(ActivityTrackerTest, ExceptionTest) { 254 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); 255 GlobalActivityTracker* global = GlobalActivityTracker::Get(); 256 257 ThreadActivityTracker* tracker = 258 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); 259 ThreadActivityTracker::Snapshot snapshot; 260 ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed()); 261 262 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 263 ASSERT_EQ(0U, snapshot.last_exception.activity_type); 264 265 char origin; 266 global->RecordException(&origin, 42); 267 268 ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); 269 EXPECT_EQ(Activity::ACT_EXCEPTION, snapshot.last_exception.activity_type); 270 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin), 271 snapshot.last_exception.origin_address); 272 EXPECT_EQ(42U, snapshot.last_exception.data.exception.code); 273 } 274 275 TEST_F(ActivityTrackerTest, CreateWithFileTest) { 276 const char temp_name[] = "CreateWithFileTest"; 277 ScopedTempDir temp_dir; 278 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 279 FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name); 280 const size_t temp_size = 64 << 10; // 64 KiB 281 282 // Create a global tracker on a new file. 283 ASSERT_FALSE(PathExists(temp_file)); 284 GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "foo", 3); 285 GlobalActivityTracker* global = GlobalActivityTracker::Get(); 286 EXPECT_EQ(std::string("foo"), global->allocator()->Name()); 287 global->ReleaseTrackerForCurrentThreadForTesting(); 288 delete global; 289 290 // Create a global tracker over an existing file, replacing it. If the 291 // replacement doesn't work, the name will remain as it was first created. 292 ASSERT_TRUE(PathExists(temp_file)); 293 GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "bar", 3); 294 global = GlobalActivityTracker::Get(); 295 EXPECT_EQ(std::string("bar"), global->allocator()->Name()); 296 global->ReleaseTrackerForCurrentThreadForTesting(); 297 delete global; 298 } 299 300 301 // GlobalActivityTracker tests below. 302 303 TEST_F(ActivityTrackerTest, BasicTest) { 304 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); 305 GlobalActivityTracker* global = GlobalActivityTracker::Get(); 306 307 // Ensure the data repositories have backing store, indicated by non-zero ID. 308 EXPECT_NE(0U, global->process_data().id()); 309 EXPECT_NE(0U, global->global_data().id()); 310 EXPECT_NE(global->process_data().id(), global->global_data().id()); 311 } 312 313 class SimpleActivityThread : public SimpleThread { 314 public: 315 SimpleActivityThread(const std::string& name, 316 const void* origin, 317 Activity::Type activity, 318 const ActivityData& data) 319 : SimpleThread(name, Options()), 320 origin_(origin), 321 activity_(activity), 322 data_(data), 323 exit_condition_(&lock_) {} 324 325 ~SimpleActivityThread() override {} 326 327 void Run() override { 328 ThreadActivityTracker::ActivityId id = 329 GlobalActivityTracker::Get() 330 ->GetOrCreateTrackerForCurrentThread() 331 ->PushActivity(origin_, activity_, data_); 332 333 { 334 AutoLock auto_lock(lock_); 335 ready_ = true; 336 while (!exit_) 337 exit_condition_.Wait(); 338 } 339 340 GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id); 341 } 342 343 void Exit() { 344 AutoLock auto_lock(lock_); 345 exit_ = true; 346 exit_condition_.Signal(); 347 } 348 349 void WaitReady() { 350 SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_); 351 } 352 353 private: 354 const void* origin_; 355 Activity::Type activity_; 356 ActivityData data_; 357 358 bool ready_ = false; 359 bool exit_ = false; 360 Lock lock_; 361 ConditionVariable exit_condition_; 362 363 DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread); 364 }; 365 366 TEST_F(ActivityTrackerTest, ThreadDeathTest) { 367 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); 368 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); 369 const size_t starting_active = GetGlobalActiveTrackerCount(); 370 const size_t starting_inactive = GetGlobalInactiveTrackerCount(); 371 372 SimpleActivityThread t1("t1", nullptr, Activity::ACT_TASK, 373 ActivityData::ForTask(11)); 374 t1.Start(); 375 t1.WaitReady(); 376 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); 377 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); 378 379 t1.Exit(); 380 t1.Join(); 381 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); 382 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); 383 384 // Start another thread and ensure it re-uses the existing memory. 385 386 SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK, 387 ActivityData::ForTask(22)); 388 t2.Start(); 389 t2.WaitReady(); 390 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); 391 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); 392 393 t2.Exit(); 394 t2.Join(); 395 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); 396 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); 397 } 398 399 TEST_F(ActivityTrackerTest, ProcessDeathTest) { 400 // This doesn't actually create and destroy a process. Instead, it uses for- 401 // testing interfaces to simulate data created by other processes. 402 const ProcessId other_process_id = GetCurrentProcId() + 1; 403 404 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); 405 GlobalActivityTracker* global = GlobalActivityTracker::Get(); 406 ThreadActivityTracker* thread = global->GetOrCreateTrackerForCurrentThread(); 407 408 // Get callbacks for process exit. 409 global->SetProcessExitCallback( 410 Bind(&ActivityTrackerTest::HandleProcessExit, Unretained(this))); 411 412 // Pretend than another process has started. 413 global->RecordProcessLaunch(other_process_id, FILE_PATH_LITERAL("foo --bar")); 414 415 // Do some activities. 416 PendingTask task(FROM_HERE, base::Bind(&DoNothing)); 417 ScopedTaskRunActivity activity(task); 418 ActivityUserData& user_data = activity.user_data(); 419 ASSERT_NE(0U, user_data.id()); 420 421 // Get the memory-allocator references to that data. 422 PersistentMemoryAllocator::Reference proc_data_ref = 423 global->allocator()->GetAsReference( 424 global->process_data().GetBaseAddress(), 425 GlobalActivityTracker::kTypeIdProcessDataRecord); 426 ASSERT_TRUE(proc_data_ref); 427 PersistentMemoryAllocator::Reference tracker_ref = 428 global->allocator()->GetAsReference( 429 thread->GetBaseAddress(), 430 GlobalActivityTracker::kTypeIdActivityTracker); 431 ASSERT_TRUE(tracker_ref); 432 PersistentMemoryAllocator::Reference user_data_ref = 433 global->allocator()->GetAsReference( 434 user_data.GetBaseAddress(), 435 GlobalActivityTracker::kTypeIdUserDataRecord); 436 ASSERT_TRUE(user_data_ref); 437 438 // Make a copy of the thread-tracker state so it can be restored later. 439 const size_t tracker_size = global->allocator()->GetAllocSize(tracker_ref); 440 std::unique_ptr<char[]> tracker_copy(new char[tracker_size]); 441 memcpy(tracker_copy.get(), thread->GetBaseAddress(), tracker_size); 442 443 // Change the objects to appear to be owned by another process. 444 int64_t owning_id; 445 int64_t stamp; 446 ASSERT_TRUE(ActivityUserData::GetOwningProcessId( 447 global->process_data().GetBaseAddress(), &owning_id, &stamp)); 448 EXPECT_NE(other_process_id, owning_id); 449 ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId( 450 thread->GetBaseAddress(), &owning_id, &stamp)); 451 EXPECT_NE(other_process_id, owning_id); 452 ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(), 453 &owning_id, &stamp)); 454 EXPECT_NE(other_process_id, owning_id); 455 global->process_data().SetOwningProcessIdForTesting(other_process_id, stamp); 456 thread->SetOwningProcessIdForTesting(other_process_id, stamp); 457 user_data.SetOwningProcessIdForTesting(other_process_id, stamp); 458 ASSERT_TRUE(ActivityUserData::GetOwningProcessId( 459 global->process_data().GetBaseAddress(), &owning_id, &stamp)); 460 EXPECT_EQ(other_process_id, owning_id); 461 ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId( 462 thread->GetBaseAddress(), &owning_id, &stamp)); 463 EXPECT_EQ(other_process_id, owning_id); 464 ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(), 465 &owning_id, &stamp)); 466 EXPECT_EQ(other_process_id, owning_id); 467 468 // Check that process exit will perform callback and free the allocations. 469 ASSERT_EQ(0, exit_id); 470 ASSERT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecord, 471 global->allocator()->GetType(proc_data_ref)); 472 ASSERT_EQ(GlobalActivityTracker::kTypeIdActivityTracker, 473 global->allocator()->GetType(tracker_ref)); 474 ASSERT_EQ(GlobalActivityTracker::kTypeIdUserDataRecord, 475 global->allocator()->GetType(user_data_ref)); 476 global->RecordProcessExit(other_process_id, 0); 477 EXPECT_EQ(other_process_id, exit_id); 478 EXPECT_EQ("foo --bar", exit_command); 479 EXPECT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecordFree, 480 global->allocator()->GetType(proc_data_ref)); 481 EXPECT_EQ(GlobalActivityTracker::kTypeIdActivityTrackerFree, 482 global->allocator()->GetType(tracker_ref)); 483 EXPECT_EQ(GlobalActivityTracker::kTypeIdUserDataRecordFree, 484 global->allocator()->GetType(user_data_ref)); 485 486 // Restore memory contents and types so things don't crash when doing real 487 // process clean-up. 488 memcpy(const_cast<void*>(thread->GetBaseAddress()), tracker_copy.get(), 489 tracker_size); 490 global->allocator()->ChangeType( 491 proc_data_ref, GlobalActivityTracker::kTypeIdProcessDataRecord, 492 GlobalActivityTracker::kTypeIdUserDataRecordFree, false); 493 global->allocator()->ChangeType( 494 tracker_ref, GlobalActivityTracker::kTypeIdActivityTracker, 495 GlobalActivityTracker::kTypeIdActivityTrackerFree, false); 496 global->allocator()->ChangeType( 497 user_data_ref, GlobalActivityTracker::kTypeIdUserDataRecord, 498 GlobalActivityTracker::kTypeIdUserDataRecordFree, false); 499 } 500 501 } // namespace debug 502 } // namespace base 503