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 |