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

Unified Diff: base/debug/activity_tracker.h

Issue 2422213002: Added support for storing arbitrary user data. (Closed)
Patch Set: some 'git cl format' changes Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/debug/activity_analyzer_unittest.cc ('k') | base/debug/activity_tracker.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_;
« no previous file with comments | « base/debug/activity_analyzer_unittest.cc ('k') | base/debug/activity_tracker.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698