Chromium Code Reviews| 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..ffc3b19c6c7ae7bf81e1dc93499b705c8a157709 |
| --- /dev/null |
| +++ b/base/debug/activity_tracker.h |
| @@ -0,0 +1,344 @@ |
| +// 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 "base/base_export.h" |
| +#include "base/feature_list.h" |
| +#include "base/files/file_path.h" |
| +#include "base/metrics/persistent_memory_allocator.h" |
| +#include "base/threading/thread_checker.h" |
| +#include "base/threading/thread_local_storage.h" |
| + |
| +namespace base { |
| + |
| +struct PendingTask; |
| + |
| +class Lock; |
| +class MemoryMappedFile; |
| +class WaitableEvent; |
| + |
| +namespace debug { |
| + |
| +class ThreadActivityAnalyzer; |
| + |
| +#if !defined(OS_NACL) // NACL doesn't support any kind of file access in build. |
|
manzagop (departed)
2016/06/01 21:59:41
Do you need this #if at the callsite in chrome_br
bcwhite
2016/06/02 16:18:16
No because that file is under chrome/ which isn't
|
| +// 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); |
| +#endif // !defined(OS_NACL) |
| + |
| + |
| +// 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_address; |
| + } lock; |
| + struct { |
| + uint64_t event_address; |
| + } event; |
| + |
| + static StackEntryData ForTask(uint64_t sequence) { |
| + StackEntryData data; |
| + data.task.sequence_id = sequence; |
| + return data; |
| + } |
| + |
| + static StackEntryData ForLock(const void* lock) { |
|
manzagop (departed)
2016/06/01 21:59:41
Add a comment to specificy this is "ForLockAcquisi
bcwhite
2016/06/02 16:18:16
Yeah, lots of comments required everywhere.
The s
|
| + StackEntryData data; |
| + data.lock.lock_address = reinterpret_cast<uintptr_t>(lock); |
| + return data; |
| + } |
| + |
| + static StackEntryData ForEvent(const void* event) { |
| + StackEntryData data; |
| + data.event.event_address = reinterpret_cast<uintptr_t>(event); |
| + return data; |
| + } |
| + }; |
| + |
| + struct StackEntry { |
| + int64_t time_ticks; |
| + uintptr_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, |
| + bool lock_allowed) |
| + : ThreadActivityTracker::ScopedActivity( |
| + GetOrCreateTracker(lock_allowed), |
| + source, |
| + activity, |
| + data) {} |
| + |
| + private: |
| + 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(); |
| + } |
| + }; |
| + |
| + ~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); |
| + |
| +#if !defined(OS_NACL) |
| + static void CreateWithFile(const FilePath& file_path, |
| + size_t size, |
| + uint64_t id, |
| + StringPiece name, |
| + int stack_depth); |
| +#endif // !defined(OS_NACL) |
| + |
| + // 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 if it exists. This is inline for |
| + // performance reasons. Ownership remains with the global tracker. |
| + ThreadActivityTracker* GetTrackerForCurrentThread() { |
| + void* tracker = this_thread_tracker_.Get(); |
| + 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() { |
| + ThreadActivityTracker* tracker = GetTrackerForCurrentThread(); |
| + if (!tracker) |
| + tracker = CreateTrackerForCurrentThread(); |
| + return tracker; |
| + } |
| + |
| + // 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; |
| + |
| + enum : int { |
| + kMaxThreadCount = 100, |
| + }; |
| + |
| + 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_; |
| + |
| + // These have to be lock-free because lock activity is tracked and causes |
| + // re-entry problems. |
| + std::atomic<int> thread_tracker_count_; |
| + std::atomic<int> available_memories_count_; |
| + std::atomic<PersistentMemoryAllocator::Reference> |
| + available_memories_[kMaxThreadCount]; |
| + |
| + static GlobalActivityTracker* g_tracker_; |
| +}; |
| + |
| + |
| +class BASE_EXPORT ScopedTaskActivity |
| + : public GlobalActivityTracker::ScopedThreadActivity { |
| + public: |
| + ScopedTaskActivity(const base::PendingTask& task); |
| +}; |
| + |
| +class BASE_EXPORT ScopedLockActivity |
| + : public GlobalActivityTracker::ScopedThreadActivity { |
| + public: |
| + ScopedLockActivity(const base::internal::LockImpl* lock); |
| +}; |
| + |
| +class BASE_EXPORT ScopedEventActivity |
| + : public GlobalActivityTracker::ScopedThreadActivity { |
| + public: |
| + ScopedEventActivity(const base::WaitableEvent* event); |
| +}; |
| + |
| +} // namespace debug |
| +} // namespace base |
| + |
| +#endif // BASE_METRICS_ACTIVITY_TRACKER_H_ |