| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Activity tracking provides a low-overhead method of collecting information |
| // about the state of the application for analysis both while it is running |
| // and after it has terminated unexpectedly. Its primary purpose is to help |
| // locate reasons the browser becomes unresponsive by providing insight into |
| // what all the various threads and processes are (or were) doing. |
| |
| #ifndef BASE_DEBUG_ACTIVITY_TRACKER_H_ |
| #define BASE_DEBUG_ACTIVITY_TRACKER_H_ |
| |
| // std::atomic is undesired due to performance issues when used as global |
| // variables. There are no such instances here. This module uses the |
| // 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/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/memory/shared_memory.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_local_storage.h" |
| |
| namespace base { |
| |
| struct PendingTask; |
| |
| class FilePath; |
| class Lock; |
| class PlatformThreadHandle; |
| class Process; |
| class WaitableEvent; |
| |
| namespace debug { |
| |
| class ThreadActivityTracker; |
| |
| |
| enum : int { |
| // The maximum number of call-stack addresses stored per activity. This |
| // cannot be changed without also changing the version number of the |
| // structure. See kTypeIdActivityTracker in GlobalActivityTracker. |
| 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. |
| int32_t info; // An arbitrary value used for information purposes. |
| } generic; |
| struct { |
| uint64_t sequence_id; // The sequence identifier of the posted task. |
| } task; |
| struct { |
| uint64_t lock_address; // The memory address of the lock object. |
| } lock; |
| struct { |
| uint64_t event_address; // The memory address of the event object. |
| } event; |
| struct { |
| int64_t thread_id; // A unique identifier for a thread within a process. |
| } thread; |
| 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 |
| // ensure that no fields remain unpopulated should the set of recorded |
| // fields change. They're defined inline where practical because they |
| // reduce to loading a small local structure with a few values, roughly |
| // the same as loading all those values into parameters. |
| |
| static ActivityData ForGeneric(uint32_t id, int32_t info) { |
| ActivityData data; |
| data.generic.id = id; |
| data.generic.info = info; |
| return data; |
| } |
| |
| static ActivityData ForTask(uint64_t sequence) { |
| ActivityData data; |
| data.task.sequence_id = sequence; |
| return data; |
| } |
| |
| static ActivityData ForLock(const void* lock) { |
| ActivityData data; |
| data.lock.lock_address = reinterpret_cast<uintptr_t>(lock); |
| return data; |
| } |
| |
| static ActivityData ForEvent(const void* event) { |
| ActivityData data; |
| data.event.event_address = reinterpret_cast<uintptr_t>(event); |
| return data; |
| } |
| |
| static ActivityData ForThread(const PlatformThreadHandle& handle); |
| static ActivityData ForThread(const int64_t id) { |
| ActivityData data; |
| data.thread.thread_id = id; |
| return data; |
| } |
| |
| static ActivityData ForProcess(const int64_t id) { |
| ActivityData data; |
| 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". |
| extern const ActivityData kNullActivityData; |
| |
| |
| // A helper class that is used for managing memory allocations within a |
| // persistent memory allocator. Instances of this class are NOT thread-safe. |
| // Use from a single thread or protect access with a lock. |
| class BASE_EXPORT ActivityTrackerMemoryAllocator { |
| public: |
| using Reference = PersistentMemoryAllocator::Reference; |
| |
| // 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. 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, |
| bool make_iterable); |
| ~ActivityTrackerMemoryAllocator(); |
| |
| // Gets a reference to an object of the configured type. This can return |
| // a null reference if it was not possible to allocate the memory. |
| Reference GetObjectReference(); |
| |
| // Returns an object to the "free" pool. |
| void ReleaseObjectReference(Reference ref); |
| |
| // Helper function to access an object allocated using this instance. |
| template <typename T> |
| T* GetAsObject(Reference ref) { |
| return allocator_->GetAsObject<T>(ref); |
| } |
| |
| // Similar to GetAsObject() but converts references to arrays of objects. |
| template <typename T> |
| T* GetAsArray(Reference ref, size_t count) { |
| return allocator_->GetAsArray<T>(ref, object_type_, count); |
| } |
| |
| // The current "used size" of the internal cache, visible for testing. |
| size_t cache_used() const { return cache_used_; } |
| |
| private: |
| PersistentMemoryAllocator* const allocator_; |
| const uint32_t object_type_; |
| 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_; |
| |
| // The cache of released object memories. |
| std::unique_ptr<Reference[]> cache_values_; |
| size_t cache_used_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ActivityTrackerMemoryAllocator); |
| }; |
| |
| |
| // This structure is the full contents recorded for every activity pushed |
| // onto the stack. The |activity_type| indicates what is actually stored in |
| // the |data| field. All fields must be explicitly sized types to ensure no |
| // interoperability problems between 32-bit and 64-bit systems. |
| struct Activity { |
| // SHA1(base::debug::Activity): Increment this if structure changes! |
| static constexpr uint32_t kPersistentTypeId = 0x99425159 + 1; |
| // Expected size for 32/64-bit check. Update this if structure changes! |
| static constexpr size_t kExpectedInstanceSize = |
| 48 + 8 * kActivityCallStackSize; |
| |
| // The type of an activity on the stack. Activities are broken into |
| // categories with the category ID taking the top 4 bits and the lower |
| // bits representing an action within that category. This combination |
| // makes it easy to "switch" based on the type during analysis. |
| enum Type : uint8_t { |
| // This "null" constant is used to indicate "do not change" in calls. |
| ACT_NULL = 0, |
| |
| // Task activities involve callbacks posted to a thread or thread-pool |
| // using the PostTask() method or any of its friends. |
| ACT_TASK = 1 << 4, |
| ACT_TASK_RUN = ACT_TASK, |
| |
| // Lock activities involve the acquisition of "mutex" locks. |
| ACT_LOCK = 2 << 4, |
| ACT_LOCK_ACQUIRE = ACT_LOCK, |
| ACT_LOCK_RELEASE, |
| |
| // Event activities involve operations on a WaitableEvent. |
| ACT_EVENT = 3 << 4, |
| ACT_EVENT_WAIT = ACT_EVENT, |
| ACT_EVENT_SIGNAL, |
| |
| // Thread activities involve the life management of threads. |
| ACT_THREAD = 4 << 4, |
| ACT_THREAD_START = ACT_THREAD, |
| ACT_THREAD_JOIN, |
| |
| // Process activities involve the life management of processes. |
| ACT_PROCESS = 5 << 4, |
| 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, |
| |
| // These constants can be used to separate the category and action from |
| // a combined activity type. |
| ACT_CATEGORY_MASK = 0xF << 4, |
| ACT_ACTION_MASK = 0xF |
| }; |
| |
| // Internal representation of time. During collection, this is in "ticks" |
| // but when returned in a snapshot, it is "wall time". |
| int64_t time_internal; |
| |
| // The address that pushed the activity onto the stack as a raw number. |
| uint64_t calling_address; |
| |
| // The address that is the origin of the activity if it not obvious from |
| // the call stack. This is useful for things like tasks that are posted |
| // from a completely different thread though most activities will leave |
| // it null. |
| uint64_t origin_address; |
| |
| // Array of program-counters that make up the top of the call stack. |
| // Despite the fixed size, this list is always null-terminated. Entries |
| // after the terminator have no meaning and may or may not also be null. |
| // The list will be completely empty if call-stack collection is not |
| // enabled. |
| uint64_t call_stack[kActivityCallStackSize]; |
| |
| // Reference to arbitrary user data within the persistent memory segment |
| // and a unique identifier for it. |
| uint32_t user_data_ref; |
| uint32_t user_data_id; |
| |
| // The (enumerated) type of the activity. This defines what fields of the |
| // |data| record are valid. |
| uint8_t activity_type; |
| |
| // 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]; |
| |
| // Information specific to the |activity_type|. |
| ActivityData data; |
| |
| static void FillFrom(Activity* activity, |
| const void* program_counter, |
| const void* origin, |
| Type type, |
| const ActivityData& data); |
| }; |
| |
| // 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. |
| class BASE_EXPORT ActivityUserData { |
| public: |
| // 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, |
| BOOL_VALUE, |
| SIGNED_VALUE, |
| UNSIGNED_VALUE, |
| }; |
| |
| class BASE_EXPORT TypedValue { |
| public: |
| TypedValue(); |
| TypedValue(const TypedValue& other); |
| ~TypedValue(); |
| |
| ValueType type() const { return type_; } |
| |
| // These methods return the extracted value in the correct format. |
| StringPiece Get() const; |
| StringPiece GetString() const; |
| bool GetBool() const; |
| char GetChar() const; |
| int64_t GetInt() const; |
| uint64_t GetUint() const; |
| |
| // These methods return references to process memory as originally provided |
| // to corresponding Set calls. USE WITH CAUTION! There is no guarantee that |
| // the referenced memory is assessible or useful. It's possible that: |
| // - the memory was free'd and reallocated for a different purpose |
| // - the memory has been released back to the OS |
| // - the memory belongs to a different process's address space |
| // Dereferencing the returned StringPiece when the memory is not accessible |
| // will cause the program to SEGV! |
| StringPiece GetReference() const; |
| StringPiece GetStringReference() const; |
| |
| private: |
| friend class ActivityUserData; |
| |
| ValueType type_ = END_OF_VALUES; |
| 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. |
| }; |
| |
| 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); |
| 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; |
| } |
| |
| // 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 SetString(StringPiece name, StringPiece16 value) { |
| SetString(name, UTF16ToUTF8(value)); |
| } |
| void SetBool(StringPiece name, bool value) { |
| char cvalue = value ? 1 : 0; |
| Set(name, BOOL_VALUE, &cvalue, sizeof(cvalue)); |
| } |
| 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()); |
| } |
| |
| // 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. |
| 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); |
| |
| protected: |
| virtual void Set(StringPiece name, |
| ValueType type, |
| const void* memory, |
| size_t size); |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(ActivityTrackerTest, UserDataTest); |
| |
| 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. |
| }; |
| |
| // Header to a key/value record held in persistent memory. |
| struct FieldHeader { |
| FieldHeader(); |
| ~FieldHeader(); |
| |
| 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 { |
| 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 SetReference(StringPiece name, |
| ValueType type, |
| const void* memory, |
| 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. |
| void ImportExistingData() const; |
| |
| // A map of all the values within the memory block, keyed by name for quick |
| // updates of the values. This is "mutable" because it changes on "const" |
| // objects even when the actual data values can't change. |
| mutable std::map<StringPiece, ValueInfo> values_; |
| |
| // Information about the memory block in which new data can be stored. These |
| // are "mutable" because they change even on "const" objects that are just |
| // skipping already set values. |
| mutable char* memory_; |
| mutable size_t available_; |
| |
| // A pointer to the memory header for this instance. |
| MemoryHeader* const header_; |
| |
| // 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; |
| |
| 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 |
| // memory allocation. In order to support an operational mode where another |
| // thread is analyzing this data in real-time, atomic operations are used |
| // where necessary to guarantee a consistent view from the outside. |
| // |
| // This class is not generally used directly but instead managed by the |
| // GlobalActivityTracker instance and updated using Scoped*Activity local |
| // objects. |
| class BASE_EXPORT ThreadActivityTracker { |
| public: |
| using ActivityId = uint32_t; |
| |
| // This structure contains all the common information about the thread so |
| // it doesn't have to be repeated in every entry on the stack. It is defined |
| // and used completely within the .cc file. |
| struct Header; |
| |
| // This structure holds a copy of all the internal data at the moment the |
| // "snapshot" operation is done. It is disconnected from the live tracker |
| // so that continued operation of the thread will not cause changes here. |
| struct BASE_EXPORT Snapshot { |
| // Explicit constructor/destructor are needed because of complex types |
| // with non-trivial default constructors and destructors. |
| Snapshot(); |
| ~Snapshot(); |
| |
| // The name of the thread as set when it was created. The name may be |
| // 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 |
| // and both can be re-used after the process/thread exits. |
| int64_t process_id = 0; |
| int64_t thread_id = 0; |
| |
| // The current stack of activities that are underway for this thread. It |
| // is limited in its maximum size with later entries being left off. |
| std::vector<Activity> activity_stack; |
| |
| // 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 |
| // 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 |
| // even if activity tracking is not enabled. |
| class BASE_EXPORT ScopedActivity { |
| public: |
| ScopedActivity(ThreadActivityTracker* tracker, |
| const void* program_counter, |
| const void* origin, |
| Activity::Type type, |
| const ActivityData& data); |
| ~ScopedActivity(); |
| |
| // Changes some basic metadata about the activity. |
| void ChangeTypeAndData(Activity::Type type, const ActivityData& data); |
| |
| protected: |
| // 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_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ScopedActivity); |
| }; |
| |
| // A ThreadActivityTracker runs on top of memory that is managed externally. |
| // It must be large enough for the internal header and a few Activity |
| // blocks. See SizeForStackDepth(). |
| ThreadActivityTracker(void* base, size_t size); |
| virtual ~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. 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 |
| ActivityId PushActivity(const void* origin, |
| Activity::Type type, |
| const ActivityData& data) { |
| return PushActivity(GetProgramCounter(), origin, type, data); |
| } |
| |
| // Changes the activity |type| and |data| of the top-most entry on the stack. |
| // This is useful if the information has changed and it is desireable to |
| // track that change without creating a new stack entry. If the type is |
| // ACT_NULL or the data is kNullActivityData then that value will remain |
| // 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(ActivityId id, |
| Activity::Type type, |
| const ActivityData& data); |
| |
| // Indicates that an activity has completed. |
| void PopActivity(ActivityId id); |
| |
| // Sets the user-data information for an activity. |
| std::unique_ptr<ActivityUserData> GetUserData( |
| ActivityId id, |
| ActivityTrackerMemoryAllocator* allocator); |
| |
| // Returns if there is true use-data associated with a given ActivityId since |
| // it's possible than any returned object is just a sink. |
| bool HasUserData(ActivityId id); |
| |
| // Release the user-data information for an activity. |
| 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; |
| |
| // Gets a copy of the tracker contents for analysis. Returns false if a |
| // snapshot was not possible, perhaps because the data is not valid; the |
| // contents of |output_snapshot| are undefined in that case. The current |
| // 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(); |
| |
| // Access the "data version" value so tests can determine if an activity |
| // was pushed and popped in a single call. |
| uint32_t GetDataVersionForTesting(); |
| |
| // 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); |
| |
| private: |
| friend class ActivityTrackerTest; |
| |
| bool CalledOnValidThread(); |
| |
| std::unique_ptr<ActivityUserData> CreateUserDataForActivity( |
| Activity* activity, |
| ActivityTrackerMemoryAllocator* allocator); |
| |
| Header* const header_; // Pointer to the Header structure. |
| Activity* const stack_; // The stack of activities. |
| |
| #if DCHECK_IS_ON() |
| // The ActivityTracker is thread bound, and will be invoked across all the |
| // sequences that run on the thread. A ThreadChecker does not work here, as it |
| // asserts on running in the same sequence each time. |
| const PlatformThreadRef thread_id_; // The thread this instance is bound to. |
| #endif |
| const uint32_t stack_slots_; // The total number of stack slots. |
| |
| bool valid_ = false; // Tracks whether the data is valid or not. |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker); |
| }; |
| |
| |
| // The global tracker manages all the individual thread trackers. Memory for |
| // the thread trackers is taken from a PersistentMemoryAllocator which allows |
| // for the data to be analyzed by a parallel process or even post-mortem. |
| class BASE_EXPORT GlobalActivityTracker { |
| public: |
| // Type identifiers used when storing in persistent memory so they can be |
| // identified during extraction; the first 4 bytes of the SHA1 of the name |
| // is used as a unique integer. A "version number" is added to the base |
| // so that, if the structure of that object changes, stored older versions |
| // 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 |
| kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1 |
| kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100, |
| |
| 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 { |
| ModuleInfo(); |
| ModuleInfo(ModuleInfo&& rhs); |
| ModuleInfo(const ModuleInfo& rhs); |
| ~ModuleInfo(); |
| |
| ModuleInfo& operator=(ModuleInfo&& rhs); |
| ModuleInfo& operator=(const ModuleInfo& rhs); |
| |
| // Information about where and when the module was loaded/unloaded. |
| bool is_loaded = false; // Was the last operation a load or unload? |
| uintptr_t address = 0; // Address of the last load operation. |
| int64_t load_time = 0; // Time of last change; set automatically. |
| |
| // Information about the module itself. These never change no matter how |
| // many times a module may be loaded and unloaded. |
| size_t size = 0; // The size of the loaded module. |
| uint32_t timestamp = 0; // Opaque "timestamp" for the module. |
| uint32_t age = 0; // Opaque "age" for the module. |
| uint8_t identifier[16]; // Opaque identifier (GUID, etc.) for the module. |
| std::string file; // The full path to the file. (UTF-8) |
| std::string debug_file; // The full path to the debug file. |
| }; |
| |
| // This is a thin wrapper around the thread-tracker's ScopedActivity that |
| // allows thread-safe access to data values. It is safe to use even if |
| // activity tracking is not enabled. |
| class BASE_EXPORT ScopedThreadActivity |
| : public ThreadActivityTracker::ScopedActivity { |
| public: |
| ScopedThreadActivity(const void* program_counter, |
| const void* origin, |
| Activity::Type type, |
| const ActivityData& data, |
| bool lock_allowed); |
| ~ScopedThreadActivity(); |
| |
| // Returns an object for manipulating user data. |
| ActivityUserData& user_data(); |
| |
| private: |
| // Gets (or creates) a tracker for the current thread. If locking is not |
| // allowed (because a lock is being tracked which would cause recursion) |
| // then the attempt to create one if none found will be skipped. Once |
| // the tracker for this thread has been created for other reasons, locks |
| // will be tracked. The thread-tracker uses locks. |
| static ThreadActivityTracker* GetOrCreateTracker(bool lock_allowed) { |
| GlobalActivityTracker* global_tracker = Get(); |
| if (!global_tracker) |
| return nullptr; |
| |
| if (lock_allowed) |
| return global_tracker->GetOrCreateTrackerForCurrentThread(); |
| else |
| return global_tracker->GetTrackerForCurrentThread(); |
| } |
| |
| // An object that manages additional user data, created only upon request. |
| std::unique_ptr<ActivityUserData> user_data_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedThreadActivity); |
| }; |
| |
| ~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); |
| |
| #if !defined(OS_NACL) |
| // Like above but internally creates an allocator around a disk file with |
| // the specified |size| at the given |file_path|. Any existing file will be |
| // overwritten. The |id| and |name| are arbitrary and stored in the allocator |
| // for reference by whatever process reads it. Returns true if successful. |
| static bool CreateWithFile(const FilePath& file_path, |
| size_t size, |
| uint64_t id, |
| StringPiece name, |
| int stack_depth); |
| #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. |
| static bool CreateWithLocalMemory(size_t size, |
| uint64_t id, |
| StringPiece name, |
| int stack_depth, |
| int64_t process_id); |
| |
| #if !defined(STARBOARD) |
| // Like above but internally creates an allocator using a shared-memory |
| // segment. The segment must already be mapped into the local memory space. |
| static bool CreateWithSharedMemory(std::unique_ptr<SharedMemory> shm, |
| uint64_t id, |
| StringPiece name, |
| int stack_depth); |
| |
| // Like above but takes a handle to an existing shared memory segment and |
| // maps it before creating the tracker. |
| static bool CreateWithSharedMemoryHandle(const SharedMemoryHandle& handle, |
| size_t size, |
| uint64_t id, |
| StringPiece name, |
| int stack_depth); |
| #endif // !defined(STARBOARD) |
| |
| // Gets the global activity-tracker or null if none exists. |
| static GlobalActivityTracker* Get() { |
| return reinterpret_cast<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; } |
| |
| // Gets the persistent-memory-allocator in which data is stored. Callers |
| // can store additional records here to pass more information to the |
| // analysis process. |
| PersistentMemoryAllocator* allocator() { return allocator_.get(); } |
| |
| // Gets the thread's activity-tracker if it exists. This is inline for |
| // performance reasons and it uses thread-local-storage (TLS) so that there |
| // is no significant lookup time required to find the one for the calling |
| // thread. Ownership remains with the global tracker. |
| ThreadActivityTracker* GetTrackerForCurrentThread() { |
| // It is not safe to use TLS once TLS has been destroyed. |
| if (base::ThreadLocalStorage::HasBeenDestroyed()) |
| return nullptr; |
| |
| return reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get()); |
| } |
| |
| // Gets the thread's activity-tracker or creates one if none exists. This |
| // is inline for performance reasons. Ownership remains with the global |
| // tracker. |
| ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() { |
| ThreadActivityTracker* tracker = GetTrackerForCurrentThread(); |
| if (tracker) |
| return tracker; |
| return CreateTrackerForCurrentThread(); |
| } |
| |
| // Creates an activity-tracker for the current thread. |
| ThreadActivityTracker* CreateTrackerForCurrentThread(); |
| |
| // 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); |
| static void RecordLogMessageIfEnabled(StringPiece message) { |
| GlobalActivityTracker* tracker = Get(); |
| if (tracker) |
| tracker->RecordLogMessage(message); |
| } |
| |
| // Records a module load/unload event. This is safe to call multiple times |
| // even with the same information. |
| void RecordModuleInfo(const ModuleInfo& info); |
| static void RecordModuleInfoIfEnabled(const ModuleInfo& info) { |
| GlobalActivityTracker* tracker = Get(); |
| if (tracker) |
| tracker->RecordModuleInfo(info); |
| } |
| |
| // Record field trial information. This call is thread-safe. In addition to |
| // this, construction of a GlobalActivityTracker will cause all existing |
| // active field trials to be fetched and recorded. |
| void RecordFieldTrial(const std::string& trial_name, StringPiece group_name); |
| static void RecordFieldTrialIfEnabled(const std::string& trial_name, |
| StringPiece group_name) { |
| GlobalActivityTracker* tracker = Get(); |
| if (tracker) |
| 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(GetProgramCounter(), origin, code); |
| } |
| void RecordException(const void* pc, const void* origin, uint32_t code); |
| |
| // Marks the tracked data as deleted. |
| void MarkDeleted(); |
| |
| // 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_; } |
| |
| private: |
| friend class GlobalActivityAnalyzer; |
| friend class ScopedThreadActivity; |
| friend class ActivityTrackerTest; |
| |
| enum : int { |
| // The maximum number of threads that can be tracked within a process. If |
| // more than this number run concurrently, tracking of new ones may cease. |
| kMaxThreadCount = 100, |
| kCachedThreadMemories = 10, |
| kCachedUserDataMemories = 10, |
| }; |
| |
| // 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 { |
| public: |
| ThreadSafeUserData(void* memory, size_t size, int64_t pid = 0); |
| ~ThreadSafeUserData() override; |
| |
| private: |
| void Set(StringPiece name, |
| ValueType type, |
| const void* memory, |
| size_t size) override; |
| |
| Lock data_lock_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadSafeUserData); |
| }; |
| |
| // State of a module as stored in persistent memory. This supports a single |
| // loading of a module only. If modules are loaded multiple times at |
| // different addresses, only the last will be recorded and an unload will |
| // not revert to the information of any other addresses. |
| struct BASE_EXPORT ModuleInfoRecord { |
| // SHA1(ModuleInfoRecord): Increment this if structure changes! |
| static constexpr uint32_t kPersistentTypeId = 0x05DB5F41 + 1; |
| |
| // Expected size for 32/64-bit check by PersistentMemoryAllocator. |
| static constexpr size_t kExpectedInstanceSize = |
| OwningProcess::kExpectedInstanceSize + 56; |
| |
| // The atomic unfortunately makes this a "complex" class on some compilers |
| // and thus requires an out-of-line constructor & destructor even though |
| // they do nothing. |
| 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. |
| uint32_t timestamp; // Opaque timestamp of the module. |
| uint32_t age; // Opaque "age" associated with the module. |
| uint8_t identifier[16]; // Opaque identifier for the module. |
| std::atomic<uint32_t> changes; // Number load/unload actions. |
| uint16_t pickle_size; // The size of the following pickle. |
| uint8_t loaded; // Flag if module is loaded or not. |
| char pickle[1]; // Other strings; may allocate larger. |
| |
| // Decodes/encodes storage structure from more generic info structure. |
| bool DecodeTo(GlobalActivityTracker::ModuleInfo* info, |
| size_t record_size) const; |
| static ModuleInfoRecord* CreateFrom( |
| const GlobalActivityTracker::ModuleInfo& info, |
| PersistentMemoryAllocator* allocator); |
| |
| // Updates the core information without changing the encoded strings. This |
| // is useful when a known module changes state (i.e. new load or unload). |
| bool UpdateFrom(const GlobalActivityTracker::ModuleInfo& info); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ModuleInfoRecord); |
| }; |
| |
| // A thin wrapper around the main thread-tracker that keeps additional |
| // information that the global tracker needs to handle joined threads. |
| class ManagedActivityTracker : public ThreadActivityTracker { |
| public: |
| ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference, |
| void* base, |
| size_t size); |
| ~ManagedActivityTracker() override; |
| |
| // The reference into persistent memory from which the thread-tracker's |
| // memory was created. |
| const PersistentMemoryAllocator::Reference mem_reference_; |
| |
| // The physical address used for the thread-tracker's memory. |
| void* const mem_base_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ManagedActivityTracker); |
| }; |
| |
| // 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); |
| |
| // 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_; |
| |
| // The size (in bytes) of memory required by a ThreadActivityTracker to |
| // 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. |
| ThreadLocalStorage::Slot this_thread_tracker_; |
| |
| // The number of thread trackers currently active. |
| std::atomic<int> thread_tracker_count_; |
| |
| // A caching memory allocator for thread-tracker objects. |
| ActivityTrackerMemoryAllocator thread_tracker_allocator_; |
| Lock thread_tracker_allocator_lock_; |
| |
| // A caching memory allocator for user data attached to activity data. |
| ActivityTrackerMemoryAllocator user_data_allocator_; |
| Lock user_data_allocator_lock_; |
| |
| // An object for holding arbitrary key value pairs with thread-safe access. |
| ThreadSafeUserData process_data_; |
| |
| // A map of global module information, keyed by module path. |
| std::map<const std::string, ModuleInfoRecord*> modules_; |
| Lock modules_lock_; |
| |
| // The active global activity tracker. |
| static subtle::AtomicWord g_tracker_; |
| |
| // A lock that is used to protect access to the following fields. |
| 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); |
| }; |
| |
| |
| // Record entry in to and out of an arbitrary block of code. |
| class BASE_EXPORT ScopedActivity |
| : public GlobalActivityTracker::ScopedThreadActivity { |
| public: |
| // Track activity at the specified FROM_HERE location for an arbitrary |
| // 4-bit |action|, an arbitrary 32-bit |id|, and 32-bits of arbitrary |
| // |info|. None of these values affect operation; they're all purely |
| // for association and analysis. To have unique identifiers across a |
| // diverse code-base, create the number by taking the first 8 characters |
| // of the hash of the activity being tracked. |
| // |
| // For example: |
| // Tracking method: void MayNeverExit(uint32_t foo) {...} |
| // echo -n "MayNeverExit" | sha1sum => e44873ccab21e2b71270da24aa1... |
| // |
| // void MayNeverExit(int32_t foo) { |
| // base::debug::ScopedActivity track_me(0, 0xE44873CC, foo); |
| // ... |
| // } |
| ALWAYS_INLINE |
| ScopedActivity(uint8_t action, uint32_t id, int32_t info) |
| : ScopedActivity(GetProgramCounter(), action, id, info) {} |
| ScopedActivity() : ScopedActivity(0, 0, 0) {} |
| |
| // Changes the |action| and/or |info| of this activity on the stack. This |
| // is useful for tracking progress through a function, updating the action |
| // to indicate "milestones" in the block (max 16 milestones: 0-15) or the |
| // info to reflect other changes. Changing both is not atomic so a snapshot |
| // operation could occur between the update of |action| and |info|. |
| void ChangeAction(uint8_t action); |
| void ChangeInfo(int32_t info); |
| void ChangeActionAndInfo(uint8_t action, int32_t info); |
| |
| private: |
| // Constructs the object using a passed-in program-counter. |
| ScopedActivity(const void* program_counter, |
| uint8_t action, |
| uint32_t id, |
| int32_t info); |
| |
| // A copy of the ID code so it doesn't have to be passed by the caller when |
| // changing the |info| field. |
| uint32_t id_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedActivity); |
| }; |
| |
| |
| // These "scoped" classes provide easy tracking of various blocking actions. |
| |
| class BASE_EXPORT ScopedTaskRunActivity |
| : public GlobalActivityTracker::ScopedThreadActivity { |
| public: |
| ALWAYS_INLINE |
| explicit ScopedTaskRunActivity(const PendingTask& task) |
| : ScopedTaskRunActivity(GetProgramCounter(), task) {} |
| |
| private: |
| ScopedTaskRunActivity(const void* program_counter, const PendingTask& task); |
| DISALLOW_COPY_AND_ASSIGN(ScopedTaskRunActivity); |
| }; |
| |
| class BASE_EXPORT ScopedLockAcquireActivity |
| : public GlobalActivityTracker::ScopedThreadActivity { |
| public: |
| ALWAYS_INLINE |
| explicit ScopedLockAcquireActivity(const base::internal::LockImpl* lock) |
| : ScopedLockAcquireActivity(GetProgramCounter(), lock) {} |
| |
| private: |
| ScopedLockAcquireActivity(const void* program_counter, |
| const base::internal::LockImpl* lock); |
| DISALLOW_COPY_AND_ASSIGN(ScopedLockAcquireActivity); |
| }; |
| |
| class BASE_EXPORT ScopedEventWaitActivity |
| : public GlobalActivityTracker::ScopedThreadActivity { |
| public: |
| ALWAYS_INLINE |
| explicit ScopedEventWaitActivity(const WaitableEvent* event) |
| : ScopedEventWaitActivity(GetProgramCounter(), event) {} |
| |
| private: |
| ScopedEventWaitActivity(const void* program_counter, |
| const WaitableEvent* event); |
| DISALLOW_COPY_AND_ASSIGN(ScopedEventWaitActivity); |
| }; |
| |
| class BASE_EXPORT ScopedThreadJoinActivity |
| : public GlobalActivityTracker::ScopedThreadActivity { |
| public: |
| ALWAYS_INLINE |
| explicit ScopedThreadJoinActivity(const PlatformThreadHandle* thread) |
| : ScopedThreadJoinActivity(GetProgramCounter(), thread) {} |
| |
| private: |
| ScopedThreadJoinActivity(const void* program_counter, |
| const PlatformThreadHandle* thread); |
| DISALLOW_COPY_AND_ASSIGN(ScopedThreadJoinActivity); |
| }; |
| |
| // Some systems don't have base::Process |
| #if !defined(OS_NACL) && !defined(OS_IOS) |
| class BASE_EXPORT ScopedProcessWaitActivity |
| : public GlobalActivityTracker::ScopedThreadActivity { |
| public: |
| ALWAYS_INLINE |
| explicit ScopedProcessWaitActivity(const Process* process) |
| : ScopedProcessWaitActivity(GetProgramCounter(), process) {} |
| |
| private: |
| ScopedProcessWaitActivity(const void* program_counter, |
| const Process* process); |
| DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); |
| }; |
| #endif |
| |
| } // namespace debug |
| } // namespace base |
| |
| #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_ |