Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(243)

Side by Side Diff: base/debug/activity_tracker.h

Issue 1980743002: Track thread activities in order to diagnose hangs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@readwrite-mmf
Patch Set: track locks and waitable events Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
11 #include "base/base_export.h"
12 #include "base/feature_list.h"
13 #include "base/files/file_path.h"
14 #include "base/metrics/persistent_memory_allocator.h"
15 #include "base/threading/thread_checker.h"
16 #include "base/threading/thread_local_storage.h"
17
18 namespace base {
19
20 struct PendingTask;
21
22 class Lock;
23 class MemoryMappedFile;
24 class WaitableEvent;
25
26 namespace debug {
27
28 class ThreadActivityAnalyzer;
29
30 #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
31 // Enables the global activity tracker according to a field trial setting,
32 // using the specified |file| (without extension) for storing information
33 // from this run.
34 BASE_EXPORT void SetupGlobalActivityTrackerFieldTrial(const FilePath& file);
35 #endif // !defined(OS_NACL)
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_address;
56 } lock;
57 struct {
58 uint64_t event_address;
59 } event;
60
61 static StackEntryData ForTask(uint64_t sequence) {
62 StackEntryData data;
63 data.task.sequence_id = sequence;
64 return data;
65 }
66
67 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
68 StackEntryData data;
69 data.lock.lock_address = reinterpret_cast<uintptr_t>(lock);
70 return data;
71 }
72
73 static StackEntryData ForEvent(const void* event) {
74 StackEntryData data;
75 data.event.event_address = reinterpret_cast<uintptr_t>(event);
76 return data;
77 }
78 };
79
80 struct StackEntry {
81 int64_t time_ticks;
82 uintptr_t source_address;
83 uint8_t activity_type;
84 StackEntryData data;
85 };
86
87 class BASE_EXPORT ScopedActivity {
88 public:
89 ScopedActivity(ThreadActivityTracker* tracker,
90 const void* source,
91 ActivityType activity,
92 const StackEntryData& data)
93 : tracker_(tracker), source_(source) {
94 if (tracker_)
95 tracker_->PushActivity(source, activity, data);
96 }
97 ~ScopedActivity() {
98 if (tracker_)
99 tracker_->PopActivity(source_);
100 }
101
102 private:
103 ThreadActivityTracker* const tracker_;
104 const void* const source_;
105 };
106
107 // A ThreadActivityTracker runs on top of memory that is managed externally.
108 // It must be large enough for the internal header and a few StackEntry
109 // blocks. See SizeForStackDepth().
110 ThreadActivityTracker(void* base, size_t size);
111 virtual ~ThreadActivityTracker();
112
113 // Indicate that a method of the given (arbitrary) identifier has started.
114 void PushActivity(const void* source,
115 ActivityType activity,
116 const StackEntryData& data);
117
118 // Indicate that a method of the given (arbitrary) identifier has finished.
119 void PopActivity(const void* source);
120
121 // Returns whether the current data is valid or not. It is not valid if
122 // corruption is detected in the header or other data structures. Fetching
123 // a copy of the stack will return nothing if the data is not valid.
124 bool is_valid() { return valid_; }
125
126 // Calculates the memory size required for a given stack depth, including
127 // the internal header structure for the stack.
128 static size_t SizeForStackDepth(int stack_depth);
129
130 private:
131 friend class ThreadActivityAnalyzer;
132 friend class ActivityTrackerTest;
133
134 struct Header;
135
136 Header* const header_;
137 StackEntry* const stack_;
138 const uint32_t stack_slots_;
139
140 bool valid_ = false;
141
142 base::ThreadChecker thread_checker_;
143
144 DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker);
145 };
146
147
148 // Be sure not to create multiple Analyzers for given tracker data as parallel
149 // operation could lead to inconsistencies from concurrent synchronization with
150 // an active tracker.
151 class BASE_EXPORT ThreadActivityAnalyzer {
152 public:
153 using StackEntry = ThreadActivityTracker::StackEntry;
154
155 // Creates an analyzer for an existing activity-tracker. The passed tracker
156 // must live at least as long as the created object. The tracker may continue
157 // to be active even with an attached analyzer.
158 explicit ThreadActivityAnalyzer(ThreadActivityTracker* tracker);
159
160 // Creates an anaylzer for a block of memory held within a persistent-memory
161 // |allocator| at the given |reference|. The memory must live at least as
162 // long as the created object. It's permissable for a tracker to remain active
163 // on the memory from this thread, other threads, or even other processes.
164 // The reference must be to an object of type kTypeIdActivityTracker, above.
165 ThreadActivityAnalyzer(PersistentMemoryAllocator* allocator,
166 PersistentMemoryAllocator::Reference reference);
167
168 // Creates an anaylzer for a block of memory currently or previously in-use
169 // by an activity-tracker. The memory must live at least as long as the
170 // created object. It's permissable for a tracker to remain active on the
171 // memory from this thread, other threads, or even other processes.
172 ThreadActivityAnalyzer(void* base, size_t size);
173
174 ~ThreadActivityAnalyzer();
175
176 // Gets a copy of the current stack contents. The return value is the current
177 // depth of the stack which may be greater than the number of StackEntry
178 // records returned. If so, the returned stack has the "base" of the stack
179 // with later entries omitted.
180 uint32_t SnapshotStack(std::vector<StackEntry>* snapshot);
181
182 private:
183 ThreadActivityTracker tracker_;
184
185 DISALLOW_COPY_AND_ASSIGN(ThreadActivityAnalyzer);
186 };
187
188
189 class BASE_EXPORT GlobalActivityTracker {
190 public:
191 // Type identifiers used when storing in persistent memory so they can be
192 // identified during extraction; the first 4 bytes of the SHA1 of the name
193 // is used as a unique integer. A "version number" is added to the base
194 // so that, if the structure of that object changes, stored older versions
195 // will be safely ignored. These are public so that an external process
196 // can recognize records of this type within an allocator and use them to
197 // create ThreadActivityAnalyzer objects.
198 enum : uint32_t {
199 kTypeIdActivityTracker = 0x5D7381AF + 1, // SHA1(ActivityTracker) v1
200 kTypeIdActivityTrackerFree = 0x3F0272FB, // SHA1(ActivityTrackerFree)
201 };
202
203 class BASE_EXPORT ScopedThreadActivity
204 : public ThreadActivityTracker::ScopedActivity {
205 public:
206 ScopedThreadActivity(const void* source,
207 ThreadActivityTracker::ActivityType activity,
208 const ThreadActivityTracker::StackEntryData& data,
209 bool lock_allowed)
210 : ThreadActivityTracker::ScopedActivity(
211 GetOrCreateTracker(lock_allowed),
212 source,
213 activity,
214 data) {}
215
216 private:
217 static ThreadActivityTracker* GetOrCreateTracker(bool lock_allowed) {
218 GlobalActivityTracker* global_tracker = Get();
219 if (!global_tracker)
220 return nullptr;
221 if (lock_allowed)
222 return global_tracker->GetOrCreateTrackerForCurrentThread();
223 else
224 return global_tracker->GetTrackerForCurrentThread();
225 }
226 };
227
228 ~GlobalActivityTracker();
229
230 static void CreateWithAllocator(
231 std::unique_ptr<PersistentMemoryAllocator> allocator,
232 int stack_depth);
233
234 static void CreateWithLocalMemory(size_t size,
235 uint64_t id,
236 StringPiece name,
237 int stack_depth);
238
239 #if !defined(OS_NACL)
240 static void CreateWithFile(const FilePath& file_path,
241 size_t size,
242 uint64_t id,
243 StringPiece name,
244 int stack_depth);
245 #endif // !defined(OS_NACL)
246
247 // Gets the global activity-tracker or null if none exists.
248 static GlobalActivityTracker* Get() { return g_tracker_; }
249
250 // Gets the persistent-memory-allocator in which data is stored. Callers
251 // can store additional records here to pass additional information to
252 // the analysis process.
253 PersistentMemoryAllocator* allocator() { return allocator_.get(); }
254
255 // Gets the thread's activity-tracker if it exists. This is inline for
256 // performance reasons. Ownership remains with the global tracker.
257 ThreadActivityTracker* GetTrackerForCurrentThread() {
258 void* tracker = this_thread_tracker_.Get();
259 return reinterpret_cast<ThreadActivityTracker*>(tracker);
260 }
261
262 // Gets the thread's activity-tracker or creates one if none exists. This
263 // is inline for performance reasons. Ownership remains with the global
264 // tracker.
265 ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() {
266 ThreadActivityTracker* tracker = GetTrackerForCurrentThread();
267 if (!tracker)
268 tracker = CreateTrackerForCurrentThread();
269 return tracker;
270 }
271
272 // Creates an activity-tracker for the current thread.
273 ThreadActivityTracker* CreateTrackerForCurrentThread();
274
275 // Releases the activity-tracker for the current thread (for testing only).
276 void ReleaseTrackerForCurrentThreadForTesting();
277
278 private:
279 friend class ActivityTrackerTest;
280
281 enum : int {
282 kMaxThreadCount = 100,
283 };
284
285 class ManagedActivityTracker : public ThreadActivityTracker {
286 public:
287 ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference,
288 void* base,
289 size_t size);
290 ~ManagedActivityTracker() override;
291
292 private:
293 const PersistentMemoryAllocator::Reference mem_reference_;
294 void* const mem_base_;
295 };
296
297 GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator,
298 int stack_depth);
299
300 // Returns the memory used by an activity-tracker managed by this class.
301 void ReturnTrackerMemory(ManagedActivityTracker* tracker,
302 PersistentMemoryAllocator::Reference mem_reference,
303 void* mem_base);
304
305 static void OnTLSDestroy(void* value);
306
307 std::unique_ptr<PersistentMemoryAllocator> allocator_;
308 const size_t stack_memory_size_;
309
310 base::ThreadLocalStorage::Slot this_thread_tracker_;
311
312 // These have to be lock-free because lock activity is tracked and causes
313 // re-entry problems.
314 std::atomic<int> thread_tracker_count_;
315 std::atomic<int> available_memories_count_;
316 std::atomic<PersistentMemoryAllocator::Reference>
317 available_memories_[kMaxThreadCount];
318
319 static GlobalActivityTracker* g_tracker_;
320 };
321
322
323 class BASE_EXPORT ScopedTaskActivity
324 : public GlobalActivityTracker::ScopedThreadActivity {
325 public:
326 ScopedTaskActivity(const base::PendingTask& task);
327 };
328
329 class BASE_EXPORT ScopedLockActivity
330 : public GlobalActivityTracker::ScopedThreadActivity {
331 public:
332 ScopedLockActivity(const base::internal::LockImpl* lock);
333 };
334
335 class BASE_EXPORT ScopedEventActivity
336 : public GlobalActivityTracker::ScopedThreadActivity {
337 public:
338 ScopedEventActivity(const base::WaitableEvent* event);
339 };
340
341 } // namespace debug
342 } // namespace base
343
344 #endif // BASE_METRICS_ACTIVITY_TRACKER_H_
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/debug/activity_tracker.cc » ('j') | base/debug/activity_tracker.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698