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_ |