• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
TestActivityTracker(std::unique_ptr<char[]> memory,size_t mem_size)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 
~TestActivityTracker()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 
ActivityTrackerTest()52   ActivityTrackerTest() {}
53 
~ActivityTrackerTest()54   ~ActivityTrackerTest() override {
55     GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
56     if (global_tracker) {
57       global_tracker->ReleaseTrackerForCurrentThreadForTesting();
58       delete global_tracker;
59     }
60   }
61 
CreateActivityTracker()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 
GetGlobalActiveTrackerCount()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 
GetGlobalInactiveTrackerCount()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 
GetGlobalUserDataMemoryCacheUsed()83   size_t GetGlobalUserDataMemoryCacheUsed() {
84     return GlobalActivityTracker::Get()->user_data_allocator_.cache_used();
85   }
86 
HandleProcessExit(int64_t id,int64_t stamp,int code,GlobalActivityTracker::ProcessPhase phase,std::string && command,ActivityUserData::Snapshot && data)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 
DoNothing()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 
TEST_F(ActivityTrackerTest,UserDataTest)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 
TEST_F(ActivityTrackerTest,PushPopTest)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 
TEST_F(ActivityTrackerTest,ScopedTaskTest)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 
TEST_F(ActivityTrackerTest,ExceptionTest)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 
TEST_F(ActivityTrackerTest,CreateWithFileTest)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 
TEST_F(ActivityTrackerTest,BasicTest)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:
SimpleActivityThread(const std::string & name,const void * origin,Activity::Type activity,const ActivityData & data)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 
~SimpleActivityThread()325   ~SimpleActivityThread() override {}
326 
Run()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 
Exit()343   void Exit() {
344     AutoLock auto_lock(lock_);
345     exit_ = true;
346     exit_condition_.Signal();
347   }
348 
WaitReady()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 
TEST_F(ActivityTrackerTest,ThreadDeathTest)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 
TEST_F(ActivityTrackerTest,ProcessDeathTest)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