Chromium Code Reviews| Index: base/debug/activity_tracker.cc |
| diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc |
| index 562e662c98972f0588ba3ee9266b73c813b8e620..1b278c5f7303387f3d8aafc72df4cd53a9adc0d8 100644 |
| --- a/base/debug/activity_tracker.cc |
| +++ b/base/debug/activity_tracker.cc |
| @@ -16,6 +16,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" |
| @@ -38,8 +39,9 @@ const int kMinStackDepth = 2; |
| // The amount of memory set aside for holding arbitrary user data (key/value |
| // pairs) globally or associated with ActivityData entries. |
| -const size_t kUserDataSize = 1024; // bytes |
| -const size_t kGlobalDataSize = 4096; // bytes |
| +const size_t kUserDataSize = 1024; // bytes |
| +const size_t kGlobalDataSize = 4096; // bytes |
| +const size_t kGlobalModulesSize = 4096; // bytes |
| const size_t kMaxUserDataNameLength = |
| static_cast<size_t>(std::numeric_limits<uint8_t>::max()); |
| @@ -58,6 +60,89 @@ union ThreadRef { |
| #endif |
| }; |
| +// State of a module as stared in persistent memory. |
|
manzagop (departed)
2016/12/14 20:08:53
typo: stared -> stored
bcwhite
2016/12/14 21:59:05
Done.
|
| +struct ModuleInfoStore { |
| + // Expected size for 32/64-bit check. |
| + static constexpr size_t kExpectedInstanceSize = 24; |
|
manzagop (departed)
2016/12/14 20:08:53
nit: I like having units in variable names (kExpec
manzagop (departed)
2016/12/14 20:08:53
Unused?
bcwhite
2016/12/14 21:59:05
The constant is required (and used) by the Persist
|
| + |
| + uint64_t address; // The base address of the module. |
| + uint64_t size; // The size of the module in bytes. |
| + uint16_t pickle_size; // The size of the following pickle. |
| + uint8_t loaded; // Flag indicating if module is loaded or not. |
| + char pickle[5]; // Other strings; may allocate larger. |
| + |
| + // Decodes/encodes storage structure from more generic info structure. |
| + bool DecodeTo(GlobalActivityTracker::ModuleInfo* info); |
| + bool EncodeFrom(const GlobalActivityTracker::ModuleInfo& info, size_t size); |
|
manzagop (departed)
2016/12/14 20:08:53
Why do you need the size? Ah it's the size budget.
bcwhite
2016/12/14 21:59:05
Done.
|
| + |
| + // Updates the core information without changing the encoded strings. This is |
| + // useful when a known module changes state (i.e. new load or unload). |
| + bool UpdateFrom(const GlobalActivityTracker::ModuleInfo& info); |
| + |
| + // Deterimes the required memory size for the encoded storage. |
|
manzagop (departed)
2016/12/14 20:08:53
typo: Deterimes
bcwhite
2016/12/14 21:59:05
Done.
|
| + static size_t EncodedSize(const GlobalActivityTracker::ModuleInfo& info); |
| +}; |
| + |
| +static constexpr size_t kModuleInfoStoreHeaderSize = |
| + sizeof(ModuleInfoStore) - sizeof(((ModuleInfoStore*)0)->pickle); |
|
manzagop (departed)
2016/12/14 20:08:53
Size of on the null pointer smells funny to me. Wo
bcwhite
2016/12/14 21:59:05
Done.
|
| + |
| +bool ModuleInfoStore::DecodeTo(GlobalActivityTracker::ModuleInfo* info) { |
| + std::atomic_thread_fence(std::memory_order_acquire); |
| + info->is_loaded = loaded != 0; |
| + info->address = static_cast<uintptr_t>(address); |
| + info->size = static_cast<size_t>(size); |
| + |
| + 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 ModuleInfoStore::EncodeFrom(const GlobalActivityTracker::ModuleInfo& info, |
| + size_t 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 (pickler.size() + kModuleInfoStoreHeaderSize > size) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + |
| + memcpy(pickle, pickler.data(), pickler.size()); |
| + return UpdateFrom(info); |
| +} |
| + |
| +bool ModuleInfoStore::UpdateFrom( |
| + const GlobalActivityTracker::ModuleInfo& info) { |
| + address = info.address; |
| + size = info.size; |
| + loaded = info.is_loaded ? 1 : 0; |
| + std::atomic_thread_fence(std::memory_order_release); |
| + return true; |
| +} |
| + |
| +// static |
| +size_t ModuleInfoStore::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); |
| + |
| + return kModuleInfoStoreHeaderSize + sizer.payload_size(); |
| +} |
| + |
| // Determines the previous aligned index. |
| size_t RoundDownToAlignment(size_t index, size_t alignment) { |
| return index & (0 - alignment); |
| @@ -1065,6 +1150,38 @@ 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()) { |
| + ModuleInfoStore* store = allocator_->GetAsObject<ModuleInfoStore>( |
| + found->second, kTypeIdModulesRecord); |
| + DCHECK(store); |
| + |
| + // Get should never fail but need to be tolerant in production. |
| + if (store) { |
| + // 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 accomodate a possibly longer length. |
|
manzagop (departed)
2016/12/14 20:08:53
typo: accommodate
bcwhite
2016/12/14 21:59:05
Done.
|
| + store->UpdateFrom(info); |
| + return; |
|
manzagop (departed)
2016/12/14 20:08:53
return outside the if(store)?
bcwhite
2016/12/14 21:59:05
If it fails to load it, a new record will be creat
manzagop (departed)
2016/12/16 16:09:31
But then the map insertion will fail as the key al
bcwhite
2016/12/16 18:35:15
Good point. Always return.
|
| + } |
| + } |
| + |
| + size_t required_size = ModuleInfoStore::EncodedSize(info); |
| + PersistentMemoryAllocator::Reference ref = |
| + allocator_->Allocate(required_size, kTypeIdModulesRecord); |
| + ModuleInfoStore* store = |
| + allocator_->GetAsObject<ModuleInfoStore>(ref, kTypeIdModulesRecord); |
| + if (!store) |
| + return; |
| + |
| + bool success = store->EncodeFrom(info, allocator_->GetAllocSize(ref)); |
| + DCHECK(success); |
| + modules_.insert(std::make_pair(info.file, ref)); |
| +} |
| + |
| GlobalActivityTracker::GlobalActivityTracker( |
| std::unique_ptr<PersistentMemoryAllocator> allocator, |
| int stack_depth) |
| @@ -1098,8 +1215,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)); |
| } |