Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(744)

Unified Diff: base/debug/activity_tracker.cc

Issue 2566983009: Support storing information about what modules are loaded in the process. (Closed)
Patch Set: changed GUID to generic 'identifier'; noted it and others as 'opaque' with no defined meaning Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/debug/activity_tracker.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/debug/activity_tracker.cc
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index c728fa052a5d6f2ce57abd52f101e846cf5d67b0..deb17179904c532642c29bdfc85100628613b079 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,119 @@ 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 dynamic information.
+ info->is_loaded = loaded != 0;
+ info->address = static_cast<uintptr_t>(address);
+ info->load_time = load_time;
+
+ // 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;
+ }
+
+ // Copy out the static information. These never change so don't have to be
+ // protected by the atomic |current_changes| operations.
+ info->size = static_cast<size_t>(size);
+ info->timestamp = timestamp;
+ info->age = age;
+ memcpy(info->identifier, identifier, sizeof(info->identifier));
+
+ 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->debug_file);
+}
+
+bool GlobalActivityTracker::ModuleInfoRecord::EncodeFrom(
+ const GlobalActivityTracker::ModuleInfo& info,
+ size_t record_size) {
+ Pickle pickler;
+ bool okay =
+ pickler.WriteString(info.file) && pickler.WriteString(info.debug_file);
+ if (!okay) {
+ NOTREACHED();
+ return false;
+ }
+ if (offsetof(ModuleInfoRecord, pickle) + pickler.size() > record_size) {
+ NOTREACHED();
+ return false;
+ }
+
+ // These fields never changes and are done before the record is made
+ // iterable so no thread protection is necessary.
+ size = info.size;
+ timestamp = info.timestamp;
+ age = info.age;
+ memcpy(identifier, info.identifier, sizeof(identifier));
+ memcpy(pickle, pickler.data(), pickler.size());
+ pickle_size = pickler.size();
+ changes.store(0, std::memory_order_relaxed);
+
+ // Now set those fields that can change.
+ 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;
+ }
+
+ loaded = info.is_loaded ? 1 : 0;
+ address = info.address;
+ load_time = Time::Now().ToInternalValue();
+
+ 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.debug_file);
+
+ return offsetof(ModuleInfoRecord, pickle) + sizeof(Pickle::Header) +
+ sizer.payload_size();
+}
+
GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity(
const void* program_counter,
const void* origin,
@@ -1022,13 +1143,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 +1182,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 +1242,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));
}
« no previous file with comments | « base/debug/activity_tracker.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698