You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
586 lines
21 KiB
586 lines
21 KiB
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/debug/activity_tracker.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "base/bind.h"
|
|
#include "base/bind_helpers.h"
|
|
#include "base/files/file.h"
|
|
#include "base/files/file_util.h"
|
|
#include "base/files/memory_mapped_file.h"
|
|
#include "base/files/scoped_temp_dir.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/pending_task.h"
|
|
#include "base/rand_util.h"
|
|
#include "base/synchronization/condition_variable.h"
|
|
#include "base/synchronization/lock.h"
|
|
#include "base/synchronization/spin_wait.h"
|
|
#include "base/threading/platform_thread.h"
|
|
#include "base/threading/simple_thread.h"
|
|
#include "base/time/time.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace base {
|
|
namespace debug {
|
|
|
|
namespace {
|
|
|
|
class TestActivityTracker : public ThreadActivityTracker {
|
|
public:
|
|
TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size)
|
|
: ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size),
|
|
mem_segment_(std::move(memory)) {}
|
|
|
|
~TestActivityTracker() override = default;
|
|
|
|
private:
|
|
std::unique_ptr<char[]> mem_segment_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
class ActivityTrackerTest : public testing::Test {
|
|
public:
|
|
const int kMemorySize = 1 << 20; // 1MiB
|
|
const int kStackSize = 1 << 10; // 1KiB
|
|
|
|
using ActivityId = ThreadActivityTracker::ActivityId;
|
|
|
|
ActivityTrackerTest() = default;
|
|
|
|
~ActivityTrackerTest() override {
|
|
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
|
|
if (global_tracker) {
|
|
global_tracker->ReleaseTrackerForCurrentThreadForTesting();
|
|
delete global_tracker;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() {
|
|
std::unique_ptr<char[]> memory(new char[kStackSize]);
|
|
return std::make_unique<TestActivityTracker>(std::move(memory), kStackSize);
|
|
}
|
|
|
|
size_t GetGlobalActiveTrackerCount() {
|
|
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
|
|
if (!global_tracker)
|
|
return 0;
|
|
return global_tracker->thread_tracker_count_.load(
|
|
std::memory_order_relaxed);
|
|
}
|
|
|
|
size_t GetGlobalInactiveTrackerCount() {
|
|
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
|
|
if (!global_tracker)
|
|
return 0;
|
|
AutoLock autolock(global_tracker->thread_tracker_allocator_lock_);
|
|
return global_tracker->thread_tracker_allocator_.cache_used();
|
|
}
|
|
|
|
size_t GetGlobalUserDataMemoryCacheUsed() {
|
|
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);
|
|
}
|
|
|
|
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));
|
|
size_t space = sizeof(buffer) - sizeof(ActivityUserData::MemoryHeader);
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetInt("foo", 1);
|
|
space -= 24;
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetUint("b", 1U); // Small names fit beside header in a word.
|
|
space -= 16;
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.Set("c", buffer, 10);
|
|
space -= 24;
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetString("dear john", "it's been fun");
|
|
space -= 32;
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.Set("c", buffer, 20);
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetString("dear john", "but we're done together");
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetString("dear john", "bye");
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetChar("d", 'x');
|
|
space -= 8;
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetBool("ee", true);
|
|
space -= 16;
|
|
ASSERT_EQ(space, data.available_);
|
|
|
|
data.SetString("f", "");
|
|
space -= 8;
|
|
ASSERT_EQ(space, data.available_);
|
|
}
|
|
|
|
TEST_F(ActivityTrackerTest, PushPopTest) {
|
|
std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker();
|
|
ThreadActivityTracker::Snapshot snapshot;
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(0U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(0U, snapshot.activity_stack.size());
|
|
|
|
char origin1;
|
|
ActivityId id1 = tracker->PushActivity(&origin1, Activity::ACT_TASK,
|
|
ActivityData::ForTask(11));
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(1U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(1U, snapshot.activity_stack.size());
|
|
EXPECT_NE(0, snapshot.activity_stack[0].time_internal);
|
|
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
|
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1),
|
|
snapshot.activity_stack[0].origin_address);
|
|
EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id);
|
|
|
|
char origin2;
|
|
char lock2;
|
|
ActivityId id2 = tracker->PushActivity(&origin2, Activity::ACT_LOCK,
|
|
ActivityData::ForLock(&lock2));
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(2U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(2U, snapshot.activity_stack.size());
|
|
EXPECT_LE(snapshot.activity_stack[0].time_internal,
|
|
snapshot.activity_stack[1].time_internal);
|
|
EXPECT_EQ(Activity::ACT_LOCK, snapshot.activity_stack[1].activity_type);
|
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin2),
|
|
snapshot.activity_stack[1].origin_address);
|
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2),
|
|
snapshot.activity_stack[1].data.lock.lock_address);
|
|
|
|
tracker->PopActivity(id2);
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(1U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(1U, snapshot.activity_stack.size());
|
|
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
|
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1),
|
|
snapshot.activity_stack[0].origin_address);
|
|
EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id);
|
|
|
|
tracker->PopActivity(id1);
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(0U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(0U, snapshot.activity_stack.size());
|
|
}
|
|
|
|
TEST_F(ActivityTrackerTest, ScopedTaskTest) {
|
|
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
|
|
|
|
ThreadActivityTracker* tracker =
|
|
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
|
|
ThreadActivityTracker::Snapshot snapshot;
|
|
ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(0U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(0U, snapshot.activity_stack.size());
|
|
|
|
{
|
|
PendingTask task1(FROM_HERE, DoNothing());
|
|
ScopedTaskRunActivity activity1(task1);
|
|
ActivityUserData& user_data1 = activity1.user_data();
|
|
(void)user_data1; // Tell compiler it's been used.
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(1U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(1U, snapshot.activity_stack.size());
|
|
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
|
|
|
|
{
|
|
PendingTask task2(FROM_HERE, DoNothing());
|
|
ScopedTaskRunActivity activity2(task2);
|
|
ActivityUserData& user_data2 = activity2.user_data();
|
|
(void)user_data2; // Tell compiler it's been used.
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(2U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(2U, snapshot.activity_stack.size());
|
|
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[1].activity_type);
|
|
}
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(1U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(1U, snapshot.activity_stack.size());
|
|
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
|
|
}
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(0U, snapshot.activity_stack_depth);
|
|
ASSERT_EQ(0U, snapshot.activity_stack.size());
|
|
ASSERT_EQ(2U, GetGlobalUserDataMemoryCacheUsed());
|
|
}
|
|
|
|
namespace {
|
|
|
|
class SimpleLockThread : public SimpleThread {
|
|
public:
|
|
SimpleLockThread(const std::string& name, Lock* lock)
|
|
: SimpleThread(name, Options()),
|
|
lock_(lock),
|
|
data_changed_(false),
|
|
is_running_(false) {}
|
|
|
|
~SimpleLockThread() override = default;
|
|
|
|
void Run() override {
|
|
ThreadActivityTracker* tracker =
|
|
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
|
|
uint32_t pre_version = tracker->GetDataVersionForTesting();
|
|
|
|
is_running_.store(true, std::memory_order_relaxed);
|
|
lock_->Acquire();
|
|
data_changed_ = tracker->GetDataVersionForTesting() != pre_version;
|
|
lock_->Release();
|
|
is_running_.store(false, std::memory_order_relaxed);
|
|
}
|
|
|
|
bool IsRunning() { return is_running_.load(std::memory_order_relaxed); }
|
|
|
|
bool WasDataChanged() { return data_changed_; };
|
|
|
|
private:
|
|
Lock* lock_;
|
|
bool data_changed_;
|
|
std::atomic<bool> is_running_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SimpleLockThread);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ActivityTrackerTest, LockTest) {
|
|
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
|
|
|
|
ThreadActivityTracker* tracker =
|
|
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
|
|
ThreadActivityTracker::Snapshot snapshot;
|
|
ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
|
|
|
|
Lock lock;
|
|
uint32_t pre_version = tracker->GetDataVersionForTesting();
|
|
|
|
// Check no activity when only "trying" a lock.
|
|
EXPECT_TRUE(lock.Try());
|
|
EXPECT_EQ(pre_version, tracker->GetDataVersionForTesting());
|
|
lock.Release();
|
|
EXPECT_EQ(pre_version, tracker->GetDataVersionForTesting());
|
|
|
|
// Check no activity when acquiring a free lock.
|
|
SimpleLockThread t1("locker1", &lock);
|
|
t1.Start();
|
|
t1.Join();
|
|
EXPECT_FALSE(t1.WasDataChanged());
|
|
|
|
// Check that activity is recorded when acquring a busy lock.
|
|
SimpleLockThread t2("locker2", &lock);
|
|
lock.Acquire();
|
|
t2.Start();
|
|
while (!t2.IsRunning())
|
|
PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
|
|
// t2 can't join until the lock is released but have to give time for t2 to
|
|
// actually block on the lock before releasing it or the results will not
|
|
// be correct.
|
|
PlatformThread::Sleep(TimeDelta::FromMilliseconds(200));
|
|
lock.Release();
|
|
// Now the results will be valid.
|
|
t2.Join();
|
|
EXPECT_TRUE(t2.WasDataChanged());
|
|
}
|
|
|
|
TEST_F(ActivityTrackerTest, ExceptionTest) {
|
|
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
|
|
GlobalActivityTracker* global = GlobalActivityTracker::Get();
|
|
|
|
ThreadActivityTracker* tracker =
|
|
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
|
|
ThreadActivityTracker::Snapshot snapshot;
|
|
ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
ASSERT_EQ(0U, snapshot.last_exception.activity_type);
|
|
|
|
char origin;
|
|
global->RecordException(&origin, 42);
|
|
|
|
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
|
|
EXPECT_EQ(Activity::ACT_EXCEPTION, snapshot.last_exception.activity_type);
|
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin),
|
|
snapshot.last_exception.origin_address);
|
|
EXPECT_EQ(42U, snapshot.last_exception.data.exception.code);
|
|
}
|
|
|
|
TEST_F(ActivityTrackerTest, CreateWithFileTest) {
|
|
const char temp_name[] = "CreateWithFileTest";
|
|
ScopedTempDir temp_dir;
|
|
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
|
|
FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
|
|
const size_t temp_size = 64 << 10; // 64 KiB
|
|
|
|
// Create a global tracker on a new file.
|
|
ASSERT_FALSE(PathExists(temp_file));
|
|
GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "foo", 3);
|
|
GlobalActivityTracker* global = GlobalActivityTracker::Get();
|
|
EXPECT_EQ(std::string("foo"), global->allocator()->Name());
|
|
global->ReleaseTrackerForCurrentThreadForTesting();
|
|
delete global;
|
|
|
|
// Create a global tracker over an existing file, replacing it. If the
|
|
// replacement doesn't work, the name will remain as it was first created.
|
|
ASSERT_TRUE(PathExists(temp_file));
|
|
GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "bar", 3);
|
|
global = GlobalActivityTracker::Get();
|
|
EXPECT_EQ(std::string("bar"), global->allocator()->Name());
|
|
global->ReleaseTrackerForCurrentThreadForTesting();
|
|
delete global;
|
|
}
|
|
|
|
|
|
// GlobalActivityTracker tests below.
|
|
|
|
TEST_F(ActivityTrackerTest, BasicTest) {
|
|
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
|
|
GlobalActivityTracker* global = GlobalActivityTracker::Get();
|
|
|
|
// Ensure the data repositories have backing store, indicated by non-zero ID.
|
|
EXPECT_NE(0U, global->process_data().id());
|
|
}
|
|
|
|
namespace {
|
|
|
|
class SimpleActivityThread : public SimpleThread {
|
|
public:
|
|
SimpleActivityThread(const std::string& name,
|
|
const void* origin,
|
|
Activity::Type activity,
|
|
const ActivityData& data)
|
|
: SimpleThread(name, Options()),
|
|
origin_(origin),
|
|
activity_(activity),
|
|
data_(data),
|
|
ready_(false),
|
|
exit_(false),
|
|
exit_condition_(&lock_) {}
|
|
|
|
~SimpleActivityThread() override = default;
|
|
|
|
void Run() override {
|
|
ThreadActivityTracker::ActivityId id =
|
|
GlobalActivityTracker::Get()
|
|
->GetOrCreateTrackerForCurrentThread()
|
|
->PushActivity(origin_, activity_, data_);
|
|
|
|
{
|
|
AutoLock auto_lock(lock_);
|
|
ready_.store(true, std::memory_order_release);
|
|
while (!exit_.load(std::memory_order_relaxed))
|
|
exit_condition_.Wait();
|
|
}
|
|
|
|
GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id);
|
|
}
|
|
|
|
void Exit() {
|
|
AutoLock auto_lock(lock_);
|
|
exit_.store(true, std::memory_order_relaxed);
|
|
exit_condition_.Signal();
|
|
}
|
|
|
|
void WaitReady() {
|
|
SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_.load(std::memory_order_acquire));
|
|
}
|
|
|
|
private:
|
|
const void* origin_;
|
|
Activity::Type activity_;
|
|
ActivityData data_;
|
|
|
|
std::atomic<bool> ready_;
|
|
std::atomic<bool> exit_;
|
|
Lock lock_;
|
|
ConditionVariable exit_condition_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ActivityTrackerTest, ThreadDeathTest) {
|
|
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
|
|
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
|
|
const size_t starting_active = GetGlobalActiveTrackerCount();
|
|
const size_t starting_inactive = GetGlobalInactiveTrackerCount();
|
|
|
|
SimpleActivityThread t1("t1", nullptr, Activity::ACT_TASK,
|
|
ActivityData::ForTask(11));
|
|
t1.Start();
|
|
t1.WaitReady();
|
|
EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
|
|
EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
|
|
|
|
t1.Exit();
|
|
t1.Join();
|
|
EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
|
|
EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
|
|
|
|
// Start another thread and ensure it re-uses the existing memory.
|
|
|
|
SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK,
|
|
ActivityData::ForTask(22));
|
|
t2.Start();
|
|
t2.WaitReady();
|
|
EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
|
|
EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
|
|
|
|
t2.Exit();
|
|
t2.Join();
|
|
EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
|
|
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 int64_t other_process_id = GetCurrentProcId() + 1;
|
|
|
|
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
|
|
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, 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. Use a "past"
|
|
// time so that exit-time is always later than create-time.
|
|
const int64_t past_stamp = Time::Now().ToInternalValue() - 1;
|
|
int64_t 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,
|
|
past_stamp);
|
|
thread->SetOwningProcessIdForTesting(other_process_id, past_stamp);
|
|
user_data.SetOwningProcessIdForTesting(other_process_id, past_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
|