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

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

Issue 2255503002: New cache for the activity tracker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed some review comments by manzagop 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 | « no previous file | 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 23 matching lines...) Expand all
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 class ThreadActivityTracker; 42 class ThreadActivityTracker;
43 43
44
45 //=============================================================================
46 // This class provides a lock-free FIFO of any atomic type with the
47 // limitation that there must be at least one "invalid" value. This is
48 // built as a completely generic type and can (hopefully) be moved to a
49 // more generally useful place in the future.
50 template <typename T, size_t SIZE, T INVALID_VALUE>
51 class LockFreeSimpleFifo {
52 enum : size_t {
53 CAPACITY = SIZE + 1 // +1 because one slot must always be free.
54 };
55
56 public:
57 LockFreeSimpleFifo() : head_(0), tail_(0) {
58 // Ensure that the underlying atomics are also lock-free. This should
59 // evaluate to a constant at compile time and so produce no code, but
60 // a static_assert will not compile.
61 CHECK(head_.is_lock_free());
62 CHECK(stack_[0].is_lock_free());
63
64 // All elements must be "invalid" to start in order for the push/pop
65 // operations to work.
66 for (size_t i = 0; i < CAPACITY; ++i)
67 stack_[i].store(INVALID_VALUE, std::memory_order_relaxed);
68 }
69
70 T invalid_value() { return INVALID_VALUE; }
71 size_t size() { return SIZE; }
72 size_t used() {
73 return (head_.load(std::memory_order_relaxed) + CAPACITY -
74 tail_.load(std::memory_order_relaxed)) %
75 CAPACITY;
76 }
77 bool empty() {
78 return empty(head_.load(std::memory_order_relaxed),
79 tail_.load(std::memory_order_relaxed));
80 }
81 bool full() {
82 return full(head_.load(std::memory_order_relaxed),
83 tail_.load(std::memory_order_relaxed));
84 }
85
86 // Adds a new |value| to the end of the FIFO and returns true on success
87 // or false if the stack was full.
88 bool push(T value);
89
90 // Retrieves the first value off the FIFO and returns it or the "invalid"
91 // value if the stack is empty.
92 T pop();
93
94 private:
95 // Reports if the stack is empty/full based on explicit head/tail values.
96 // Note that the % operaton in C/C++ is a "remainder" operator and thus will
97 // not provide correct "modulus" arithmetic if the left value is negative.
98 bool empty(size_t head, size_t tail) { return head == tail; }
99 bool full(size_t head, size_t tail) {
100 return (tail + CAPACITY - 1) % CAPACITY == head;
101 }
102
103 std::atomic<T> stack_[CAPACITY];
104 std::atomic<size_t> head_; // One past the newest value; where to push.
105 std::atomic<size_t> tail_; // The oldest value; first to pop.
106
107 DISALLOW_COPY_AND_ASSIGN(LockFreeSimpleFifo);
108 };
109
110 template <typename T, size_t SIZE, T INVALID_VALUE>
111 bool LockFreeSimpleFifo<T, SIZE, INVALID_VALUE>::push(T value) {
112 // Pushing the "invalid" value is not allowed; it would be skipped by pop.
113 CHECK_NE(INVALID_VALUE, value);
114
115 while (true) {
116 // Get the head of the stack and acquire its contents.
117 size_t head = head_.load(std::memory_order_acquire);
118 DCHECK_LE(0U, head);
119 DCHECK_GT(CAPACITY, head);
120
121 // If the stack is full, fail.
122 if (full(head, tail_.load(std::memory_order_relaxed)))
123 return false;
124
125 // Write the value being pushed to the top of the fifo, exchanging it
126 // with the "invalid" value that should be there. If the atomic operation
127 // fails then something else has snuck in and pushed something else to
128 // that slot. A "strong" exchange is used to avoid mistakenly incrementing
129 // past the new value.
130 T value_expected = INVALID_VALUE;
131 if (!stack_[head].compare_exchange_strong(value_expected, value,
132 std::memory_order_release,
133 std::memory_order_relaxed)) {
134 // Something got pushed so increment the head pointer past it. This is
135 // done to handle the case where whatever has written the value somehow
136 // died between writing the value and incrementing the head pointer. In
137 // that (unusual) case, simply trying again would lead to an infinite
138 // loop. We try but don't care if it fails because failure just indicates
139 // that the other thread is behaving properly. A "strong" exchange is
140 // necessary to avoid false failures.
141 head_.compare_exchange_strong(head, head + 1,
manzagop (departed) 2016/08/18 14:14:46 I've just noticed this could do a spurious push, i
142 std::memory_order_relaxed,
143 std::memory_order_relaxed);
144
145 // Try again.
146 continue;
147 }
148
149 // Increment the head, releasing the newly stored value. This could fail
150 // if another thread tried a simultaneous push, saw this value, and
151 // bumped the head in the crash-safty increment just above. Because the
152 // result is ignored, a "strong" exchange is necessary to ensure a
153 // false failure doesn't occur.
154 head_.compare_exchange_strong(head, head + 1,
155 std::memory_order_release,
156 std::memory_order_relaxed);
157
158 // Success!
159 return true;
160 }
161 }
162
163 template <typename T, size_t SIZE, T INVALID_VALUE>
164 T LockFreeSimpleFifo<T, SIZE, INVALID_VALUE>::pop() {
165 while (true) {
166 // Get the current number of elements on the stack.
167 size_t tail = tail_.load(std::memory_order_relaxed);
168 DCHECK_LE(0U, tail);
169 DCHECK_GT(CAPACITY, tail);
170
171 // If the stack is empty, fail.
172 if (empty(head_.load(std::memory_order_relaxed), tail))
173 return INVALID_VALUE;
174
175 // Read a value from the bottom of the fifo, writing the "invalid" value
176 // in its place.
177 T value = stack_[tail].exchange(INVALID_VALUE, std::memory_order_relaxed);
178 if (value == INVALID_VALUE) {
179 // Another thread has already taken the "tail" value so increment the
180 // tail pointer past it. This is done to handle the case where whatever
181 // took the value somehow died between reading the value and incrementing
182 // the tail pointer. In that (unusual) case, simply trying again would
183 // lead to an infinite loop. We try but don't care if it fails because
184 // that just indicates that the other thread is behaving properly. A
185 // "strong" exchange is necessary to avoid false failures.
186 tail_.compare_exchange_strong(tail, tail + 1,
187 std::memory_order_relaxed,
188 std::memory_order_relaxed);
189
190 // Try again.
191 continue;
192 }
193
194 // Increment the tail, releasing the newly stored value. This could fail
195 // if another thread tried a simultaneous pop, saw the update, and
196 // bumped the tail in the crash-safty increment just above. Because the
197 // result is ignored, a "strong" exchange is necessary to ensure a
198 // false failure doesn't occur.
199 tail_.compare_exchange_strong(tail, tail + 1,
200 std::memory_order_release,
201 std::memory_order_relaxed);
202
203 // Success!
204 DCHECK_NE(INVALID_VALUE, value);
205 return value;
206 }
207 }
208 //=============================================================================
209
210
44 enum : int { 211 enum : int {
45 // The maximum number of call-stack addresses stored per activity. This 212 // The maximum number of call-stack addresses stored per activity. This
46 // cannot be changed without also changing the version number of the 213 // cannot be changed without also changing the version number of the
47 // structure. See kTypeIdActivityTracker in GlobalActivityTracker. 214 // structure. See kTypeIdActivityTracker in GlobalActivityTracker.
48 kActivityCallStackSize = 10, 215 kActivityCallStackSize = 10,
49 }; 216 };
50 217
51 // The data associated with an activity is dependent upon the activity type. 218 // 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 219 // 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 220 // sized types to ensure no interoperability problems between 32-bit and
(...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 // The size (in bytes) of memory required by a ThreadActivityTracker to 668 // The size (in bytes) of memory required by a ThreadActivityTracker to
502 // provide the stack-depth requested during construction. 669 // provide the stack-depth requested during construction.
503 const size_t stack_memory_size_; 670 const size_t stack_memory_size_;
504 671
505 // The activity tracker for the currently executing thread. 672 // The activity tracker for the currently executing thread.
506 base::ThreadLocalStorage::Slot this_thread_tracker_; 673 base::ThreadLocalStorage::Slot this_thread_tracker_;
507 674
508 // These have to be lock-free because lock activity is tracked and causes 675 // These have to be lock-free because lock activity is tracked and causes
509 // re-entry problems. 676 // re-entry problems.
510 std::atomic<int> thread_tracker_count_; 677 std::atomic<int> thread_tracker_count_;
511 std::atomic<int> available_memories_count_; 678 LockFreeSimpleFifo<PersistentMemoryAllocator::Reference,
512 std::atomic<PersistentMemoryAllocator::Reference> 679 kMaxThreadCount,
513 available_memories_[kMaxThreadCount]; 680 PersistentMemoryAllocator::kReferenceNull>
681 available_memories_;
514 682
515 // The active global activity tracker. 683 // The active global activity tracker.
516 static GlobalActivityTracker* g_tracker_; 684 static GlobalActivityTracker* g_tracker_;
517 685
518 DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker); 686 DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker);
519 }; 687 };
520 688
521 689
522 // Record entry in to and out of an arbitrary block of code. 690 // Record entry in to and out of an arbitrary block of code.
523 class BASE_EXPORT ScopedActivity 691 class BASE_EXPORT ScopedActivity
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
607 explicit ScopedProcessWaitActivity(const base::Process* process); 775 explicit ScopedProcessWaitActivity(const base::Process* process);
608 private: 776 private:
609 DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); 777 DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity);
610 }; 778 };
611 #endif 779 #endif
612 780
613 } // namespace debug 781 } // namespace debug
614 } // namespace base 782 } // namespace base
615 783
616 #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_ 784 #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_
OLDNEW
« no previous file with comments | « no previous file | base/debug/activity_tracker.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698