123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- // 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_analyzer.h"
- #include <utility>
- #include "base/check_op.h"
- #include "base/containers/contains.h"
- #include "base/files/file.h"
- #include "base/files/file_path.h"
- #include "base/files/memory_mapped_file.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/no_destructor.h"
- #include "base/ranges/algorithm.h"
- #include "base/strings/string_util.h"
- #include "build/build_config.h"
- namespace base {
- namespace debug {
- namespace {
- const ActivityUserData::Snapshot& GetEmptyUserDataSnapshot() {
- // An empty snapshot that can be returned when there otherwise is none.
- static const NoDestructor<ActivityUserData::Snapshot> empty_snapshot;
- return *empty_snapshot;
- }
- // DO NOT CHANGE VALUES. This is logged persistently in a histogram.
- enum AnalyzerCreationError {
- kInvalidMemoryMappedFile,
- kPmaBadFile,
- kPmaUninitialized,
- kPmaDeleted,
- kPmaCorrupt,
- kAnalyzerCreationErrorMax // Keep this last.
- };
- void LogAnalyzerCreationError(AnalyzerCreationError error) {
- UmaHistogramEnumeration("ActivityTracker.Collect.AnalyzerCreationError",
- error, kAnalyzerCreationErrorMax);
- }
- } // namespace
- ThreadActivityAnalyzer::Snapshot::Snapshot() = default;
- ThreadActivityAnalyzer::Snapshot::~Snapshot() = default;
- ThreadActivityAnalyzer::ThreadActivityAnalyzer(
- const ThreadActivityTracker& tracker)
- : activity_snapshot_valid_(tracker.CreateSnapshot(&activity_snapshot_)) {}
- ThreadActivityAnalyzer::ThreadActivityAnalyzer(void* base, size_t size)
- : ThreadActivityAnalyzer(ThreadActivityTracker(base, size)) {}
- ThreadActivityAnalyzer::ThreadActivityAnalyzer(
- PersistentMemoryAllocator* allocator,
- PersistentMemoryAllocator::Reference reference)
- : ThreadActivityAnalyzer(allocator->GetAsArray<char>(
- reference,
- GlobalActivityTracker::kTypeIdActivityTracker,
- PersistentMemoryAllocator::kSizeAny),
- allocator->GetAllocSize(reference)) {}
- ThreadActivityAnalyzer::~ThreadActivityAnalyzer() = default;
- void ThreadActivityAnalyzer::AddGlobalInformation(
- GlobalActivityAnalyzer* global) {
- if (!IsValid())
- return;
- // User-data is held at the global scope even though it's referenced at the
- // thread scope.
- activity_snapshot_.user_data_stack.clear();
- for (auto& activity : activity_snapshot_.activity_stack) {
- // 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_snapshot_.process_id, activity.user_data_ref,
- activity.user_data_id));
- }
- }
- GlobalActivityAnalyzer::GlobalActivityAnalyzer(
- std::unique_ptr<PersistentMemoryAllocator> allocator)
- : allocator_(std::move(allocator)),
- analysis_stamp_(0LL),
- allocator_iterator_(allocator_.get()) {
- DCHECK(allocator_);
- }
- GlobalActivityAnalyzer::~GlobalActivityAnalyzer() = default;
- // static
- std::unique_ptr<GlobalActivityAnalyzer>
- GlobalActivityAnalyzer::CreateWithAllocator(
- std::unique_ptr<PersistentMemoryAllocator> allocator) {
- if (allocator->GetMemoryState() ==
- PersistentMemoryAllocator::MEMORY_UNINITIALIZED) {
- LogAnalyzerCreationError(kPmaUninitialized);
- return nullptr;
- }
- if (allocator->GetMemoryState() ==
- PersistentMemoryAllocator::MEMORY_DELETED) {
- LogAnalyzerCreationError(kPmaDeleted);
- return nullptr;
- }
- if (allocator->IsCorrupt()) {
- LogAnalyzerCreationError(kPmaCorrupt);
- return nullptr;
- }
- return std::make_unique<GlobalActivityAnalyzer>(std::move(allocator));
- }
- #if !BUILDFLAG(IS_NACL)
- // static
- std::unique_ptr<GlobalActivityAnalyzer> GlobalActivityAnalyzer::CreateWithFile(
- const FilePath& file_path) {
- // Map the file read-write so it can guarantee consistency between
- // the analyzer and any trackers that my still be active.
- std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
- if (!mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE)) {
- LogAnalyzerCreationError(kInvalidMemoryMappedFile);
- return nullptr;
- }
- if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
- LogAnalyzerCreationError(kPmaBadFile);
- return nullptr;
- }
- return CreateWithAllocator(std::make_unique<FilePersistentMemoryAllocator>(
- std::move(mmfile), 0, 0, StringPiece(), /*readonly=*/true));
- }
- #endif // !BUILDFLAG(IS_NACL)
- // static
- std::unique_ptr<GlobalActivityAnalyzer>
- GlobalActivityAnalyzer::CreateWithSharedMemory(
- base::ReadOnlySharedMemoryMapping mapping) {
- if (!mapping.IsValid() ||
- !ReadOnlySharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(
- mapping)) {
- return nullptr;
- }
- return CreateWithAllocator(
- std::make_unique<ReadOnlySharedPersistentMemoryAllocator>(
- std::move(mapping), 0, StringPiece()));
- }
- ProcessId GlobalActivityAnalyzer::GetFirstProcess() {
- PrepareAllAnalyzers();
- return GetNextProcess();
- }
- ProcessId GlobalActivityAnalyzer::GetNextProcess() {
- if (process_ids_.empty())
- return 0;
- ProcessId pid = process_ids_.back();
- process_ids_.pop_back();
- return pid;
- }
- ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer(
- ProcessId pid) {
- analyzers_iterator_ = analyzers_.begin();
- analyzers_iterator_pid_ = pid;
- if (analyzers_iterator_ == analyzers_.end())
- return nullptr;
- 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());
- 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();
- }
- ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetAnalyzerForThread(
- const ThreadKey& key) {
- auto found = analyzers_.find(key);
- if (found == analyzers_.end())
- return nullptr;
- return found->second.get();
- }
- ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot(
- ProcessId pid,
- uint32_t ref,
- uint32_t id) {
- ActivityUserData::Snapshot snapshot;
- void* memory = allocator_->GetAsArray<char>(
- ref, GlobalActivityTracker::kTypeIdUserDataRecord,
- PersistentMemoryAllocator::kSizeAny);
- if (memory) {
- size_t size = allocator_->GetAllocSize(ref);
- const ActivityUserData user_data(memory, size);
- user_data.CreateSnapshot(&snapshot);
- ProcessId 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();
- }
- }
- return snapshot;
- }
- const ActivityUserData::Snapshot&
- GlobalActivityAnalyzer::GetProcessDataSnapshot(ProcessId pid) {
- auto iter = process_data_.find(pid);
- if (iter == process_data_.end())
- return GetEmptyUserDataSnapshot();
- if (iter->second.create_stamp > analysis_stamp_)
- return GetEmptyUserDataSnapshot();
- DCHECK_EQ(pid, iter->second.process_id);
- return iter->second.data;
- }
- std::vector<std::string> GlobalActivityAnalyzer::GetLogMessages() {
- std::vector<std::string> messages;
- PersistentMemoryAllocator::Reference ref;
- PersistentMemoryAllocator::Iterator iter(allocator_.get());
- while ((ref = iter.GetNextOfType(
- GlobalActivityTracker::kTypeIdGlobalLogMessage)) != 0) {
- const char* message = allocator_->GetAsArray<char>(
- ref, GlobalActivityTracker::kTypeIdGlobalLogMessage,
- PersistentMemoryAllocator::kSizeAny);
- if (message)
- messages.push_back(message);
- }
- return messages;
- }
- std::vector<GlobalActivityTracker::ModuleInfo>
- GlobalActivityAnalyzer::GetModules(ProcessId pid) {
- std::vector<GlobalActivityTracker::ModuleInfo> modules;
- PersistentMemoryAllocator::Iterator iter(allocator_.get());
- const GlobalActivityTracker::ModuleInfoRecord* record;
- while (
- (record =
- iter.GetNextOfObject<GlobalActivityTracker::ModuleInfoRecord>()) !=
- nullptr) {
- ProcessId process_id;
- int64_t create_stamp;
- if (!OwningProcess::GetOwningProcessId(&record->owner, &process_id,
- &create_stamp) ||
- pid != process_id || create_stamp > analysis_stamp_) {
- continue;
- }
- GlobalActivityTracker::ModuleInfo info;
- if (record->DecodeTo(&info, allocator_->GetAllocSize(
- allocator_->GetAsReference(record)))) {
- modules.push_back(std::move(info));
- }
- }
- return modules;
- }
- GlobalActivityAnalyzer::ProgramLocation
- GlobalActivityAnalyzer::GetProgramLocationFromAddress(uint64_t address) {
- // This should be implemented but it's never been a priority.
- return { 0, 0 };
- }
- bool GlobalActivityAnalyzer::IsDataComplete() const {
- DCHECK(allocator_);
- return !allocator_->IsFull();
- }
- GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot() = default;
- GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
- const UserDataSnapshot& rhs) = default;
- GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
- UserDataSnapshot&& rhs) = default;
- GlobalActivityAnalyzer::UserDataSnapshot::~UserDataSnapshot() = default;
- 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;
- PersistentMemoryAllocator::Reference ref;
- while ((ref = allocator_iterator_.GetNext(&type)) != 0) {
- switch (type) {
- case GlobalActivityTracker::kTypeIdActivityTracker:
- case GlobalActivityTracker::kTypeIdActivityTrackerFree:
- 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;
- }
- }
- // Clear out any old information.
- analyzers_.clear();
- process_data_.clear();
- process_ids_.clear();
- std::set<ProcessId> 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;
- 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.
- ProcessId 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::Contains(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.
- ProcessId process_id;
- int64_t create_stamp;
- ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
- DCHECK(!base::Contains(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.
- ranges::reverse(process_ids_);
- }
- } // namespace debug
- } // namespace base
|