Chromium Code Reviews| Index: base/debug/activity_tracker.cc |
| diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc |
| index c728fa052a5d6f2ce57abd52f101e846cf5d67b0..b458797c2ebdd45d38ff9343656172ca5fe7b8b8 100644 |
| --- a/base/debug/activity_tracker.cc |
| +++ b/base/debug/activity_tracker.cc |
| @@ -17,6 +17,7 @@ |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/pending_task.h" |
| +#include "base/pickle.h" |
| #include "base/process/process.h" |
| #include "base/process/process_handle.h" |
| #include "base/stl_util.h" |
| @@ -44,6 +45,9 @@ const size_t kGlobalDataSize = 4096; // bytes |
| const size_t kMaxUserDataNameLength = |
| static_cast<size_t>(std::numeric_limits<uint8_t>::max()); |
| +// A constant used to indicate that module information is changing. |
| +const uint32_t kModuleInformationChanging = 0x80000000; |
| + |
| union ThreadRef { |
| int64_t as_id; |
| #if defined(OS_WIN) |
| @@ -478,6 +482,10 @@ const void* ActivityUserData::GetBaseAddress() { |
| // the very first time the thread is seen. All fields must be of exact sizes |
| // so there is no issue moving between 32 and 64-bit builds. |
| struct ThreadActivityTracker::Header { |
| + // Defined in .h for analyzer access. Increment this if structure changes! |
| + static constexpr uint32_t kPersistentTypeId = |
| + GlobalActivityTracker::kTypeIdActivityTracker; |
| + |
| // Expected size for 32/64-bit check. |
| static constexpr size_t kExpectedInstanceSize = 80; |
| @@ -894,6 +902,116 @@ size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) { |
| GlobalActivityTracker* GlobalActivityTracker::g_tracker_ = nullptr; |
| +GlobalActivityTracker::ModuleInfo::ModuleInfo() {} |
| +GlobalActivityTracker::ModuleInfo::ModuleInfo(ModuleInfo&& rhs) = default; |
| +GlobalActivityTracker::ModuleInfo::ModuleInfo(const ModuleInfo& rhs) = default; |
| +GlobalActivityTracker::ModuleInfo::~ModuleInfo() {} |
| + |
| +GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=( |
| + ModuleInfo&& rhs) = default; |
| +GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=( |
| + const ModuleInfo& rhs) = default; |
| + |
| +GlobalActivityTracker::ModuleInfoRecord::ModuleInfoRecord() {} |
| +GlobalActivityTracker::ModuleInfoRecord::~ModuleInfoRecord() {} |
| + |
| +bool GlobalActivityTracker::ModuleInfoRecord::DecodeTo( |
| + GlobalActivityTracker::ModuleInfo* info, |
| + size_t record_size) const { |
| + // Get the current "changes" indicator, acquiring all the other values. |
| + uint32_t current_changes = changes.load(std::memory_order_acquire); |
| + |
| + // Copy out the information. |
| + info->address = static_cast<uintptr_t>(address); |
| + info->size = static_cast<size_t>(size); |
| + info->is_loaded = loaded != 0; |
| + |
| + // Check to make sure no information changed while being read. A "seq-cst" |
| + // operation is expensive but is only done during analysis and it's the only |
| + // way to ensure this occurs after all the accesses above. If changes did |
| + // occur then return a "not loaded" result so that |size| and |address| |
| + // aren't expected to be accurate. |
| + if ((current_changes & kModuleInformationChanging) != 0 || |
| + changes.load(std::memory_order_seq_cst) != current_changes) { |
| + info->is_loaded = false; |
| + } |
| + |
| + // Pickled information never changes so can always be returned. |
| + if (offsetof(ModuleInfoRecord, pickle) + pickle_size > record_size) |
| + return false; |
| + Pickle pickler(pickle, pickle_size); |
| + PickleIterator iter(pickler); |
| + return iter.ReadString(&info->file) && iter.ReadString(&info->version) && |
| + iter.ReadString(&info->identifier) && |
| + iter.ReadString(&info->debug_file) && |
| + iter.ReadString(&info->debug_identifier); |
| +} |
| + |
| +bool GlobalActivityTracker::ModuleInfoRecord::EncodeFrom( |
| + const GlobalActivityTracker::ModuleInfo& info, |
| + size_t record_size) { |
| + Pickle pickler; |
| + bool okay = pickler.WriteString(info.file) && |
| + pickler.WriteString(info.version) && |
| + pickler.WriteString(info.identifier) && |
| + pickler.WriteString(info.debug_file) && |
| + pickler.WriteString(info.debug_identifier); |
| + if (!okay) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + if (offsetof(ModuleInfoRecord, pickle) + pickler.size() > record_size) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + |
| + // The pickle never changes and this is done before the record is made |
| + // iterable so no thread protection is necessary. |
| + memcpy(pickle, pickler.data(), pickler.size()); |
| + pickle_size = pickler.size(); |
| + changes.store(0, std::memory_order_relaxed); |
| + return UpdateFrom(info); |
| +} |
| + |
| +bool GlobalActivityTracker::ModuleInfoRecord::UpdateFrom( |
| + const GlobalActivityTracker::ModuleInfo& info) { |
| + // Updates can occur after the record is made visible so make changes atomic. |
| + // A "strong" exchange ensures no false failures. |
| + uint32_t old_changes = changes.load(std::memory_order_relaxed); |
| + uint32_t new_changes = old_changes | kModuleInformationChanging; |
| + if ((old_changes & kModuleInformationChanging) != 0 || |
| + !changes.compare_exchange_strong(old_changes, new_changes, |
| + std::memory_order_acquire, |
| + std::memory_order_acquire)) { |
| + NOTREACHED() << "Multiple sources are updating module information."; |
| + return false; |
| + } |
| + |
| + address = info.address; |
| + size = info.size; |
| + loaded = info.is_loaded ? 1 : 0; |
| + |
| + bool success = changes.compare_exchange_strong(new_changes, old_changes + 1, |
| + std::memory_order_release, |
| + std::memory_order_relaxed); |
| + DCHECK(success); |
| + return true; |
| +} |
| + |
| +// static |
| +size_t GlobalActivityTracker::ModuleInfoRecord::EncodedSize( |
| + const GlobalActivityTracker::ModuleInfo& info) { |
| + PickleSizer sizer; |
| + sizer.AddString(info.file); |
| + sizer.AddString(info.version); |
| + sizer.AddString(info.identifier); |
| + sizer.AddString(info.debug_file); |
| + sizer.AddString(info.debug_identifier); |
|
manzagop (departed)
2017/01/13 23:01:02
Siggi was suggesting we do as little work for stor
bcwhite
2017/01/14 01:23:24
According to my brief conversation with Chris, it
|
| + |
| + return offsetof(ModuleInfoRecord, pickle) + sizeof(Pickle::Header) + |
| + sizer.payload_size(); |
| +} |
| + |
| GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity( |
| const void* program_counter, |
| const void* origin, |
| @@ -1022,13 +1140,8 @@ ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() { |
| // TODO(bcwhite): Review this after major compiler releases. |
| DCHECK(mem_reference); |
| void* mem_base; |
| -#if 0 // TODO(bcwhite): Update this for new GetAsObject functionality. |
| - mem_base = allocator_->GetAsObject<ThreadActivityTracker::Header>( |
| - mem_reference, kTypeIdActivityTracker); |
| -#else |
| - mem_base = allocator_->GetAsArray<char>(mem_reference, kTypeIdActivityTracker, |
| - PersistentMemoryAllocator::kSizeAny); |
| -#endif |
| + mem_base = |
| + allocator_->GetAsObject<ThreadActivityTracker::Header>(mem_reference); |
| DCHECK(mem_base); |
| DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference)); |
| @@ -1066,6 +1179,33 @@ void GlobalActivityTracker::RecordLogMessage(StringPiece message) { |
| } |
| } |
| +void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) { |
| + AutoLock lock(modules_lock_); |
| + auto found = modules_.find(info.file); |
| + if (found != modules_.end()) { |
| + ModuleInfoRecord* record = found->second; |
| + DCHECK(record); |
| + |
| + // Update the basic state of module information that has been already |
| + // recorded. It is assumed that the string information (identifier, |
| + // version, etc.) remain unchanged which means that there's no need |
| + // to create a new record to accommodate a possibly longer length. |
| + record->UpdateFrom(info); |
| + return; |
| + } |
| + |
| + size_t required_size = ModuleInfoRecord::EncodedSize(info); |
| + ModuleInfoRecord* record = |
| + allocator_->AllocateObject<ModuleInfoRecord>(required_size); |
| + if (!record) |
| + return; |
| + |
| + bool success = record->EncodeFrom(info, required_size); |
| + DCHECK(success); |
| + allocator_->MakeIterable(record); |
| + modules_.insert(std::make_pair(info.file, record)); |
| +} |
| + |
| GlobalActivityTracker::GlobalActivityTracker( |
| std::unique_ptr<PersistentMemoryAllocator> allocator, |
| int stack_depth) |
| @@ -1099,8 +1239,7 @@ GlobalActivityTracker::GlobalActivityTracker( |
| DCHECK(!g_tracker_); |
| g_tracker_ = this; |
| - // The global user-data record must be iterable in order to be found by an |
| - // analyzer. |
| + // The global records must be iterable in order to be found by an analyzer. |
| allocator_->MakeIterable(allocator_->GetAsReference( |
| user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); |
| } |