Home | History | Annotate | Download | only in debug
      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