// 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/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" 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(::tracked_objects::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(); // 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; 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. bool valid_ = false; // Tracks whether the data is valid or not. base::ThreadChecker thread_checker_; 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, kTypeIdGlobalDataRecord = kTypeIdUserDataRecord + 0x200, 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 // accesses the global tracker to provide some of the information, notably // which thread-tracker to use. It is safe to create 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. static void 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 void CreateWithLocalMemory(size_t size, uint64_t id, StringPiece name, int stack_depth, int64_t process_id); // 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() { 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(::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: 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; bool EncodeFrom(const GlobalActivityTracker::ModuleInfo& info, size_t record_size); // 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); // Determines the required memory size for the encoded storage. static size_t EncodedSize(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. base::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_; 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 arbitrary key value pairs with thread-safe access. ThreadSafeUserData process_data_; ThreadSafeUserData global_data_; // A map of global module information, keyed by module path. std::map<const std::string, ModuleInfoRecord*> modules_; base::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. 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); }; // 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(::tracked_objects::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 base::PendingTask& task) : ScopedTaskRunActivity(::tracked_objects::GetProgramCounter(), task) {} private: ScopedTaskRunActivity(const void* program_counter, const base::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(::tracked_objects::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 base::WaitableEvent* event) : ScopedEventWaitActivity(::tracked_objects::GetProgramCounter(), event) {} private: ScopedEventWaitActivity(const void* program_counter, const base::WaitableEvent* event); DISALLOW_COPY_AND_ASSIGN(ScopedEventWaitActivity); }; class BASE_EXPORT ScopedThreadJoinActivity : public GlobalActivityTracker::ScopedThreadActivity { public: ALWAYS_INLINE explicit ScopedThreadJoinActivity(const base::PlatformThreadHandle* thread) : ScopedThreadJoinActivity(::tracked_objects::GetProgramCounter(), thread) {} private: ScopedThreadJoinActivity(const void* program_counter, const base::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 base::Process* process) : ScopedProcessWaitActivity(::tracked_objects::GetProgramCounter(), process) {} private: ScopedProcessWaitActivity(const void* program_counter, const base::Process* process); DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); }; #endif } // namespace debug } // namespace base #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_