| Index: base/debug/activity_tracker.h
|
| diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
|
| index f9da9035672e19eb8373bda69cff61ba5138559d..24de43174487caf65b3ca0ed507652891094afaa 100644
|
| --- a/base/debug/activity_tracker.h
|
| +++ b/base/debug/activity_tracker.h
|
| @@ -16,12 +16,14 @@
|
| // PersistentMemoryAllocator which also uses std::atomic and is written
|
| // by the same author.
|
| #include <atomic>
|
| +#include <map>
|
| #include <memory>
|
| #include <string>
|
| #include <vector>
|
|
|
| #include "base/base_export.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/threading/platform_thread.h"
|
| @@ -136,12 +138,14 @@ class ActivityTrackerMemoryAllocator {
|
| // Creates a instance for allocating objects of a fixed |object_type|, a
|
| // corresponding |object_free| type, and the |object_size|. An internal
|
| // cache of the last |cache_size| released references will be kept for
|
| - // quick future fetches.
|
| + // quick future fetches. If |make_iterable| then allocated objects will
|
| + // be marked "iterable" in the allocator.
|
| ActivityTrackerMemoryAllocator(PersistentMemoryAllocator* allocator,
|
| uint32_t object_type,
|
| uint32_t object_free_type,
|
| size_t object_size,
|
| - size_t cache_size);
|
| + size_t cache_size,
|
| + bool make_iterable);
|
| ~ActivityTrackerMemoryAllocator();
|
|
|
| // Gets a reference to an object of the configured type. This can return
|
| @@ -160,6 +164,7 @@ class ActivityTrackerMemoryAllocator {
|
| const uint32_t object_free_type_;
|
| const size_t object_size_;
|
| const size_t cache_size_;
|
| + const bool make_iterable_;
|
|
|
| // An iterator for going through persistent memory looking for free'd objects.
|
| PersistentMemoryAllocator::Iterator iterator_;
|
| @@ -239,6 +244,9 @@ struct Activity {
|
| // enabled.
|
| uint64_t call_stack[kActivityCallStackSize];
|
|
|
| + // Reference to arbitrary user data within the persistent memory segment.
|
| + uint32_t user_data;
|
| +
|
| // The (enumerated) type of the activity. This defines what fields of the
|
| // |data| record are valid.
|
| uint8_t activity_type;
|
| @@ -246,7 +254,7 @@ struct Activity {
|
| // Padding to ensure that the next member begins on a 64-bit boundary
|
| // even on 32-bit builds which ensures inter-operability between CPU
|
| // architectures. New fields can be taken from this space.
|
| - uint8_t padding[7];
|
| + uint8_t padding[3];
|
|
|
| // Information specific to the |activity_type|.
|
| ActivityData data;
|
| @@ -287,6 +295,114 @@ struct BASE_EXPORT ActivitySnapshot {
|
| uint32_t activity_stack_depth = 0;
|
| };
|
|
|
| +// 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.
|
| +class BASE_EXPORT ActivityUserData {
|
| + // List of known value type. REFERENCE types must immediately follow the non-
|
| + // external types.
|
| + enum ValueType : uint8_t {
|
| + END_OF_VALUES = 0,
|
| + RAW_VALUE,
|
| + RAW_VALUE_REFERENCE,
|
| + STRING_VALUE,
|
| + STRING_VALUE_REFERENCE,
|
| + CHAR_VALUE,
|
| + SIGNED_VALUE,
|
| + UNSIGNED_VALUE,
|
| + };
|
| +
|
| + public:
|
| + ActivityUserData(void* memory, size_t size);
|
| + ~ActivityUserData();
|
| +
|
| + // Writes a |value| (as part of a key/value pair) that will be included with
|
| + // the activity in any reports. The same |name| can be written multiple times
|
| + // with each successive call overwriting the previously stored |value|. For
|
| + // raw and string values, the maximum size of successive writes is limited by
|
| + // the first call. The length of "name" is limited to 255 characters.
|
| + //
|
| + // This information is stored on a "best effort" basis. It may be dropped if
|
| + // the memory buffer is full or the associated activity is beyond the maximum
|
| + // recording depth.
|
| + void Set(StringPiece name, const void* memory, size_t size) {
|
| + Set(name, RAW_VALUE, memory, size);
|
| + }
|
| + void SetString(StringPiece name, StringPiece value) {
|
| + Set(name, STRING_VALUE, value.data(), value.length());
|
| + }
|
| + void SetChar(StringPiece name, char value) {
|
| + Set(name, CHAR_VALUE, &value, sizeof(value));
|
| + }
|
| + void SetInt(StringPiece name, int64_t value) {
|
| + Set(name, SIGNED_VALUE, &value, sizeof(value));
|
| + }
|
| + void SetUint(StringPiece name, uint64_t value) {
|
| + Set(name, UNSIGNED_VALUE, &value, sizeof(value));
|
| + }
|
| +
|
| + // These function as above but don't actually copy the data into the
|
| + // persistent memory. They store unaltered pointers along with a size. These
|
| + // can be used in conjuction with a memory dump to find certain large pieces
|
| + // of information.
|
| + void SetReference(StringPiece name, const void* memory, size_t size) {
|
| + SetReference(name, RAW_VALUE_REFERENCE, memory, size);
|
| + }
|
| + void SetStringReference(StringPiece name, StringPiece value) {
|
| + SetReference(name, STRING_VALUE_REFERENCE, value.data(), value.length());
|
| + }
|
| +
|
| + private:
|
| + FRIEND_TEST_ALL_PREFIXES(ActivityTrackerTest, UserDataTest);
|
| +
|
| + enum : size_t { kMemoryAlignment = sizeof(uint64_t) };
|
| +
|
| + // 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 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.
|
| + };
|
| +
|
| + // This record is used to hold known value is a map so that they can be
|
| + // found and overwritten later.
|
| + struct ValueInfo {
|
| + ValueInfo();
|
| + ValueInfo(ValueInfo&&);
|
| + ~ValueInfo();
|
| +
|
| + StringPiece name; // The "key" of the record.
|
| + ValueType type; // The type of the value.
|
| + void* memory; // Where the "value" is held.
|
| + std::atomic<uint16_t>* size_ptr; // Address of the actual size of value.
|
| + size_t extent; // The total storage of the value,
|
| + }; // typically rounded up for alignment.
|
| +
|
| + void Set(StringPiece name, ValueType type, const void* memory, size_t size);
|
| + void SetReference(StringPiece name,
|
| + ValueType type,
|
| + const void* memory,
|
| + size_t size);
|
| +
|
| + // TODO(bcwhite): Add Get() methods for Analyzer to use.
|
| +
|
| + std::map<StringPiece, ValueInfo> values_;
|
| +
|
| + char* memory_;
|
| + size_t available_;
|
| +
|
| + base::ThreadChecker thread_checker_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ActivityUserData);
|
| +};
|
|
|
| // This class manages tracking a stack of activities for a single thread in
|
| // a persistent manner, implementing a bounded-size stack in a fixed-size
|
| @@ -299,6 +415,8 @@ struct BASE_EXPORT ActivitySnapshot {
|
| // objects.
|
| class BASE_EXPORT ThreadActivityTracker {
|
| public:
|
| + using ActivityId = uint32_t;
|
| +
|
| // This is the base class for having the compiler manage an activity on the
|
| // tracker's stack. It does nothing but call methods on the passed |tracker|
|
| // if it is not null, making it safe (and cheap) to create these objects
|
| @@ -309,27 +427,26 @@ class BASE_EXPORT ThreadActivityTracker {
|
| const void* program_counter,
|
| const void* origin,
|
| Activity::Type type,
|
| - const ActivityData& data)
|
| - : tracker_(tracker) {
|
| - if (tracker_)
|
| - tracker_->PushActivity(program_counter, origin, type, data);
|
| - }
|
| + const ActivityData& data);
|
| + ~ScopedActivity();
|
|
|
| - ~ScopedActivity() {
|
| - if (tracker_)
|
| - tracker_->PopActivity();
|
| - }
|
| + // Changes some basic metadata about the activity.
|
| + void ChangeTypeAndData(Activity::Type type, const ActivityData& data);
|
|
|
| - void ChangeTypeAndData(Activity::Type type, const ActivityData& data) {
|
| - if (tracker_)
|
| - tracker_->ChangeActivity(type, data);
|
| - }
|
| + // Returns an object for manipulating user data.
|
| + ActivityUserData& user_data();
|
|
|
| private:
|
| // The thread tracker to which this object reports. It can be null if
|
| // activity tracking is not (yet) enabled.
|
| ThreadActivityTracker* const tracker_;
|
|
|
| + // An identifier that indicates a specific activity on the stack.
|
| + ActivityId activity_id_;
|
| +
|
| + // An object that manages additional user data, created only upon request.
|
| + std::unique_ptr<ActivityUserData> user_data_;
|
| +
|
| DISALLOW_COPY_AND_ASSIGN(ScopedActivity);
|
| };
|
|
|
| @@ -342,19 +459,21 @@ class BASE_EXPORT ThreadActivityTracker {
|
| // Indicates that an activity has started from a given |origin| address in
|
| // the code, though it can be null if the creator's address is not known.
|
| // The |type| and |data| describe the activity. |program_counter| should be
|
| - // the result of GetProgramCounter() where push is called.
|
| - void PushActivity(const void* program_counter,
|
| - const void* origin,
|
| - Activity::Type type,
|
| - const ActivityData& data);
|
| + // the result of GetProgramCounter() where push is called. Returned is an
|
| + // ID that can be used to adjust the pushed activity.
|
| + ActivityId PushActivity(const void* program_counter,
|
| + const void* origin,
|
| + Activity::Type type,
|
| + const ActivityData& data);
|
|
|
| // An inlined version of the above that gets the program counter where it
|
| // is called.
|
| ALWAYS_INLINE
|
| - void PushActivity(const void* origin,
|
| - Activity::Type type,
|
| - const ActivityData& data) {
|
| - PushActivity(::tracked_objects::GetProgramCounter(), origin, type, data);
|
| + ActivityId PushActivity(const void* origin,
|
| + Activity::Type type,
|
| + const ActivityData& data) {
|
| + return PushActivity(::tracked_objects::GetProgramCounter(), origin, type,
|
| + data);
|
| }
|
|
|
| // Changes the activity |type| and |data| of the top-most entry on the stack.
|
| @@ -364,10 +483,15 @@ class BASE_EXPORT ThreadActivityTracker {
|
| // unchanged. The type, if changed, must remain in the same category.
|
| // Changing both is not atomic so a snapshot operation could occur between
|
| // the update of |type| and |data| or between update of |data| fields.
|
| - void ChangeActivity(Activity::Type type, const ActivityData& data);
|
| + void ChangeActivity(ActivityId id,
|
| + Activity::Type type,
|
| + const ActivityData& data);
|
|
|
| // Indicates that an activity has completed.
|
| - void PopActivity();
|
| + void PopActivity(ActivityId id);
|
| +
|
| + // Returns an object capable of storing arbitrary user data.
|
| + std::unique_ptr<ActivityUserData> GetUserData(ActivityId id);
|
|
|
| // 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.
|
| @@ -415,8 +539,12 @@ 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 + 2, // SHA1(ActivityTracker) v2
|
| + kTypeIdActivityTracker = 0x5D7381AF + 2, // SHA1(ActivityTracker) v2
|
| + kTypeIdUserDataRecord = 0x615EDDD7 + 1, // SHA1(UserDataRecord) v1
|
| + kTypeIdGlobalDataRecord = 0xAFE61ABE + 1, // SHA1(GlobalDataRecord) v1
|
| +
|
| kTypeIdActivityTrackerFree = ~kTypeIdActivityTracker,
|
| + kTypeIdUserDataRecordFree = ~kTypeIdUserDataRecord,
|
| };
|
|
|
| // This is a thin wrapper around the thread-tracker's ScopedActivity that
|
| @@ -517,6 +645,17 @@ class BASE_EXPORT GlobalActivityTracker {
|
| // Releases the activity-tracker for the current thread (for testing only).
|
| void ReleaseTrackerForCurrentThreadForTesting();
|
|
|
| + // Gets a reference to memory for holding user-defined activity data. If
|
| + // the reference is valid, it's memory will be returned. If not, then a
|
| + // new reference will be created (and stored) and that memory returned.
|
| + void* GetUserDataMemory(PersistentMemoryAllocator::Reference* reference);
|
| +
|
| + // Releases memory for user-defined activity data.
|
| + void ReleaseUserDataMemory(PersistentMemoryAllocator::Reference* reference);
|
| +
|
| + // Accesses the global data record for storing arbitrary key/value pairs.
|
| + ActivityUserData& user_data() { return user_data_; }
|
| +
|
| private:
|
| friend class ActivityTrackerTest;
|
|
|
| @@ -525,6 +664,7 @@ class BASE_EXPORT GlobalActivityTracker {
|
| // more than this number run concurrently, tracking of new ones may cease.
|
| kMaxThreadCount = 100,
|
| kCachedThreadMemories = 10,
|
| + kCachedUserDataMemories = 10,
|
| };
|
|
|
| // A thin wrapper around the main thread-tracker that keeps additional
|
| @@ -580,6 +720,14 @@ class BASE_EXPORT GlobalActivityTracker {
|
| ActivityTrackerMemoryAllocator thread_tracker_allocator_;
|
| base::Lock thread_tracker_allocator_lock_;
|
|
|
| + // A caching memory allocator for user data attached to activity data.
|
| + ActivityTrackerMemoryAllocator user_data_allocator_;
|
| + base::Lock user_data_allocator_lock_;
|
| +
|
| + // An object for holding global arbitrary key value pairs. Values must always
|
| + // be written from the main UI thread.
|
| + ActivityUserData user_data_;
|
| +
|
| // The active global activity tracker.
|
| static GlobalActivityTracker* g_tracker_;
|
|
|
|
|