Index: base/debug/activity_tracker_unittest.cc |
diff --git a/base/debug/activity_tracker_unittest.cc b/base/debug/activity_tracker_unittest.cc |
index aced4fb36a1169154b028a7ee5afd0610ae3ae3c..e9934d1ffda954d4ef17487fa940c85633134dc3 100644 |
--- a/base/debug/activity_tracker_unittest.cc |
+++ b/base/debug/activity_tracker_unittest.cc |
@@ -84,45 +84,73 @@ class ActivityTrackerTest : public testing::Test { |
return GlobalActivityTracker::Get()->user_data_allocator_.cache_used(); |
} |
+ void HandleProcessExit(int64_t id, |
+ int64_t stamp, |
+ int code, |
+ GlobalActivityTracker::ProcessPhase phase, |
+ std::string&& command, |
+ ActivityUserData::Snapshot&& data) { |
+ exit_id = id; |
+ exit_stamp = stamp; |
+ exit_code = code; |
+ exit_phase = phase; |
+ exit_command = std::move(command); |
+ exit_data = std::move(data); |
+ } |
+ |
static void DoNothing() {} |
+ |
+ int64_t exit_id = 0; |
+ int64_t exit_stamp; |
+ int exit_code; |
+ GlobalActivityTracker::ProcessPhase exit_phase; |
+ std::string exit_command; |
+ ActivityUserData::Snapshot exit_data; |
}; |
TEST_F(ActivityTrackerTest, UserDataTest) { |
char buffer[256]; |
memset(buffer, 0, sizeof(buffer)); |
ActivityUserData data(buffer, sizeof(buffer)); |
- const size_t space = sizeof(buffer) - 8; |
+ size_t space = sizeof(buffer) - sizeof(ActivityUserData::MemoryHeader); |
ASSERT_EQ(space, data.available_); |
data.SetInt("foo", 1); |
- ASSERT_EQ(space - 24, data.available_); |
+ space -= 24; |
+ ASSERT_EQ(space, data.available_); |
data.SetUint("b", 1U); // Small names fit beside header in a word. |
- ASSERT_EQ(space - 24 - 16, data.available_); |
+ space -= 16; |
+ ASSERT_EQ(space, data.available_); |
data.Set("c", buffer, 10); |
- ASSERT_EQ(space - 24 - 16 - 24, data.available_); |
+ space -= 24; |
+ ASSERT_EQ(space, data.available_); |
data.SetString("dear john", "it's been fun"); |
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_); |
+ space -= 32; |
+ ASSERT_EQ(space, data.available_); |
data.Set("c", buffer, 20); |
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_); |
+ ASSERT_EQ(space, data.available_); |
data.SetString("dear john", "but we're done together"); |
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_); |
+ ASSERT_EQ(space, data.available_); |
data.SetString("dear john", "bye"); |
- ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_); |
+ ASSERT_EQ(space, data.available_); |
data.SetChar("d", 'x'); |
- ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8, data.available_); |
+ space -= 8; |
+ ASSERT_EQ(space, data.available_); |
data.SetBool("ee", true); |
- ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8 - 16, data.available_); |
+ space -= 16; |
+ ASSERT_EQ(space, data.available_); |
data.SetString("f", ""); |
- ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8 - 16 - 8, data.available_); |
+ space -= 8; |
+ ASSERT_EQ(space, data.available_); |
} |
TEST_F(ActivityTrackerTest, PushPopTest) { |
@@ -250,6 +278,16 @@ TEST_F(ActivityTrackerTest, CreateWithFileTest) { |
// GlobalActivityTracker tests below. |
+TEST_F(ActivityTrackerTest, BasicTest) { |
+ GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); |
+ GlobalActivityTracker* global = GlobalActivityTracker::Get(); |
+ |
+ // Ensure the data repositories have backing store, indicated by non-zero ID. |
+ EXPECT_NE(0U, global->process_data().id()); |
+ EXPECT_NE(0U, global->global_data().id()); |
+ EXPECT_NE(global->process_data().id(), global->global_data().id()); |
+} |
+ |
class SimpleActivityThread : public SimpleThread { |
public: |
SimpleActivityThread(const std::string& name, |
@@ -336,5 +374,107 @@ TEST_F(ActivityTrackerTest, ThreadDeathTest) { |
EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); |
} |
+TEST_F(ActivityTrackerTest, ProcessDeathTest) { |
+ // This doesn't actually create and destroy a process. Instead, it uses for- |
+ // testing interfaces to simulate data created by other processes. |
+ const ProcessId other_process_id = GetCurrentProcId() + 1; |
+ |
+ GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); |
+ GlobalActivityTracker* global = GlobalActivityTracker::Get(); |
+ ThreadActivityTracker* thread = global->GetOrCreateTrackerForCurrentThread(); |
+ |
+ // Get callbacks for process exit. |
+ global->SetProcessExitCallback( |
+ Bind(&ActivityTrackerTest::HandleProcessExit, Unretained(this))); |
+ |
+ // Pretend than another process has started. |
+ global->RecordProcessLaunch(other_process_id, FILE_PATH_LITERAL("foo --bar")); |
+ |
+ // Do some activities. |
+ PendingTask task(FROM_HERE, base::Bind(&DoNothing)); |
+ ScopedTaskRunActivity activity(task); |
+ ActivityUserData& user_data = activity.user_data(); |
+ ASSERT_NE(0U, user_data.id()); |
+ |
+ // Get the memory-allocator references to that data. |
+ PersistentMemoryAllocator::Reference proc_data_ref = |
+ global->allocator()->GetAsReference( |
+ global->process_data().GetBaseAddress(), |
+ GlobalActivityTracker::kTypeIdProcessDataRecord); |
+ ASSERT_TRUE(proc_data_ref); |
+ PersistentMemoryAllocator::Reference tracker_ref = |
+ global->allocator()->GetAsReference( |
+ thread->GetBaseAddress(), |
+ GlobalActivityTracker::kTypeIdActivityTracker); |
+ ASSERT_TRUE(tracker_ref); |
+ PersistentMemoryAllocator::Reference user_data_ref = |
+ global->allocator()->GetAsReference( |
+ user_data.GetBaseAddress(), |
+ GlobalActivityTracker::kTypeIdUserDataRecord); |
+ ASSERT_TRUE(user_data_ref); |
+ |
+ // Make a copy of the thread-tracker state so it can be restored later. |
+ const size_t tracker_size = global->allocator()->GetAllocSize(tracker_ref); |
+ std::unique_ptr<char[]> tracker_copy(new char[tracker_size]); |
+ memcpy(tracker_copy.get(), thread->GetBaseAddress(), tracker_size); |
+ |
+ // Change the objects to appear to be owned by another process. |
+ ProcessId owning_id; |
+ int64_t stamp; |
+ ASSERT_TRUE(ActivityUserData::GetOwningProcessId( |
+ global->process_data().GetBaseAddress(), &owning_id, &stamp)); |
+ EXPECT_NE(other_process_id, owning_id); |
+ ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId( |
+ thread->GetBaseAddress(), &owning_id, &stamp)); |
+ EXPECT_NE(other_process_id, owning_id); |
+ ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(), |
+ &owning_id, &stamp)); |
+ EXPECT_NE(other_process_id, owning_id); |
+ global->process_data().SetOwningProcessIdForTesting(other_process_id, stamp); |
+ thread->SetOwningProcessIdForTesting(other_process_id, stamp); |
+ user_data.SetOwningProcessIdForTesting(other_process_id, stamp); |
+ ASSERT_TRUE(ActivityUserData::GetOwningProcessId( |
+ global->process_data().GetBaseAddress(), &owning_id, &stamp)); |
+ EXPECT_EQ(other_process_id, owning_id); |
+ ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId( |
+ thread->GetBaseAddress(), &owning_id, &stamp)); |
+ EXPECT_EQ(other_process_id, owning_id); |
+ ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(), |
+ &owning_id, &stamp)); |
+ EXPECT_EQ(other_process_id, owning_id); |
+ |
+ // Check that process exit will perform callback and free the allocations. |
+ ASSERT_EQ(0, exit_id); |
+ ASSERT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecord, |
+ global->allocator()->GetType(proc_data_ref)); |
+ ASSERT_EQ(GlobalActivityTracker::kTypeIdActivityTracker, |
+ global->allocator()->GetType(tracker_ref)); |
+ ASSERT_EQ(GlobalActivityTracker::kTypeIdUserDataRecord, |
+ global->allocator()->GetType(user_data_ref)); |
+ global->RecordProcessExit(other_process_id, 0); |
+ EXPECT_EQ(other_process_id, exit_id); |
+ EXPECT_EQ("foo --bar", exit_command); |
+ EXPECT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecordFree, |
+ global->allocator()->GetType(proc_data_ref)); |
+ EXPECT_EQ(GlobalActivityTracker::kTypeIdActivityTrackerFree, |
+ global->allocator()->GetType(tracker_ref)); |
+ EXPECT_EQ(GlobalActivityTracker::kTypeIdUserDataRecordFree, |
+ global->allocator()->GetType(user_data_ref)); |
+ |
+ // Restore memory contents and types so things don't crash when doing real |
+ // process clean-up. |
+ memcpy(const_cast<void*>(thread->GetBaseAddress()), tracker_copy.get(), |
+ tracker_size); |
+ global->allocator()->ChangeType( |
+ proc_data_ref, GlobalActivityTracker::kTypeIdProcessDataRecord, |
+ GlobalActivityTracker::kTypeIdUserDataRecordFree, false); |
+ global->allocator()->ChangeType( |
+ tracker_ref, GlobalActivityTracker::kTypeIdActivityTracker, |
+ GlobalActivityTracker::kTypeIdActivityTrackerFree, false); |
+ global->allocator()->ChangeType( |
+ user_data_ref, GlobalActivityTracker::kTypeIdUserDataRecord, |
+ GlobalActivityTracker::kTypeIdUserDataRecordFree, false); |
+} |
+ |
} // namespace debug |
} // namespace base |