| Index: base/debug/activity_tracker.h
|
| diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a4cd75888dc938026fde92574a44e3cf96b59780
|
| --- /dev/null
|
| +++ b/base/debug/activity_tracker.h
|
| @@ -0,0 +1,312 @@
|
| +// 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.
|
| +
|
| +#ifndef BASE_METRICS_ACTIVITY_TRACKER_H_
|
| +#define BASE_METRICS_ACTIVITY_TRACKER_H_
|
| +
|
| +#include <atomic>
|
| +#include <memory>
|
| +#include <set>
|
| +#include <vector>
|
| +
|
| +#include "base/base_export.h"
|
| +#include "base/feature_list.h"
|
| +#include "base/files/file_path.h"
|
| +#include "base/metrics/persistent_memory_allocator.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/threading/thread_checker.h"
|
| +#include "base/threading/thread_local_storage.h"
|
| +
|
| +namespace base {
|
| +
|
| +struct PendingTask;
|
| +
|
| +class Lock;
|
| +class MemoryMappedFile;
|
| +
|
| +namespace debug {
|
| +
|
| +class ThreadActivityAnalyzer;
|
| +
|
| +// Enables the global activity tracker according to a field trial setting,
|
| +// using the specified |file| (without extension) for storing information
|
| +// from this run.
|
| +BASE_EXPORT void SetupGlobalActivityTrackerFieldTrial(const FilePath& file);
|
| +
|
| +
|
| +// This class manages tracking a stack of activities for a single thread in
|
| +// a persistent manner. However, 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.
|
| +class BASE_EXPORT ThreadActivityTracker {
|
| + public:
|
| + enum ActivityType : uint8_t {
|
| + ACT_TASK,
|
| + ACT_LOCK,
|
| + ACT_EVENT,
|
| + };
|
| +
|
| + union StackEntryData {
|
| + struct {
|
| + uint64_t sequence_id;
|
| + } task;
|
| + struct {
|
| + uint64_t lock_id;
|
| + } lock;
|
| + struct {
|
| + } event;
|
| +
|
| + static StackEntryData ForTask(uint64_t sequence) {
|
| + StackEntryData data;
|
| + data.task.sequence_id = sequence;
|
| + return data;
|
| + }
|
| +
|
| + static StackEntryData ForLock(uint64_t lock) {
|
| + StackEntryData data;
|
| + data.lock.lock_id = 0;
|
| + return data;
|
| + }
|
| + };
|
| +
|
| + struct StackEntry {
|
| + int64_t time_ticks;
|
| + intptr_t source_address;
|
| + uint8_t activity_type;
|
| + StackEntryData data;
|
| + };
|
| +
|
| + class BASE_EXPORT ScopedActivity {
|
| + public:
|
| + ScopedActivity(ThreadActivityTracker* tracker,
|
| + const void* source,
|
| + ActivityType activity,
|
| + const StackEntryData& data)
|
| + : tracker_(tracker), source_(source) {
|
| + if (tracker_)
|
| + tracker_->PushActivity(source, activity, data);
|
| + }
|
| + ~ScopedActivity() {
|
| + if (tracker_)
|
| + tracker_->PopActivity(source_);
|
| + }
|
| +
|
| + private:
|
| + ThreadActivityTracker* const tracker_;
|
| + const void* const source_;
|
| + };
|
| +
|
| + // A ThreadActivityTracker runs on top of memory that is managed externally.
|
| + // It must be large enough for the internal header and a few StackEntry
|
| + // blocks. See SizeForStackDepth().
|
| + ThreadActivityTracker(void* base, size_t size);
|
| + virtual ~ThreadActivityTracker();
|
| +
|
| + // Indicate that a method of the given (arbitrary) identifier has started.
|
| + void PushActivity(const void* source,
|
| + ActivityType activity,
|
| + const StackEntryData& data);
|
| +
|
| + // Indicate that a method of the given (arbitrary) identifier has finished.
|
| + void PopActivity(const void* source);
|
| +
|
| + // Returns whether the current data is valid or not. It is not valid if
|
| + // corruption is detected in the header or other data structures. Fetching
|
| + // a copy of the stack will return nothing if the data is not valid.
|
| + bool is_valid() { return valid_; }
|
| +
|
| + // 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 ThreadActivityAnalyzer;
|
| + friend class ActivityTrackerTest;
|
| +
|
| + struct Header;
|
| +
|
| + Header* const header_;
|
| + StackEntry* const stack_;
|
| + const uint32_t stack_slots_;
|
| +
|
| + bool valid_ = false;
|
| +
|
| + base::ThreadChecker thread_checker_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker);
|
| +};
|
| +
|
| +
|
| +// Be sure not to create multiple Analyzers for given tracker data as parallel
|
| +// operation could lead to inconsistencies from concurrent synchronization with
|
| +// an active tracker.
|
| +class BASE_EXPORT ThreadActivityAnalyzer {
|
| + public:
|
| + using StackEntry = ThreadActivityTracker::StackEntry;
|
| +
|
| + // Creates an analyzer for an existing activity-tracker. The passed tracker
|
| + // must live at least as long as the created object. The tracker may continue
|
| + // to be active even with an attached analyzer.
|
| + explicit ThreadActivityAnalyzer(ThreadActivityTracker* tracker);
|
| +
|
| + // Creates an anaylzer for a block of memory held within a persistent-memory
|
| + // |allocator| at the given |reference|. The memory must live at least as
|
| + // long as the created object. It's permissable for a tracker to remain active
|
| + // on the memory from this thread, other threads, or even other processes.
|
| + // The reference must be to an object of type kTypeIdActivityTracker, above.
|
| + ThreadActivityAnalyzer(PersistentMemoryAllocator* allocator,
|
| + PersistentMemoryAllocator::Reference reference);
|
| +
|
| + // Creates an anaylzer for a block of memory currently or previously in-use
|
| + // by an activity-tracker. The memory must live at least as long as the
|
| + // created object. It's permissable for a tracker to remain active on the
|
| + // memory from this thread, other threads, or even other processes.
|
| + ThreadActivityAnalyzer(void* base, size_t size);
|
| +
|
| + ~ThreadActivityAnalyzer();
|
| +
|
| + // Gets a copy of the current stack contents. The return value is the current
|
| + // depth of the stack which may be greater than the number of StackEntry
|
| + // records returned. If so, the returned stack has the "base" of the stack
|
| + // with later entries omitted.
|
| + uint32_t SnapshotStack(std::vector<StackEntry>* snapshot);
|
| +
|
| + private:
|
| + ThreadActivityTracker tracker_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThreadActivityAnalyzer);
|
| +};
|
| +
|
| +
|
| +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 and use them to
|
| + // create ThreadActivityAnalyzer objects.
|
| + enum : uint32_t {
|
| + kTypeIdActivityTracker = 0x5D7381AF + 1, // SHA1(ActivityTracker) v1
|
| + kTypeIdActivityTrackerFree = 0x3F0272FB, // SHA1(ActivityTrackerFree)
|
| + };
|
| +
|
| + class BASE_EXPORT ScopedThreadActivity
|
| + : public ThreadActivityTracker::ScopedActivity {
|
| + public:
|
| + ScopedThreadActivity(const void* source,
|
| + ThreadActivityTracker::ActivityType activity,
|
| + const ThreadActivityTracker::StackEntryData& data)
|
| + : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(),
|
| + source,
|
| + activity,
|
| + data) {}
|
| +
|
| + private:
|
| + static ThreadActivityTracker* GetOrCreateTracker() {
|
| + GlobalActivityTracker* global_tracker = Get();
|
| + if (!global_tracker)
|
| + return nullptr;
|
| + return global_tracker->GetOrCreateTrackerForCurrentThread();
|
| + }
|
| + };
|
| +
|
| + ~GlobalActivityTracker();
|
| +
|
| + static void CreateWithAllocator(
|
| + std::unique_ptr<PersistentMemoryAllocator> allocator,
|
| + int stack_depth);
|
| +
|
| + static void CreateWithLocalMemory(size_t size,
|
| + uint64_t id,
|
| + StringPiece name,
|
| + int stack_depth);
|
| +
|
| + static void CreateWithFile(const FilePath& file_path,
|
| + size_t size,
|
| + uint64_t id,
|
| + StringPiece name,
|
| + int stack_depth);
|
| +
|
| + // Gets the global activity-tracker or null if none exists.
|
| + static GlobalActivityTracker* Get() { return g_tracker_; }
|
| +
|
| + // Gets the persistent-memory-allocator in which data is stored. Callers
|
| + // can store additional records here to pass additional information to
|
| + // the analysis process.
|
| + PersistentMemoryAllocator* allocator() { return allocator_.get(); }
|
| +
|
| + // Gets the thread's activity-tracker, assuming it already exists. This
|
| + // is inline for performance reasons. Ownership remains with the global
|
| + // tracker.
|
| + ThreadActivityTracker* GetTrackerForCurrentThread() {
|
| + void* tracker = this_thread_tracker_.Get();
|
| + DCHECK(tracker);
|
| + return reinterpret_cast<ThreadActivityTracker*>(tracker);
|
| + }
|
| +
|
| + // 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() {
|
| + void* tracker = this_thread_tracker_.Get();
|
| + if (tracker)
|
| + return reinterpret_cast<ThreadActivityTracker*>(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();
|
| +
|
| + private:
|
| + friend class ActivityTrackerTest;
|
| +
|
| + class ManagedActivityTracker : public ThreadActivityTracker {
|
| + public:
|
| + ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference,
|
| + void* base,
|
| + size_t size);
|
| + ~ManagedActivityTracker() override;
|
| +
|
| + private:
|
| + const PersistentMemoryAllocator::Reference mem_reference_;
|
| + void* const mem_base_;
|
| + };
|
| +
|
| + GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator,
|
| + int stack_depth);
|
| +
|
| + // Returns the memory used by an activity-tracker managed by this class.
|
| + void ReturnTrackerMemory(ManagedActivityTracker* tracker,
|
| + PersistentMemoryAllocator::Reference mem_reference,
|
| + void* mem_base);
|
| +
|
| + static void OnTLSDestroy(void* value);
|
| +
|
| + std::unique_ptr<PersistentMemoryAllocator> allocator_;
|
| + const size_t stack_memory_size_;
|
| +
|
| + base::ThreadLocalStorage::Slot this_thread_tracker_;
|
| +
|
| + Lock lock_;
|
| + std::set<ManagedActivityTracker*> thread_trackers_;
|
| + std::vector<PersistentMemoryAllocator::Reference> available_memories_;
|
| +
|
| + static GlobalActivityTracker* g_tracker_;
|
| +};
|
| +
|
| +class BASE_EXPORT ScopedTaskActivity
|
| + : public GlobalActivityTracker::ScopedThreadActivity {
|
| + public:
|
| + ScopedTaskActivity(const base::PendingTask& task);
|
| +};
|
| +
|
| +} // namespace debug
|
| +} // namespace base
|
| +
|
| +#endif // BASE_METRICS_ACTIVITY_TRACKER_H_
|
|
|