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

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

Issue 2235273002: Refactor embedded structures to top-level scope. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: removed new ResourceData class (for later CL) Created 4 years, 4 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
« no previous file with comments | « base/debug/activity_analyzer_unittest.cc ('k') | base/debug/activity_tracker.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Activity tracking provides a low-overhead method of collecting information 5 // Activity tracking provides a low-overhead method of collecting information
6 // about the state of the application for analysis both while it is running 6 // about the state of the application for analysis both while it is running
7 // and after it has terminated unexpectedly. Its primary purpose is to help 7 // and after it has terminated unexpectedly. Its primary purpose is to help
8 // locate reasons the browser becomes unresponsive by providing insight into 8 // locate reasons the browser becomes unresponsive by providing insight into
9 // what all the various threads and processes are (or were) doing. 9 // what all the various threads and processes are (or were) doing.
10 10
(...skipping 21 matching lines...) Expand all
32 32
33 class FilePath; 33 class FilePath;
34 class Lock; 34 class Lock;
35 class MemoryMappedFile; 35 class MemoryMappedFile;
36 class PlatformThreadHandle; 36 class PlatformThreadHandle;
37 class Process; 37 class Process;
38 class WaitableEvent; 38 class WaitableEvent;
39 39
40 namespace debug { 40 namespace debug {
41 41
42 // This class manages tracking a stack of activities for a single thread in 42 class ThreadActivityTracker;
43 // a persistent manner, implementing a bounded-size stack in a fixed-size
44 // memory allocation. In order to support an operational mode where another
45 // thread is analyzing this data in real-time, atomic operations are used
46 // where necessary to guarantee a consistent view from the outside.
47 //
48 // This class is not generally used directly but instead managed by the
49 // GlobalActivityTracker instance and updated using Scoped*Activity local
50 // objects.
51 class BASE_EXPORT ThreadActivityTracker {
52 public:
53 enum : int {
54 // The maximum number of call-stack addresses stored per activity. This
55 // cannot be changed without also changing the version number of the
56 // structure. See kTypeIdActivityTracker in GlobalActivityTracker.
57 kActivityCallStackSize = 10
58 };
59 43
44 enum : int {
45 // The maximum number of call-stack addresses stored per activity. This
46 // cannot be changed without also changing the version number of the
47 // structure. See kTypeIdActivityTracker in GlobalActivityTracker.
48 kActivityCallStackSize = 10,
49 };
50
51 // The data associated with an activity is dependent upon the activity type.
52 // This union defines all of the various fields. All fields must be explicitly
53 // sized types to ensure no interoperability problems between 32-bit and
54 // 64-bit systems.
55 union ActivityData {
56 // Generic activities don't have any defined structure.
57 struct {
58 uint32_t id; // An arbitrary identifier used for association.
59 int32_t info; // An arbitrary value used for information purposes.
60 } generic;
61 struct {
62 uint64_t sequence_id; // The sequence identifier of the posted task.
63 } task;
64 struct {
65 uint64_t lock_address; // The memory address of the lock object.
66 } lock;
67 struct {
68 uint64_t event_address; // The memory address of the event object.
69 } event;
70 struct {
71 int64_t thread_id; // A unique identifier for a thread within a process.
72 } thread;
73 struct {
74 int64_t process_id; // A unique identifier for a process.
75 } process;
76
77 // These methods create an ActivityData object from the appropriate
78 // parameters. Objects of this type should always be created this way to
79 // ensure that no fields remain unpopulated should the set of recorded
80 // fields change. They're defined inline where practical because they
81 // reduce to loading a small local structure with a few values, roughly
82 // the same as loading all those values into parameters.
83
84 static ActivityData ForGeneric(uint32_t id, int32_t info) {
85 ActivityData data;
86 data.generic.id = id;
87 data.generic.info = info;
88 return data;
89 }
90
91 static ActivityData ForTask(uint64_t sequence) {
92 ActivityData data;
93 data.task.sequence_id = sequence;
94 return data;
95 }
96
97 static ActivityData ForLock(const void* lock) {
98 ActivityData data;
99 data.lock.lock_address = reinterpret_cast<uintptr_t>(lock);
100 return data;
101 }
102
103 static ActivityData ForEvent(const void* event) {
104 ActivityData data;
105 data.event.event_address = reinterpret_cast<uintptr_t>(event);
106 return data;
107 }
108
109 static ActivityData ForThread(const PlatformThreadHandle& handle);
110 static ActivityData ForThread(const int64_t id) {
111 ActivityData data;
112 data.thread.thread_id = id;
113 return data;
114 }
115
116 static ActivityData ForProcess(const int64_t id) {
117 ActivityData data;
118 data.process.process_id = id;
119 return data;
120 }
121 };
122
123 // A "null" activity-data that can be passed to indicate "do not change".
124 extern const ActivityData kNullActivityData;
125
126 // This structure is the full contents recorded for every activity pushed
127 // onto the stack. The |activity_type| indicates what is actually stored in
128 // the |data| field. All fields must be explicitly sized types to ensure no
129 // interoperability problems between 32-bit and 64-bit systems.
130 struct Activity {
60 // The type of an activity on the stack. Activities are broken into 131 // The type of an activity on the stack. Activities are broken into
61 // categories with the category ID taking the top 4 bits and the lower 132 // categories with the category ID taking the top 4 bits and the lower
62 // bits representing an action within that category. This combination 133 // bits representing an action within that category. This combination
63 // makes it easy to "switch" based on the type during analysis. 134 // makes it easy to "switch" based on the type during analysis.
64 enum ActivityType : uint8_t { 135 enum Type : uint8_t {
65 // This "null" constant is used to indicate "do not change" in calls. 136 // This "null" constant is used to indicate "do not change" in calls.
66 ACT_NULL = 0, 137 ACT_NULL = 0,
67 138
68 // Task activities involve callbacks posted to a thread or thread-pool 139 // Task activities involve callbacks posted to a thread or thread-pool
69 // using the PostTask() method or any of its friends. 140 // using the PostTask() method or any of its friends.
70 ACT_TASK = 1 << 4, 141 ACT_TASK = 1 << 4,
71 ACT_TASK_RUN = ACT_TASK, 142 ACT_TASK_RUN = ACT_TASK,
72 143
73 // Lock activities involve the acquisition of "mutex" locks. 144 // Lock activities involve the acquisition of "mutex" locks.
74 ACT_LOCK = 2 << 4, 145 ACT_LOCK = 2 << 4,
(...skipping 17 matching lines...) Expand all
92 163
93 // Generic activities are user defined and can be anything. 164 // Generic activities are user defined and can be anything.
94 ACT_GENERIC = 15 << 4, 165 ACT_GENERIC = 15 << 4,
95 166
96 // These constants can be used to separate the category and action from 167 // These constants can be used to separate the category and action from
97 // a combined activity type. 168 // a combined activity type.
98 ACT_CATEGORY_MASK = 0xF << 4, 169 ACT_CATEGORY_MASK = 0xF << 4,
99 ACT_ACTION_MASK = 0xF 170 ACT_ACTION_MASK = 0xF
100 }; 171 };
101 172
102 // The data associated with an activity is dependent upon the activity type. 173 // Internal representation of time. During collection, this is in "ticks"
103 // This union defines all of the various fields. All fields must be explicitly 174 // but when returned in a snapshot, it is "wall time".
104 // sized types to ensure no interoperability problems between 32-bit and 175 int64_t time_internal;
105 // 64-bit systems.
106 union ActivityData {
107 // Generic activities don't have any defined structure.
108 struct {
109 uint32_t id; // An arbitrary identifier used for association.
110 int32_t info; // An arbitrary value used for information purposes.
111 } generic;
112 struct {
113 uint64_t sequence_id; // The sequence identifier of the posted task.
114 } task;
115 struct {
116 uint64_t lock_address; // The memory address of the lock object.
117 } lock;
118 struct {
119 uint64_t event_address; // The memory address of the event object.
120 } event;
121 struct {
122 int64_t thread_id; // A unique identifier for a thread within a process.
123 } thread;
124 struct {
125 int64_t process_id; // A unique identifier for a process.
126 } process;
127 176
128 // These methods create an ActivityData object from the appropriate 177 // The address that is the origin of the activity if it not obvious from
129 // parameters. Objects of this type should always be created this way to 178 // the call stack. This is useful for things like tasks that are posted
130 // ensure that no fields remain unpopulated should the set of recorded 179 // from a completely different thread though most activities will leave
131 // fields change. They're defined inline where practical because they 180 // it null.
132 // reduce to loading a small local structure with a few values, roughly 181 uint64_t origin_address;
133 // the same as loading all those values into parameters.
134 182
135 static ActivityData ForGeneric(uint32_t id, int32_t info) { 183 // Array of program-counters that make up the top of the call stack.
136 ActivityData data; 184 // Despite the fixed size, this list is always null-terminated. Entries
137 data.generic.id = id; 185 // after the terminator have no meaning and may or may not also be null.
138 data.generic.info = info; 186 // The list will be completely empty if call-stack collection is not
139 return data; 187 // enabled.
140 } 188 uint64_t call_stack[kActivityCallStackSize];
141 189
142 static ActivityData ForTask(uint64_t sequence) { 190 // The (enumerated) type of the activity. This defines what fields of the
143 ActivityData data; 191 // |data| record are valid.
144 data.task.sequence_id = sequence; 192 uint8_t activity_type;
145 return data;
146 }
147 193
148 static ActivityData ForLock(const void* lock) { 194 // Padding to ensure that the next member begins on a 64-bit boundary
149 ActivityData data; 195 // even on 32-bit builds which ensures inter-operability between CPU
150 data.lock.lock_address = reinterpret_cast<uintptr_t>(lock); 196 // architectures. New fields can be taken from this space.
151 return data; 197 uint8_t padding[7];
152 }
153 198
154 static ActivityData ForEvent(const void* event) { 199 // Information specific to the |activity_type|.
155 ActivityData data; 200 ActivityData data;
156 data.event.event_address = reinterpret_cast<uintptr_t>(event);
157 return data;
158 }
159 201
160 static ActivityData ForThread(const PlatformThreadHandle& handle); 202 static void FillFrom(Activity* activity,
161 static ActivityData ForThread(const int64_t id) { 203 const void* origin,
162 ActivityData data; 204 Type type,
163 data.thread.thread_id = id; 205 const ActivityData& data);
164 return data; 206 };
165 }
166 207
167 static ActivityData ForProcess(const int64_t id) { 208 // This structure holds a copy of all the internal data at the moment the
168 ActivityData data; 209 // "snapshot" operation is done. It is disconnected from the live tracker
169 data.process.process_id = id; 210 // so that continued operation of the thread will not cause changes here.
170 return data; 211 struct BASE_EXPORT ActivitySnapshot {
171 } 212 // Explicit constructor/destructor are needed because of complex types
172 }; 213 // with non-trivial default constructors and destructors.
214 ActivitySnapshot();
215 ~ActivitySnapshot();
173 216
174 // This structure is the full contents recorded for every activity pushed 217 // The name of the thread as set when it was created. The name may be
175 // onto the stack. The |activity_type| indicates what is actually stored in 218 // truncated due to internal length limitations.
176 // the |data| field. All fields must be explicitly sized types to ensure no 219 std::string thread_name;
177 // interoperability problems between 32-bit and 64-bit systems.
178 struct Activity {
179 // Internal representation of time. During collection, this is in "ticks"
180 // but when returned in a snapshot, it is "wall time".
181 int64_t time_internal;
182 220
183 // The address that is the origin of the activity if it not obvious from 221 // The process and thread IDs. These values have no meaning other than
184 // the call stack. This is useful for things like tasks that are posted 222 // they uniquely identify a running process and a running thread within
185 // from a completely different thread though most activities will leave 223 // that process. Thread-IDs can be re-used across different processes
186 // it null. 224 // and both can be re-used after the process/thread exits.
187 uint64_t origin_address; 225 int64_t process_id = 0;
226 int64_t thread_id = 0;
188 227
189 // Array of program-counters that make up the top of the call stack. 228 // The current stack of activities that are underway for this thread. It
190 // Despite the fixed size, this list is always null-terminated. Entries 229 // is limited in its maximum size with later entries being left off.
191 // after the terminator have no meaning and may or may not also be null. 230 std::vector<Activity> activity_stack;
192 // The list will be completely empty if call-stack collection is not
193 // enabled.
194 uint64_t call_stack[kActivityCallStackSize];
195 231
196 // The (enumerated) type of the activity. This defines what fields of the 232 // The current total depth of the activity stack, including those later
197 // |data| record are valid. 233 // entries not recorded in the |activity_stack| vector.
198 uint8_t activity_type; 234 uint32_t activity_stack_depth = 0;
235 };
199 236
200 // Padding to ensure that the next member begins on a 64-bit boundary
201 // even on 32-bit builds which ensures inter-operability between CPU
202 // architectures. New fields can be taken from this space.
203 uint8_t padding[7];
204 237
205 // Information specific to the |activity_type|. 238 // This class manages tracking a stack of activities for a single thread in
206 ActivityData data; 239 // a persistent manner, implementing a bounded-size stack in a fixed-size
207 }; 240 // memory allocation. In order to support an operational mode where another
208 241 // thread is analyzing this data in real-time, atomic operations are used
209 // This structure holds a copy of all the internal data at the moment the 242 // where necessary to guarantee a consistent view from the outside.
210 // "snapshot" operation is done. It is disconnected from the live tracker 243 //
211 // so that continued operation of the thread will not cause changes here. 244 // This class is not generally used directly but instead managed by the
212 struct BASE_EXPORT ActivitySnapshot { 245 // GlobalActivityTracker instance and updated using Scoped*Activity local
213 // Explicit constructor/destructor are needed because of complex types 246 // objects.
214 // with non-trivial default constructors and destructors. 247 class BASE_EXPORT ThreadActivityTracker {
215 ActivitySnapshot(); 248 public:
216 ~ActivitySnapshot();
217
218 // The name of the thread as set when it was created. The name may be
219 // truncated due to internal length limitations.
220 std::string thread_name;
221
222 // The process and thread IDs. These values have no meaning other than
223 // they uniquely identify a running process and a running thread within
224 // that process. Thread-IDs can be re-used across different processes
225 // and both can be re-used after the process/thread exits.
226 int64_t process_id = 0;
227 int64_t thread_id = 0;
228
229 // The current stack of activities that are underway for this thread. It
230 // is limited in its maximum size with later entries being left off.
231 std::vector<Activity> activity_stack;
232
233 // The current total depth of the activity stack, including those later
234 // entries not recorded in the |activity_stack| vector.
235 uint32_t activity_stack_depth = 0;
236 };
237
238 // This is the base class for having the compiler manage an activity on the 249 // This is the base class for having the compiler manage an activity on the
239 // tracker's stack. It does nothing but call methods on the passed |tracker| 250 // tracker's stack. It does nothing but call methods on the passed |tracker|
240 // if it is not null, making it safe (and cheap) to create these objects 251 // if it is not null, making it safe (and cheap) to create these objects
241 // even if activity tracking is not enabled. 252 // even if activity tracking is not enabled.
242 class BASE_EXPORT ScopedActivity { 253 class BASE_EXPORT ScopedActivity {
243 public: 254 public:
244 ScopedActivity(ThreadActivityTracker* tracker, 255 ScopedActivity(ThreadActivityTracker* tracker,
245 const void* origin, 256 const void* origin,
246 ActivityType type, 257 Activity::Type type,
247 const ActivityData& data) 258 const ActivityData& data)
248 : tracker_(tracker) { 259 : tracker_(tracker) {
249 if (tracker_) 260 if (tracker_)
250 tracker_->PushActivity(origin, type, data); 261 tracker_->PushActivity(origin, type, data);
251 } 262 }
252 263
253 ~ScopedActivity() { 264 ~ScopedActivity() {
254 if (tracker_) 265 if (tracker_)
255 tracker_->PopActivity(); 266 tracker_->PopActivity();
256 } 267 }
257 268
258 void ChangeTypeAndData(ActivityType type, const ActivityData& data) { 269 void ChangeTypeAndData(Activity::Type type, const ActivityData& data) {
259 if (tracker_) 270 if (tracker_)
260 tracker_->ChangeActivity(type, data); 271 tracker_->ChangeActivity(type, data);
261 } 272 }
262 273
263 private: 274 private:
264 // The thread tracker to which this object reports. It can be null if 275 // The thread tracker to which this object reports. It can be null if
265 // activity tracking is not (yet) enabled. 276 // activity tracking is not (yet) enabled.
266 ThreadActivityTracker* const tracker_; 277 ThreadActivityTracker* const tracker_;
267 278
268 DISALLOW_COPY_AND_ASSIGN(ScopedActivity); 279 DISALLOW_COPY_AND_ASSIGN(ScopedActivity);
269 }; 280 };
270 281
271 // A ThreadActivityTracker runs on top of memory that is managed externally. 282 // A ThreadActivityTracker runs on top of memory that is managed externally.
272 // It must be large enough for the internal header and a few Activity 283 // It must be large enough for the internal header and a few Activity
273 // blocks. See SizeForStackDepth(). 284 // blocks. See SizeForStackDepth().
274 ThreadActivityTracker(void* base, size_t size); 285 ThreadActivityTracker(void* base, size_t size);
275 virtual ~ThreadActivityTracker(); 286 virtual ~ThreadActivityTracker();
276 287
277 // Indicates that an activity has started from a given |origin| address in 288 // Indicates that an activity has started from a given |origin| address in
278 // the code, though it can be null if the creator's address is not known. 289 // the code, though it can be null if the creator's address is not known.
279 // The |type| and |data| describe the activity. 290 // The |type| and |data| describe the activity.
280 void PushActivity(const void* origin, 291 void PushActivity(const void* origin,
281 ActivityType type, 292 Activity::Type type,
282 const ActivityData& data); 293 const ActivityData& data);
283 294
284 // Changes the activity |type| and |data| of the top-most entry on the stack. 295 // Changes the activity |type| and |data| of the top-most entry on the stack.
285 // This is useful if the information has changed and it is desireable to 296 // This is useful if the information has changed and it is desireable to
286 // track that change without creating a new stack entry. If the type is 297 // track that change without creating a new stack entry. If the type is
287 // ACT_NULL or the data is kNullActivityData then that value will remain 298 // ACT_NULL or the data is kNullActivityData then that value will remain
288 // unchanged. The type, if changed, must remain in the same category. 299 // unchanged. The type, if changed, must remain in the same category.
289 // Changing both is not atomic so a snapshot operation could occur between 300 // Changing both is not atomic so a snapshot operation could occur between
290 // the update of |type| and |data| or between update of |data| fields. 301 // the update of |type| and |data| or between update of |data| fields.
291 void ChangeActivity(ActivityType type, const ActivityData& data); 302 void ChangeActivity(Activity::Type type, const ActivityData& data);
292 303
293 // Indicates that an activity has completed. 304 // Indicates that an activity has completed.
294 void PopActivity(); 305 void PopActivity();
295 306
296 // Returns whether the current data is valid or not. It is not valid if 307 // Returns whether the current data is valid or not. It is not valid if
297 // corruption has been detected in the header or other data structures. 308 // corruption has been detected in the header or other data structures.
298 bool IsValid() const; 309 bool IsValid() const;
299 310
300 // Gets a copy of the tracker contents for analysis. Returns false if a 311 // Gets a copy of the tracker contents for analysis. Returns false if a
301 // snapshot was not possible, perhaps because the data is not valid; the 312 // snapshot was not possible, perhaps because the data is not valid; the
302 // contents of |output_snapshot| are undefined in that case. The current 313 // contents of |output_snapshot| are undefined in that case. The current
303 // implementation does not support concurrent snapshot operations. 314 // implementation does not support concurrent snapshot operations.
304 bool Snapshot(ActivitySnapshot* output_snapshot) const; 315 bool Snapshot(ActivitySnapshot* output_snapshot) const;
305 316
306 // Calculates the memory size required for a given stack depth, including 317 // Calculates the memory size required for a given stack depth, including
307 // the internal header structure for the stack. 318 // the internal header structure for the stack.
308 static size_t SizeForStackDepth(int stack_depth); 319 static size_t SizeForStackDepth(int stack_depth);
309 320
310 // A "null" activity-data that can be passed to indicate "do not change".
311 static const ActivityData kNullActivityData;
312
313 private: 321 private:
314 friend class ActivityTrackerTest; 322 friend class ActivityTrackerTest;
315 323
316 // This structure contains all the common information about the thread so 324 // This structure contains all the common information about the thread so
317 // it doesn't have to be repeated in every entry on the stack. It is defined 325 // it doesn't have to be repeated in every entry on the stack. It is defined
318 // and used completely within the .cc file. 326 // and used completely within the .cc file.
319 struct Header; 327 struct Header;
320 328
321 Header* const header_; // Pointer to the Header structure. 329 Header* const header_; // Pointer to the Header structure.
322 Activity* const stack_; // The stack of activities. 330 Activity* const stack_; // The stack of activities.
(...skipping 24 matching lines...) Expand all
347 }; 355 };
348 356
349 // This is a thin wrapper around the thread-tracker's ScopedActivity that 357 // This is a thin wrapper around the thread-tracker's ScopedActivity that
350 // accesses the global tracker to provide some of the information, notably 358 // accesses the global tracker to provide some of the information, notably
351 // which thread-tracker to use. It is safe to create even if activity 359 // which thread-tracker to use. It is safe to create even if activity
352 // tracking is not enabled. 360 // tracking is not enabled.
353 class BASE_EXPORT ScopedThreadActivity 361 class BASE_EXPORT ScopedThreadActivity
354 : public ThreadActivityTracker::ScopedActivity { 362 : public ThreadActivityTracker::ScopedActivity {
355 public: 363 public:
356 ScopedThreadActivity(const void* origin, 364 ScopedThreadActivity(const void* origin,
357 ThreadActivityTracker::ActivityType type, 365 Activity::Type type,
358 const ThreadActivityTracker::ActivityData& data, 366 const ActivityData& data,
359 bool lock_allowed) 367 bool lock_allowed)
360 : ThreadActivityTracker::ScopedActivity( 368 : ThreadActivityTracker::ScopedActivity(
361 GetOrCreateTracker(lock_allowed), 369 GetOrCreateTracker(lock_allowed),
362 origin, 370 origin,
363 type, 371 type,
364 data) {} 372 data) {}
365 373
366 private: 374 private:
367 // Gets (or creates) a tracker for the current thread. If locking is not 375 // Gets (or creates) a tracker for the current thread. If locking is not
368 // allowed (because a lock is being tracked which would cause recursion) 376 // allowed (because a lock is being tracked which would cause recursion)
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
599 explicit ScopedProcessWaitActivity(const base::Process* process); 607 explicit ScopedProcessWaitActivity(const base::Process* process);
600 private: 608 private:
601 DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); 609 DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity);
602 }; 610 };
603 #endif 611 #endif
604 612
605 } // namespace debug 613 } // namespace debug
606 } // namespace base 614 } // namespace base
607 615
608 #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_ 616 #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_
OLDNEW
« no previous file with comments | « base/debug/activity_analyzer_unittest.cc ('k') | base/debug/activity_tracker.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698