OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #ifndef BASE_METRICS_ACTIVITY_TRACKER_H_ |
| 6 #define BASE_METRICS_ACTIVITY_TRACKER_H_ |
| 7 |
| 8 #include <atomic> |
| 9 #include <memory> |
| 10 #include <set> |
| 11 #include <vector> |
| 12 |
| 13 #include "base/base_export.h" |
| 14 #include "base/feature_list.h" |
| 15 #include "base/files/file_path.h" |
| 16 #include "base/metrics/persistent_memory_allocator.h" |
| 17 #include "base/synchronization/lock.h" |
| 18 #include "base/threading/thread_checker.h" |
| 19 #include "base/threading/thread_local_storage.h" |
| 20 |
| 21 namespace base { |
| 22 |
| 23 struct PendingTask; |
| 24 |
| 25 class Lock; |
| 26 class MemoryMappedFile; |
| 27 |
| 28 namespace debug { |
| 29 |
| 30 class ThreadActivityAnalyzer; |
| 31 |
| 32 // Enables the global activity tracker according to a field trial setting, |
| 33 // using the specified |file| (without extension) for storing information |
| 34 // from this run. |
| 35 BASE_EXPORT void SetupGlobalActivityTrackerFieldTrial(const FilePath& file); |
| 36 |
| 37 |
| 38 // This class manages tracking a stack of activities for a single thread in |
| 39 // a persistent manner. However, in order to support an operational mode where |
| 40 // another thread is analyzing this data in real-time, atomic operations are |
| 41 // used where necessary to guarantee a consistent view from the outside. |
| 42 class BASE_EXPORT ThreadActivityTracker { |
| 43 public: |
| 44 enum ActivityType : uint8_t { |
| 45 ACT_TASK, |
| 46 ACT_LOCK, |
| 47 ACT_EVENT, |
| 48 }; |
| 49 |
| 50 union StackEntryData { |
| 51 struct { |
| 52 uint64_t sequence_id; |
| 53 } task; |
| 54 struct { |
| 55 uint64_t lock_id; |
| 56 } lock; |
| 57 struct { |
| 58 } event; |
| 59 |
| 60 static StackEntryData ForTask(uint64_t sequence) { |
| 61 StackEntryData data; |
| 62 data.task.sequence_id = sequence; |
| 63 return data; |
| 64 } |
| 65 |
| 66 static StackEntryData ForLock(uint64_t lock) { |
| 67 StackEntryData data; |
| 68 data.lock.lock_id = 0; |
| 69 return data; |
| 70 } |
| 71 }; |
| 72 |
| 73 struct StackEntry { |
| 74 int64_t time_ticks; |
| 75 intptr_t source_address; |
| 76 uint8_t activity_type; |
| 77 StackEntryData data; |
| 78 }; |
| 79 |
| 80 class BASE_EXPORT ScopedActivity { |
| 81 public: |
| 82 ScopedActivity(ThreadActivityTracker* tracker, |
| 83 const void* source, |
| 84 ActivityType activity, |
| 85 const StackEntryData& data) |
| 86 : tracker_(tracker), source_(source) { |
| 87 if (tracker_) |
| 88 tracker_->PushActivity(source, activity, data); |
| 89 } |
| 90 ~ScopedActivity() { |
| 91 if (tracker_) |
| 92 tracker_->PopActivity(source_); |
| 93 } |
| 94 |
| 95 private: |
| 96 ThreadActivityTracker* const tracker_; |
| 97 const void* const source_; |
| 98 }; |
| 99 |
| 100 // A ThreadActivityTracker runs on top of memory that is managed externally. |
| 101 // It must be large enough for the internal header and a few StackEntry |
| 102 // blocks. See SizeForStackDepth(). |
| 103 ThreadActivityTracker(void* base, size_t size); |
| 104 virtual ~ThreadActivityTracker(); |
| 105 |
| 106 // Indicate that a method of the given (arbitrary) identifier has started. |
| 107 void PushActivity(const void* source, |
| 108 ActivityType activity, |
| 109 const StackEntryData& data); |
| 110 |
| 111 // Indicate that a method of the given (arbitrary) identifier has finished. |
| 112 void PopActivity(const void* source); |
| 113 |
| 114 // Returns whether the current data is valid or not. It is not valid if |
| 115 // corruption is detected in the header or other data structures. Fetching |
| 116 // a copy of the stack will return nothing if the data is not valid. |
| 117 bool is_valid() { return valid_; } |
| 118 |
| 119 // Calculates the memory size required for a given stack depth, including |
| 120 // the internal header structure for the stack. |
| 121 static size_t SizeForStackDepth(int stack_depth); |
| 122 |
| 123 private: |
| 124 friend class ThreadActivityAnalyzer; |
| 125 friend class ActivityTrackerTest; |
| 126 |
| 127 struct Header; |
| 128 |
| 129 Header* const header_; |
| 130 StackEntry* const stack_; |
| 131 const uint32_t stack_slots_; |
| 132 |
| 133 bool valid_ = false; |
| 134 |
| 135 base::ThreadChecker thread_checker_; |
| 136 |
| 137 DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker); |
| 138 }; |
| 139 |
| 140 |
| 141 // Be sure not to create multiple Analyzers for given tracker data as parallel |
| 142 // operation could lead to inconsistencies from concurrent synchronization with |
| 143 // an active tracker. |
| 144 class BASE_EXPORT ThreadActivityAnalyzer { |
| 145 public: |
| 146 using StackEntry = ThreadActivityTracker::StackEntry; |
| 147 |
| 148 // Creates an analyzer for an existing activity-tracker. The passed tracker |
| 149 // must live at least as long as the created object. The tracker may continue |
| 150 // to be active even with an attached analyzer. |
| 151 explicit ThreadActivityAnalyzer(ThreadActivityTracker* tracker); |
| 152 |
| 153 // Creates an anaylzer for a block of memory held within a persistent-memory |
| 154 // |allocator| at the given |reference|. The memory must live at least as |
| 155 // long as the created object. It's permissable for a tracker to remain active |
| 156 // on the memory from this thread, other threads, or even other processes. |
| 157 // The reference must be to an object of type kTypeIdActivityTracker, above. |
| 158 ThreadActivityAnalyzer(PersistentMemoryAllocator* allocator, |
| 159 PersistentMemoryAllocator::Reference reference); |
| 160 |
| 161 // Creates an anaylzer for a block of memory currently or previously in-use |
| 162 // by an activity-tracker. The memory must live at least as long as the |
| 163 // created object. It's permissable for a tracker to remain active on the |
| 164 // memory from this thread, other threads, or even other processes. |
| 165 ThreadActivityAnalyzer(void* base, size_t size); |
| 166 |
| 167 ~ThreadActivityAnalyzer(); |
| 168 |
| 169 // Gets a copy of the current stack contents. The return value is the current |
| 170 // depth of the stack which may be greater than the number of StackEntry |
| 171 // records returned. If so, the returned stack has the "base" of the stack |
| 172 // with later entries omitted. |
| 173 uint32_t SnapshotStack(std::vector<StackEntry>* snapshot); |
| 174 |
| 175 private: |
| 176 ThreadActivityTracker tracker_; |
| 177 |
| 178 DISALLOW_COPY_AND_ASSIGN(ThreadActivityAnalyzer); |
| 179 }; |
| 180 |
| 181 |
| 182 class BASE_EXPORT GlobalActivityTracker { |
| 183 public: |
| 184 // Type identifiers used when storing in persistent memory so they can be |
| 185 // identified during extraction; the first 4 bytes of the SHA1 of the name |
| 186 // is used as a unique integer. A "version number" is added to the base |
| 187 // so that, if the structure of that object changes, stored older versions |
| 188 // will be safely ignored. These are public so that an external process |
| 189 // can recognize records of this type within an allocator and use them to |
| 190 // create ThreadActivityAnalyzer objects. |
| 191 enum : uint32_t { |
| 192 kTypeIdActivityTracker = 0x5D7381AF + 1, // SHA1(ActivityTracker) v1 |
| 193 kTypeIdActivityTrackerFree = 0x3F0272FB, // SHA1(ActivityTrackerFree) |
| 194 }; |
| 195 |
| 196 class BASE_EXPORT ScopedThreadActivity |
| 197 : public ThreadActivityTracker::ScopedActivity { |
| 198 public: |
| 199 ScopedThreadActivity(const void* source, |
| 200 ThreadActivityTracker::ActivityType activity, |
| 201 const ThreadActivityTracker::StackEntryData& data) |
| 202 : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(), |
| 203 source, |
| 204 activity, |
| 205 data) {} |
| 206 |
| 207 private: |
| 208 static ThreadActivityTracker* GetOrCreateTracker() { |
| 209 GlobalActivityTracker* global_tracker = Get(); |
| 210 if (!global_tracker) |
| 211 return nullptr; |
| 212 return global_tracker->GetOrCreateTrackerForCurrentThread(); |
| 213 } |
| 214 }; |
| 215 |
| 216 ~GlobalActivityTracker(); |
| 217 |
| 218 static void CreateWithAllocator( |
| 219 std::unique_ptr<PersistentMemoryAllocator> allocator, |
| 220 int stack_depth); |
| 221 |
| 222 static void CreateWithLocalMemory(size_t size, |
| 223 uint64_t id, |
| 224 StringPiece name, |
| 225 int stack_depth); |
| 226 |
| 227 static void CreateWithFile(const FilePath& file_path, |
| 228 size_t size, |
| 229 uint64_t id, |
| 230 StringPiece name, |
| 231 int stack_depth); |
| 232 |
| 233 // Gets the global activity-tracker or null if none exists. |
| 234 static GlobalActivityTracker* Get() { return g_tracker_; } |
| 235 |
| 236 // Gets the persistent-memory-allocator in which data is stored. Callers |
| 237 // can store additional records here to pass additional information to |
| 238 // the analysis process. |
| 239 PersistentMemoryAllocator* allocator() { return allocator_.get(); } |
| 240 |
| 241 // Gets the thread's activity-tracker, assuming it already exists. This |
| 242 // is inline for performance reasons. Ownership remains with the global |
| 243 // tracker. |
| 244 ThreadActivityTracker* GetTrackerForCurrentThread() { |
| 245 void* tracker = this_thread_tracker_.Get(); |
| 246 DCHECK(tracker); |
| 247 return reinterpret_cast<ThreadActivityTracker*>(tracker); |
| 248 } |
| 249 |
| 250 // Gets the thread's activity-tracker or creates one if none exists. This |
| 251 // is inline for performance reasons. Ownership remains with the global |
| 252 // tracker. |
| 253 ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() { |
| 254 void* tracker = this_thread_tracker_.Get(); |
| 255 if (tracker) |
| 256 return reinterpret_cast<ThreadActivityTracker*>(tracker); |
| 257 return CreateTrackerForCurrentThread(); |
| 258 } |
| 259 |
| 260 // Creates an activity-tracker for the current thread. |
| 261 ThreadActivityTracker* CreateTrackerForCurrentThread(); |
| 262 |
| 263 // Releases the activity-tracker for the current thread (for testing only). |
| 264 void ReleaseTrackerForCurrentThreadForTesting(); |
| 265 |
| 266 private: |
| 267 friend class ActivityTrackerTest; |
| 268 |
| 269 class ManagedActivityTracker : public ThreadActivityTracker { |
| 270 public: |
| 271 ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference, |
| 272 void* base, |
| 273 size_t size); |
| 274 ~ManagedActivityTracker() override; |
| 275 |
| 276 private: |
| 277 const PersistentMemoryAllocator::Reference mem_reference_; |
| 278 void* const mem_base_; |
| 279 }; |
| 280 |
| 281 GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator, |
| 282 int stack_depth); |
| 283 |
| 284 // Returns the memory used by an activity-tracker managed by this class. |
| 285 void ReturnTrackerMemory(ManagedActivityTracker* tracker, |
| 286 PersistentMemoryAllocator::Reference mem_reference, |
| 287 void* mem_base); |
| 288 |
| 289 static void OnTLSDestroy(void* value); |
| 290 |
| 291 std::unique_ptr<PersistentMemoryAllocator> allocator_; |
| 292 const size_t stack_memory_size_; |
| 293 |
| 294 base::ThreadLocalStorage::Slot this_thread_tracker_; |
| 295 |
| 296 Lock lock_; |
| 297 std::set<ManagedActivityTracker*> thread_trackers_; |
| 298 std::vector<PersistentMemoryAllocator::Reference> available_memories_; |
| 299 |
| 300 static GlobalActivityTracker* g_tracker_; |
| 301 }; |
| 302 |
| 303 class BASE_EXPORT ScopedTaskActivity |
| 304 : public GlobalActivityTracker::ScopedThreadActivity { |
| 305 public: |
| 306 ScopedTaskActivity(const base::PendingTask& task); |
| 307 }; |
| 308 |
| 309 } // namespace debug |
| 310 } // namespace base |
| 311 |
| 312 #endif // BASE_METRICS_ACTIVITY_TRACKER_H_ |
OLD | NEW |