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)); |
} |