OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 #include "base/tracked_objects.h" | 5 #include "base/tracked_objects.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 | 8 |
9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
12 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
13 #include "base/threading/thread_restrictions.h" | 13 #include "base/threading/thread_restrictions.h" |
14 | 14 |
15 using base::TimeDelta; | 15 using base::TimeDelta; |
16 | 16 |
17 namespace tracked_objects { | 17 namespace tracked_objects { |
18 | 18 |
19 // A TLS slot to the TrackRegistry for the current thread. | 19 // A TLS slot to the TrackRegistry for the current thread. |
20 // static | 20 // static |
21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); | 21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); |
22 | 22 |
23 // A global state variable to prevent repeated initialization during tests. | 23 // A global state variable to prevent repeated initialization during tests. |
24 // static | 24 // static |
25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; | 25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; |
26 | 26 |
| 27 // A locked protected counter to assign sequence number to threads. |
| 28 // static |
| 29 int ThreadData::thread_number_counter = 0; |
| 30 |
27 //------------------------------------------------------------------------------ | 31 //------------------------------------------------------------------------------ |
28 // Death data tallies durations when a death takes place. | 32 // Death data tallies durations when a death takes place. |
29 | 33 |
30 void DeathData::RecordDeath(const TimeDelta& duration) { | 34 void DeathData::RecordDeath(const TimeDelta& queue_duration, |
| 35 const TimeDelta& run_duration) { |
31 ++count_; | 36 ++count_; |
32 life_duration_ += duration; | 37 queue_duration_ += queue_duration; |
33 int64 milliseconds = duration.InMilliseconds(); | 38 run_duration_ += run_duration; |
34 square_duration_ += milliseconds * milliseconds; | |
35 } | 39 } |
36 | 40 |
37 int DeathData::AverageMsDuration() const { | 41 int DeathData::AverageMsRunDuration() const { |
38 return static_cast<int>(life_duration_.InMilliseconds() / count_); | 42 return static_cast<int>(run_duration_.InMilliseconds() / count_); |
39 } | 43 } |
40 | 44 |
41 double DeathData::StandardDeviation() const { | 45 int DeathData::AverageMsQueueDuration() const { |
42 double average = AverageMsDuration(); | 46 return static_cast<int>(queue_duration_.InMilliseconds() / count_); |
43 double variance = static_cast<float>(square_duration_)/count_ | |
44 - average * average; | |
45 return sqrt(variance); | |
46 } | 47 } |
47 | 48 |
48 | |
49 void DeathData::AddDeathData(const DeathData& other) { | 49 void DeathData::AddDeathData(const DeathData& other) { |
50 count_ += other.count_; | 50 count_ += other.count_; |
51 life_duration_ += other.life_duration_; | 51 queue_duration_ += other.queue_duration_; |
52 square_duration_ += other.square_duration_; | 52 run_duration_ += other.run_duration_; |
53 } | 53 } |
54 | 54 |
55 void DeathData::Write(std::string* output) const { | 55 void DeathData::Write(std::string* output) const { |
56 if (!count_) | 56 if (!count_) |
57 return; | 57 return; |
58 if (1 == count_) { | 58 base::StringAppendF(output, "%s:%d, ", |
59 base::StringAppendF(output, "(1)Life in %dms ", AverageMsDuration()); | 59 (count_ == 1) ? "Life" : "Lives", count_); |
60 } else { | 60 base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ", |
61 base::StringAppendF(output, "(%d)Lives %dms/life ", | 61 run_duration_.InMilliseconds(), |
62 count_, AverageMsDuration()); | 62 AverageMsRunDuration()); |
63 } | 63 base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ", |
| 64 queue_duration_.InMilliseconds(), |
| 65 AverageMsQueueDuration()); |
64 } | 66 } |
65 | 67 |
66 void DeathData::Clear() { | 68 void DeathData::Clear() { |
67 count_ = 0; | 69 count_ = 0; |
68 life_duration_ = TimeDelta(); | 70 queue_duration_ = TimeDelta(); |
69 square_duration_ = 0; | 71 run_duration_ = TimeDelta(); |
70 } | 72 } |
71 | 73 |
72 //------------------------------------------------------------------------------ | 74 //------------------------------------------------------------------------------ |
73 BirthOnThread::BirthOnThread(const Location& location) | 75 BirthOnThread::BirthOnThread(const Location& location) |
74 : location_(location), | 76 : location_(location), |
75 birth_thread_(ThreadData::current()) { } | 77 birth_thread_(ThreadData::Get()) { } |
76 | 78 |
77 //------------------------------------------------------------------------------ | 79 //------------------------------------------------------------------------------ |
78 Births::Births(const Location& location) | 80 Births::Births(const Location& location) |
79 : BirthOnThread(location), | 81 : BirthOnThread(location), |
80 birth_count_(1) { } | 82 birth_count_(1) { } |
81 | 83 |
82 //------------------------------------------------------------------------------ | 84 //------------------------------------------------------------------------------ |
83 // ThreadData maintains the central data for all births and death. | 85 // ThreadData maintains the central data for all births and death. |
84 | 86 |
85 // static | 87 // static |
86 ThreadData* ThreadData::first_ = NULL; | 88 ThreadData* ThreadData::first_ = NULL; |
87 // static | 89 // static |
88 base::Lock ThreadData::list_lock_; | 90 base::Lock ThreadData::list_lock_; |
89 | 91 |
90 // static | 92 // static |
91 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 93 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
92 | 94 |
| 95 ThreadData::ThreadData(const std::string& suggested_name) : next_(NULL) { |
| 96 DCHECK_GE(suggested_name.size(), 0u); |
| 97 thread_name_ = suggested_name; |
| 98 } |
| 99 |
93 ThreadData::ThreadData() : next_(NULL) { | 100 ThreadData::ThreadData() : next_(NULL) { |
94 // This shouldn't use the MessageLoop::current() LazyInstance since this might | 101 int thread_number; |
95 // be used on a non-joinable thread. | 102 { |
96 // http://crbug.com/62728 | 103 base::AutoLock lock(list_lock_); |
97 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | 104 thread_number = ++thread_number_counter; |
98 message_loop_ = MessageLoop::current(); | 105 } |
| 106 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); |
99 } | 107 } |
100 | 108 |
101 ThreadData::~ThreadData() {} | 109 ThreadData::~ThreadData() {} |
102 | 110 |
103 // static | 111 // static |
104 ThreadData* ThreadData::current() { | 112 void ThreadData::InitializeThreadContext(const std::string& suggested_name) { |
105 if (!tls_index_.initialized()) | 113 if (!tls_index_.initialized()) |
106 return NULL; | 114 return; // For unittests only. |
107 | 115 RegisterCurrentContext(new ThreadData(suggested_name)); |
108 ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get()); | |
109 if (!registry) { | |
110 // We have to create a new registry for ThreadData. | |
111 bool too_late_to_create = false; | |
112 { | |
113 registry = new ThreadData; | |
114 base::AutoLock lock(list_lock_); | |
115 // Use lock to insure we have most recent status. | |
116 if (!IsActive()) { | |
117 too_late_to_create = true; | |
118 } else { | |
119 // Use lock to insert into list. | |
120 registry->next_ = first_; | |
121 first_ = registry; | |
122 } | |
123 } // Release lock. | |
124 if (too_late_to_create) { | |
125 delete registry; | |
126 registry = NULL; | |
127 } else { | |
128 tls_index_.Set(registry); | |
129 } | |
130 } | |
131 return registry; | |
132 } | 116 } |
133 | 117 |
134 // static | 118 // static |
| 119 ThreadData* ThreadData::Get() { |
| 120 if (!tls_index_.initialized()) |
| 121 return NULL; // For unittests only. |
| 122 ThreadData* registered = static_cast<ThreadData*>(tls_index_.Get()); |
| 123 if (!registered) { |
| 124 // We have to create a new registry entry for this ThreadData. |
| 125 // TODO(jar): Host all unamed (Worker) threads in *one* ThreadData instance, |
| 126 // (with locking protection on that instance) or else recycle and re-use |
| 127 // worker thread ThreadData when the worker thread terminates. |
| 128 registered = RegisterCurrentContext(new ThreadData()); |
| 129 } |
| 130 return registered; |
| 131 } |
| 132 |
| 133 // static |
| 134 ThreadData* ThreadData::RegisterCurrentContext(ThreadData* unregistered) { |
| 135 DCHECK_EQ(tls_index_.Get(), static_cast<void*>(0)); |
| 136 bool too_late_to_register = false; |
| 137 { |
| 138 base::AutoLock lock(list_lock_); |
| 139 // Use lock to insure we have most recent status. |
| 140 if (!IsActive()) { |
| 141 too_late_to_register = true; |
| 142 } else { |
| 143 // Use list_lock_ to insert as new head of list. |
| 144 unregistered->next_ = first_; |
| 145 first_ = unregistered; |
| 146 } |
| 147 } // Release lock. |
| 148 if (too_late_to_register) { |
| 149 delete unregistered; |
| 150 unregistered = NULL; |
| 151 } else { |
| 152 tls_index_.Set(unregistered); |
| 153 } |
| 154 return unregistered; |
| 155 } |
| 156 |
| 157 // static |
135 void ThreadData::WriteHTML(const std::string& query, std::string* output) { | 158 void ThreadData::WriteHTML(const std::string& query, std::string* output) { |
136 if (!ThreadData::IsActive()) | 159 if (!ThreadData::IsActive()) |
137 return; // Not yet initialized. | 160 return; // Not yet initialized. |
138 | 161 |
139 DCHECK(ThreadData::current()); | |
140 DataCollector collected_data; // Gather data. | 162 DataCollector collected_data; // Gather data. |
141 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 163 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
142 | 164 |
143 // Data Gathering is complete. Now to sort/process/render. | 165 // Data Gathering is complete. Now to sort/process/render. |
144 DataCollector::Collection* collection = collected_data.collection(); | 166 DataCollector::Collection* collection = collected_data.collection(); |
145 | 167 |
146 // Create filtering and sort comparison object. | 168 // Create filtering and sort comparison object. |
147 Comparator comparator; | 169 Comparator comparator; |
148 comparator.ParseQuery(query); | 170 comparator.ParseQuery(query); |
149 | 171 |
150 // Filter out acceptable (matching) instances. | 172 // Filter out acceptable (matching) instances. |
151 DataCollector::Collection match_array; | 173 DataCollector::Collection match_array; |
152 for (DataCollector::Collection::iterator it = collection->begin(); | 174 for (DataCollector::Collection::iterator it = collection->begin(); |
153 it != collection->end(); ++it) { | 175 it != collection->end(); ++it) { |
154 if (comparator.Acceptable(*it)) | 176 if (comparator.Acceptable(*it)) |
155 match_array.push_back(*it); | 177 match_array.push_back(*it); |
156 } | 178 } |
157 | 179 |
158 comparator.Sort(&match_array); | 180 comparator.Sort(&match_array); |
159 | 181 |
160 WriteHTMLTotalAndSubtotals(match_array, comparator, output); | 182 WriteHTMLTotalAndSubtotals(match_array, comparator, output); |
161 | 183 |
162 comparator.Clear(); // Delete tiebreaker_ instances. | 184 comparator.Clear(); // Delete tiebreaker_ instances. |
163 | 185 |
164 output->append("</pre>"); | 186 output->append("</pre>"); |
165 | 187 |
166 const char* help_string = "The following are the keywords that can be used to" | 188 const char* help_string = "The following are the keywords that can be used to" |
167 " sort and aggregate the data, or to select data.<br><ul>" | 189 " sort and aggregate the data, or to select data.<br><ul>" |
168 "<li><b>count</b> Number of instances seen." | 190 "<li><b>Count</b> Number of instances seen." |
169 "<li><b>duration</b> Duration in ms from construction to descrution." | 191 "<li><b>Duration</b> Average duration in ms of Run() time." |
170 "<li><b>birth</b> Thread on which the task was constructed." | 192 "<li><b>TotalDuration</b> Summed durations in ms of Run() times." |
171 "<li><b>death</b> Thread on which the task was run and deleted." | 193 "<li><b>AverageQueueDuration</b> Average duration in ms of queueing time." |
172 "<li><b>file</b> File in which the task was contructed." | 194 "<li><b>TotalQueueDuration</b> Summed durations in ms of Run() times." |
173 "<li><b>function</b> Function in which the task was constructed." | 195 "<li><b>Birth</b> Thread on which the task was constructed." |
174 "<li><b>line</b> Line number of the file in which the task was constructed." | 196 "<li><b>Death</b> Thread on which the task was run and deleted." |
| 197 "<li><b>File</b> File in which the task was contructed." |
| 198 "<li><b>Function</b> Function in which the task was constructed." |
| 199 "<li><b>Line</b> Line number of the file in which the task was constructed." |
175 "</ul><br>" | 200 "</ul><br>" |
176 "As examples:<ul>" | 201 "As examples:<ul>" |
177 "<li><b>about:tracking/file</b> would sort the above data by file, and" | 202 "<li><b>about:tracking/file</b> would sort the above data by file, and" |
178 " aggregate data on a per-file basis." | 203 " aggregate data on a per-file basis." |
179 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" | 204 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" |
180 " constructed in a file containing the text |Dns|." | 205 " constructed in a file containing the text |Dns|." |
| 206 "<li><b>about:tracking/death/duration</b> would sort the data by death" |
| 207 " thread(i.e., where tasks ran) and then by the average runtime for the" |
| 208 " tasks. Form an aggregation group, one per thread, showing the results on" |
| 209 " each thread." |
181 "<li><b>about:tracking/birth/death</b> would sort the above list by birth" | 210 "<li><b>about:tracking/birth/death</b> would sort the above list by birth" |
182 " thread, and then by death thread, and would aggregate data for each pair" | 211 " thread, and then by death thread, and would aggregate data for each pair" |
183 " of lifetime events." | 212 " of lifetime events." |
184 "</ul>" | 213 "</ul>" |
185 " The data can be reset to zero (discarding all births, deaths, etc.) using" | 214 " The data can be reset to zero (discarding all births, deaths, etc.) using" |
186 " <b>about:tracking/reset</b>. The existing stats will be displayed, but" | 215 " <b>about:tracking/reset</b>. The existing stats will be displayed, but" |
187 " the internal stats will be set to zero, and start accumulating afresh." | 216 " the internal stats will be set to zero, and start accumulating afresh." |
188 " This option is very helpful if you only wish to consider tasks created" | 217 " This option is very helpful if you only wish to consider tasks created" |
189 " after some point in time.<br><br>" | 218 " after some point in time.<br><br>" |
190 "If you wish to monitor Renderer events, be sure to run in --single-process" | 219 "If you wish to monitor Renderer events, be sure to run in --single-process" |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
227 output->append("<br>"); | 256 output->append("<br>"); |
228 subtotals.Write(output); | 257 subtotals.Write(output); |
229 output->append("<br><hr><br>"); | 258 output->append("<br><hr><br>"); |
230 subtotals.Clear(); | 259 subtotals.Clear(); |
231 } | 260 } |
232 } | 261 } |
233 } | 262 } |
234 } | 263 } |
235 | 264 |
236 Births* ThreadData::TallyABirth(const Location& location) { | 265 Births* ThreadData::TallyABirth(const Location& location) { |
237 { | |
238 // This shouldn't use the MessageLoop::current() LazyInstance since this | |
239 // might be used on a non-joinable thread. | |
240 // http://crbug.com/62728 | |
241 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | |
242 if (!message_loop_) // In case message loop wasn't yet around... | |
243 message_loop_ = MessageLoop::current(); // Find it now. | |
244 } | |
245 | |
246 BirthMap::iterator it = birth_map_.find(location); | 266 BirthMap::iterator it = birth_map_.find(location); |
247 if (it != birth_map_.end()) { | 267 if (it != birth_map_.end()) { |
248 it->second->RecordBirth(); | 268 it->second->RecordBirth(); |
249 return it->second; | 269 return it->second; |
250 } | 270 } |
251 | 271 |
252 Births* tracker = new Births(location); | 272 Births* tracker = new Births(location); |
253 // Lock since the map may get relocated now, and other threads sometimes | 273 // Lock since the map may get relocated now, and other threads sometimes |
254 // snapshot it (but they lock before copying it). | 274 // snapshot it (but they lock before copying it). |
255 base::AutoLock lock(lock_); | 275 base::AutoLock lock(lock_); |
256 birth_map_[location] = tracker; | 276 birth_map_[location] = tracker; |
257 return tracker; | 277 return tracker; |
258 } | 278 } |
259 | 279 |
260 void ThreadData::TallyADeath(const Births& lifetimes, | 280 void ThreadData::TallyADeath(const Births& the_birth, |
261 const TimeDelta& duration) { | 281 const TimeDelta& queue_duration, |
262 { | 282 const TimeDelta& run_duration) { |
263 // http://crbug.com/62728 | 283 DeathMap::iterator it = death_map_.find(&the_birth); |
264 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | |
265 if (!message_loop_) // In case message loop wasn't yet around... | |
266 message_loop_ = MessageLoop::current(); // Find it now. | |
267 } | |
268 | |
269 DeathMap::iterator it = death_map_.find(&lifetimes); | |
270 if (it != death_map_.end()) { | 284 if (it != death_map_.end()) { |
271 it->second.RecordDeath(duration); | 285 it->second.RecordDeath(queue_duration, run_duration); |
272 return; | 286 return; |
273 } | 287 } |
274 | 288 |
275 base::AutoLock lock(lock_); // Lock since the map may get relocated now. | 289 base::AutoLock lock(lock_); // Lock since the map may get relocated now. |
276 death_map_[&lifetimes].RecordDeath(duration); | 290 death_map_[&the_birth].RecordDeath(queue_duration, run_duration); |
277 } | 291 } |
278 | 292 |
279 // static | 293 // static |
280 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 294 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
281 if (IsActive()) { | 295 #if !defined(TRACK_ALL_TASK_OBJECTS) |
282 ThreadData* current_thread_data = current(); | 296 return NULL; // Not compiled in. |
283 if (current_thread_data) { | 297 #else |
284 return current_thread_data->TallyABirth(location); | 298 if (!IsActive()) |
285 } | 299 return NULL; |
286 } | 300 ThreadData* current_thread_data = Get(); |
287 | 301 if (!current_thread_data) |
288 return NULL; | 302 return NULL; |
| 303 return current_thread_data->TallyABirth(location); |
| 304 #endif |
289 } | 305 } |
290 | 306 |
291 // static | 307 // static |
292 void ThreadData::TallyADeathIfActive(const Births* the_birth, | 308 void ThreadData::TallyADeathIfActive(const Births* the_birth, |
293 const base::TimeDelta& duration) { | 309 const base::TimeTicks& time_posted, |
294 if (IsActive() && the_birth) { | 310 const base::TimeTicks& delayed_start_time, |
295 current()->TallyADeath(*the_birth, duration); | 311 const base::TimeTicks& start_of_run) { |
296 } | 312 #if !defined(TRACK_ALL_TASK_OBJECTS) |
| 313 return; // Not compiled in. |
| 314 #else |
| 315 if (!IsActive() || !the_birth) |
| 316 return; |
| 317 ThreadData* current_thread_data = Get(); |
| 318 if (!current_thread_data) |
| 319 return; |
| 320 |
| 321 // To avoid conflating our stats with the delay duration in a PostDelayedTask, |
| 322 // we identify such tasks, and replace their post_time with the time they |
| 323 // were sechudled (requested?) to emerge from the delayed task queue. This |
| 324 // means that queueing delay for such tasks will show how long they went |
| 325 // unserviced, after they *could* be serviced. This is the same stat as we |
| 326 // have for non-delayed tasks, and we consistently call it queueing delay. |
| 327 base::TimeTicks effective_post_time = |
| 328 (delayed_start_time.is_null()) ? time_posted : delayed_start_time; |
| 329 base::TimeDelta queue_duration = start_of_run - effective_post_time; |
| 330 base::TimeDelta run_duration = Now() - start_of_run; |
| 331 current_thread_data->TallyADeath(*the_birth, queue_duration, run_duration); |
| 332 #endif |
297 } | 333 } |
298 | 334 |
299 // static | 335 // static |
300 ThreadData* ThreadData::first() { | 336 ThreadData* ThreadData::first() { |
301 base::AutoLock lock(list_lock_); | 337 base::AutoLock lock(list_lock_); |
302 return first_; | 338 return first_; |
303 } | 339 } |
304 | 340 |
305 const std::string ThreadData::ThreadName() const { | |
306 if (message_loop_) | |
307 return message_loop_->thread_name(); | |
308 return "ThreadWithoutMessageLoop"; | |
309 } | |
310 | |
311 // This may be called from another thread. | 341 // This may be called from another thread. |
312 void ThreadData::SnapshotBirthMap(BirthMap *output) const { | 342 void ThreadData::SnapshotBirthMap(BirthMap *output) const { |
313 base::AutoLock lock(lock_); | 343 base::AutoLock lock(lock_); |
314 for (BirthMap::const_iterator it = birth_map_.begin(); | 344 for (BirthMap::const_iterator it = birth_map_.begin(); |
315 it != birth_map_.end(); ++it) | 345 it != birth_map_.end(); ++it) |
316 (*output)[it->first] = it->second; | 346 (*output)[it->first] = it->second; |
317 } | 347 } |
318 | 348 |
319 // This may be called from another thread. | 349 // This may be called from another thread. |
320 void ThreadData::SnapshotDeathMap(DeathMap *output) const { | 350 void ThreadData::SnapshotDeathMap(DeathMap *output) const { |
321 base::AutoLock lock(lock_); | 351 base::AutoLock lock(lock_); |
322 for (DeathMap::const_iterator it = death_map_.begin(); | 352 for (DeathMap::const_iterator it = death_map_.begin(); |
323 it != death_map_.end(); ++it) | 353 it != death_map_.end(); ++it) |
324 (*output)[it->first] = it->second; | 354 (*output)[it->first] = it->second; |
325 } | 355 } |
326 | 356 |
327 // static | 357 // static |
328 void ThreadData::ResetAllThreadData() { | 358 void ThreadData::ResetAllThreadData() { |
329 ThreadData* my_list = ThreadData::current()->first(); | 359 ThreadData* my_list = Get()->first(); |
330 | 360 |
331 for (ThreadData* thread_data = my_list; | 361 for (ThreadData* thread_data = my_list; |
332 thread_data; | 362 thread_data; |
333 thread_data = thread_data->next()) | 363 thread_data = thread_data->next()) |
334 thread_data->Reset(); | 364 thread_data->Reset(); |
335 } | 365 } |
336 | 366 |
337 void ThreadData::Reset() { | 367 void ThreadData::Reset() { |
338 base::AutoLock lock(lock_); | 368 base::AutoLock lock(lock_); |
339 for (DeathMap::iterator it = death_map_.begin(); | 369 for (DeathMap::iterator it = death_map_.begin(); |
340 it != death_map_.end(); ++it) | 370 it != death_map_.end(); ++it) |
341 it->second.Clear(); | 371 it->second.Clear(); |
342 for (BirthMap::iterator it = birth_map_.begin(); | 372 for (BirthMap::iterator it = birth_map_.begin(); |
343 it != birth_map_.end(); ++it) | 373 it != birth_map_.end(); ++it) |
344 it->second->Clear(); | 374 it->second->Clear(); |
345 } | 375 } |
346 | 376 |
347 #ifdef OS_WIN | |
348 // A class used to count down which is accessed by several threads. This is | |
349 // used to make sure RunOnAllThreads() actually runs a task on the expected | |
350 // count of threads. | |
351 class ThreadData::ThreadSafeDownCounter { | |
352 public: | |
353 // Constructor sets the count, once and for all. | |
354 explicit ThreadSafeDownCounter(size_t count); | |
355 | |
356 // Decrement the count, and return true if we hit zero. Also delete this | |
357 // instance automatically when we hit zero. | |
358 bool LastCaller(); | |
359 | |
360 private: | |
361 size_t remaining_count_; | |
362 base::Lock lock_; // protect access to remaining_count_. | |
363 }; | |
364 | |
365 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) | |
366 : remaining_count_(count) { | |
367 DCHECK_GT(remaining_count_, 0u); | |
368 } | |
369 | |
370 bool ThreadData::ThreadSafeDownCounter::LastCaller() { | |
371 { | |
372 base::AutoLock lock(lock_); | |
373 if (--remaining_count_) | |
374 return false; | |
375 } // Release lock, so we can delete everything in this instance. | |
376 delete this; | |
377 return true; | |
378 } | |
379 | |
380 // A Task class that runs a static method supplied, and checks to see if this | |
381 // is the last tasks instance (on last thread) that will run the method. | |
382 // IF this is the last run, then the supplied event is signalled. | |
383 class ThreadData::RunTheStatic : public Task { | |
384 public: | |
385 typedef void (*FunctionPointer)(); | |
386 RunTheStatic(FunctionPointer function, | |
387 HANDLE completion_handle, | |
388 ThreadSafeDownCounter* counter); | |
389 // Run the supplied static method, and optionally set the event. | |
390 void Run(); | |
391 | |
392 private: | |
393 FunctionPointer function_; | |
394 HANDLE completion_handle_; | |
395 // Make sure enough tasks are called before completion is signaled. | |
396 ThreadSafeDownCounter* counter_; | |
397 | |
398 DISALLOW_COPY_AND_ASSIGN(RunTheStatic); | |
399 }; | |
400 | |
401 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function, | |
402 HANDLE completion_handle, | |
403 ThreadSafeDownCounter* counter) | |
404 : function_(function), | |
405 completion_handle_(completion_handle), | |
406 counter_(counter) { | |
407 } | |
408 | |
409 void ThreadData::RunTheStatic::Run() { | |
410 function_(); | |
411 if (counter_->LastCaller()) | |
412 SetEvent(completion_handle_); | |
413 } | |
414 | |
415 // TODO(jar): This should use condition variables, and be cross platform. | |
416 void ThreadData::RunOnAllThreads(void (*function)()) { | |
417 ThreadData* list = first(); // Get existing list. | |
418 | |
419 std::vector<MessageLoop*> message_loops; | |
420 for (ThreadData* it = list; it; it = it->next()) { | |
421 if (current() != it && it->message_loop()) | |
422 message_loops.push_back(it->message_loop()); | |
423 } | |
424 | |
425 ThreadSafeDownCounter* counter = | |
426 new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us! | |
427 | |
428 HANDLE completion_handle = CreateEvent(NULL, false, false, NULL); | |
429 // Tell all other threads to run. | |
430 for (size_t i = 0; i < message_loops.size(); ++i) | |
431 message_loops[i]->PostTask( | |
432 FROM_HERE, new RunTheStatic(function, completion_handle, counter)); | |
433 | |
434 // Also run Task on our thread. | |
435 RunTheStatic local_task(function, completion_handle, counter); | |
436 local_task.Run(); | |
437 | |
438 WaitForSingleObject(completion_handle, INFINITE); | |
439 int ret_val = CloseHandle(completion_handle); | |
440 DCHECK(ret_val); | |
441 } | |
442 #endif // OS_WIN | |
443 | |
444 // static | 377 // static |
445 bool ThreadData::StartTracking(bool status) { | 378 bool ThreadData::StartTracking(bool status) { |
446 #if !defined(TRACK_ALL_TASK_OBJECTS) | 379 #if !defined(TRACK_ALL_TASK_OBJECTS) |
447 return false; // Not compiled in. | 380 return false; // Not compiled in. |
448 #endif | 381 #else |
449 | |
450 if (!status) { | 382 if (!status) { |
451 base::AutoLock lock(list_lock_); | 383 base::AutoLock lock(list_lock_); |
452 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); | 384 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); |
453 status_ = SHUTDOWN; | 385 status_ = SHUTDOWN; |
454 return true; | 386 return true; |
455 } | 387 } |
456 base::AutoLock lock(list_lock_); | 388 base::AutoLock lock(list_lock_); |
457 DCHECK_EQ(UNINITIALIZED, status_); | 389 DCHECK_EQ(UNINITIALIZED, status_); |
458 CHECK(tls_index_.Initialize(NULL)); | 390 CHECK(tls_index_.Initialize(NULL)); |
459 status_ = ACTIVE; | 391 status_ = ACTIVE; |
460 return true; | 392 return true; |
| 393 #endif |
461 } | 394 } |
462 | 395 |
463 // static | 396 // static |
464 bool ThreadData::IsActive() { | 397 bool ThreadData::IsActive() { |
465 return status_ == ACTIVE; | 398 return status_ == ACTIVE; |
466 } | 399 } |
467 | 400 |
468 #ifdef OS_WIN | |
469 // static | 401 // static |
470 void ThreadData::ShutdownMultiThreadTracking() { | 402 base::TimeTicks ThreadData::Now() { |
471 // Using lock, guarantee that no new ThreadData instances will be created. | 403 #if defined(TRACK_ALL_TASK_OBJECTS) |
472 if (!StartTracking(false)) | 404 if (status_ == ACTIVE) |
473 return; | 405 return base::TimeTicks::Now(); |
474 | 406 #endif |
475 RunOnAllThreads(ShutdownDisablingFurtherTracking); | 407 return base::TimeTicks(); // Super fast when disabled, or not compiled in. |
476 | |
477 // Now the *only* threads that might change the database are the threads with | |
478 // no messages loops. They might still be adding data to their birth records, | |
479 // but since no objects are deleted on those threads, there will be no further | |
480 // access to to cross-thread data. | |
481 // We could do a cleanup on all threads except for the ones without | |
482 // MessageLoops, but we won't bother doing cleanup (destruction of data) yet. | |
483 return; | |
484 } | 408 } |
485 #endif | |
486 | 409 |
487 // static | 410 // static |
488 void ThreadData::ShutdownSingleThreadedCleanup() { | 411 void ThreadData::ShutdownSingleThreadedCleanup() { |
489 // We must be single threaded... but be careful anyway. | 412 // We must be single threaded... but be careful anyway. |
490 if (!StartTracking(false)) | 413 if (!StartTracking(false)) |
491 return; | 414 return; |
492 ThreadData* thread_data_list; | 415 ThreadData* thread_data_list; |
493 { | 416 { |
494 base::AutoLock lock(list_lock_); | 417 base::AutoLock lock(list_lock_); |
495 thread_data_list = first_; | 418 thread_data_list = first_; |
(...skipping 11 matching lines...) Expand all Loading... |
507 next_thread_data->death_map_.clear(); | 430 next_thread_data->death_map_.clear(); |
508 delete next_thread_data; // Includes all Death Records. | 431 delete next_thread_data; // Includes all Death Records. |
509 } | 432 } |
510 | 433 |
511 CHECK(tls_index_.initialized()); | 434 CHECK(tls_index_.initialized()); |
512 tls_index_.Free(); | 435 tls_index_.Free(); |
513 DCHECK(!tls_index_.initialized()); | 436 DCHECK(!tls_index_.initialized()); |
514 status_ = UNINITIALIZED; | 437 status_ = UNINITIALIZED; |
515 } | 438 } |
516 | 439 |
517 // static | |
518 void ThreadData::ShutdownDisablingFurtherTracking() { | |
519 // Redundantly set status SHUTDOWN on this thread. | |
520 if (!StartTracking(false)) | |
521 return; | |
522 } | |
523 | |
524 //------------------------------------------------------------------------------ | 440 //------------------------------------------------------------------------------ |
525 // Individual 3-tuple of birth (place and thread) along with death thread, and | 441 // Individual 3-tuple of birth (place and thread) along with death thread, and |
526 // the accumulated stats for instances (DeathData). | 442 // the accumulated stats for instances (DeathData). |
527 | 443 |
528 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, | 444 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, |
529 const ThreadData& death_thread, | 445 const ThreadData& death_thread, |
530 const DeathData& death_data) | 446 const DeathData& death_data) |
531 : birth_(&birth_on_thread), | 447 : birth_(&birth_on_thread), |
532 death_thread_(&death_thread), | 448 death_thread_(&death_thread), |
533 death_data_(death_data) { | 449 death_data_(death_data) { |
534 } | 450 } |
535 | 451 |
536 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) | 452 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) |
537 : birth_(&birth_on_thread), | 453 : birth_(&birth_on_thread), |
538 death_thread_(NULL), | 454 death_thread_(NULL), |
539 death_data_(DeathData(count)) { | 455 death_data_(DeathData(count)) { |
540 } | 456 } |
541 | 457 |
542 const std::string Snapshot::DeathThreadName() const { | 458 const std::string Snapshot::DeathThreadName() const { |
543 if (death_thread_) | 459 if (death_thread_) |
544 return death_thread_->ThreadName(); | 460 return death_thread_->thread_name(); |
545 return "Still_Alive"; | 461 return "Still_Alive"; |
546 } | 462 } |
547 | 463 |
548 void Snapshot::Write(std::string* output) const { | 464 void Snapshot::Write(std::string* output) const { |
549 death_data_.Write(output); | 465 death_data_.Write(output); |
550 base::StringAppendF(output, "%s->%s ", | 466 base::StringAppendF(output, "%s->%s ", |
551 birth_->birth_thread()->ThreadName().c_str(), | 467 birth_->birth_thread()->thread_name().c_str(), |
552 death_thread_->ThreadName().c_str()); | 468 death_thread_->thread_name().c_str()); |
553 birth_->location().Write(true, true, output); | 469 birth_->location().Write(true, true, output); |
554 } | 470 } |
555 | 471 |
556 void Snapshot::Add(const Snapshot& other) { | 472 void Snapshot::Add(const Snapshot& other) { |
557 death_data_.AddDeathData(other.death_data_); | 473 death_data_.AddDeathData(other.death_data_); |
558 } | 474 } |
559 | 475 |
560 //------------------------------------------------------------------------------ | 476 //------------------------------------------------------------------------------ |
561 // DataCollector | 477 // DataCollector |
562 | 478 |
563 DataCollector::DataCollector() { | 479 DataCollector::DataCollector() { |
564 DCHECK(ThreadData::IsActive()); | 480 DCHECK(ThreadData::IsActive()); |
565 | 481 |
566 // Get an unchanging copy of a ThreadData list. | 482 // Get an unchanging copy of a ThreadData list. |
567 ThreadData* my_list = ThreadData::current()->first(); | 483 ThreadData* my_list = ThreadData::Get()->first(); |
568 | 484 |
569 count_of_contributing_threads_ = 0; | 485 // Gather data serially. |
570 for (ThreadData* thread_data = my_list; | |
571 thread_data; | |
572 thread_data = thread_data->next()) { | |
573 ++count_of_contributing_threads_; | |
574 } | |
575 | |
576 // Gather data serially. A different constructor could be used to do in | |
577 // parallel, and then invoke an OnCompletion task. | |
578 // This hackish approach *can* get some slighly corrupt tallies, as we are | 486 // This hackish approach *can* get some slighly corrupt tallies, as we are |
579 // grabbing values without the protection of a lock, but it has the advantage | 487 // grabbing values without the protection of a lock, but it has the advantage |
580 // of working even with threads that don't have message loops. If a user | 488 // of working even with threads that don't have message loops. If a user |
581 // sees any strangeness, they can always just run their stats gathering a | 489 // sees any strangeness, they can always just run their stats gathering a |
582 // second time. | 490 // second time. |
583 // TODO(jar): Provide version that gathers stats safely via PostTask in all | |
584 // cases where thread_data supplies a message_loop to post to. Be careful to | |
585 // handle message_loops that are destroyed!?! | |
586 for (ThreadData* thread_data = my_list; | 491 for (ThreadData* thread_data = my_list; |
587 thread_data; | 492 thread_data; |
588 thread_data = thread_data->next()) { | 493 thread_data = thread_data->next()) { |
589 Append(*thread_data); | 494 Append(*thread_data); |
590 } | 495 } |
591 } | 496 } |
592 | 497 |
593 DataCollector::~DataCollector() { | 498 DataCollector::~DataCollector() { |
594 } | 499 } |
595 | 500 |
596 void DataCollector::Append(const ThreadData& thread_data) { | 501 void DataCollector::Append(const ThreadData& thread_data) { |
597 // Get copy of data (which is done under ThreadData's lock). | 502 // Get copy of data. |
598 ThreadData::BirthMap birth_map; | 503 ThreadData::BirthMap birth_map; |
599 thread_data.SnapshotBirthMap(&birth_map); | 504 thread_data.SnapshotBirthMap(&birth_map); |
600 ThreadData::DeathMap death_map; | 505 ThreadData::DeathMap death_map; |
601 thread_data.SnapshotDeathMap(&death_map); | 506 thread_data.SnapshotDeathMap(&death_map); |
602 | 507 |
603 // Use our lock to protect our accumulation activity. | |
604 base::AutoLock lock(accumulation_lock_); | |
605 | |
606 DCHECK(count_of_contributing_threads_); | |
607 | |
608 for (ThreadData::DeathMap::const_iterator it = death_map.begin(); | 508 for (ThreadData::DeathMap::const_iterator it = death_map.begin(); |
609 it != death_map.end(); ++it) { | 509 it != death_map.end(); ++it) { |
610 collection_.push_back(Snapshot(*it->first, thread_data, it->second)); | 510 collection_.push_back(Snapshot(*it->first, thread_data, it->second)); |
611 global_birth_count_[it->first] -= it->first->birth_count(); | 511 global_birth_count_[it->first] -= it->first->birth_count(); |
612 } | 512 } |
613 | 513 |
614 for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); | 514 for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); |
615 it != birth_map.end(); ++it) { | 515 it != birth_map.end(); ++it) { |
616 global_birth_count_[it->second] += it->second->birth_count(); | 516 global_birth_count_[it->second] += it->second->birth_count(); |
617 } | 517 } |
618 | |
619 --count_of_contributing_threads_; | |
620 } | 518 } |
621 | 519 |
622 DataCollector::Collection* DataCollector::collection() { | 520 DataCollector::Collection* DataCollector::collection() { |
623 DCHECK(!count_of_contributing_threads_); | |
624 return &collection_; | 521 return &collection_; |
625 } | 522 } |
626 | 523 |
627 void DataCollector::AddListOfLivingObjects() { | 524 void DataCollector::AddListOfLivingObjects() { |
628 DCHECK(!count_of_contributing_threads_); | |
629 for (BirthCount::iterator it = global_birth_count_.begin(); | 525 for (BirthCount::iterator it = global_birth_count_.begin(); |
630 it != global_birth_count_.end(); ++it) { | 526 it != global_birth_count_.end(); ++it) { |
631 if (it->second > 0) | 527 if (it->second > 0) |
632 collection_.push_back(Snapshot(*it->first, it->second)); | 528 collection_.push_back(Snapshot(*it->first, it->second)); |
633 } | 529 } |
634 } | 530 } |
635 | 531 |
636 //------------------------------------------------------------------------------ | 532 //------------------------------------------------------------------------------ |
637 // Aggregation | 533 // Aggregation |
638 | 534 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
674 base::StringAppendF(output, "All born in %s. ", | 570 base::StringAppendF(output, "All born in %s. ", |
675 birth_files_.begin()->first.c_str()); | 571 birth_files_.begin()->first.c_str()); |
676 } | 572 } |
677 } | 573 } |
678 | 574 |
679 if (birth_threads_.size() > 1) { | 575 if (birth_threads_.size() > 1) { |
680 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", | 576 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", |
681 birth_threads_.size()); | 577 birth_threads_.size()); |
682 } else { | 578 } else { |
683 base::StringAppendF(output, "All born on %s. ", | 579 base::StringAppendF(output, "All born on %s. ", |
684 birth_threads_.begin()->first->ThreadName().c_str()); | 580 birth_threads_.begin()->first->thread_name().c_str()); |
685 } | 581 } |
686 | 582 |
687 if (death_threads_.size() > 1) { | 583 if (death_threads_.size() > 1) { |
688 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", | 584 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", |
689 death_threads_.size()); | 585 death_threads_.size()); |
690 } else { | 586 } else { |
691 if (death_threads_.begin()->first) { | 587 if (death_threads_.begin()->first) { |
692 base::StringAppendF(output, "All deleted on %s. ", | 588 base::StringAppendF(output, "All deleted on %s. ", |
693 death_threads_.begin()->first->ThreadName().c_str()); | 589 death_threads_.begin()->first->thread_name().c_str()); |
694 } else { | 590 } else { |
695 output->append("All these objects are still alive."); | 591 output->append("All these objects are still alive."); |
696 } | 592 } |
697 } | 593 } |
698 | 594 |
699 if (birth_count_ > 1) | 595 if (birth_count_ > 1) |
700 base::StringAppendF(output, "Births=%d ", birth_count_); | 596 base::StringAppendF(output, "Births=%d ", birth_count_); |
701 | 597 |
702 DeathData::Write(output); | 598 DeathData::Write(output); |
703 } | 599 } |
(...skipping 24 matching lines...) Expand all Loading... |
728 } | 624 } |
729 use_tiebreaker_for_sort_only_ = false; | 625 use_tiebreaker_for_sort_only_ = false; |
730 selector_ = NIL; | 626 selector_ = NIL; |
731 } | 627 } |
732 | 628 |
733 bool Comparator::operator()(const Snapshot& left, | 629 bool Comparator::operator()(const Snapshot& left, |
734 const Snapshot& right) const { | 630 const Snapshot& right) const { |
735 switch (selector_) { | 631 switch (selector_) { |
736 case BIRTH_THREAD: | 632 case BIRTH_THREAD: |
737 if (left.birth_thread() != right.birth_thread() && | 633 if (left.birth_thread() != right.birth_thread() && |
738 left.birth_thread()->ThreadName() != | 634 left.birth_thread()->thread_name() != |
739 right.birth_thread()->ThreadName()) | 635 right.birth_thread()->thread_name()) |
740 return left.birth_thread()->ThreadName() < | 636 return left.birth_thread()->thread_name() < |
741 right.birth_thread()->ThreadName(); | 637 right.birth_thread()->thread_name(); |
742 break; | 638 break; |
743 | 639 |
744 case DEATH_THREAD: | 640 case DEATH_THREAD: |
745 if (left.death_thread() != right.death_thread() && | 641 if (left.death_thread() != right.death_thread() && |
746 left.DeathThreadName() != | 642 left.DeathThreadName() != |
747 right.DeathThreadName()) { | 643 right.DeathThreadName()) { |
748 if (!left.death_thread()) | 644 if (!left.death_thread()) |
749 return true; | 645 return true; |
750 if (!right.death_thread()) | 646 if (!right.death_thread()) |
751 return false; | 647 return false; |
(...skipping 24 matching lines...) Expand all Loading... |
776 if (left.location().line_number() != right.location().line_number()) | 672 if (left.location().line_number() != right.location().line_number()) |
777 return left.location().line_number() < | 673 return left.location().line_number() < |
778 right.location().line_number(); | 674 right.location().line_number(); |
779 break; | 675 break; |
780 | 676 |
781 case COUNT: | 677 case COUNT: |
782 if (left.count() != right.count()) | 678 if (left.count() != right.count()) |
783 return left.count() > right.count(); // Sort large at front of vector. | 679 return left.count() > right.count(); // Sort large at front of vector. |
784 break; | 680 break; |
785 | 681 |
786 case AVERAGE_DURATION: | 682 case AVERAGE_RUN_DURATION: |
787 if (!left.count() || !right.count()) | 683 if (!left.count() || !right.count()) |
788 break; | 684 break; |
789 if (left.AverageMsDuration() != right.AverageMsDuration()) | 685 if (left.AverageMsRunDuration() != right.AverageMsRunDuration()) |
790 return left.AverageMsDuration() > right.AverageMsDuration(); | 686 return left.AverageMsRunDuration() > right.AverageMsRunDuration(); |
| 687 break; |
| 688 |
| 689 case TOTAL_RUN_DURATION: |
| 690 if (!left.count() || !right.count()) |
| 691 break; |
| 692 if (left.run_duration() != right.run_duration()) |
| 693 return left.run_duration() > right.run_duration(); |
| 694 break; |
| 695 |
| 696 case AVERAGE_QUEUE_DURATION: |
| 697 if (!left.count() || !right.count()) |
| 698 break; |
| 699 if (left.AverageMsQueueDuration() != right.AverageMsQueueDuration()) |
| 700 return left.AverageMsQueueDuration() > right.AverageMsQueueDuration(); |
| 701 break; |
| 702 |
| 703 case TOTAL_QUEUE_DURATION: |
| 704 if (!left.count() || !right.count()) |
| 705 break; |
| 706 if (left.queue_duration() != right.queue_duration()) |
| 707 return left.queue_duration() > right.queue_duration(); |
791 break; | 708 break; |
792 | 709 |
793 default: | 710 default: |
794 break; | 711 break; |
795 } | 712 } |
796 if (tiebreaker_) | 713 if (tiebreaker_) |
797 return tiebreaker_->operator()(left, right); | 714 return tiebreaker_->operator()(left, right); |
798 return false; | 715 return false; |
799 } | 716 } |
800 | 717 |
801 void Comparator::Sort(DataCollector::Collection* collection) const { | 718 void Comparator::Sort(DataCollector::Collection* collection) const { |
802 std::sort(collection->begin(), collection->end(), *this); | 719 std::sort(collection->begin(), collection->end(), *this); |
803 } | 720 } |
804 | 721 |
805 bool Comparator::Equivalent(const Snapshot& left, | 722 bool Comparator::Equivalent(const Snapshot& left, |
806 const Snapshot& right) const { | 723 const Snapshot& right) const { |
807 switch (selector_) { | 724 switch (selector_) { |
808 case BIRTH_THREAD: | 725 case BIRTH_THREAD: |
809 if (left.birth_thread() != right.birth_thread() && | 726 if (left.birth_thread() != right.birth_thread() && |
810 left.birth_thread()->ThreadName() != | 727 left.birth_thread()->thread_name() != |
811 right.birth_thread()->ThreadName()) | 728 right.birth_thread()->thread_name()) |
812 return false; | 729 return false; |
813 break; | 730 break; |
814 | 731 |
815 case DEATH_THREAD: | 732 case DEATH_THREAD: |
816 if (left.death_thread() != right.death_thread() && | 733 if (left.death_thread() != right.death_thread() && |
817 left.DeathThreadName() != right.DeathThreadName()) | 734 left.DeathThreadName() != right.DeathThreadName()) |
818 return false; | 735 return false; |
819 break; | 736 break; |
820 | 737 |
821 case BIRTH_FILE: | 738 case BIRTH_FILE: |
822 if (left.location().file_name() != right.location().file_name()) { | 739 if (left.location().file_name() != right.location().file_name()) { |
823 int comp = strcmp(left.location().file_name(), | 740 int comp = strcmp(left.location().file_name(), |
824 right.location().file_name()); | 741 right.location().file_name()); |
825 if (comp) | 742 if (comp) |
826 return false; | 743 return false; |
827 } | 744 } |
828 break; | 745 break; |
829 | 746 |
830 case BIRTH_FUNCTION: | 747 case BIRTH_FUNCTION: |
831 if (left.location().function_name() != right.location().function_name()) { | 748 if (left.location().function_name() != right.location().function_name()) { |
832 int comp = strcmp(left.location().function_name(), | 749 int comp = strcmp(left.location().function_name(), |
833 right.location().function_name()); | 750 right.location().function_name()); |
834 if (comp) | 751 if (comp) |
835 return false; | 752 return false; |
836 } | 753 } |
837 break; | 754 break; |
838 | 755 |
839 case COUNT: | 756 case COUNT: |
840 if (left.count() != right.count()) | 757 case AVERAGE_RUN_DURATION: |
841 return false; | 758 case TOTAL_RUN_DURATION: |
842 break; | 759 case AVERAGE_QUEUE_DURATION: |
843 | 760 case TOTAL_QUEUE_DURATION: |
844 case AVERAGE_DURATION: | 761 // We don't produce separate aggretation when only counts or times differ. |
845 if (left.life_duration() != right.life_duration()) | |
846 return false; | |
847 break; | 762 break; |
848 | 763 |
849 default: | 764 default: |
850 break; | 765 break; |
851 } | 766 } |
852 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) | 767 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) |
853 return tiebreaker_->Equivalent(left, right); | 768 return tiebreaker_->Equivalent(left, right); |
854 return true; | 769 return true; |
855 } | 770 } |
856 | 771 |
857 bool Comparator::Acceptable(const Snapshot& sample) const { | 772 bool Comparator::Acceptable(const Snapshot& sample) const { |
858 if (required_.size()) { | 773 if (required_.size()) { |
859 switch (selector_) { | 774 switch (selector_) { |
860 case BIRTH_THREAD: | 775 case BIRTH_THREAD: |
861 if (sample.birth_thread()->ThreadName().find(required_) == | 776 if (sample.birth_thread()->thread_name().find(required_) == |
862 std::string::npos) | 777 std::string::npos) |
863 return false; | 778 return false; |
864 break; | 779 break; |
865 | 780 |
866 case DEATH_THREAD: | 781 case DEATH_THREAD: |
867 if (sample.DeathThreadName().find(required_) == std::string::npos) | 782 if (sample.DeathThreadName().find(required_) == std::string::npos) |
868 return false; | 783 return false; |
869 break; | 784 break; |
870 | 785 |
871 case BIRTH_FILE: | 786 case BIRTH_FILE: |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
927 } | 842 } |
928 | 843 |
929 void Comparator::ParseKeyphrase(const std::string& key_phrase) { | 844 void Comparator::ParseKeyphrase(const std::string& key_phrase) { |
930 typedef std::map<const std::string, Selector> KeyMap; | 845 typedef std::map<const std::string, Selector> KeyMap; |
931 static KeyMap key_map; | 846 static KeyMap key_map; |
932 static bool initialized = false; | 847 static bool initialized = false; |
933 if (!initialized) { | 848 if (!initialized) { |
934 initialized = true; | 849 initialized = true; |
935 // Sorting and aggretation keywords, which specify how to sort the data, or | 850 // Sorting and aggretation keywords, which specify how to sort the data, or |
936 // can specify a required match from the specified field in the record. | 851 // can specify a required match from the specified field in the record. |
937 key_map["count"] = COUNT; | 852 key_map["count"] = COUNT; |
938 key_map["duration"] = AVERAGE_DURATION; | 853 key_map["totalduration"] = TOTAL_RUN_DURATION; |
939 key_map["birth"] = BIRTH_THREAD; | 854 key_map["duration"] = AVERAGE_RUN_DURATION; |
940 key_map["death"] = DEATH_THREAD; | 855 key_map["totalqueueduration"] = TOTAL_QUEUE_DURATION; |
941 key_map["file"] = BIRTH_FILE; | 856 key_map["averagequeueduration"] = AVERAGE_QUEUE_DURATION; |
942 key_map["function"] = BIRTH_FUNCTION; | 857 key_map["birth"] = BIRTH_THREAD; |
943 key_map["line"] = BIRTH_LINE; | 858 key_map["death"] = DEATH_THREAD; |
| 859 key_map["file"] = BIRTH_FILE; |
| 860 key_map["function"] = BIRTH_FUNCTION; |
| 861 key_map["line"] = BIRTH_LINE; |
944 | 862 |
945 // Immediate commands that do not involve setting sort order. | 863 // Immediate commands that do not involve setting sort order. |
946 key_map["reset"] = RESET_ALL_DATA; | 864 key_map["reset"] = RESET_ALL_DATA; |
947 } | 865 } |
948 | 866 |
949 std::string required; | 867 std::string required; |
950 // Watch for: "sort_key=value" as we parse. | 868 // Watch for: "sort_key=value" as we parse. |
951 size_t equal_offset = key_phrase.find('=', 0); | 869 size_t equal_offset = key_phrase.find('=', 0); |
952 if (key_phrase.npos != equal_offset) { | 870 if (key_phrase.npos != equal_offset) { |
953 // There is a value that must be matched for the data to display. | 871 // There is a value that must be matched for the data to display. |
(...skipping 15 matching lines...) Expand all Loading... |
969 for (size_t i = 0; i < query.size();) { | 887 for (size_t i = 0; i < query.size();) { |
970 size_t slash_offset = query.find('/', i); | 888 size_t slash_offset = query.find('/', i); |
971 ParseKeyphrase(query.substr(i, slash_offset - i)); | 889 ParseKeyphrase(query.substr(i, slash_offset - i)); |
972 if (query.npos == slash_offset) | 890 if (query.npos == slash_offset) |
973 break; | 891 break; |
974 i = slash_offset + 1; | 892 i = slash_offset + 1; |
975 } | 893 } |
976 | 894 |
977 // Select subgroup ordering (if we want to display the subgroup) | 895 // Select subgroup ordering (if we want to display the subgroup) |
978 SetSubgroupTiebreaker(COUNT); | 896 SetSubgroupTiebreaker(COUNT); |
979 SetSubgroupTiebreaker(AVERAGE_DURATION); | 897 SetSubgroupTiebreaker(AVERAGE_RUN_DURATION); |
| 898 SetSubgroupTiebreaker(TOTAL_RUN_DURATION); |
980 SetSubgroupTiebreaker(BIRTH_THREAD); | 899 SetSubgroupTiebreaker(BIRTH_THREAD); |
981 SetSubgroupTiebreaker(DEATH_THREAD); | 900 SetSubgroupTiebreaker(DEATH_THREAD); |
982 SetSubgroupTiebreaker(BIRTH_FUNCTION); | 901 SetSubgroupTiebreaker(BIRTH_FUNCTION); |
983 SetSubgroupTiebreaker(BIRTH_FILE); | 902 SetSubgroupTiebreaker(BIRTH_FILE); |
984 SetSubgroupTiebreaker(BIRTH_LINE); | 903 SetSubgroupTiebreaker(BIRTH_LINE); |
985 | 904 |
986 return true; | 905 return true; |
987 } | 906 } |
988 | 907 |
989 bool Comparator::WriteSortGrouping(const Snapshot& sample, | 908 bool Comparator::WriteSortGrouping(const Snapshot& sample, |
990 std::string* output) const { | 909 std::string* output) const { |
991 bool wrote_data = false; | 910 bool wrote_data = false; |
992 switch (selector_) { | 911 switch (selector_) { |
993 case BIRTH_THREAD: | 912 case BIRTH_THREAD: |
994 base::StringAppendF(output, "All new on %s ", | 913 base::StringAppendF(output, "All new on %s ", |
995 sample.birth_thread()->ThreadName().c_str()); | 914 sample.birth_thread()->thread_name().c_str()); |
996 wrote_data = true; | 915 wrote_data = true; |
997 break; | 916 break; |
998 | 917 |
999 case DEATH_THREAD: | 918 case DEATH_THREAD: |
1000 if (sample.death_thread()) { | 919 if (sample.death_thread()) { |
1001 base::StringAppendF(output, "All deleted on %s ", | 920 base::StringAppendF(output, "All deleted on %s ", |
1002 sample.DeathThreadName().c_str()); | 921 sample.DeathThreadName().c_str()); |
1003 } else { | 922 } else { |
1004 output->append("All still alive "); | 923 output->append("All still alive "); |
1005 } | 924 } |
(...skipping 20 matching lines...) Expand all Loading... |
1026 return wrote_data; | 945 return wrote_data; |
1027 } | 946 } |
1028 | 947 |
1029 void Comparator::WriteSnapshot(const Snapshot& sample, | 948 void Comparator::WriteSnapshot(const Snapshot& sample, |
1030 std::string* output) const { | 949 std::string* output) const { |
1031 sample.death_data().Write(output); | 950 sample.death_data().Write(output); |
1032 if (!(combined_selectors_ & BIRTH_THREAD) || | 951 if (!(combined_selectors_ & BIRTH_THREAD) || |
1033 !(combined_selectors_ & DEATH_THREAD)) | 952 !(combined_selectors_ & DEATH_THREAD)) |
1034 base::StringAppendF(output, "%s->%s ", | 953 base::StringAppendF(output, "%s->%s ", |
1035 (combined_selectors_ & BIRTH_THREAD) ? "*" : | 954 (combined_selectors_ & BIRTH_THREAD) ? "*" : |
1036 sample.birth().birth_thread()->ThreadName().c_str(), | 955 sample.birth().birth_thread()->thread_name().c_str(), |
1037 (combined_selectors_ & DEATH_THREAD) ? "*" : | 956 (combined_selectors_ & DEATH_THREAD) ? "*" : |
1038 sample.DeathThreadName().c_str()); | 957 sample.DeathThreadName().c_str()); |
1039 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), | 958 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), |
1040 !(combined_selectors_ & BIRTH_FUNCTION), | 959 !(combined_selectors_ & BIRTH_FUNCTION), |
1041 output); | 960 output); |
1042 } | 961 } |
1043 | 962 |
1044 } // namespace tracked_objects | 963 } // namespace tracked_objects |
OLD | NEW |