Chromium Code Reviews| Index: base/debug/activity_tracker.h |
| diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h |
| index 719a31865ca76ad1def768204a022400e16c3f2f..af78454d2a655dae8271aeec9dac683c63af8dc5 100644 |
| --- a/base/debug/activity_tracker.h |
| +++ b/base/debug/activity_tracker.h |
| @@ -23,12 +23,15 @@ |
| #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" |
| @@ -41,7 +44,6 @@ class FilePath; |
| class Lock; |
| class PlatformThreadHandle; |
| class Process; |
| -class StaticAtomicSequenceNumber; |
| class WaitableEvent; |
| namespace debug { |
| @@ -56,6 +58,39 @@ enum : int { |
| kActivityCallStackSize = 10, |
| }; |
| +// A class for keeping all information needed to verify that a structure is |
| +// associated with a given process. |
| +struct OwningProcess { |
| + OwningProcess(); |
| + ~OwningProcess(); |
| + |
| + // Initializes structure with the current process id and the current time. |
| + // These can uniquely identify a process. A unique non-zero data_id will be |
| + // set making it possible to tell using atomic reads if the data has changed. |
| + void Release_Initialize(); |
| + |
| + // Explicitly sets the process ID. |
| + void SetOwningProcessIdForTesting(ProcessId 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, |
| + ProcessId* 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 |
| @@ -355,7 +390,7 @@ class BASE_EXPORT ActivityUserData { |
| // 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 memory_ ? id_->load(std::memory_order_relaxed) : 0; |
| + 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 |
| @@ -411,6 +446,16 @@ class BASE_EXPORT ActivityUserData { |
| // Gets the base memory address used for storing data. |
| const void* GetBaseAddress(); |
| + // Explicitly sets the process ID. |
| + void SetOwningProcessIdForTesting(ProcessId 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, |
| + ProcessId* out_id, |
| + int64_t* out_stamp); |
| + |
| protected: |
| virtual void Set(StringPiece name, |
| ValueType type, |
| @@ -422,20 +467,31 @@ class BASE_EXPORT ActivityUserData { |
| enum : size_t { kMemoryAlignment = sizeof(uint64_t) }; |
| - // A structure used to reference data held outside of persistent memory. |
| - struct ReferenceRecord { |
| - uint64_t address; |
| - uint64_t size; |
| + // 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 Header { |
| + 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 { |
| @@ -470,12 +526,8 @@ class BASE_EXPORT ActivityUserData { |
| mutable char* memory_; |
| mutable size_t available_; |
| - // A pointer to the unique ID for this instance. |
| - std::atomic<uint32_t>* const id_; |
| - |
| - // This ID is used to create unique indentifiers for user data so that it's |
| - // possible to tell if the information has been overwritten. |
| - static StaticAtomicSequenceNumber next_id_; |
| + // A pointer to the memory header for this instance. |
| + MemoryHeader* const header_; |
| DISALLOW_COPY_AND_ASSIGN(ActivityUserData); |
| }; |
| @@ -618,6 +670,19 @@ class BASE_EXPORT ThreadActivityTracker { |
| // implementation does not support concurrent snapshot operations. |
| bool CreateSnapshot(Snapshot* output_snapshot) const; |
| + // Gets the base memory address used for storing data. |
| + const void* GetBaseAddress(); |
|
manzagop (departed)
2017/03/07 22:08:57
nit: const method?
bcwhite
2017/03/09 14:07:54
Done.
|
| + |
| + // Explicitly sets the process ID. |
| + void SetOwningProcessIdForTesting(ProcessId 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, |
| + ProcessId* 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); |
| @@ -649,15 +714,45 @@ class BASE_EXPORT GlobalActivityTracker { |
| // will be safely ignored. These are public so that an external process |
| // can recognize records of this type within an allocator. |
| enum : uint32_t { |
| - kTypeIdActivityTracker = 0x5D7381AF + 3, // SHA1(ActivityTracker) v3 |
| - kTypeIdUserDataRecord = 0x615EDDD7 + 2, // SHA1(UserDataRecord) v2 |
| + kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4 |
| + kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3 |
| kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1 |
| - kTypeIdGlobalDataRecord = kTypeIdUserDataRecord + 1000, |
| + 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 its important to ensure that what is returned was created before |
|
manzagop (departed)
2017/03/07 22:08:57
typo: its -> it's
bcwhite
2017/03/09 14:07:54
Done.
|
| + // 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 { |
| @@ -789,6 +884,47 @@ class BASE_EXPORT GlobalActivityTracker { |
| // Releases the activity-tracker for the current thread (for testing only). |
| void ReleaseTrackerForCurrentThreadForTesting(); |
| + // Sets a task-runner that can be used for background work. |
| + void SetBackgroundTaskRunner(const scoped_refptr<TaskRunner>& runner); |
| + |
| + // Sets an optional callback to be called when a process exits. |
| + void SetProcessExitCallback(ProcessExitCallback callback); |
| + |
| + // Manages process lifetimes. These are called by the process that launched |
| + // and reaped the subprocess, not the subprocess itself. |
| + 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); |
| @@ -818,7 +954,12 @@ class BASE_EXPORT GlobalActivityTracker { |
| tracker->RecordFieldTrial(trial_name, group_name); |
| } |
| + // 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: |
| @@ -837,10 +978,10 @@ class BASE_EXPORT GlobalActivityTracker { |
| // A wrapper around ActivityUserData that is thread-safe and thus can be used |
| // in the global scope without the requirement of being called from only one |
| // thread. |
| - class GlobalUserData : public ActivityUserData { |
| + class ThreadSafeUserData : public ActivityUserData { |
| public: |
| - GlobalUserData(void* memory, size_t size); |
| - ~GlobalUserData() override; |
| + ThreadSafeUserData(void* memory, size_t size); |
| + ~ThreadSafeUserData() override; |
| private: |
| void Set(StringPiece name, |
| @@ -850,7 +991,7 @@ class BASE_EXPORT GlobalActivityTracker { |
| Lock data_lock_; |
| - DISALLOW_COPY_AND_ASSIGN(GlobalUserData); |
| + DISALLOW_COPY_AND_ASSIGN(ThreadSafeUserData); |
| }; |
| // State of a module as stored in persistent memory. This supports a single |
| @@ -862,7 +1003,8 @@ class BASE_EXPORT GlobalActivityTracker { |
| static constexpr uint32_t kPersistentTypeId = 0x05DB5F41 + 1; |
| // Expected size for 32/64-bit check by PersistentMemoryAllocator. |
| - static constexpr size_t kExpectedInstanceSize = 56; |
| + 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 |
| @@ -870,6 +1012,7 @@ class BASE_EXPORT GlobalActivityTracker { |
| ModuleInfoRecord(); |
| ~ModuleInfoRecord(); |
| + OwningProcess owner; // The process that created this record. |
| uint64_t address; // The base address of the module. |
| uint64_t load_time; // Time of last load/unload. |
| uint64_t size; // The size of the module in bytes. |
| @@ -933,6 +1076,12 @@ class BASE_EXPORT GlobalActivityTracker { |
| // 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(ProcessId 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_; |
| @@ -955,9 +1104,9 @@ class BASE_EXPORT GlobalActivityTracker { |
| ActivityTrackerMemoryAllocator user_data_allocator_; |
| base::Lock user_data_allocator_lock_; |
| - // An object for holding global arbitrary key value pairs. Values must always |
| - // be written from the main UI thread. |
| - GlobalUserData global_data_; |
| + // 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_; |
| @@ -966,6 +1115,21 @@ class BASE_EXPORT GlobalActivityTracker { |
| // The active global activity tracker. |
| static subtle::AtomicWord g_tracker_; |
| + // A lock that is used to procect access to the following fields. |
|
manzagop (departed)
2017/03/07 22:08:57
typo: procect
bcwhite
2017/03/09 14:07:54
Done.
|
| + 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); |
| }; |