Chromium Code Reviews| Index: base/debug/activity_analyzer.cc |
| diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc |
| index 7c421e9630922a9135baae81bae052d3e21fb7d7..f7f69e7d357b4bf130bf4eefb35b120af622e9d6 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,87 @@ 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); |
| + |
| + // 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); |
| + } |
| + |
| + // Track PIDs. |
|
manzagop (departed)
2017/03/30 18:13:24
May be worth noting in the comment the assumption
bcwhite
2017/03/30 22:20:51
I had to do the add in both sections because some
|
| + 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 |