summaryrefslogtreecommitdiff
path: root/base/debug
diff options
context:
space:
mode:
authorJakub Pawlowski <jpawlowski@google.com>2017-12-20 18:26:31 +0000
committerMyles Watson <mylesgw@google.com>2017-12-20 18:47:15 +0000
commit70cd4fac31a9b0865dab6574540f70cc103337dc (patch)
treea9d13e0b40d89d8960e80b71a7c21e639ea6c2d5 /base/debug
parentbf8c17f71511c1e90cd8cccfe71f0852c566bd3b (diff)
downloadlibchrome-70cd4fac31a9b0865dab6574540f70cc103337dc.tar.gz
Revert "Uprev the library to r462023 from Chromium"
This reverts commit bf8c17f71511c1e90cd8cccfe71f0852c566bd3b. Reason for revert: https://buganizer.corp.google.com/issues/70858501 Change-Id: Iedb1193d46ea2211f8b6fdace41902ad8df6d754
Diffstat (limited to 'base/debug')
-rw-r--r--base/debug/activity_tracker.cc781
-rw-r--r--base/debug/activity_tracker.h318
-rw-r--r--base/debug/activity_tracker_unittest.cc188
-rw-r--r--base/debug/stack_trace.cc19
-rw-r--r--base/debug/stack_trace.h22
5 files changed, 244 insertions, 1084 deletions
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index 5081c1c9d2..40e9b9537c 100644
--- a/base/debug/activity_tracker.cc
+++ b/base/debug/activity_tracker.cc
@@ -23,7 +23,6 @@
#include "base/process/process_handle.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
namespace base {
@@ -31,13 +30,18 @@ namespace debug {
namespace {
+// A number that identifies the memory as having been initialized. It's
+// arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker).
+// A version number is added on so that major structure changes won't try to
+// read an older version (since the cookie won't match).
+const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2
+
// The minimum depth a stack should support.
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 = 1 << 10; // 1 KiB
-const size_t kProcessDataSize = 4 << 10; // 4 KiB
const size_t kGlobalDataSize = 16 << 10; // 16 KiB
const size_t kMaxUserDataNameLength =
static_cast<size_t>(std::numeric_limits<uint8_t>::max());
@@ -45,13 +49,6 @@ const size_t kMaxUserDataNameLength =
// A constant used to indicate that module information is changing.
const uint32_t kModuleInformationChanging = 0x80000000;
-// The key used to record process information.
-const char kProcessPhaseDataKey[] = "process-phase";
-
-// An atomically incrementing number, used to check for recreations of objects
-// in the same memory space.
-StaticAtomicSequenceNumber g_next_id;
-
union ThreadRef {
int64_t as_id;
#if defined(OS_WIN)
@@ -67,43 +64,6 @@ union ThreadRef {
#endif
};
-// Gets the next non-zero identifier. It is only unique within a process.
-uint32_t GetNextDataId() {
- uint32_t id;
- while ((id = g_next_id.GetNext()) == 0)
- ;
- return id;
-}
-
-// Gets the current process-id, either from the GlobalActivityTracker if it
-// exists (where the PID can be defined for testing) or from the system if
-// there isn't such.
-int64_t GetProcessId() {
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- if (global)
- return global->process_id();
- return GetCurrentProcId();
-}
-
-// Finds and reuses a specific allocation or creates a new one.
-PersistentMemoryAllocator::Reference AllocateFrom(
- PersistentMemoryAllocator* allocator,
- uint32_t from_type,
- size_t size,
- uint32_t to_type) {
- PersistentMemoryAllocator::Iterator iter(allocator);
- PersistentMemoryAllocator::Reference ref;
- while ((ref = iter.GetNextOfType(from_type)) != 0) {
- DCHECK_LE(size, allocator->GetAllocSize(ref));
- // This can fail if a another thread has just taken it. It is assumed that
- // the memory is cleared during the "free" operation.
- if (allocator->ChangeType(ref, to_type, from_type, /*clear=*/false))
- return ref;
- }
-
- return allocator->Allocate(size, to_type);
-}
-
// Determines the previous aligned index.
size_t RoundDownToAlignment(size_t index, size_t alignment) {
return index & (0 - alignment);
@@ -114,43 +74,8 @@ size_t RoundUpToAlignment(size_t index, size_t alignment) {
return (index + (alignment - 1)) & (0 - alignment);
}
-// Converts "tick" timing into wall time.
-Time WallTimeFromTickTime(int64_t ticks_start, int64_t ticks, Time time_start) {
- return time_start + TimeDelta::FromInternalValue(ticks - ticks_start);
-}
-
} // namespace
-OwningProcess::OwningProcess() {}
-OwningProcess::~OwningProcess() {}
-
-void OwningProcess::Release_Initialize(int64_t pid) {
- uint32_t old_id = data_id.load(std::memory_order_acquire);
- DCHECK_EQ(0U, old_id);
- process_id = pid != 0 ? pid : GetProcessId();
- create_stamp = Time::Now().ToInternalValue();
- data_id.store(GetNextDataId(), std::memory_order_release);
-}
-
-void OwningProcess::SetOwningProcessIdForTesting(int64_t pid, int64_t stamp) {
- DCHECK_NE(0U, data_id);
- process_id = pid;
- create_stamp = stamp;
-}
-
-// static
-bool OwningProcess::GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp) {
- const OwningProcess* info = reinterpret_cast<const OwningProcess*>(memory);
- uint32_t id = info->data_id.load(std::memory_order_acquire);
- if (id == 0)
- return false;
-
- *out_id = info->process_id;
- *out_stamp = info->create_stamp;
- return id == info->data_id.load(std::memory_order_seq_cst);
-}
// It doesn't matter what is contained in this (though it will be all zeros)
// as only the address of it is important.
@@ -321,42 +246,32 @@ StringPiece ActivityUserData::TypedValue::GetStringReference() const {
return ref_value_;
}
-// These are required because std::atomic is (currently) not a POD type and
-// thus clang requires explicit out-of-line constructors and destructors even
-// when they do nothing.
ActivityUserData::ValueInfo::ValueInfo() {}
ActivityUserData::ValueInfo::ValueInfo(ValueInfo&&) = default;
ActivityUserData::ValueInfo::~ValueInfo() {}
-ActivityUserData::MemoryHeader::MemoryHeader() {}
-ActivityUserData::MemoryHeader::~MemoryHeader() {}
-ActivityUserData::FieldHeader::FieldHeader() {}
-ActivityUserData::FieldHeader::~FieldHeader() {}
-ActivityUserData::ActivityUserData() : ActivityUserData(nullptr, 0, -1) {}
+StaticAtomicSequenceNumber ActivityUserData::next_id_;
-ActivityUserData::ActivityUserData(void* memory, size_t size, int64_t pid)
+ActivityUserData::ActivityUserData(void* memory, size_t size)
: memory_(reinterpret_cast<char*>(memory)),
available_(RoundDownToAlignment(size, kMemoryAlignment)),
- header_(reinterpret_cast<MemoryHeader*>(memory)),
- orig_data_id(0),
- orig_process_id(0),
- orig_create_stamp(0) {
+ id_(reinterpret_cast<std::atomic<uint32_t>*>(memory)) {
// It's possible that no user data is being stored.
if (!memory_)
return;
- static_assert(0 == sizeof(MemoryHeader) % kMemoryAlignment, "invalid header");
- DCHECK_LT(sizeof(MemoryHeader), available_);
- if (header_->owner.data_id.load(std::memory_order_acquire) == 0)
- header_->owner.Release_Initialize(pid);
- memory_ += sizeof(MemoryHeader);
- available_ -= sizeof(MemoryHeader);
-
- // Make a copy of identifying information for later comparison.
- *const_cast<uint32_t*>(&orig_data_id) =
- header_->owner.data_id.load(std::memory_order_acquire);
- *const_cast<int64_t*>(&orig_process_id) = header_->owner.process_id;
- *const_cast<int64_t*>(&orig_create_stamp) = header_->owner.create_stamp;
+ DCHECK_LT(kMemoryAlignment, available_);
+ if (id_->load(std::memory_order_relaxed) == 0) {
+ // Generate a new ID and store it in the first 32-bit word of memory_.
+ // |id_| must be non-zero for non-sink instances.
+ uint32_t id;
+ while ((id = next_id_.GetNext()) == 0)
+ ;
+ id_->store(id, std::memory_order_relaxed);
+ DCHECK_NE(0U, id_->load(std::memory_order_relaxed));
+ }
+ memory_ += kMemoryAlignment;
+ available_ -= kMemoryAlignment;
// If there is already data present, load that. This allows the same class
// to be used for analysis through snapshots.
@@ -365,85 +280,6 @@ ActivityUserData::ActivityUserData(void* memory, size_t size, int64_t pid)
ActivityUserData::~ActivityUserData() {}
-bool ActivityUserData::CreateSnapshot(Snapshot* output_snapshot) const {
- DCHECK(output_snapshot);
- DCHECK(output_snapshot->empty());
-
- // Find any new data that may have been added by an active instance of this
- // class that is adding records.
- ImportExistingData();
-
- // Add all the values to the snapshot.
- for (const auto& entry : values_) {
- TypedValue value;
- const size_t size = entry.second.size_ptr->load(std::memory_order_acquire);
- value.type_ = entry.second.type;
- DCHECK_GE(entry.second.extent, size);
-
- switch (entry.second.type) {
- case RAW_VALUE:
- case STRING_VALUE:
- value.long_value_ =
- std::string(reinterpret_cast<char*>(entry.second.memory), size);
- break;
- case RAW_VALUE_REFERENCE:
- case STRING_VALUE_REFERENCE: {
- ReferenceRecord* ref =
- reinterpret_cast<ReferenceRecord*>(entry.second.memory);
- value.ref_value_ = StringPiece(
- reinterpret_cast<char*>(static_cast<uintptr_t>(ref->address)),
- static_cast<size_t>(ref->size));
- } break;
- case BOOL_VALUE:
- case CHAR_VALUE:
- value.short_value_ = *reinterpret_cast<char*>(entry.second.memory);
- break;
- case SIGNED_VALUE:
- case UNSIGNED_VALUE:
- value.short_value_ = *reinterpret_cast<uint64_t*>(entry.second.memory);
- break;
- case END_OF_VALUES: // Included for completeness purposes.
- NOTREACHED();
- }
- auto inserted = output_snapshot->insert(
- std::make_pair(entry.second.name.as_string(), std::move(value)));
- DCHECK(inserted.second); // True if inserted, false if existed.
- }
-
- // Another import attempt will validate that the underlying memory has not
- // been reused for another purpose. Entries added since the first import
- // will be ignored here but will be returned if another snapshot is created.
- ImportExistingData();
- if (!memory_) {
- output_snapshot->clear();
- return false;
- }
-
- // Successful snapshot.
- return true;
-}
-
-const void* ActivityUserData::GetBaseAddress() const {
- // The |memory_| pointer advances as elements are written but the |header_|
- // value is always at the start of the block so just return that.
- return header_;
-}
-
-void ActivityUserData::SetOwningProcessIdForTesting(int64_t pid,
- int64_t stamp) {
- if (!header_)
- return;
- header_->owner.SetOwningProcessIdForTesting(pid, stamp);
-}
-
-// static
-bool ActivityUserData::GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp) {
- const MemoryHeader* header = reinterpret_cast<const MemoryHeader*>(memory);
- return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp);
-}
-
void ActivityUserData::Set(StringPiece name,
ValueType type,
const void* memory,
@@ -472,13 +308,13 @@ void ActivityUserData::Set(StringPiece name,
// following field will be aligned properly.
size_t name_size = name.length();
size_t name_extent =
- RoundUpToAlignment(sizeof(FieldHeader) + name_size, kMemoryAlignment) -
- sizeof(FieldHeader);
+ RoundUpToAlignment(sizeof(Header) + name_size, kMemoryAlignment) -
+ sizeof(Header);
size_t value_extent = RoundUpToAlignment(size, kMemoryAlignment);
// The "base size" is the size of the header and (padded) string key. Stop
// now if there's not room enough for even this.
- size_t base_size = sizeof(FieldHeader) + name_extent;
+ size_t base_size = sizeof(Header) + name_extent;
if (base_size > available_)
return;
@@ -502,7 +338,7 @@ void ActivityUserData::Set(StringPiece name,
}
// Allocate a chunk of memory.
- FieldHeader* header = reinterpret_cast<FieldHeader*>(memory_);
+ Header* header = reinterpret_cast<Header*>(memory_);
memory_ += full_size;
available_ -= full_size;
@@ -512,9 +348,9 @@ void ActivityUserData::Set(StringPiece name,
DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed));
header->name_size = static_cast<uint8_t>(name_size);
header->record_size = full_size;
- char* name_memory = reinterpret_cast<char*>(header) + sizeof(FieldHeader);
+ char* name_memory = reinterpret_cast<char*>(header) + sizeof(Header);
void* value_memory =
- reinterpret_cast<char*>(header) + sizeof(FieldHeader) + name_extent;
+ reinterpret_cast<char*>(header) + sizeof(Header) + name_extent;
memcpy(name_memory, name.data(), name_size);
header->type.store(type, std::memory_order_release);
@@ -528,7 +364,7 @@ void ActivityUserData::Set(StringPiece name,
info->name = persistent_name;
info->memory = value_memory;
info->size_ptr = &header->value_size;
- info->extent = full_size - sizeof(FieldHeader) - name_extent;
+ info->extent = full_size - sizeof(Header) - name_extent;
info->type = type;
}
@@ -553,12 +389,8 @@ void ActivityUserData::SetReference(StringPiece name,
}
void ActivityUserData::ImportExistingData() const {
- // It's possible that no user data is being stored.
- if (!memory_)
- return;
-
- while (available_ > sizeof(FieldHeader)) {
- FieldHeader* header = reinterpret_cast<FieldHeader*>(memory_);
+ while (available_ > sizeof(Header)) {
+ Header* header = reinterpret_cast<Header*>(memory_);
ValueType type =
static_cast<ValueType>(header->type.load(std::memory_order_acquire));
if (type == END_OF_VALUES)
@@ -566,8 +398,8 @@ void ActivityUserData::ImportExistingData() const {
if (header->record_size > available_)
return;
- size_t value_offset = RoundUpToAlignment(
- sizeof(FieldHeader) + header->name_size, kMemoryAlignment);
+ size_t value_offset = RoundUpToAlignment(sizeof(Header) + header->name_size,
+ kMemoryAlignment);
if (header->record_size == value_offset &&
header->value_size.load(std::memory_order_relaxed) == 1) {
value_offset -= 1;
@@ -576,7 +408,7 @@ void ActivityUserData::ImportExistingData() const {
return;
ValueInfo info;
- info.name = StringPiece(memory_ + sizeof(FieldHeader), header->name_size);
+ info.name = StringPiece(memory_ + sizeof(Header), header->name_size);
info.type = type;
info.memory = memory_ + value_offset;
info.size_ptr = &header->value_size;
@@ -588,14 +420,60 @@ void ActivityUserData::ImportExistingData() const {
memory_ += header->record_size;
available_ -= header->record_size;
}
+}
+
+bool ActivityUserData::CreateSnapshot(Snapshot* output_snapshot) const {
+ DCHECK(output_snapshot);
+ DCHECK(output_snapshot->empty());
+
+ // Find any new data that may have been added by an active instance of this
+ // class that is adding records.
+ ImportExistingData();
- // Check if memory has been completely reused.
- if (header_->owner.data_id.load(std::memory_order_acquire) != orig_data_id ||
- header_->owner.process_id != orig_process_id ||
- header_->owner.create_stamp != orig_create_stamp) {
- memory_ = nullptr;
- values_.clear();
+ for (const auto& entry : values_) {
+ TypedValue value;
+ value.type_ = entry.second.type;
+ DCHECK_GE(entry.second.extent,
+ entry.second.size_ptr->load(std::memory_order_relaxed));
+
+ switch (entry.second.type) {
+ case RAW_VALUE:
+ case STRING_VALUE:
+ value.long_value_ =
+ std::string(reinterpret_cast<char*>(entry.second.memory),
+ entry.second.size_ptr->load(std::memory_order_relaxed));
+ break;
+ case RAW_VALUE_REFERENCE:
+ case STRING_VALUE_REFERENCE: {
+ ReferenceRecord* ref =
+ reinterpret_cast<ReferenceRecord*>(entry.second.memory);
+ value.ref_value_ = StringPiece(
+ reinterpret_cast<char*>(static_cast<uintptr_t>(ref->address)),
+ static_cast<size_t>(ref->size));
+ } break;
+ case BOOL_VALUE:
+ case CHAR_VALUE:
+ value.short_value_ = *reinterpret_cast<char*>(entry.second.memory);
+ break;
+ case SIGNED_VALUE:
+ case UNSIGNED_VALUE:
+ value.short_value_ = *reinterpret_cast<uint64_t*>(entry.second.memory);
+ break;
+ case END_OF_VALUES: // Included for completeness purposes.
+ NOTREACHED();
+ }
+ auto inserted = output_snapshot->insert(
+ std::make_pair(entry.second.name.as_string(), std::move(value)));
+ DCHECK(inserted.second); // True if inserted, false if existed.
}
+
+ return true;
+}
+
+const void* ActivityUserData::GetBaseAddress() {
+ // The |memory_| pointer advances as elements are written but the |id_|
+ // value is always at the start of the block so just return that.
+ return id_;
}
// This information is kept for every thread that is tracked. It is filled
@@ -607,16 +485,27 @@ struct ThreadActivityTracker::Header {
GlobalActivityTracker::kTypeIdActivityTracker;
// Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize =
- OwningProcess::kExpectedInstanceSize + Activity::kExpectedInstanceSize +
- 72;
+ static constexpr size_t kExpectedInstanceSize = 80;
+
+ // This unique number indicates a valid initialization of the memory.
+ std::atomic<uint32_t> cookie;
- // This information uniquely identifies a process.
- OwningProcess owner;
+ // The number of Activity slots (spaces that can hold an Activity) that
+ // immediately follow this structure in memory.
+ uint32_t stack_slots;
- // The thread-id (thread_ref.as_id) to which this data belongs. This number
- // is not guaranteed to mean anything but combined with the process-id from
- // OwningProcess is unique among all active trackers.
+ // The process-id and thread-id (thread_ref.as_id) to which this data belongs.
+ // These identifiers are not guaranteed to mean anything but are unique, in
+ // combination, among all active trackers. It would be nice to always have
+ // the process_id be a 64-bit value but the necessity of having it atomic
+ // (for the memory barriers it provides) limits it to the natural word size
+ // of the machine.
+#ifdef ARCH_CPU_64_BITS
+ std::atomic<int64_t> process_id;
+#else
+ std::atomic<int32_t> process_id;
+ int32_t process_id_padding;
+#endif
ThreadRef thread_ref;
// The start-time and start-ticks when the data was created. Each activity
@@ -625,19 +514,12 @@ struct ThreadActivityTracker::Header {
int64_t start_time;
int64_t start_ticks;
- // The number of Activity slots (spaces that can hold an Activity) that
- // immediately follow this structure in memory.
- uint32_t stack_slots;
-
- // Some padding to keep everything 64-bit aligned.
- uint32_t padding;
-
// The current depth of the stack. This may be greater than the number of
// slots. If the depth exceeds the number of slots, the newest entries
// won't be recorded.
std::atomic<uint32_t> current_depth;
- // A memory location used to indicate if changes have been made to the data
+ // A memory location used to indicate if changes have been made to the stack
// that would invalidate an in-progress read of its contents. The active
// tracker will zero the value whenever something gets popped from the
// stack. A monitoring tracker can write a non-zero value here, copy the
@@ -645,11 +527,7 @@ struct ThreadActivityTracker::Header {
// the contents didn't change while being copied. This can handle concurrent
// snapshot operations only if each snapshot writes a different bit (which
// is not the current implementation so no parallel snapshots allowed).
- std::atomic<uint32_t> data_unchanged;
-
- // The last "exception" activity. This can't be stored on the stack because
- // that could get popped as things unwind.
- Activity last_exception;
+ std::atomic<uint32_t> stack_unchanged;
// The name of the thread (up to a maximum length). Dynamic-length names
// are not practical since the memory has to come from the same persistent
@@ -718,16 +596,15 @@ ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size)
"ActivityData.data is not 64-bit aligned");
// Provided memory should either be completely initialized or all zeros.
- if (header_->owner.data_id.load(std::memory_order_relaxed) == 0) {
+ if (header_->cookie.load(std::memory_order_relaxed) == 0) {
// This is a new file. Double-check other fields and then initialize.
- DCHECK_EQ(0, header_->owner.process_id);
- DCHECK_EQ(0, header_->owner.create_stamp);
+ DCHECK_EQ(0, header_->process_id.load(std::memory_order_relaxed));
DCHECK_EQ(0, header_->thread_ref.as_id);
DCHECK_EQ(0, header_->start_time);
DCHECK_EQ(0, header_->start_ticks);
DCHECK_EQ(0U, header_->stack_slots);
DCHECK_EQ(0U, header_->current_depth.load(std::memory_order_relaxed));
- DCHECK_EQ(0U, header_->data_unchanged.load(std::memory_order_relaxed));
+ DCHECK_EQ(0U, header_->stack_unchanged.load(std::memory_order_relaxed));
DCHECK_EQ(0, stack_[0].time_internal);
DCHECK_EQ(0U, stack_[0].origin_address);
DCHECK_EQ(0U, stack_[0].call_stack[0]);
@@ -739,6 +616,7 @@ ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size)
header_->thread_ref.as_handle =
PlatformThread::CurrentHandle().platform_handle();
#endif
+ header_->process_id.store(GetCurrentProcId(), std::memory_order_relaxed);
header_->start_time = base::Time::Now().ToInternalValue();
header_->start_ticks = base::TimeTicks::Now().ToInternalValue();
@@ -748,7 +626,7 @@ ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size)
// This is done last so as to guarantee that everything above is "released"
// by the time this value gets written.
- header_->owner.Release_Initialize();
+ header_->cookie.store(kHeaderCookie, std::memory_order_release);
valid_ = true;
DCHECK(IsValid());
@@ -841,28 +719,40 @@ void ThreadActivityTracker::PopActivity(ActivityId id) {
// The stack has shrunk meaning that some other thread trying to copy the
// contents for reporting purposes could get bad data. That thread would
- // have written a non-zero value into |data_unchanged|; clearing it here
+ // have written a non-zero value into |stack_unchanged|; clearing it here
// will let that thread detect that something did change. This needs to
// happen after the atomic |depth| operation above so a "release" store
// is required.
- header_->data_unchanged.store(0, std::memory_order_release);
+ header_->stack_unchanged.store(0, std::memory_order_release);
}
std::unique_ptr<ActivityUserData> ThreadActivityTracker::GetUserData(
ActivityId id,
ActivityTrackerMemoryAllocator* allocator) {
- // Don't allow user data for lock acquisition as recursion may occur.
- if (stack_[id].activity_type == Activity::ACT_LOCK_ACQUIRE) {
- NOTREACHED();
- return MakeUnique<ActivityUserData>();
- }
-
// User-data is only stored for activities actually held in the stack.
- if (id >= stack_slots_)
- return MakeUnique<ActivityUserData>();
+ if (id < stack_slots_) {
+ // Don't allow user data for lock acquisition as recursion may occur.
+ if (stack_[id].activity_type == Activity::ACT_LOCK_ACQUIRE) {
+ NOTREACHED();
+ return MakeUnique<ActivityUserData>(nullptr, 0);
+ }
+
+ // Get (or reuse) a block of memory and create a real UserData object
+ // on it.
+ PersistentMemoryAllocator::Reference ref = allocator->GetObjectReference();
+ void* memory =
+ allocator->GetAsArray<char>(ref, PersistentMemoryAllocator::kSizeAny);
+ if (memory) {
+ std::unique_ptr<ActivityUserData> user_data =
+ MakeUnique<ActivityUserData>(memory, kUserDataSize);
+ stack_[id].user_data_ref = ref;
+ stack_[id].user_data_id = user_data->id();
+ return user_data;
+ }
+ }
- // Create and return a real UserData object.
- return CreateUserDataForActivity(&stack_[id], allocator);
+ // Return a dummy object that will still accept (but ignore) Set() calls.
+ return MakeUnique<ActivityUserData>(nullptr, 0);
}
bool ThreadActivityTracker::HasUserData(ActivityId id) {
@@ -880,27 +770,12 @@ void ThreadActivityTracker::ReleaseUserData(
}
}
-void ThreadActivityTracker::RecordExceptionActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data) {
- // A thread-checker creates a lock to check the thread-id which means
- // re-entry into this code if lock acquisitions are being tracked.
- DCHECK(thread_checker_.CalledOnValidThread());
-
- // Fill the reusable exception activity.
- Activity::FillFrom(&header_->last_exception, program_counter, origin, type,
- data);
-
- // The data has changed meaning that some other thread trying to copy the
- // contents for reporting purposes could get bad data.
- header_->data_unchanged.store(0, std::memory_order_relaxed);
-}
-
bool ThreadActivityTracker::IsValid() const {
- if (header_->owner.data_id.load(std::memory_order_acquire) == 0 ||
- header_->owner.process_id == 0 || header_->thread_ref.as_id == 0 ||
- header_->start_time == 0 || header_->start_ticks == 0 ||
+ if (header_->cookie.load(std::memory_order_acquire) != kHeaderCookie ||
+ header_->process_id.load(std::memory_order_relaxed) == 0 ||
+ header_->thread_ref.as_id == 0 ||
+ header_->start_time == 0 ||
+ header_->start_ticks == 0 ||
header_->stack_slots != stack_slots_ ||
header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') {
return false;
@@ -931,21 +806,20 @@ bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const {
output_snapshot->activity_stack.reserve(stack_slots_);
for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
- // Remember the data IDs to ensure nothing is replaced during the snapshot
- // operation. Use "acquire" so that all the non-atomic fields of the
- // structure are valid (at least at the current moment in time).
- const uint32_t starting_id =
- header_->owner.data_id.load(std::memory_order_acquire);
- const int64_t starting_create_stamp = header_->owner.create_stamp;
- const int64_t starting_process_id = header_->owner.process_id;
+ // Remember the process and thread IDs to ensure they aren't replaced
+ // during the snapshot operation. Use "acquire" to ensure that all the
+ // non-atomic fields of the structure are valid (at least at the current
+ // moment in time).
+ const int64_t starting_process_id =
+ header_->process_id.load(std::memory_order_acquire);
const int64_t starting_thread_id = header_->thread_ref.as_id;
- // Write a non-zero value to |data_unchanged| so it's possible to detect
+ // Write a non-zero value to |stack_unchanged| so it's possible to detect
// at the end that nothing has changed since copying the data began. A
// "cst" operation is required to ensure it occurs before everything else.
// Using "cst" memory ordering is relatively expensive but this is only
// done during analysis so doesn't directly affect the worker threads.
- header_->data_unchanged.store(1, std::memory_order_seq_cst);
+ header_->stack_unchanged.store(1, std::memory_order_seq_cst);
// Fetching the current depth also "acquires" the contents of the stack.
depth = header_->current_depth.load(std::memory_order_acquire);
@@ -957,26 +831,29 @@ bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const {
count * sizeof(Activity));
}
- // Capture the last exception.
- memcpy(&output_snapshot->last_exception, &header_->last_exception,
- sizeof(Activity));
-
- // TODO(bcwhite): Snapshot other things here.
-
// Retry if something changed during the copy. A "cst" operation ensures
// it must happen after all the above operations.
- if (!header_->data_unchanged.load(std::memory_order_seq_cst))
+ if (!header_->stack_unchanged.load(std::memory_order_seq_cst))
continue;
// Stack copied. Record it's full depth.
output_snapshot->activity_stack_depth = depth;
- // Get the general thread information.
+ // TODO(bcwhite): Snapshot other things here.
+
+ // Get the general thread information. Loading of "process_id" is guaranteed
+ // to be last so that it's possible to detect below if any content has
+ // changed while reading it. It's technically possible for a thread to end,
+ // have its data cleared, a new thread get created with the same IDs, and
+ // it perform an action which starts tracking all in the time since the
+ // ID reads above but the chance is so unlikely that it's not worth the
+ // effort and complexity of protecting against it (perhaps with an
+ // "unchanged" field like is done for the stack).
output_snapshot->thread_name =
std::string(header_->thread_name, sizeof(header_->thread_name) - 1);
- output_snapshot->create_stamp = header_->owner.create_stamp;
output_snapshot->thread_id = header_->thread_ref.as_id;
- output_snapshot->process_id = header_->owner.process_id;
+ output_snapshot->process_id =
+ header_->process_id.load(std::memory_order_seq_cst);
// All characters of the thread-name buffer were copied so as to not break
// if the trailing NUL were missing. Now limit the length if the actual
@@ -984,11 +861,9 @@ bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const {
output_snapshot->thread_name.resize(
strlen(output_snapshot->thread_name.c_str()));
- // If the data ID has changed then the tracker has exited and the memory
- // reused by a new one. Try again.
- if (header_->owner.data_id.load(std::memory_order_seq_cst) != starting_id ||
- output_snapshot->create_stamp != starting_create_stamp ||
- output_snapshot->process_id != starting_process_id ||
+ // If the process or thread ID has changed then the tracker has exited and
+ // the memory reused by a new one. Try again.
+ if (output_snapshot->process_id != starting_process_id ||
output_snapshot->thread_id != starting_thread_id) {
continue;
}
@@ -1004,14 +879,10 @@ bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const {
const int64_t start_ticks = header_->start_ticks;
for (Activity& activity : output_snapshot->activity_stack) {
activity.time_internal =
- WallTimeFromTickTime(start_ticks, activity.time_internal, start_time)
+ (start_time +
+ TimeDelta::FromInternalValue(activity.time_internal - start_ticks))
.ToInternalValue();
}
- output_snapshot->last_exception.time_internal =
- WallTimeFromTickTime(start_ticks,
- output_snapshot->last_exception.time_internal,
- start_time)
- .ToInternalValue();
// Success!
return true;
@@ -1021,48 +892,11 @@ bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const {
return false;
}
-const void* ThreadActivityTracker::GetBaseAddress() {
- return header_;
-}
-
-void ThreadActivityTracker::SetOwningProcessIdForTesting(int64_t pid,
- int64_t stamp) {
- header_->owner.SetOwningProcessIdForTesting(pid, stamp);
-}
-
-// static
-bool ThreadActivityTracker::GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp) {
- const Header* header = reinterpret_cast<const Header*>(memory);
- return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp);
-}
-
// static
size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) {
return static_cast<size_t>(stack_depth) * sizeof(Activity) + sizeof(Header);
}
-std::unique_ptr<ActivityUserData>
-ThreadActivityTracker::CreateUserDataForActivity(
- Activity* activity,
- ActivityTrackerMemoryAllocator* allocator) {
- DCHECK_EQ(0U, activity->user_data_ref);
-
- PersistentMemoryAllocator::Reference ref = allocator->GetObjectReference();
- void* memory = allocator->GetAsArray<char>(ref, kUserDataSize);
- if (memory) {
- std::unique_ptr<ActivityUserData> user_data =
- MakeUnique<ActivityUserData>(memory, kUserDataSize);
- activity->user_data_ref = ref;
- activity->user_data_id = user_data->id();
- return user_data;
- }
-
- // Return a dummy object that will still accept (but ignore) Set() calls.
- return MakeUnique<ActivityUserData>();
-}
-
// The instantiation of the GlobalActivityTracker object.
// The object held here will obviously not be destructed at process exit
// but that's best since PersistentMemoryAllocator objects (that underlie
@@ -1145,9 +979,6 @@ bool GlobalActivityTracker::ModuleInfoRecord::EncodeFrom(
pickle_size = pickler.size();
changes.store(0, std::memory_order_relaxed);
- // Initialize the owner info.
- owner.Release_Initialize();
-
// Now set those fields that can change.
return UpdateFrom(info);
}
@@ -1216,23 +1047,21 @@ ActivityUserData& GlobalActivityTracker::ScopedThreadActivity::user_data() {
user_data_ =
tracker_->GetUserData(activity_id_, &global->user_data_allocator_);
} else {
- user_data_ = MakeUnique<ActivityUserData>();
+ user_data_ = MakeUnique<ActivityUserData>(nullptr, 0);
}
}
return *user_data_;
}
-GlobalActivityTracker::ThreadSafeUserData::ThreadSafeUserData(void* memory,
- size_t size,
- int64_t pid)
- : ActivityUserData(memory, size, pid) {}
+GlobalActivityTracker::GlobalUserData::GlobalUserData(void* memory, size_t size)
+ : ActivityUserData(memory, size) {}
-GlobalActivityTracker::ThreadSafeUserData::~ThreadSafeUserData() {}
+GlobalActivityTracker::GlobalUserData::~GlobalUserData() {}
-void GlobalActivityTracker::ThreadSafeUserData::Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) {
+void GlobalActivityTracker::GlobalUserData::Set(StringPiece name,
+ ValueType type,
+ const void* memory,
+ size_t size) {
AutoLock lock(data_lock_);
ActivityUserData::Set(name, type, memory, size);
}
@@ -1255,11 +1084,10 @@ GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() {
void GlobalActivityTracker::CreateWithAllocator(
std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id) {
+ int stack_depth) {
// There's no need to do anything with the result. It is self-managing.
GlobalActivityTracker* global_tracker =
- new GlobalActivityTracker(std::move(allocator), stack_depth, process_id);
+ new GlobalActivityTracker(std::move(allocator), stack_depth);
// Create a tracker for this thread since it is known.
global_tracker->CreateTrackerForCurrentThread();
}
@@ -1285,7 +1113,7 @@ void GlobalActivityTracker::CreateWithFile(const FilePath& file_path,
DCHECK(success);
CreateWithAllocator(MakeUnique<FilePersistentMemoryAllocator>(
std::move(mapped_file), size, id, name, false),
- stack_depth, 0);
+ stack_depth);
}
#endif // !defined(OS_NACL)
@@ -1293,37 +1121,11 @@ void GlobalActivityTracker::CreateWithFile(const FilePath& file_path,
void GlobalActivityTracker::CreateWithLocalMemory(size_t size,
uint64_t id,
StringPiece name,
- int stack_depth,
- int64_t process_id) {
+ int stack_depth) {
CreateWithAllocator(
- MakeUnique<LocalPersistentMemoryAllocator>(size, id, name), stack_depth,
- process_id);
+ MakeUnique<LocalPersistentMemoryAllocator>(size, id, name), stack_depth);
}
-// static
-void GlobalActivityTracker::SetForTesting(
- std::unique_ptr<GlobalActivityTracker> tracker) {
- CHECK(!subtle::NoBarrier_Load(&g_tracker_));
- subtle::Release_Store(&g_tracker_,
- reinterpret_cast<uintptr_t>(tracker.release()));
-}
-
-// static
-std::unique_ptr<GlobalActivityTracker>
-GlobalActivityTracker::ReleaseForTesting() {
- GlobalActivityTracker* tracker = Get();
- if (!tracker)
- return nullptr;
-
- // Thread trackers assume that the global tracker is present for some
- // operations so ensure that there aren't any.
- tracker->ReleaseTrackerForCurrentThreadForTesting();
- DCHECK_EQ(0, tracker->thread_tracker_count_.load(std::memory_order_relaxed));
-
- subtle::Release_Store(&g_tracker_, 0);
- return WrapUnique(tracker);
-};
-
ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() {
DCHECK(!this_thread_tracker_.Get());
@@ -1380,181 +1182,8 @@ ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() {
void GlobalActivityTracker::ReleaseTrackerForCurrentThreadForTesting() {
ThreadActivityTracker* tracker =
reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get());
- if (tracker) {
- this_thread_tracker_.Set(nullptr);
+ if (tracker)
delete tracker;
- }
-}
-
-void GlobalActivityTracker::SetBackgroundTaskRunner(
- const scoped_refptr<TaskRunner>& runner) {
- AutoLock lock(global_tracker_lock_);
- background_task_runner_ = runner;
-}
-
-void GlobalActivityTracker::SetProcessExitCallback(
- ProcessExitCallback callback) {
- AutoLock lock(global_tracker_lock_);
- process_exit_callback_ = callback;
-}
-
-void GlobalActivityTracker::RecordProcessLaunch(
- ProcessId process_id,
- const FilePath::StringType& cmd) {
- const int64_t pid = process_id;
- DCHECK_NE(GetProcessId(), pid);
- DCHECK_NE(0, pid);
-
- base::AutoLock lock(global_tracker_lock_);
- if (base::ContainsKey(known_processes_, pid)) {
- // TODO(bcwhite): Measure this in UMA.
- NOTREACHED() << "Process #" << process_id
- << " was previously recorded as \"launched\""
- << " with no corresponding exit.";
- known_processes_.erase(pid);
- }
-
-#if defined(OS_WIN)
- known_processes_.insert(std::make_pair(pid, UTF16ToUTF8(cmd)));
-#else
- known_processes_.insert(std::make_pair(pid, cmd));
-#endif
-}
-
-void GlobalActivityTracker::RecordProcessLaunch(
- ProcessId process_id,
- const FilePath::StringType& exe,
- const FilePath::StringType& args) {
- const int64_t pid = process_id;
- if (exe.find(FILE_PATH_LITERAL(" "))) {
- RecordProcessLaunch(pid, FilePath::StringType(FILE_PATH_LITERAL("\"")) +
- exe + FILE_PATH_LITERAL("\" ") + args);
- } else {
- RecordProcessLaunch(pid, exe + FILE_PATH_LITERAL(' ') + args);
- }
-}
-
-void GlobalActivityTracker::RecordProcessExit(ProcessId process_id,
- int exit_code) {
- const int64_t pid = process_id;
- DCHECK_NE(GetProcessId(), pid);
- DCHECK_NE(0, pid);
-
- scoped_refptr<TaskRunner> task_runner;
- std::string command_line;
- {
- base::AutoLock lock(global_tracker_lock_);
- task_runner = background_task_runner_;
- auto found = known_processes_.find(pid);
- if (found != known_processes_.end()) {
- command_line = std::move(found->second);
- known_processes_.erase(found);
- } else {
- DLOG(ERROR) << "Recording exit of unknown process #" << process_id;
- }
- }
-
- // Use the current time to differentiate the process that just exited
- // from any that might be created in the future with the same ID.
- int64_t now_stamp = Time::Now().ToInternalValue();
-
- // The persistent allocator is thread-safe so run the iteration and
- // adjustments on a worker thread if one was provided.
- if (task_runner && !task_runner->RunsTasksOnCurrentThread()) {
- task_runner->PostTask(
- FROM_HERE,
- Bind(&GlobalActivityTracker::CleanupAfterProcess, Unretained(this), pid,
- now_stamp, exit_code, Passed(&command_line)));
- return;
- }
-
- CleanupAfterProcess(pid, now_stamp, exit_code, std::move(command_line));
-}
-
-void GlobalActivityTracker::SetProcessPhase(ProcessPhase phase) {
- process_data().SetInt(kProcessPhaseDataKey, phase);
-}
-
-void GlobalActivityTracker::CleanupAfterProcess(int64_t process_id,
- int64_t exit_stamp,
- int exit_code,
- std::string&& command_line) {
- // The process may not have exited cleanly so its necessary to go through
- // all the data structures it may have allocated in the persistent memory
- // segment and mark them as "released". This will allow them to be reused
- // later on.
-
- PersistentMemoryAllocator::Iterator iter(allocator_.get());
- PersistentMemoryAllocator::Reference ref;
-
- ProcessExitCallback process_exit_callback;
- {
- AutoLock lock(global_tracker_lock_);
- process_exit_callback = process_exit_callback_;
- }
- if (process_exit_callback) {
- // Find the processes user-data record so the process phase can be passed
- // to the callback.
- ActivityUserData::Snapshot process_data_snapshot;
- while ((ref = iter.GetNextOfType(kTypeIdProcessDataRecord)) != 0) {
- const void* memory = allocator_->GetAsArray<char>(
- ref, kTypeIdProcessDataRecord, PersistentMemoryAllocator::kSizeAny);
- int64_t found_id;
- int64_t create_stamp;
- if (ActivityUserData::GetOwningProcessId(memory, &found_id,
- &create_stamp)) {
- if (found_id == process_id && create_stamp < exit_stamp) {
- const ActivityUserData process_data(const_cast<void*>(memory),
- allocator_->GetAllocSize(ref));
- process_data.CreateSnapshot(&process_data_snapshot);
- break; // No need to look for any others.
- }
- }
- }
- iter.Reset(); // So it starts anew when used below.
-
- // Record the process's phase at exit so callback doesn't need to go
- // searching based on a private key value.
- ProcessPhase exit_phase = PROCESS_PHASE_UNKNOWN;
- auto phase = process_data_snapshot.find(kProcessPhaseDataKey);
- if (phase != process_data_snapshot.end())
- exit_phase = static_cast<ProcessPhase>(phase->second.GetInt());
-
- // Perform the callback.
- process_exit_callback.Run(process_id, exit_stamp, exit_code, exit_phase,
- std::move(command_line),
- std::move(process_data_snapshot));
- }
-
- // Find all allocations associated with the exited process and free them.
- uint32_t type;
- while ((ref = iter.GetNext(&type)) != 0) {
- switch (type) {
- case kTypeIdActivityTracker:
- case kTypeIdUserDataRecord:
- case kTypeIdProcessDataRecord:
- case ModuleInfoRecord::kPersistentTypeId: {
- const void* memory = allocator_->GetAsArray<char>(
- ref, type, PersistentMemoryAllocator::kSizeAny);
- int64_t found_id;
- int64_t create_stamp;
-
- // By convention, the OwningProcess structure is always the first
- // field of the structure so there's no need to handle all the
- // cases separately.
- if (OwningProcess::GetOwningProcessId(memory, &found_id,
- &create_stamp)) {
- // Only change the type to be "free" if the process ID matches and
- // the creation time is before the exit time (so PID re-use doesn't
- // cause the erasure of something that is in-use). Memory is cleared
- // here, rather than when it's needed, so as to limit the impact at
- // that critical time.
- if (found_id == process_id && create_stamp < exit_stamp)
- allocator_->ChangeType(ref, ~type, type, /*clear=*/true);
- }
- } break;
- }
- }
}
void GlobalActivityTracker::RecordLogMessage(StringPiece message) {
@@ -1604,11 +1233,9 @@ void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name,
GlobalActivityTracker::GlobalActivityTracker(
std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id)
+ int stack_depth)
: allocator_(std::move(allocator)),
stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)),
- process_id_(process_id == 0 ? GetCurrentProcId() : process_id),
this_thread_tracker_(&OnTLSDestroy),
thread_tracker_count_(0),
thread_tracker_allocator_(allocator_.get(),
@@ -1622,38 +1249,25 @@ GlobalActivityTracker::GlobalActivityTracker(
kTypeIdUserDataRecordFree,
kUserDataSize,
kCachedUserDataMemories,
- /*make_iterable=*/true),
- process_data_(allocator_->GetAsArray<char>(
- AllocateFrom(allocator_.get(),
- kTypeIdProcessDataRecordFree,
- kProcessDataSize,
- kTypeIdProcessDataRecord),
- kTypeIdProcessDataRecord,
- kProcessDataSize),
- kProcessDataSize,
- process_id_),
+ /*make_iterable=*/false),
global_data_(
allocator_->GetAsArray<char>(
allocator_->Allocate(kGlobalDataSize, kTypeIdGlobalDataRecord),
kTypeIdGlobalDataRecord,
- kGlobalDataSize),
- kGlobalDataSize,
- process_id_) {
- DCHECK_NE(0, process_id_);
+ PersistentMemoryAllocator::kSizeAny),
+ kGlobalDataSize) {
+ // Ensure the passed memory is valid and empty (iterator finds nothing).
+ uint32_t type;
+ DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type));
// Ensure that there is no other global object and then make this one such.
DCHECK(!g_tracker_);
subtle::Release_Store(&g_tracker_, reinterpret_cast<uintptr_t>(this));
- // The data records must be iterable in order to be found by an analyzer.
- allocator_->MakeIterable(allocator_->GetAsReference(
- process_data_.GetBaseAddress(), kTypeIdProcessDataRecord));
+ // The global records must be iterable in order to be found by an analyzer.
allocator_->MakeIterable(allocator_->GetAsReference(
global_data_.GetBaseAddress(), kTypeIdGlobalDataRecord));
- // Note that this process has launched.
- SetProcessPhase(PROCESS_LAUNCHED);
-
// Fetch and record all activated field trials.
FieldTrial::ActiveGroups active_groups;
FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
@@ -1662,7 +1276,7 @@ GlobalActivityTracker::GlobalActivityTracker(
}
GlobalActivityTracker::~GlobalActivityTracker() {
- DCHECK(Get() == nullptr || Get() == this);
+ DCHECK_EQ(Get(), this);
DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed));
subtle::Release_Store(&g_tracker_, 0);
}
@@ -1683,23 +1297,6 @@ void GlobalActivityTracker::ReturnTrackerMemory(
thread_tracker_allocator_.ReleaseObjectReference(mem_reference);
}
-void GlobalActivityTracker::RecordExceptionImpl(const void* pc,
- const void* origin,
- uint32_t code) {
- // Get an existing tracker for this thread. It's not possible to create
- // one at this point because such would involve memory allocations and
- // other potentially complex operations that can cause failures if done
- // within an exception handler. In most cases various operations will
- // have already created the tracker so this shouldn't generally be a
- // problem.
- ThreadActivityTracker* tracker = GetTrackerForCurrentThread();
- if (!tracker)
- return;
-
- tracker->RecordExceptionActivity(pc, origin, Activity::ACT_EXCEPTION,
- ActivityData::ForException(code));
-}
-
// static
void GlobalActivityTracker::OnTLSDestroy(void* value) {
delete reinterpret_cast<ManagedActivityTracker*>(value);
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
index c8cf1e972e..719a31865c 100644
--- a/base/debug/activity_tracker.h
+++ b/base/debug/activity_tracker.h
@@ -23,15 +23,12 @@
#include "base/atomicops.h"
#include "base/base_export.h"
-#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/location.h"
#include "base/metrics/persistent_memory_allocator.h"
-#include "base/process/process_handle.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_local_storage.h"
@@ -44,6 +41,7 @@ class FilePath;
class Lock;
class PlatformThreadHandle;
class Process;
+class StaticAtomicSequenceNumber;
class WaitableEvent;
namespace debug {
@@ -58,48 +56,11 @@ enum : int {
kActivityCallStackSize = 10,
};
-// A class for keeping all information needed to verify that a structure is
-// associated with a given process.
-struct OwningProcess {
- OwningProcess();
- ~OwningProcess();
-
- // Initializes structure with the current process id and the current time.
- // These can uniquely identify a process. A unique non-zero data_id will be
- // set making it possible to tell using atomic reads if the data has changed.
- void Release_Initialize(int64_t pid = 0);
-
- // Explicitly sets the process ID.
- void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp);
-
- // Gets the associated process ID, in native form, and the creation timestamp
- // from memory without loading the entire structure for analysis. This will
- // return false if no valid process ID is available.
- static bool GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp);
-
- // SHA1(base::debug::OwningProcess): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0xB1179672 + 1;
-
- // Expected size for 32/64-bit check by PersistentMemoryAllocator.
- static constexpr size_t kExpectedInstanceSize = 24;
-
- std::atomic<uint32_t> data_id;
- uint32_t padding;
- int64_t process_id;
- int64_t create_stamp;
-};
-
// The data associated with an activity is dependent upon the activity type.
// This union defines all of the various fields. All fields must be explicitly
// sized types to ensure no interoperability problems between 32-bit and
// 64-bit systems.
union ActivityData {
- // Expected size for 32/64-bit check.
- // TODO(bcwhite): VC2015 doesn't allow statics in unions. Fix when it does.
- // static constexpr size_t kExpectedInstanceSize = 8;
-
// Generic activities don't have any defined structure.
struct {
uint32_t id; // An arbitrary identifier used for association.
@@ -120,9 +81,6 @@ union ActivityData {
struct {
int64_t process_id; // A unique identifier for a process.
} process;
- struct {
- uint32_t code; // An "exception code" number.
- } exception;
// These methods create an ActivityData object from the appropriate
// parameters. Objects of this type should always be created this way to
@@ -168,12 +126,6 @@ union ActivityData {
data.process.process_id = id;
return data;
}
-
- static ActivityData ForException(const uint32_t code) {
- ActivityData data;
- data.exception.code = code;
- return data;
- }
};
// A "null" activity-data that can be passed to indicate "do not change".
@@ -285,9 +237,6 @@ struct Activity {
ACT_PROCESS_START = ACT_PROCESS,
ACT_PROCESS_WAIT,
- // Exception activities indicate the occurence of something unexpected.
- ACT_EXCEPTION = 14 << 4,
-
// Generic activities are user defined and can be anything.
ACT_GENERIC = 15 << 4,
@@ -344,9 +293,7 @@ struct Activity {
// This class manages arbitrary user data that can be associated with activities
// done by a thread by supporting key/value pairs of any type. This can provide
// additional information during debugging. It is also used to store arbitrary
-// global data. All updates must be done from the same thread though other
-// threads can read it concurrently if they create new objects using the same
-// memory.
+// global data. All updates must be done from the same thread.
class BASE_EXPORT ActivityUserData {
public:
// List of known value type. REFERENCE types must immediately follow the non-
@@ -393,7 +340,7 @@ class BASE_EXPORT ActivityUserData {
private:
friend class ActivityUserData;
- ValueType type_ = END_OF_VALUES;
+ ValueType type_;
uint64_t short_value_; // Used to hold copy of numbers, etc.
std::string long_value_; // Used to hold copy of raw/string data.
StringPiece ref_value_; // Used to hold reference to external data.
@@ -401,17 +348,14 @@ class BASE_EXPORT ActivityUserData {
using Snapshot = std::map<std::string, TypedValue>;
- // Initialize the object either as a "sink" that just accepts and discards
- // data or an active one that writes to a given (zeroed) memory block.
- ActivityUserData();
- ActivityUserData(void* memory, size_t size, int64_t pid = 0);
+ ActivityUserData(void* memory, size_t size);
virtual ~ActivityUserData();
// Gets the unique ID number for this user data. If this changes then the
// contents have been overwritten by another thread. The return value is
// always non-zero unless it's actually just a data "sink".
uint32_t id() const {
- return header_ ? header_->owner.data_id.load(std::memory_order_relaxed) : 0;
+ return memory_ ? id_->load(std::memory_order_relaxed) : 0;
}
// Writes a |value| (as part of a key/value pair) that will be included with
@@ -459,23 +403,13 @@ class BASE_EXPORT ActivityUserData {
// Creates a snapshot of the key/value pairs contained within. The returned
// data will be fixed, independent of whatever changes afterward. There is
- // some protection against concurrent modification. This will return false
- // if the data is invalid or if a complete overwrite of the contents is
- // detected.
+ // protection against concurrent modification of the values but no protection
+ // against a complete overwrite of the contents; the caller must ensure that
+ // the memory segment is not going to be re-initialized while this runs.
bool CreateSnapshot(Snapshot* output_snapshot) const;
// Gets the base memory address used for storing data.
- const void* GetBaseAddress() const;
-
- // Explicitly sets the process ID.
- void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp);
-
- // Gets the associated process ID, in native form, and the creation timestamp
- // from tracker memory without loading the entire structure for analysis. This
- // will return false if no valid process ID is available.
- static bool GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp);
+ const void* GetBaseAddress();
protected:
virtual void Set(StringPiece name,
@@ -488,31 +422,20 @@ class BASE_EXPORT ActivityUserData {
enum : size_t { kMemoryAlignment = sizeof(uint64_t) };
- // A structure that defines the structure header in memory.
- struct MemoryHeader {
- MemoryHeader();
- ~MemoryHeader();
-
- OwningProcess owner; // Information about the creating process.
+ // A structure used to reference data held outside of persistent memory.
+ struct ReferenceRecord {
+ uint64_t address;
+ uint64_t size;
};
// Header to a key/value record held in persistent memory.
- struct FieldHeader {
- FieldHeader();
- ~FieldHeader();
-
+ struct Header {
std::atomic<uint8_t> type; // Encoded ValueType
uint8_t name_size; // Length of "name" key.
std::atomic<uint16_t> value_size; // Actual size of of the stored value.
uint16_t record_size; // Total storage of name, value, header.
};
- // A structure used to reference data held outside of persistent memory.
- struct ReferenceRecord {
- uint64_t address;
- uint64_t size;
- };
-
// This record is used to hold known value is a map so that they can be
// found and overwritten later.
struct ValueInfo {
@@ -533,10 +456,7 @@ class BASE_EXPORT ActivityUserData {
size_t size);
// Loads any data already in the memory segment. This allows for accessing
- // records created previously. If this detects that the underlying data has
- // gone away (cleared by another thread/process), it will invalidate all the
- // data in this object and turn it into simple "sink" with no values to
- // return.
+ // records created previously.
void ImportExistingData() const;
// A map of all the values within the memory block, keyed by name for quick
@@ -550,14 +470,12 @@ class BASE_EXPORT ActivityUserData {
mutable char* memory_;
mutable size_t available_;
- // A pointer to the memory header for this instance.
- MemoryHeader* const header_;
+ // A pointer to the unique ID for this instance.
+ std::atomic<uint32_t>* const id_;
- // These hold values used when initially creating the object. They are
- // compared against current header values to check for outside changes.
- const uint32_t orig_data_id;
- const int64_t orig_process_id;
- const int64_t orig_create_stamp;
+ // This ID is used to create unique indentifiers for user data so that it's
+ // possible to tell if the information has been overwritten.
+ static StaticAtomicSequenceNumber next_id_;
DISALLOW_COPY_AND_ASSIGN(ActivityUserData);
};
@@ -593,9 +511,6 @@ class BASE_EXPORT ThreadActivityTracker {
// truncated due to internal length limitations.
std::string thread_name;
- // The timestamp at which this process was created.
- int64_t create_stamp;
-
// The process and thread IDs. These values have no meaning other than
// they uniquely identify a running process and a running thread within
// that process. Thread-IDs can be re-used across different processes
@@ -610,9 +525,6 @@ class BASE_EXPORT ThreadActivityTracker {
// The current total depth of the activity stack, including those later
// entries not recorded in the |activity_stack| vector.
uint32_t activity_stack_depth = 0;
-
- // The last recorded "exception" activity.
- Activity last_exception;
};
// This is the base class for having the compiler manage an activity on the
@@ -696,12 +608,6 @@ class BASE_EXPORT ThreadActivityTracker {
void ReleaseUserData(ActivityId id,
ActivityTrackerMemoryAllocator* allocator);
- // Save an exception. |origin| is the location of the exception.
- void RecordExceptionActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data);
-
// Returns whether the current data is valid or not. It is not valid if
// corruption has been detected in the header or other data structures.
bool IsValid() const;
@@ -712,19 +618,6 @@ class BASE_EXPORT ThreadActivityTracker {
// implementation does not support concurrent snapshot operations.
bool CreateSnapshot(Snapshot* output_snapshot) const;
- // Gets the base memory address used for storing data.
- const void* GetBaseAddress();
-
- // Explicitly sets the process ID.
- void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp);
-
- // Gets the associated process ID, in native form, and the creation timestamp
- // from tracker memory without loading the entire structure for analysis. This
- // will return false if no valid process ID is available.
- static bool GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp);
-
// Calculates the memory size required for a given stack depth, including
// the internal header structure for the stack.
static size_t SizeForStackDepth(int stack_depth);
@@ -732,10 +625,6 @@ class BASE_EXPORT ThreadActivityTracker {
private:
friend class ActivityTrackerTest;
- std::unique_ptr<ActivityUserData> CreateUserDataForActivity(
- Activity* activity,
- ActivityTrackerMemoryAllocator* allocator);
-
Header* const header_; // Pointer to the Header structure.
Activity* const stack_; // The stack of activities.
const uint32_t stack_slots_; // The total number of stack slots.
@@ -760,45 +649,15 @@ class BASE_EXPORT GlobalActivityTracker {
// will be safely ignored. These are public so that an external process
// can recognize records of this type within an allocator.
enum : uint32_t {
- kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4
- kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3
+ kTypeIdActivityTracker = 0x5D7381AF + 3, // SHA1(ActivityTracker) v3
+ kTypeIdUserDataRecord = 0x615EDDD7 + 2, // SHA1(UserDataRecord) v2
kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
- kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,
- kTypeIdGlobalDataRecord = kTypeIdUserDataRecord + 0x200,
+ kTypeIdGlobalDataRecord = kTypeIdUserDataRecord + 1000,
kTypeIdActivityTrackerFree = ~kTypeIdActivityTracker,
kTypeIdUserDataRecordFree = ~kTypeIdUserDataRecord,
- kTypeIdProcessDataRecordFree = ~kTypeIdProcessDataRecord,
- };
-
- // An enumeration of common process life stages. All entries are given an
- // explicit number so they are known and remain constant; this allows for
- // cross-version analysis either locally or on a server.
- enum ProcessPhase : int {
- // The phases are generic and may have meaning to the tracker.
- PROCESS_PHASE_UNKNOWN = 0,
- PROCESS_LAUNCHED = 1,
- PROCESS_LAUNCH_FAILED = 2,
- PROCESS_EXITED_CLEANLY = 10,
- PROCESS_EXITED_WITH_CODE = 11,
-
- // Add here whatever is useful for analysis.
- PROCESS_SHUTDOWN_STARTED = 100,
- PROCESS_MAIN_LOOP_STARTED = 101,
};
- // A callback made when a process exits to allow immediate analysis of its
- // data. Note that the system may reuse the |process_id| so when fetching
- // records it's important to ensure that what is returned was created before
- // the |exit_stamp|. Movement of |process_data| information is allowed.
- using ProcessExitCallback =
- Callback<void(int64_t process_id,
- int64_t exit_stamp,
- int exit_code,
- ProcessPhase exit_phase,
- std::string&& command_line,
- ActivityUserData::Snapshot&& process_data)>;
-
// This structure contains information about a loaded module, as shown to
// users of the tracker.
struct BASE_EXPORT ModuleInfo {
@@ -869,12 +728,9 @@ class BASE_EXPORT GlobalActivityTracker {
// Creates a global tracker using a given persistent-memory |allocator| and
// providing the given |stack_depth| to each thread tracker it manages. The
// created object is activated so tracking will begin immediately upon return.
- // The |process_id| can be zero to get it from the OS but is taken for testing
- // purposes.
static void CreateWithAllocator(
std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id);
+ int stack_depth);
#if !defined(OS_NACL)
// Like above but internally creates an allocator around a disk file with
@@ -889,13 +745,11 @@ class BASE_EXPORT GlobalActivityTracker {
#endif // !defined(OS_NACL)
// Like above but internally creates an allocator using local heap memory of
- // the specified size. This is used primarily for unit tests. The |process_id|
- // can be zero to get it from the OS but is taken for testing purposes.
+ // the specified size. This is used primarily for unit tests.
static void CreateWithLocalMemory(size_t size,
uint64_t id,
StringPiece name,
- int stack_depth,
- int64_t process_id);
+ int stack_depth);
// Gets the global activity-tracker or null if none exists.
static GlobalActivityTracker* Get() {
@@ -903,15 +757,6 @@ class BASE_EXPORT GlobalActivityTracker {
subtle::Acquire_Load(&g_tracker_));
}
- // Sets the global activity-tracker for testing purposes.
- static void SetForTesting(std::unique_ptr<GlobalActivityTracker> tracker);
-
- // This access to the persistent allocator is only for testing; it extracts
- // the global tracker completely. All tracked threads must exit before
- // calling this. Tracking for the current thread will be automatically
- // stopped.
- static std::unique_ptr<GlobalActivityTracker> ReleaseForTesting();
-
// Convenience method for determining if a global tracker is active.
static bool IsEnabled() { return Get() != nullptr; }
@@ -944,50 +789,6 @@ class BASE_EXPORT GlobalActivityTracker {
// Releases the activity-tracker for the current thread (for testing only).
void ReleaseTrackerForCurrentThreadForTesting();
- // Sets a task-runner that can be used for background work.
- void SetBackgroundTaskRunner(const scoped_refptr<TaskRunner>& runner);
-
- // Sets an optional callback to be called when a process exits.
- void SetProcessExitCallback(ProcessExitCallback callback);
-
- // Manages process lifetimes. These are called by the process that launched
- // and reaped the subprocess, not the subprocess itself. If it is expensive
- // to generate the parameters, Get() the global tracker and call these
- // conditionally rather than using the static versions.
- void RecordProcessLaunch(ProcessId process_id,
- const FilePath::StringType& cmd);
- void RecordProcessLaunch(ProcessId process_id,
- const FilePath::StringType& exe,
- const FilePath::StringType& args);
- void RecordProcessExit(ProcessId process_id, int exit_code);
- static void RecordProcessLaunchIfEnabled(ProcessId process_id,
- const FilePath::StringType& cmd) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordProcessLaunch(process_id, cmd);
- }
- static void RecordProcessLaunchIfEnabled(ProcessId process_id,
- const FilePath::StringType& exe,
- const FilePath::StringType& args) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordProcessLaunch(process_id, exe, args);
- }
- static void RecordProcessExitIfEnabled(ProcessId process_id, int exit_code) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordProcessExit(process_id, exit_code);
- }
-
- // Sets the "phase" of the current process, useful for knowing what it was
- // doing when it last reported.
- void SetProcessPhase(ProcessPhase phase);
- static void SetProcessPhaseIfEnabled(ProcessPhase phase) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->SetProcessPhase(phase);
- }
-
// Records a log message. The current implementation does NOT recycle these
// only store critical messages such as FATAL ones.
void RecordLogMessage(StringPiece message);
@@ -1017,23 +818,7 @@ class BASE_EXPORT GlobalActivityTracker {
tracker->RecordFieldTrial(trial_name, group_name);
}
- // Record exception information for the current thread.
- ALWAYS_INLINE
- void RecordException(const void* origin, uint32_t code) {
- return RecordExceptionImpl(::tracked_objects::GetProgramCounter(), origin,
- code);
- }
-
- // Gets the process ID used for tracking. This is typically the same as what
- // the OS thinks is the current process but can be overridden for testing.
- int64_t process_id() { return process_id_; };
-
- // Accesses the process data record for storing arbitrary key/value pairs.
- // Updates to this are thread-safe.
- ActivityUserData& process_data() { return process_data_; }
-
// Accesses the global data record for storing arbitrary key/value pairs.
- // Updates to this are thread-safe.
ActivityUserData& global_data() { return global_data_; }
private:
@@ -1052,10 +837,10 @@ class BASE_EXPORT GlobalActivityTracker {
// A wrapper around ActivityUserData that is thread-safe and thus can be used
// in the global scope without the requirement of being called from only one
// thread.
- class ThreadSafeUserData : public ActivityUserData {
+ class GlobalUserData : public ActivityUserData {
public:
- ThreadSafeUserData(void* memory, size_t size, int64_t pid = 0);
- ~ThreadSafeUserData() override;
+ GlobalUserData(void* memory, size_t size);
+ ~GlobalUserData() override;
private:
void Set(StringPiece name,
@@ -1065,7 +850,7 @@ class BASE_EXPORT GlobalActivityTracker {
Lock data_lock_;
- DISALLOW_COPY_AND_ASSIGN(ThreadSafeUserData);
+ DISALLOW_COPY_AND_ASSIGN(GlobalUserData);
};
// State of a module as stored in persistent memory. This supports a single
@@ -1077,8 +862,7 @@ class BASE_EXPORT GlobalActivityTracker {
static constexpr uint32_t kPersistentTypeId = 0x05DB5F41 + 1;
// Expected size for 32/64-bit check by PersistentMemoryAllocator.
- static constexpr size_t kExpectedInstanceSize =
- OwningProcess::kExpectedInstanceSize + 56;
+ static constexpr size_t kExpectedInstanceSize = 56;
// The atomic unfortunately makes this a "complex" class on some compilers
// and thus requires an out-of-line constructor & destructor even though
@@ -1086,7 +870,6 @@ class BASE_EXPORT GlobalActivityTracker {
ModuleInfoRecord();
~ModuleInfoRecord();
- OwningProcess owner; // The process that created this record.
uint64_t address; // The base address of the module.
uint64_t load_time; // Time of last load/unload.
uint64_t size; // The size of the module in bytes.
@@ -1138,30 +921,18 @@ class BASE_EXPORT GlobalActivityTracker {
// Creates a global tracker using a given persistent-memory |allocator| and
// providing the given |stack_depth| to each thread tracker it manages. The
// created object is activated so tracking has already started upon return.
- // The |process_id| can be zero to get it from the OS but is taken for testing
- // purposes.
GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id);
+ int stack_depth);
// Returns the memory used by an activity-tracker managed by this class.
// It is called during the destruction of a ManagedActivityTracker object.
void ReturnTrackerMemory(ManagedActivityTracker* tracker);
- // Records exception information.
- void RecordExceptionImpl(const void* pc, const void* origin, uint32_t code);
-
// Releases the activity-tracker associcated with thread. It is called
// automatically when a thread is joined and thus there is nothing more to
// be tracked. |value| is a pointer to a ManagedActivityTracker.
static void OnTLSDestroy(void* value);
- // Does process-exit work. This can be run on any thread.
- void CleanupAfterProcess(int64_t process_id,
- int64_t exit_stamp,
- int exit_code,
- std::string&& command_line);
-
// The persistent-memory allocator from which the memory for all trackers
// is taken.
std::unique_ptr<PersistentMemoryAllocator> allocator_;
@@ -1170,10 +941,6 @@ class BASE_EXPORT GlobalActivityTracker {
// provide the stack-depth requested during construction.
const size_t stack_memory_size_;
- // The process-id of the current process. This is kept as a member variable,
- // defined during initialization, for testing purposes.
- const int64_t process_id_;
-
// The activity tracker for the currently executing thread.
base::ThreadLocalStorage::Slot this_thread_tracker_;
@@ -1188,9 +955,9 @@ class BASE_EXPORT GlobalActivityTracker {
ActivityTrackerMemoryAllocator user_data_allocator_;
base::Lock user_data_allocator_lock_;
- // An object for holding arbitrary key value pairs with thread-safe access.
- ThreadSafeUserData process_data_;
- ThreadSafeUserData global_data_;
+ // An object for holding global arbitrary key value pairs. Values must always
+ // be written from the main UI thread.
+ GlobalUserData global_data_;
// A map of global module information, keyed by module path.
std::map<const std::string, ModuleInfoRecord*> modules_;
@@ -1199,21 +966,6 @@ class BASE_EXPORT GlobalActivityTracker {
// The active global activity tracker.
static subtle::AtomicWord g_tracker_;
- // A lock that is used to protect access to the following fields.
- base::Lock global_tracker_lock_;
-
- // The collection of processes being tracked and their command-lines.
- std::map<int64_t, std::string> known_processes_;
-
- // A task-runner that can be used for doing background processing.
- scoped_refptr<TaskRunner> background_task_runner_;
-
- // A callback performed when a subprocess exits, including its exit-code
- // and the phase it was in when that occurred. This will be called via
- // the |background_task_runner_| if one is set or whatever thread reaped
- // the process otherwise.
- ProcessExitCallback process_exit_callback_;
-
DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker);
};
diff --git a/base/debug/activity_tracker_unittest.cc b/base/debug/activity_tracker_unittest.cc
index c7efa580e8..aced4fb36a 100644
--- a/base/debug/activity_tracker_unittest.cc
+++ b/base/debug/activity_tracker_unittest.cc
@@ -84,73 +84,45 @@ class ActivityTrackerTest : public testing::Test {
return GlobalActivityTracker::Get()->user_data_allocator_.cache_used();
}
- void HandleProcessExit(int64_t id,
- int64_t stamp,
- int code,
- GlobalActivityTracker::ProcessPhase phase,
- std::string&& command,
- ActivityUserData::Snapshot&& data) {
- exit_id = id;
- exit_stamp = stamp;
- exit_code = code;
- exit_phase = phase;
- exit_command = std::move(command);
- exit_data = std::move(data);
- }
-
static void DoNothing() {}
-
- int64_t exit_id = 0;
- int64_t exit_stamp;
- int exit_code;
- GlobalActivityTracker::ProcessPhase exit_phase;
- std::string exit_command;
- ActivityUserData::Snapshot exit_data;
};
TEST_F(ActivityTrackerTest, UserDataTest) {
char buffer[256];
memset(buffer, 0, sizeof(buffer));
ActivityUserData data(buffer, sizeof(buffer));
- size_t space = sizeof(buffer) - sizeof(ActivityUserData::MemoryHeader);
+ const size_t space = sizeof(buffer) - 8;
ASSERT_EQ(space, data.available_);
data.SetInt("foo", 1);
- space -= 24;
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24, data.available_);
data.SetUint("b", 1U); // Small names fit beside header in a word.
- space -= 16;
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16, data.available_);
data.Set("c", buffer, 10);
- space -= 24;
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24, data.available_);
data.SetString("dear john", "it's been fun");
- space -= 32;
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
data.Set("c", buffer, 20);
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
data.SetString("dear john", "but we're done together");
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
data.SetString("dear john", "bye");
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24 - 32, data.available_);
data.SetChar("d", 'x');
- space -= 8;
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8, data.available_);
data.SetBool("ee", true);
- space -= 16;
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8 - 16, data.available_);
data.SetString("f", "");
- space -= 8;
- ASSERT_EQ(space, data.available_);
+ ASSERT_EQ(space - 24 - 16 - 24 - 32 - 8 - 16 - 8, data.available_);
}
TEST_F(ActivityTrackerTest, PushPopTest) {
@@ -204,7 +176,7 @@ TEST_F(ActivityTrackerTest, PushPopTest) {
}
TEST_F(ActivityTrackerTest, ScopedTaskTest) {
- GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
+ GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3);
ThreadActivityTracker* tracker =
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
@@ -250,28 +222,6 @@ TEST_F(ActivityTrackerTest, ScopedTaskTest) {
ASSERT_EQ(2U, GetGlobalUserDataMemoryCacheUsed());
}
-TEST_F(ActivityTrackerTest, ExceptionTest) {
- GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
-
- ThreadActivityTracker* tracker =
- GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
- ThreadActivityTracker::Snapshot snapshot;
- ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- ASSERT_EQ(0U, snapshot.last_exception.activity_type);
-
- char origin;
- global->RecordException(&origin, 42);
-
- ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
- EXPECT_EQ(Activity::ACT_EXCEPTION, snapshot.last_exception.activity_type);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin),
- snapshot.last_exception.origin_address);
- EXPECT_EQ(42U, snapshot.last_exception.data.exception.code);
-}
-
TEST_F(ActivityTrackerTest, CreateWithFileTest) {
const char temp_name[] = "CreateWithFileTest";
ScopedTempDir temp_dir;
@@ -300,16 +250,6 @@ TEST_F(ActivityTrackerTest, CreateWithFileTest) {
// GlobalActivityTracker tests below.
-TEST_F(ActivityTrackerTest, BasicTest) {
- GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
-
- // Ensure the data repositories have backing store, indicated by non-zero ID.
- EXPECT_NE(0U, global->process_data().id());
- EXPECT_NE(0U, global->global_data().id());
- EXPECT_NE(global->process_data().id(), global->global_data().id());
-}
-
class SimpleActivityThread : public SimpleThread {
public:
SimpleActivityThread(const std::string& name,
@@ -364,7 +304,7 @@ class SimpleActivityThread : public SimpleThread {
};
TEST_F(ActivityTrackerTest, ThreadDeathTest) {
- GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
+ GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3);
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
const size_t starting_active = GetGlobalActiveTrackerCount();
const size_t starting_inactive = GetGlobalInactiveTrackerCount();
@@ -396,107 +336,5 @@ TEST_F(ActivityTrackerTest, ThreadDeathTest) {
EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
}
-TEST_F(ActivityTrackerTest, ProcessDeathTest) {
- // This doesn't actually create and destroy a process. Instead, it uses for-
- // testing interfaces to simulate data created by other processes.
- const ProcessId other_process_id = GetCurrentProcId() + 1;
-
- GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- ThreadActivityTracker* thread = global->GetOrCreateTrackerForCurrentThread();
-
- // Get callbacks for process exit.
- global->SetProcessExitCallback(
- Bind(&ActivityTrackerTest::HandleProcessExit, Unretained(this)));
-
- // Pretend than another process has started.
- global->RecordProcessLaunch(other_process_id, FILE_PATH_LITERAL("foo --bar"));
-
- // Do some activities.
- PendingTask task(FROM_HERE, base::Bind(&DoNothing));
- ScopedTaskRunActivity activity(task);
- ActivityUserData& user_data = activity.user_data();
- ASSERT_NE(0U, user_data.id());
-
- // Get the memory-allocator references to that data.
- PersistentMemoryAllocator::Reference proc_data_ref =
- global->allocator()->GetAsReference(
- global->process_data().GetBaseAddress(),
- GlobalActivityTracker::kTypeIdProcessDataRecord);
- ASSERT_TRUE(proc_data_ref);
- PersistentMemoryAllocator::Reference tracker_ref =
- global->allocator()->GetAsReference(
- thread->GetBaseAddress(),
- GlobalActivityTracker::kTypeIdActivityTracker);
- ASSERT_TRUE(tracker_ref);
- PersistentMemoryAllocator::Reference user_data_ref =
- global->allocator()->GetAsReference(
- user_data.GetBaseAddress(),
- GlobalActivityTracker::kTypeIdUserDataRecord);
- ASSERT_TRUE(user_data_ref);
-
- // Make a copy of the thread-tracker state so it can be restored later.
- const size_t tracker_size = global->allocator()->GetAllocSize(tracker_ref);
- std::unique_ptr<char[]> tracker_copy(new char[tracker_size]);
- memcpy(tracker_copy.get(), thread->GetBaseAddress(), tracker_size);
-
- // Change the objects to appear to be owned by another process.
- int64_t owning_id;
- int64_t stamp;
- ASSERT_TRUE(ActivityUserData::GetOwningProcessId(
- global->process_data().GetBaseAddress(), &owning_id, &stamp));
- EXPECT_NE(other_process_id, owning_id);
- ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId(
- thread->GetBaseAddress(), &owning_id, &stamp));
- EXPECT_NE(other_process_id, owning_id);
- ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(),
- &owning_id, &stamp));
- EXPECT_NE(other_process_id, owning_id);
- global->process_data().SetOwningProcessIdForTesting(other_process_id, stamp);
- thread->SetOwningProcessIdForTesting(other_process_id, stamp);
- user_data.SetOwningProcessIdForTesting(other_process_id, stamp);
- ASSERT_TRUE(ActivityUserData::GetOwningProcessId(
- global->process_data().GetBaseAddress(), &owning_id, &stamp));
- EXPECT_EQ(other_process_id, owning_id);
- ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId(
- thread->GetBaseAddress(), &owning_id, &stamp));
- EXPECT_EQ(other_process_id, owning_id);
- ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(),
- &owning_id, &stamp));
- EXPECT_EQ(other_process_id, owning_id);
-
- // Check that process exit will perform callback and free the allocations.
- ASSERT_EQ(0, exit_id);
- ASSERT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecord,
- global->allocator()->GetType(proc_data_ref));
- ASSERT_EQ(GlobalActivityTracker::kTypeIdActivityTracker,
- global->allocator()->GetType(tracker_ref));
- ASSERT_EQ(GlobalActivityTracker::kTypeIdUserDataRecord,
- global->allocator()->GetType(user_data_ref));
- global->RecordProcessExit(other_process_id, 0);
- EXPECT_EQ(other_process_id, exit_id);
- EXPECT_EQ("foo --bar", exit_command);
- EXPECT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecordFree,
- global->allocator()->GetType(proc_data_ref));
- EXPECT_EQ(GlobalActivityTracker::kTypeIdActivityTrackerFree,
- global->allocator()->GetType(tracker_ref));
- EXPECT_EQ(GlobalActivityTracker::kTypeIdUserDataRecordFree,
- global->allocator()->GetType(user_data_ref));
-
- // Restore memory contents and types so things don't crash when doing real
- // process clean-up.
- memcpy(const_cast<void*>(thread->GetBaseAddress()), tracker_copy.get(),
- tracker_size);
- global->allocator()->ChangeType(
- proc_data_ref, GlobalActivityTracker::kTypeIdProcessDataRecord,
- GlobalActivityTracker::kTypeIdUserDataRecordFree, false);
- global->allocator()->ChangeType(
- tracker_ref, GlobalActivityTracker::kTypeIdActivityTracker,
- GlobalActivityTracker::kTypeIdActivityTrackerFree, false);
- global->allocator()->ChangeType(
- user_data_ref, GlobalActivityTracker::kTypeIdUserDataRecord,
- GlobalActivityTracker::kTypeIdUserDataRecordFree, false);
-}
-
} // namespace debug
} // namespace base
diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc
index 08dcacfa30..1996dfca18 100644
--- a/base/debug/stack_trace.cc
+++ b/base/debug/stack_trace.cc
@@ -35,7 +35,7 @@ namespace debug {
namespace {
-#if HAVE_TRACE_STACK_FRAME_POINTERS && !defined(OS_WIN)
+#if HAVE_TRACE_STACK_FRAME_POINTERS
#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__)
// GCC and LLVM generate slightly different frames on ARM, see
@@ -144,7 +144,7 @@ void* LinkStackFrames(void* fpp, void* parent_fp) {
return prev_parent_fp;
}
-#endif // HAVE_TRACE_STACK_FRAME_POINTERS && !defined(OS_WIN)
+#endif // HAVE_TRACE_STACK_FRAME_POINTERS
} // namespace
@@ -227,18 +227,6 @@ std::string StackTrace::ToString() const {
size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial) {
-// TODO(699863): Merge the frame-pointer based stack unwinder into the
-// base::debug::StackTrace platform-specific implementation files.
-#if defined(OS_WIN)
- StackTrace stack(max_depth);
- size_t count = 0;
- const void* const* frames = stack.Addresses(&count);
- if (count < skip_initial)
- return 0u;
- count -= skip_initial;
- memcpy(out_trace, frames + skip_initial, count * sizeof(void*));
- return count;
-#elif defined(OS_POSIX)
// Usage of __builtin_frame_address() enables frame pointers in this
// function even if they are not enabled globally. So 'fp' will always
// be valid.
@@ -272,10 +260,8 @@ size_t TraceStackFramePointers(const void** out_trace,
}
return depth;
-#endif
}
-#if !defined(OS_WIN)
ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp)
: fp_(fp),
parent_fp_(parent_fp),
@@ -286,7 +272,6 @@ ScopedStackFrameLinker::~ScopedStackFrameLinker() {
CHECK_EQ(parent_fp_, previous_parent_fp)
<< "Stack frame's parent pointer has changed!";
}
-#endif // !defined(OS_WIN)
#endif // HAVE_TRACE_STACK_FRAME_POINTERS
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h
index ab1d2ebe6a..4c9b73e87d 100644
--- a/base/debug/stack_trace.h
+++ b/base/debug/stack_trace.h
@@ -23,23 +23,13 @@ struct _EXCEPTION_POINTERS;
struct _CONTEXT;
#endif
-// TODO(699863): Clean up HAVE_TRACE_STACK_FRAME_POINTERS.
-#if defined(OS_POSIX)
-
-#if defined(__i386__) || defined(__x86_64__)
-#define HAVE_TRACE_STACK_FRAME_POINTERS 1
-#elif defined(__arm__) && !defined(__thumb__)
-#define HAVE_TRACE_STACK_FRAME_POINTERS 1
-#else // defined(__arm__) && !defined(__thumb__)
-#define HAVE_TRACE_STACK_FRAME_POINTERS 0
-#endif // defined(__arm__) && !defined(__thumb__)
-
-#elif defined(OS_WIN)
+#if defined(OS_POSIX) && ( \
+ defined(__i386__) || defined(__x86_64__) || \
+ (defined(__arm__) && !defined(__thumb__)))
#define HAVE_TRACE_STACK_FRAME_POINTERS 1
-
-#else // defined(OS_WIN)
+#else
#define HAVE_TRACE_STACK_FRAME_POINTERS 0
-#endif // defined(OS_WIN)
+#endif
namespace base {
namespace debug {
@@ -132,7 +122,6 @@ BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial);
-#if !defined(OS_WIN)
// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
// Both frame pointers must come from __builtin_frame_address().
@@ -182,7 +171,6 @@ class BASE_EXPORT ScopedStackFrameLinker {
DISALLOW_COPY_AND_ASSIGN(ScopedStackFrameLinker);
};
-#endif // !defined(OS_WIN)
#endif // HAVE_TRACE_STACK_FRAME_POINTERS