| Index: base/debug/activity_analyzer.cc
|
| diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc
|
| index 7c421e9630922a9135baae81bae052d3e21fb7d7..0ab7c3d0a0342c149b46ac568d6d409f48508bac 100644
|
| --- a/base/debug/activity_analyzer.cc
|
| +++ b/base/debug/activity_analyzer.cc
|
| @@ -4,9 +4,12 @@
|
|
|
| #include "base/debug/activity_analyzer.h"
|
|
|
| +#include <algorithm>
|
| +
|
| #include "base/files/file.h"
|
| #include "base/files/file_path.h"
|
| #include "base/files/memory_mapped_file.h"
|
| +#include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| #include "base/memory/ptr_util.h"
|
| #include "base/stl_util.h"
|
| @@ -15,6 +18,11 @@
|
| namespace base {
|
| namespace debug {
|
|
|
| +namespace {
|
| +// An empty snapshot that can be returned when there otherwise is none.
|
| +LazyInstance<ActivityUserData::Snapshot>::Leaky g_empty_user_data_snapshot;
|
| +} // namespace
|
| +
|
| ThreadActivityAnalyzer::Snapshot::Snapshot() {}
|
| ThreadActivityAnalyzer::Snapshot::~Snapshot() {}
|
|
|
| @@ -48,7 +56,8 @@ void ThreadActivityAnalyzer::AddGlobalInformation(
|
| // The global GetUserDataSnapshot will return an empty snapshot if the ref
|
| // or id is not valid.
|
| activity_snapshot_.user_data_stack.push_back(global->GetUserDataSnapshot(
|
| - activity.user_data_ref, activity.user_data_id));
|
| + activity_snapshot_.process_id, activity.user_data_ref,
|
| + activity.user_data_id));
|
| }
|
| }
|
|
|
| @@ -78,19 +87,42 @@ std::unique_ptr<GlobalActivityAnalyzer> GlobalActivityAnalyzer::CreateWithFile(
|
| }
|
| #endif // !defined(OS_NACL)
|
|
|
| -ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer() {
|
| +int64_t GlobalActivityAnalyzer::GetFirstProcess() {
|
| PrepareAllAnalyzers();
|
| + return GetNextProcess();
|
| +}
|
| +
|
| +int64_t GlobalActivityAnalyzer::GetNextProcess() {
|
| + if (process_ids_.empty())
|
| + return 0;
|
| + int64_t pid = process_ids_.back();
|
| + process_ids_.pop_back();
|
| + return pid;
|
| +}
|
| +
|
| +ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer(int64_t pid) {
|
| analyzers_iterator_ = analyzers_.begin();
|
| + analyzers_iterator_pid_ = pid;
|
| if (analyzers_iterator_ == analyzers_.end())
|
| return nullptr;
|
| - return analyzers_iterator_->second.get();
|
| + int64_t create_stamp;
|
| + if (analyzers_iterator_->second->GetProcessId(&create_stamp) == pid &&
|
| + create_stamp <= analysis_stamp_) {
|
| + return analyzers_iterator_->second.get();
|
| + }
|
| + return GetNextAnalyzer();
|
| }
|
|
|
| ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetNextAnalyzer() {
|
| DCHECK(analyzers_iterator_ != analyzers_.end());
|
| - ++analyzers_iterator_;
|
| - if (analyzers_iterator_ == analyzers_.end())
|
| - return nullptr;
|
| + int64_t create_stamp;
|
| + do {
|
| + ++analyzers_iterator_;
|
| + if (analyzers_iterator_ == analyzers_.end())
|
| + return nullptr;
|
| + } while (analyzers_iterator_->second->GetProcessId(&create_stamp) !=
|
| + analyzers_iterator_pid_ ||
|
| + create_stamp > analysis_stamp_);
|
| return analyzers_iterator_->second.get();
|
| }
|
|
|
| @@ -103,6 +135,7 @@ ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetAnalyzerForThread(
|
| }
|
|
|
| ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot(
|
| + int64_t pid,
|
| uint32_t ref,
|
| uint32_t id) {
|
| ActivityUserData::Snapshot snapshot;
|
| @@ -114,7 +147,11 @@ ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot(
|
| size_t size = allocator_->GetAllocSize(ref);
|
| const ActivityUserData user_data(memory, size);
|
| user_data.CreateSnapshot(&snapshot);
|
| - if (user_data.id() != id) {
|
| + int64_t process_id;
|
| + int64_t create_stamp;
|
| + if (!ActivityUserData::GetOwningProcessId(memory, &process_id,
|
| + &create_stamp) ||
|
| + process_id != pid || user_data.id() != id) {
|
| // This allocation has been overwritten since it was created. Return an
|
| // empty snapshot because whatever was captured is incorrect.
|
| snapshot.clear();
|
| @@ -124,8 +161,20 @@ ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot(
|
| return snapshot;
|
| }
|
|
|
| -ActivityUserData::Snapshot GlobalActivityAnalyzer::GetGlobalUserDataSnapshot() {
|
| - ActivityUserData::Snapshot snapshot;
|
| +const ActivityUserData::Snapshot&
|
| +GlobalActivityAnalyzer::GetProcessDataSnapshot(int64_t pid) {
|
| + auto iter = process_data_.find(pid);
|
| + if (iter == process_data_.end())
|
| + return g_empty_user_data_snapshot.Get();
|
| + if (iter->second.create_stamp > analysis_stamp_)
|
| + return g_empty_user_data_snapshot.Get();
|
| + DCHECK_EQ(pid, iter->second.process_id);
|
| + return iter->second.data;
|
| +}
|
| +
|
| +const ActivityUserData::Snapshot&
|
| +GlobalActivityAnalyzer::GetGlobalDataSnapshot() {
|
| + global_data_snapshot_.clear();
|
|
|
| PersistentMemoryAllocator::Reference ref =
|
| PersistentMemoryAllocator::Iterator(allocator_.get())
|
| @@ -136,10 +185,10 @@ ActivityUserData::Snapshot GlobalActivityAnalyzer::GetGlobalUserDataSnapshot() {
|
| if (memory) {
|
| size_t size = allocator_->GetAllocSize(ref);
|
| const ActivityUserData global_data(memory, size);
|
| - global_data.CreateSnapshot(&snapshot);
|
| + global_data.CreateSnapshot(&global_data_snapshot_);
|
| }
|
|
|
| - return snapshot;
|
| + return global_data_snapshot_;
|
| }
|
|
|
| std::vector<std::string> GlobalActivityAnalyzer::GetLogMessages() {
|
| @@ -185,7 +234,17 @@ GlobalActivityAnalyzer::GetProgramLocationFromAddress(uint64_t address) {
|
| return { 0, 0 };
|
| }
|
|
|
| +GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot() {}
|
| +GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
|
| + const UserDataSnapshot& rhs) = default;
|
| +GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
|
| + UserDataSnapshot&& rhs) = default;
|
| +GlobalActivityAnalyzer::UserDataSnapshot::~UserDataSnapshot() {}
|
| +
|
| void GlobalActivityAnalyzer::PrepareAllAnalyzers() {
|
| + // Record the time when analysis started.
|
| + analysis_stamp_ = base::Time::Now().ToInternalValue();
|
| +
|
| // Fetch all the records. This will retrieve only ones created since the
|
| // last run since the PMA iterator will continue from where it left off.
|
| uint32_t type;
|
| @@ -194,39 +253,95 @@ void GlobalActivityAnalyzer::PrepareAllAnalyzers() {
|
| switch (type) {
|
| case GlobalActivityTracker::kTypeIdActivityTracker:
|
| case GlobalActivityTracker::kTypeIdActivityTrackerFree:
|
| - // Free or not, add it to the list of references for later analysis.
|
| - tracker_references_.insert(ref);
|
| + case GlobalActivityTracker::kTypeIdProcessDataRecord:
|
| + case GlobalActivityTracker::kTypeIdProcessDataRecordFree:
|
| + case PersistentMemoryAllocator::kTypeIdTransitioning:
|
| + // Active, free, or transitioning: add it to the list of references
|
| + // for later analysis.
|
| + memory_references_.insert(ref);
|
| break;
|
| }
|
| }
|
|
|
| - // Go through all the known references and create analyzers for them with
|
| - // snapshots of the current state.
|
| + // Clear out any old information.
|
| analyzers_.clear();
|
| - for (PersistentMemoryAllocator::Reference tracker_ref : tracker_references_) {
|
| - // Get the actual data segment for the tracker. This can fail if the
|
| - // record has been marked "free" since the type will not match.
|
| - void* base = allocator_->GetAsArray<char>(
|
| - tracker_ref, GlobalActivityTracker::kTypeIdActivityTracker,
|
| + process_data_.clear();
|
| + process_ids_.clear();
|
| + std::set<int64_t> seen_pids;
|
| +
|
| + // Go through all the known references and create objects for them with
|
| + // snapshots of the current state.
|
| + for (PersistentMemoryAllocator::Reference memory_ref : memory_references_) {
|
| + // Get the actual data segment for the tracker. Any type will do since it
|
| + // is checked below.
|
| + void* const base = allocator_->GetAsArray<char>(
|
| + memory_ref, PersistentMemoryAllocator::kTypeIdAny,
|
| PersistentMemoryAllocator::kSizeAny);
|
| + const size_t size = allocator_->GetAllocSize(memory_ref);
|
| if (!base)
|
| continue;
|
|
|
| - // Create the analyzer on the data. This will capture a snapshot of the
|
| - // tracker state. This can fail if the tracker is somehow corrupted or is
|
| - // in the process of shutting down.
|
| - std::unique_ptr<ThreadActivityAnalyzer> analyzer(new ThreadActivityAnalyzer(
|
| - base, allocator_->GetAllocSize(tracker_ref)));
|
| - if (!analyzer->IsValid())
|
| - continue;
|
| - analyzer->AddGlobalInformation(this);
|
| -
|
| - // Add this analyzer to the map of known ones, indexed by a unique thread
|
| - // identifier.
|
| - DCHECK(!base::ContainsKey(analyzers_, analyzer->GetThreadKey()));
|
| - analyzer->allocator_reference_ = ref;
|
| - analyzers_[analyzer->GetThreadKey()] = std::move(analyzer);
|
| + switch (allocator_->GetType(memory_ref)) {
|
| + case GlobalActivityTracker::kTypeIdActivityTracker: {
|
| + // Create the analyzer on the data. This will capture a snapshot of the
|
| + // tracker state. This can fail if the tracker is somehow corrupted or
|
| + // is in the process of shutting down.
|
| + std::unique_ptr<ThreadActivityAnalyzer> analyzer(
|
| + new ThreadActivityAnalyzer(base, size));
|
| + if (!analyzer->IsValid())
|
| + continue;
|
| + analyzer->AddGlobalInformation(this);
|
| +
|
| + // Track PIDs.
|
| + int64_t pid = analyzer->GetProcessId();
|
| + if (seen_pids.find(pid) == seen_pids.end()) {
|
| + process_ids_.push_back(pid);
|
| + seen_pids.insert(pid);
|
| + }
|
| +
|
| + // Add this analyzer to the map of known ones, indexed by a unique
|
| + // thread
|
| + // identifier.
|
| + DCHECK(!base::ContainsKey(analyzers_, analyzer->GetThreadKey()));
|
| + analyzer->allocator_reference_ = ref;
|
| + analyzers_[analyzer->GetThreadKey()] = std::move(analyzer);
|
| + } break;
|
| +
|
| + case GlobalActivityTracker::kTypeIdProcessDataRecord: {
|
| + // Get the PID associated with this data record.
|
| + int64_t process_id;
|
| + int64_t create_stamp;
|
| + ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
|
| + DCHECK(!base::ContainsKey(process_data_, process_id));
|
| +
|
| + // Create a snapshot of the data. This can fail if the data is somehow
|
| + // corrupted or the process shutdown and the memory being released.
|
| + UserDataSnapshot& snapshot = process_data_[process_id];
|
| + snapshot.process_id = process_id;
|
| + snapshot.create_stamp = create_stamp;
|
| + const ActivityUserData process_data(base, size);
|
| + if (!process_data.CreateSnapshot(&snapshot.data))
|
| + break;
|
| +
|
| + // Check that nothing changed. If it did, forget what was recorded.
|
| + ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
|
| + if (process_id != snapshot.process_id ||
|
| + create_stamp != snapshot.create_stamp) {
|
| + process_data_.erase(process_id);
|
| + break;
|
| + }
|
| +
|
| + // Track PIDs.
|
| + if (seen_pids.find(process_id) == seen_pids.end()) {
|
| + process_ids_.push_back(process_id);
|
| + seen_pids.insert(process_id);
|
| + }
|
| + } break;
|
| + }
|
| }
|
| +
|
| + // Reverse the list of PIDs so that they get popped in the order found.
|
| + std::reverse(process_ids_.begin(), process_ids_.end());
|
| }
|
|
|
| } // namespace debug
|
|
|