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