diff options
author | Jakub Pawlowski <jpawlowski@google.com> | 2017-12-20 18:26:31 +0000 |
---|---|---|
committer | Myles Watson <mylesgw@google.com> | 2017-12-20 18:47:15 +0000 |
commit | 70cd4fac31a9b0865dab6574540f70cc103337dc (patch) | |
tree | a9d13e0b40d89d8960e80b71a7c21e639ea6c2d5 /base/debug | |
parent | bf8c17f71511c1e90cd8cccfe71f0852c566bd3b (diff) | |
download | libchrome-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.cc | 781 | ||||
-rw-r--r-- | base/debug/activity_tracker.h | 318 | ||||
-rw-r--r-- | base/debug/activity_tracker_unittest.cc | 188 | ||||
-rw-r--r-- | base/debug/stack_trace.cc | 19 | ||||
-rw-r--r-- | base/debug/stack_trace.h | 22 |
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 |