blob: 65341592a61e2aa6a7a69fa06611922495701ae8 [file] [log] [blame]
[email protected]be843e22011-06-28 17:35:181// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
5#include "base/tracked_objects.h"
6
[email protected]a5b94a92008-08-12 23:25:437#include <math.h>
8
[email protected]34b2b002009-11-20 06:53:289#include "base/format_macros.h"
[email protected]f5393332009-06-03 15:01:2910#include "base/message_loop.h"
initial.commitd7cae122008-07-26 21:49:3811#include "base/string_util.h"
[email protected]f1633932010-08-17 23:05:2812#include "base/stringprintf.h"
[email protected]34b99632011-01-01 01:01:0613#include "base/threading/thread_restrictions.h"
[email protected]b2a9bbd2011-10-31 22:36:2114#include "build/build_config.h"
initial.commitd7cae122008-07-26 21:49:3815
[email protected]e1acf6f2008-10-27 20:43:3316using base::TimeDelta;
17
initial.commitd7cae122008-07-26 21:49:3818namespace tracked_objects {
19
[email protected]b2a9bbd2011-10-31 22:36:2120namespace {
21// Flag to compile out almost all of the task tracking code.
[email protected]0411f902011-11-03 01:44:1122static const bool kTrackAllTaskObjects = true;
[email protected]3f095c0a2011-10-31 15:32:0823
[email protected]b2a9bbd2011-10-31 22:36:2124// When ThreadData is first initialized, should we start in an ACTIVE state to
25// record all of the startup-time tasks, or should we start up DEACTIVATED, so
26// that we only record after parsing the command line flag --enable-tracking.
27// Note that the flag may force either state, so this really controls only the
28// period of time up until that flag is parsed. If there is no flag seen, then
29// this state may prevail for much or all of the process lifetime.
30static const ThreadData::Status kInitialStartupState = ThreadData::ACTIVE;
31} // anonymous namespace.
[email protected]84b57952011-10-15 23:52:4532
initial.commitd7cae122008-07-26 21:49:3833//------------------------------------------------------------------------------
[email protected]63f5b0e2011-11-04 00:23:2734// DeathData tallies durations when a death takes place.
initial.commitd7cae122008-07-26 21:49:3835
[email protected]c25db182011-11-11 22:40:2736void DeathData::RecordDeath(DurationInt queue_duration,
37 DurationInt run_duration) {
initial.commitd7cae122008-07-26 21:49:3838 ++count_;
[email protected]63f5b0e2011-11-04 00:23:2739 queue_time_.AddDuration(queue_duration);
40 run_time_.AddDuration(run_duration);
initial.commitd7cae122008-07-26 21:49:3841}
42
[email protected]c25db182011-11-11 22:40:2743DurationInt DeathData::AverageMsRunDuration() const {
[email protected]63f5b0e2011-11-04 00:23:2744 return run_time_.AverageMsDuration(count_);
initial.commitd7cae122008-07-26 21:49:3845}
46
[email protected]c25db182011-11-11 22:40:2747DurationInt DeathData::AverageMsQueueDuration() const {
[email protected]63f5b0e2011-11-04 00:23:2748 return queue_time_.AverageMsDuration(count_);
initial.commitd7cae122008-07-26 21:49:3849}
50
initial.commitd7cae122008-07-26 21:49:3851void DeathData::AddDeathData(const DeathData& other) {
52 count_ += other.count_;
[email protected]63f5b0e2011-11-04 00:23:2753 queue_time_.AddData(other.queue_time_);
54 run_time_.AddData(other.run_time_);
initial.commitd7cae122008-07-26 21:49:3855}
56
[email protected]84baeca2011-10-24 18:55:1657void DeathData::WriteHTML(std::string* output) const {
initial.commitd7cae122008-07-26 21:49:3858 if (!count_)
59 return;
[email protected]84b57952011-10-15 23:52:4560 base::StringAppendF(output, "%s:%d, ",
61 (count_ == 1) ? "Life" : "Lives", count_);
[email protected]63f5b0e2011-11-04 00:23:2762 output->append("Run:");
63 run_time_.WriteHTML(count_, output);
64
65 output->append("Queue:");
66 queue_time_.WriteHTML(count_, output);
initial.commitd7cae122008-07-26 21:49:3867}
68
[email protected]84baeca2011-10-24 18:55:1669base::DictionaryValue* DeathData::ToValue() const {
70 base::DictionaryValue* dictionary = new base::DictionaryValue;
71 dictionary->Set("count", base::Value::CreateIntegerValue(count_));
72 dictionary->Set("run_ms",
[email protected]c25db182011-11-11 22:40:2773 base::Value::CreateIntegerValue(run_time_.duration()));
[email protected]84baeca2011-10-24 18:55:1674 dictionary->Set("queue_ms",
[email protected]c25db182011-11-11 22:40:2775 base::Value::CreateIntegerValue(queue_time_.duration()));
[email protected]63f5b0e2011-11-04 00:23:2776 dictionary->Set("run_ms_max",
[email protected]c25db182011-11-11 22:40:2777 base::Value::CreateIntegerValue(run_time_.max()));
[email protected]63f5b0e2011-11-04 00:23:2778 dictionary->Set("queue_ms_max",
[email protected]c25db182011-11-11 22:40:2779 base::Value::CreateIntegerValue(queue_time_.max()));
[email protected]84baeca2011-10-24 18:55:1680 return dictionary;
81}
82
initial.commitd7cae122008-07-26 21:49:3883void DeathData::Clear() {
84 count_ = 0;
[email protected]63f5b0e2011-11-04 00:23:2785 run_time_.Clear();
86 queue_time_.Clear();
initial.commitd7cae122008-07-26 21:49:3887}
88
89//------------------------------------------------------------------------------
[email protected]63f5b0e2011-11-04 00:23:2790
91void DeathData::Data::WriteHTML(int count, std::string* output) const {
92 // Be careful to leave static_casts intact, as the type returned by
93 // InMilliseconds() may not always be an int, even if it can generally fit
94 // into an int.
95 base::StringAppendF(output, "%dms",
[email protected]c25db182011-11-11 22:40:2796 static_cast<int>(duration_));
[email protected]63f5b0e2011-11-04 00:23:2797 if (count == 1) {
98 output->append(" ");
99 return;
100 }
101 base::StringAppendF(output, "(%dms/life,max:%dms) ",
[email protected]c25db182011-11-11 22:40:27102 static_cast<int>(AverageMsDuration(count)),
103 static_cast<int>(max_));
[email protected]63f5b0e2011-11-04 00:23:27104}
105
106void DeathData::Data::AddData(const Data& other) {
107 duration_ += other.duration_;
108 if (max_ > other.max_)
109 return;
110 max_ = other.max_;
111}
112
[email protected]c25db182011-11-11 22:40:27113void DeathData::Data::AddDuration(DurationInt duration) {
[email protected]63f5b0e2011-11-04 00:23:27114 duration_ += duration;
115 if (max_ > duration)
116 return;
117 max_ = duration;
118}
119
[email protected]c25db182011-11-11 22:40:27120DurationInt DeathData::Data::AverageMsDuration(int count) const {
121 if (duration_ == 0 || !count)
[email protected]63f5b0e2011-11-04 00:23:27122 return 0;
[email protected]c25db182011-11-11 22:40:27123 return (duration_ + count / 2) / count;
[email protected]63f5b0e2011-11-04 00:23:27124}
125
126void DeathData::Data::Clear() {
[email protected]c25db182011-11-11 22:40:27127 duration_ = 0;
128 max_ = 0;
[email protected]63f5b0e2011-11-04 00:23:27129}
130//------------------------------------------------------------------------------
[email protected]84baeca2011-10-24 18:55:16131BirthOnThread::BirthOnThread(const Location& location,
132 const ThreadData& current)
initial.commitd7cae122008-07-26 21:49:38133 : location_(location),
[email protected]84baeca2011-10-24 18:55:16134 birth_thread_(&current) {}
initial.commitd7cae122008-07-26 21:49:38135
136//------------------------------------------------------------------------------
[email protected]84baeca2011-10-24 18:55:16137Births::Births(const Location& location, const ThreadData& current)
138 : BirthOnThread(location, current),
[email protected]75b79202009-12-30 07:31:45139 birth_count_(1) { }
initial.commitd7cae122008-07-26 21:49:38140
141//------------------------------------------------------------------------------
[email protected]84baeca2011-10-24 18:55:16142// ThreadData maintains the central data for all births and deaths.
initial.commitd7cae122008-07-26 21:49:38143
[email protected]b2a9bbd2011-10-31 22:36:21144// TODO(jar): We should pull all these static vars together, into a struct, and
145// optimize layout so that we benefit from locality of reference during accesses
146// to them.
147
148// A TLS slot which points to the ThreadData instance for the current thread. We
149// do a fake initialization here (zeroing out data), and then the real in-place
150// construction happens when we call tls_index_.Initialize().
151// static
152base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED);
153
154// A lock-protected counter to assign sequence number to threads.
155// static
156int ThreadData::thread_number_counter_ = 0;
157
158// static
159int ThreadData::incarnation_counter_ = 0;
160
initial.commitd7cae122008-07-26 21:49:38161// static
[email protected]84baeca2011-10-24 18:55:16162ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
163
164// static
165ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL;
166
initial.commitd7cae122008-07-26 21:49:38167// static
[email protected]77169a62011-11-14 20:36:46168base::LazyInstance<base::Lock,
169 base::LeakyLazyInstanceTraits<base::Lock> >
170 ThreadData::list_lock_(base::LINKER_INITIALIZED);
initial.commitd7cae122008-07-26 21:49:38171
172// static
173ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
174
[email protected]84baeca2011-10-24 18:55:16175ThreadData::ThreadData(const std::string& suggested_name)
[email protected]b2a9bbd2011-10-31 22:36:21176 : incarnation_count_for_pool_(-1),
177 next_(NULL),
[email protected]84baeca2011-10-24 18:55:16178 is_a_worker_thread_(false) {
[email protected]84b57952011-10-15 23:52:45179 DCHECK_GE(suggested_name.size(), 0u);
180 thread_name_ = suggested_name;
[email protected]b2a9bbd2011-10-31 22:36:21181 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
[email protected]84b57952011-10-15 23:52:45182}
183
[email protected]b2a9bbd2011-10-31 22:36:21184ThreadData::ThreadData()
185 : incarnation_count_for_pool_(-1),
186 next_(NULL),
187 is_a_worker_thread_(true) {
[email protected]84b57952011-10-15 23:52:45188 int thread_number;
189 {
[email protected]77169a62011-11-14 20:36:46190 base::AutoLock lock(*list_lock_.Pointer());
[email protected]84baeca2011-10-24 18:55:16191 thread_number = ++thread_number_counter_;
[email protected]84b57952011-10-15 23:52:45192 }
193 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
[email protected]63f5b0e2011-11-04 00:23:27194 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
[email protected]359d2bf2010-11-19 20:34:18195}
initial.commitd7cae122008-07-26 21:49:38196
[email protected]d4799a32010-09-28 22:54:58197ThreadData::~ThreadData() {}
198
[email protected]84baeca2011-10-24 18:55:16199void ThreadData::PushToHeadOfList() {
200 DCHECK(!next_);
[email protected]77169a62011-11-14 20:36:46201 base::AutoLock lock(*list_lock_.Pointer());
[email protected]b2a9bbd2011-10-31 22:36:21202 incarnation_count_for_pool_ = incarnation_counter_;
[email protected]84baeca2011-10-24 18:55:16203 next_ = all_thread_data_list_head_;
204 all_thread_data_list_head_ = this;
205}
206
initial.commitd7cae122008-07-26 21:49:38207// static
[email protected]84b57952011-10-15 23:52:45208void ThreadData::InitializeThreadContext(const std::string& suggested_name) {
[email protected]b2a9bbd2011-10-31 22:36:21209 if (!Initialize()) // Always initialize if needed.
210 return;
211 ThreadData* current_thread_data =
212 reinterpret_cast<ThreadData*>(tls_index_.Get());
213 if (current_thread_data)
214 return; // Browser tests instigate this.
215 current_thread_data = new ThreadData(suggested_name);
[email protected]84baeca2011-10-24 18:55:16216 tls_index_.Set(current_thread_data);
[email protected]84b57952011-10-15 23:52:45217}
initial.commitd7cae122008-07-26 21:49:38218
[email protected]84b57952011-10-15 23:52:45219// static
220ThreadData* ThreadData::Get() {
221 if (!tls_index_.initialized())
222 return NULL; // For unittests only.
[email protected]84baeca2011-10-24 18:55:16223 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get());
224 if (registered)
225 return registered;
226
227 // We must be a worker thread, since we didn't pre-register.
228 ThreadData* worker_thread_data = NULL;
229 {
[email protected]77169a62011-11-14 20:36:46230 base::AutoLock lock(*list_lock_.Pointer());
231 if (unregistered_thread_data_pool_ &&
232 !unregistered_thread_data_pool_->empty()) {
[email protected]84baeca2011-10-24 18:55:16233 worker_thread_data =
234 const_cast<ThreadData*>(unregistered_thread_data_pool_->top());
235 unregistered_thread_data_pool_->pop();
236 }
initial.commitd7cae122008-07-26 21:49:38237 }
[email protected]84baeca2011-10-24 18:55:16238
239 // If we can't find a previously used instance, then we have to create one.
240 if (!worker_thread_data)
241 worker_thread_data = new ThreadData();
242
243 tls_index_.Set(worker_thread_data);
244 return worker_thread_data;
[email protected]84b57952011-10-15 23:52:45245}
246
247// static
[email protected]84baeca2011-10-24 18:55:16248void ThreadData::OnThreadTermination(void* thread_data) {
249 if (!kTrackAllTaskObjects)
250 return; // Not compiled in.
[email protected]84baeca2011-10-24 18:55:16251 if (!thread_data)
252 return;
253 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup();
[email protected]84baeca2011-10-24 18:55:16254}
255
256void ThreadData::OnThreadTerminationCleanup() const {
[email protected]84baeca2011-10-24 18:55:16257 if (!is_a_worker_thread_)
258 return;
[email protected]77169a62011-11-14 20:36:46259 base::AutoLock lock(*list_lock_.Pointer());
[email protected]b2a9bbd2011-10-31 22:36:21260 if (incarnation_counter_ != incarnation_count_for_pool_)
261 return; // ThreadData was constructed in an earlier unit test.
262
263 // Handle case where we are in unit tests, and have become UNINITIALIZED.
264 // In that case, the pool might be NULL. We really should detect this via the
265 // incarnation_counter_, but this call is rarely made, so we can afford to
266 // code defensively.
[email protected]77169a62011-11-14 20:36:46267 if (!unregistered_thread_data_pool_)
268 unregistered_thread_data_pool_ = new ThreadDataPool;
269 unregistered_thread_data_pool_->push(this);
initial.commitd7cae122008-07-26 21:49:38270}
271
initial.commitd7cae122008-07-26 21:49:38272// static
273void ThreadData::WriteHTML(const std::string& query, std::string* output) {
[email protected]b2a9bbd2011-10-31 22:36:21274 if (status_ == UNINITIALIZED)
initial.commitd7cae122008-07-26 21:49:38275 return; // Not yet initialized.
276
initial.commitd7cae122008-07-26 21:49:38277 DataCollector collected_data; // Gather data.
278 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
279
280 // Data Gathering is complete. Now to sort/process/render.
281 DataCollector::Collection* collection = collected_data.collection();
282
283 // Create filtering and sort comparison object.
284 Comparator comparator;
[email protected]be843e22011-06-28 17:35:18285 comparator.ParseQuery(query);
initial.commitd7cae122008-07-26 21:49:38286
287 // Filter out acceptable (matching) instances.
288 DataCollector::Collection match_array;
289 for (DataCollector::Collection::iterator it = collection->begin();
290 it != collection->end(); ++it) {
291 if (comparator.Acceptable(*it))
292 match_array.push_back(*it);
293 }
294
295 comparator.Sort(&match_array);
296
297 WriteHTMLTotalAndSubtotals(match_array, comparator, output);
298
299 comparator.Clear(); // Delete tiebreaker_ instances.
300
[email protected]75b79202009-12-30 07:31:45301 output->append("</pre>");
302
303 const char* help_string = "The following are the keywords that can be used to"
[email protected]ca1863672011-10-07 18:26:02304 " sort and aggregate the data, or to select data.<br><ul>"
[email protected]84b57952011-10-15 23:52:45305 "<li><b>Count</b> Number of instances seen."
306 "<li><b>Duration</b> Average duration in ms of Run() time."
307 "<li><b>TotalDuration</b> Summed durations in ms of Run() times."
[email protected]63f5b0e2011-11-04 00:23:27308 "<li><b>MaxDuration</b> Largest duration in ms of Run() times."
[email protected]84b57952011-10-15 23:52:45309 "<li><b>AverageQueueDuration</b> Average duration in ms of queueing time."
[email protected]63f5b0e2011-11-04 00:23:27310 "<li><b>TotalQueueDuration</b> Summed queuing durations in ms."
311 "<li><b>MaxQueueDuration</b> Largest duration in ms of queueing times."
[email protected]84b57952011-10-15 23:52:45312 "<li><b>Birth</b> Thread on which the task was constructed."
313 "<li><b>Death</b> Thread on which the task was run and deleted."
314 "<li><b>File</b> File in which the task was contructed."
315 "<li><b>Function</b> Function in which the task was constructed."
316 "<li><b>Line</b> Line number of the file in which the task was constructed."
[email protected]75b79202009-12-30 07:31:45317 "</ul><br>"
318 "As examples:<ul>"
[email protected]ca1863672011-10-07 18:26:02319 "<li><b>about:tracking/file</b> would sort the above data by file, and"
[email protected]75b79202009-12-30 07:31:45320 " aggregate data on a per-file basis."
[email protected]ca1863672011-10-07 18:26:02321 "<li><b>about:tracking/file=Dns</b> would only list data for tasks"
322 " constructed in a file containing the text |Dns|."
[email protected]84b57952011-10-15 23:52:45323 "<li><b>about:tracking/death/duration</b> would sort the data by death"
324 " thread(i.e., where tasks ran) and then by the average runtime for the"
325 " tasks. Form an aggregation group, one per thread, showing the results on"
326 " each thread."
[email protected]ca1863672011-10-07 18:26:02327 "<li><b>about:tracking/birth/death</b> would sort the above list by birth"
[email protected]75b79202009-12-30 07:31:45328 " thread, and then by death thread, and would aggregate data for each pair"
329 " of lifetime events."
330 "</ul>"
331 " The data can be reset to zero (discarding all births, deaths, etc.) using"
[email protected]ca1863672011-10-07 18:26:02332 " <b>about:tracking/reset</b>. The existing stats will be displayed, but"
333 " the internal stats will be set to zero, and start accumulating afresh."
334 " This option is very helpful if you only wish to consider tasks created"
335 " after some point in time.<br><br>"
[email protected]75b79202009-12-30 07:31:45336 "If you wish to monitor Renderer events, be sure to run in --single-process"
337 " mode.";
338 output->append(help_string);
initial.commitd7cae122008-07-26 21:49:38339}
340
341// static
342void ThreadData::WriteHTMLTotalAndSubtotals(
343 const DataCollector::Collection& match_array,
344 const Comparator& comparator,
345 std::string* output) {
[email protected]84baeca2011-10-24 18:55:16346 if (match_array.empty()) {
initial.commitd7cae122008-07-26 21:49:38347 output->append("There were no tracked matches.");
[email protected]84baeca2011-10-24 18:55:16348 return;
349 }
350 // Aggregate during printing
351 Aggregation totals;
352 for (size_t i = 0; i < match_array.size(); ++i) {
353 totals.AddDeathSnapshot(match_array[i]);
354 }
355 output->append("Aggregate Stats: ");
356 totals.WriteHTML(output);
357 output->append("<hr><hr>");
initial.commitd7cae122008-07-26 21:49:38358
[email protected]84baeca2011-10-24 18:55:16359 Aggregation subtotals;
360 for (size_t i = 0; i < match_array.size(); ++i) {
361 if (0 == i || !comparator.Equivalent(match_array[i - 1],
362 match_array[i])) {
363 // Print group's defining characteristics.
364 comparator.WriteSortGrouping(match_array[i], output);
365 output->append("<br><br>");
366 }
367 comparator.WriteSnapshotHTML(match_array[i], output);
368 output->append("<br>");
369 subtotals.AddDeathSnapshot(match_array[i]);
370 if (i + 1 >= match_array.size() ||
371 !comparator.Equivalent(match_array[i],
372 match_array[i + 1])) {
373 // Print aggregate stats for the group.
initial.commitd7cae122008-07-26 21:49:38374 output->append("<br>");
[email protected]84baeca2011-10-24 18:55:16375 subtotals.WriteHTML(output);
376 output->append("<br><hr><br>");
377 subtotals.Clear();
initial.commitd7cae122008-07-26 21:49:38378 }
379 }
380}
381
[email protected]84baeca2011-10-24 18:55:16382// static
[email protected]b2a9bbd2011-10-31 22:36:21383base::DictionaryValue* ThreadData::ToValue() {
[email protected]84baeca2011-10-24 18:55:16384 DataCollector collected_data; // Gather data.
385 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
386 base::ListValue* list = collected_data.ToValue();
387 base::DictionaryValue* dictionary = new base::DictionaryValue();
388 dictionary->Set("list", list);
[email protected]84baeca2011-10-24 18:55:16389 return dictionary;
390}
391
[email protected]75b79202009-12-30 07:31:45392Births* ThreadData::TallyABirth(const Location& location) {
initial.commitd7cae122008-07-26 21:49:38393 BirthMap::iterator it = birth_map_.find(location);
[email protected]75b79202009-12-30 07:31:45394 if (it != birth_map_.end()) {
395 it->second->RecordBirth();
initial.commitd7cae122008-07-26 21:49:38396 return it->second;
[email protected]75b79202009-12-30 07:31:45397 }
initial.commitd7cae122008-07-26 21:49:38398
[email protected]84baeca2011-10-24 18:55:16399 Births* tracker = new Births(location, *this);
initial.commitd7cae122008-07-26 21:49:38400 // Lock since the map may get relocated now, and other threads sometimes
401 // snapshot it (but they lock before copying it).
[email protected]20305ec2011-01-21 04:55:52402 base::AutoLock lock(lock_);
initial.commitd7cae122008-07-26 21:49:38403 birth_map_[location] = tracker;
404 return tracker;
405}
406
[email protected]84baeca2011-10-24 18:55:16407void ThreadData::TallyADeath(const Births& birth,
[email protected]c25db182011-11-11 22:40:27408 DurationInt queue_duration,
409 DurationInt run_duration) {
[email protected]84baeca2011-10-24 18:55:16410 DeathMap::iterator it = death_map_.find(&birth);
411 DeathData* death_data;
initial.commitd7cae122008-07-26 21:49:38412 if (it != death_map_.end()) {
[email protected]84baeca2011-10-24 18:55:16413 death_data = &it->second;
414 } else {
415 base::AutoLock lock(lock_); // Lock since the map may get relocated now.
416 death_data = &death_map_[&birth];
417 } // Release lock ASAP.
418 death_data->RecordDeath(queue_duration, run_duration);
initial.commitd7cae122008-07-26 21:49:38419}
420
421// static
[email protected]180c85e2011-07-26 18:25:16422Births* ThreadData::TallyABirthIfActive(const Location& location) {
[email protected]84baeca2011-10-24 18:55:16423 if (!kTrackAllTaskObjects)
424 return NULL; // Not compiled in.
425
[email protected]b2a9bbd2011-10-31 22:36:21426 if (!tracking_status())
[email protected]84b57952011-10-15 23:52:45427 return NULL;
428 ThreadData* current_thread_data = Get();
429 if (!current_thread_data)
430 return NULL;
431 return current_thread_data->TallyABirth(location);
[email protected]180c85e2011-07-26 18:25:16432}
433
434// static
[email protected]b2a9bbd2011-10-31 22:36:21435void ThreadData::TallyRunOnNamedThreadIfTracking(
436 const base::TrackingInfo& completed_task,
437 const TrackedTime& start_of_run,
438 const TrackedTime& end_of_run) {
[email protected]84baeca2011-10-24 18:55:16439 if (!kTrackAllTaskObjects)
440 return; // Not compiled in.
441
[email protected]b2a9bbd2011-10-31 22:36:21442 // Even if we have been DEACTIVATED, we will process any pending births so
443 // that our data structures (which counted the outstanding births) remain
444 // consistent.
445 const Births* birth = completed_task.birth_tally;
446 if (!birth)
[email protected]84b57952011-10-15 23:52:45447 return;
448 ThreadData* current_thread_data = Get();
449 if (!current_thread_data)
450 return;
451
452 // To avoid conflating our stats with the delay duration in a PostDelayedTask,
453 // we identify such tasks, and replace their post_time with the time they
[email protected]b2a9bbd2011-10-31 22:36:21454 // were scheduled (requested?) to emerge from the delayed task queue. This
[email protected]84b57952011-10-15 23:52:45455 // means that queueing delay for such tasks will show how long they went
456 // unserviced, after they *could* be serviced. This is the same stat as we
457 // have for non-delayed tasks, and we consistently call it queueing delay.
[email protected]b2a9bbd2011-10-31 22:36:21458 TrackedTime effective_post_time = completed_task.delayed_run_time.is_null()
459 ? tracked_objects::TrackedTime(completed_task.time_posted)
460 : tracked_objects::TrackedTime(completed_task.delayed_run_time);
461
462 // Watch out for a race where status_ is changing, and hence one or both
463 // of start_of_run or end_of_run is zero. IN that case, we didn't bother to
464 // get a time value since we "weren't tracking" and we were trying to be
465 // efficient by not calling for a genuine time value. For simplicity, we'll
466 // use a default zero duration when we can't calculate a true value.
[email protected]c25db182011-11-11 22:40:27467 DurationInt queue_duration = 0;
468 DurationInt run_duration = 0;
[email protected]b2a9bbd2011-10-31 22:36:21469 if (!start_of_run.is_null()) {
[email protected]c25db182011-11-11 22:40:27470 queue_duration = (start_of_run - effective_post_time).InMilliseconds();
[email protected]b2a9bbd2011-10-31 22:36:21471 if (!end_of_run.is_null())
[email protected]c25db182011-11-11 22:40:27472 run_duration = (end_of_run - start_of_run).InMilliseconds();
[email protected]b2a9bbd2011-10-31 22:36:21473 }
474 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
475}
476
477// static
478void ThreadData::TallyRunOnWorkerThreadIfTracking(
479 const Births* birth,
480 const TrackedTime& time_posted,
481 const TrackedTime& start_of_run,
482 const TrackedTime& end_of_run) {
483 if (!kTrackAllTaskObjects)
484 return; // Not compiled in.
485
486 // Even if we have been DEACTIVATED, we will process any pending births so
487 // that our data structures (which counted the outstanding births) remain
488 // consistent.
489 if (!birth)
490 return;
491
492 // TODO(jar): Support the option to coalesce all worker-thread activity under
493 // one ThreadData instance that uses locks to protect *all* access. This will
494 // reduce memory (making it provably bounded), but run incrementally slower
495 // (since we'll use locks on TallyBirth and TallyDeath). The good news is
496 // that the locks on TallyDeath will be *after* the worker thread has run, and
497 // hence nothing will be waiting for the completion (... besides some other
498 // thread that might like to run). Also, the worker threads tasks are
499 // generally longer, and hence the cost of the lock may perchance be amortized
500 // over the long task's lifetime.
501 ThreadData* current_thread_data = Get();
502 if (!current_thread_data)
503 return;
504
[email protected]c25db182011-11-11 22:40:27505 DurationInt queue_duration = 0;
506 DurationInt run_duration = 0;
[email protected]b2a9bbd2011-10-31 22:36:21507 if (!start_of_run.is_null()) {
[email protected]c25db182011-11-11 22:40:27508 queue_duration = (start_of_run - time_posted).InMilliseconds();
[email protected]b2a9bbd2011-10-31 22:36:21509 if (!end_of_run.is_null())
[email protected]c25db182011-11-11 22:40:27510 run_duration = (end_of_run - start_of_run).InMilliseconds();
[email protected]b2a9bbd2011-10-31 22:36:21511 }
[email protected]84baeca2011-10-24 18:55:16512 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
[email protected]180c85e2011-07-26 18:25:16513}
514
515// static
[email protected]dbe5d2072011-11-08 17:09:21516void ThreadData::TallyRunInAScopedRegionIfTracking(
517 const Births* birth,
518 const TrackedTime& start_of_run,
519 const TrackedTime& end_of_run) {
520 if (!kTrackAllTaskObjects)
521 return; // Not compiled in.
522
523 // Even if we have been DEACTIVATED, we will process any pending births so
524 // that our data structures (which counted the outstanding births) remain
525 // consistent.
526 if (!birth)
527 return;
528
529 ThreadData* current_thread_data = Get();
530 if (!current_thread_data)
531 return;
532
[email protected]c25db182011-11-11 22:40:27533 DurationInt queue_duration = 0;
534 DurationInt run_duration = (end_of_run - start_of_run).InMilliseconds();
[email protected]dbe5d2072011-11-08 17:09:21535 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
536}
537
538
539
540// static
initial.commitd7cae122008-07-26 21:49:38541ThreadData* ThreadData::first() {
[email protected]77169a62011-11-14 20:36:46542 base::AutoLock lock(*list_lock_.Pointer());
[email protected]84baeca2011-10-24 18:55:16543 return all_thread_data_list_head_;
initial.commitd7cae122008-07-26 21:49:38544}
545
initial.commitd7cae122008-07-26 21:49:38546// This may be called from another thread.
547void ThreadData::SnapshotBirthMap(BirthMap *output) const {
[email protected]20305ec2011-01-21 04:55:52548 base::AutoLock lock(lock_);
initial.commitd7cae122008-07-26 21:49:38549 for (BirthMap::const_iterator it = birth_map_.begin();
550 it != birth_map_.end(); ++it)
551 (*output)[it->first] = it->second;
552}
553
554// This may be called from another thread.
555void ThreadData::SnapshotDeathMap(DeathMap *output) const {
[email protected]20305ec2011-01-21 04:55:52556 base::AutoLock lock(lock_);
initial.commitd7cae122008-07-26 21:49:38557 for (DeathMap::const_iterator it = death_map_.begin();
558 it != death_map_.end(); ++it)
559 (*output)[it->first] = it->second;
560}
561
[email protected]75b79202009-12-30 07:31:45562// static
563void ThreadData::ResetAllThreadData() {
[email protected]84baeca2011-10-24 18:55:16564 ThreadData* my_list = first();
[email protected]75b79202009-12-30 07:31:45565
566 for (ThreadData* thread_data = my_list;
567 thread_data;
568 thread_data = thread_data->next())
569 thread_data->Reset();
570}
571
572void ThreadData::Reset() {
[email protected]20305ec2011-01-21 04:55:52573 base::AutoLock lock(lock_);
[email protected]75b79202009-12-30 07:31:45574 for (DeathMap::iterator it = death_map_.begin();
575 it != death_map_.end(); ++it)
576 it->second.Clear();
577 for (BirthMap::iterator it = birth_map_.begin();
578 it != birth_map_.end(); ++it)
579 it->second->Clear();
580}
581
[email protected]b2a9bbd2011-10-31 22:36:21582bool ThreadData::Initialize() {
[email protected]84baeca2011-10-24 18:55:16583 if (!kTrackAllTaskObjects)
584 return false; // Not compiled in.
[email protected]b2a9bbd2011-10-31 22:36:21585 if (status_ != UNINITIALIZED)
586 return true;
587 // Initialize all leaking constants that are difficult to toggle in and out
588 // of existance.
589 // First call must be made when single threaded at startup.
590 // Perform the "real" TLS initialization now, and leave it intact through
[email protected]84baeca2011-10-24 18:55:16591 // process termination.
[email protected]b2a9bbd2011-10-31 22:36:21592 if (!tls_index_.initialized()) // Testing may have initialized this.
[email protected]84baeca2011-10-24 18:55:16593 tls_index_.Initialize(&ThreadData::OnThreadTermination);
594 DCHECK(tls_index_.initialized());
[email protected]b2a9bbd2011-10-31 22:36:21595 status_ = kInitialStartupState;
[email protected]3f095c0a2011-10-31 15:32:08596
[email protected]77169a62011-11-14 20:36:46597 base::AutoLock lock(*list_lock_.Pointer());
[email protected]b2a9bbd2011-10-31 22:36:21598 ++incarnation_counter_;
initial.commitd7cae122008-07-26 21:49:38599 return true;
600}
601
602// static
[email protected]b2a9bbd2011-10-31 22:36:21603bool ThreadData::InitializeAndSetTrackingStatus(bool status) {
604 if (!Initialize()) // No-op if already initialized.
605 return false; // Not compiled in.
606
607 status_ = status ? ACTIVE : DEACTIVATED;
608 return true;
609}
610
611// static
612bool ThreadData::tracking_status() {
initial.commitd7cae122008-07-26 21:49:38613 return status_ == ACTIVE;
614}
615
616// static
[email protected]dda97682011-11-14 05:24:07617TrackedTime ThreadData::NowForStartOfRun() {
618 return Now();
619}
620
621// static
622TrackedTime ThreadData::NowForEndOfRun() {
623 return Now();
624}
625
626// static
[email protected]b2a9bbd2011-10-31 22:36:21627TrackedTime ThreadData::Now() {
628 if (kTrackAllTaskObjects && tracking_status())
629 return TrackedTime::Now();
630 return TrackedTime(); // Super fast when disabled, or not compiled.
[email protected]84b57952011-10-15 23:52:45631}
initial.commitd7cae122008-07-26 21:49:38632
633// static
[email protected]b2a9bbd2011-10-31 22:36:21634void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
[email protected]84baeca2011-10-24 18:55:16635 // This is only called from test code, where we need to cleanup so that
636 // additional tests can be run.
initial.commitd7cae122008-07-26 21:49:38637 // We must be single threaded... but be careful anyway.
[email protected]b2a9bbd2011-10-31 22:36:21638 if (!InitializeAndSetTrackingStatus(false))
initial.commitd7cae122008-07-26 21:49:38639 return;
640 ThreadData* thread_data_list;
[email protected]84baeca2011-10-24 18:55:16641 ThreadDataPool* final_pool;
initial.commitd7cae122008-07-26 21:49:38642 {
[email protected]77169a62011-11-14 20:36:46643 base::AutoLock lock(*list_lock_.Pointer());
[email protected]84baeca2011-10-24 18:55:16644 thread_data_list = all_thread_data_list_head_;
645 all_thread_data_list_head_ = NULL;
646 final_pool = unregistered_thread_data_pool_;
647 unregistered_thread_data_pool_ = NULL;
[email protected]b2a9bbd2011-10-31 22:36:21648 ++incarnation_counter_;
initial.commitd7cae122008-07-26 21:49:38649 }
650
[email protected]b2a9bbd2011-10-31 22:36:21651 // Put most global static back in pristine shape.
652 thread_number_counter_ = 0;
653 tls_index_.Set(NULL);
654 status_ = UNINITIALIZED;
655
656 // To avoid any chance of racing in unit tests, which is the only place we
657 // call this function, we may sometimes leak all the data structures we
658 // recovered, as they may still be in use on threads from prior tests!
659 if (leak)
660 return;
661
662 // When we want to cleanup (on a single thread), here is what we do.
663
[email protected]84baeca2011-10-24 18:55:16664 if (final_pool) {
665 // The thread_data_list contains *all* the instances, and we'll use it to
666 // delete them. This pool has pointers to some instances, and we just
667 // have to drop those pointers (and not do the deletes here).
668 while (!final_pool->empty())
669 final_pool->pop();
670 delete final_pool;
671 }
672
673 // Do actual recursive delete in all ThreadData instances.
initial.commitd7cae122008-07-26 21:49:38674 while (thread_data_list) {
675 ThreadData* next_thread_data = thread_data_list;
676 thread_data_list = thread_data_list->next();
677
678 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
679 next_thread_data->birth_map_.end() != it; ++it)
680 delete it->second; // Delete the Birth Records.
681 next_thread_data->birth_map_.clear();
682 next_thread_data->death_map_.clear();
683 delete next_thread_data; // Includes all Death Records.
684 }
initial.commitd7cae122008-07-26 21:49:38685}
686
initial.commitd7cae122008-07-26 21:49:38687//------------------------------------------------------------------------------
688// Individual 3-tuple of birth (place and thread) along with death thread, and
689// the accumulated stats for instances (DeathData).
690
691Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
692 const ThreadData& death_thread,
693 const DeathData& death_data)
694 : birth_(&birth_on_thread),
695 death_thread_(&death_thread),
696 death_data_(death_data) {
697}
698
699Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
700 : birth_(&birth_on_thread),
701 death_thread_(NULL),
702 death_data_(DeathData(count)) {
703}
704
705const std::string Snapshot::DeathThreadName() const {
706 if (death_thread_)
[email protected]84b57952011-10-15 23:52:45707 return death_thread_->thread_name();
initial.commitd7cae122008-07-26 21:49:38708 return "Still_Alive";
709}
710
[email protected]84baeca2011-10-24 18:55:16711void Snapshot::WriteHTML(std::string* output) const {
712 death_data_.WriteHTML(output);
[email protected]f1633932010-08-17 23:05:28713 base::StringAppendF(output, "%s->%s ",
[email protected]84b57952011-10-15 23:52:45714 birth_->birth_thread()->thread_name().c_str(),
[email protected]84baeca2011-10-24 18:55:16715 DeathThreadName().c_str());
initial.commitd7cae122008-07-26 21:49:38716 birth_->location().Write(true, true, output);
717}
718
[email protected]84baeca2011-10-24 18:55:16719base::DictionaryValue* Snapshot::ToValue() const {
720 base::DictionaryValue* dictionary = new base::DictionaryValue;
721 dictionary->Set("death_data", death_data_.ToValue());
722 dictionary->Set("birth_thread",
723 base::Value::CreateStringValue(birth_->birth_thread()->thread_name()));
724 dictionary->Set("death_thread",
725 base::Value::CreateStringValue(DeathThreadName()));
726 dictionary->Set("location", birth_->location().ToValue());
727 return dictionary;
728}
729
initial.commitd7cae122008-07-26 21:49:38730//------------------------------------------------------------------------------
731// DataCollector
732
733DataCollector::DataCollector() {
[email protected]b2a9bbd2011-10-31 22:36:21734 if (!kTrackAllTaskObjects)
735 return; // Not compiled in.
initial.commitd7cae122008-07-26 21:49:38736
[email protected]75b79202009-12-30 07:31:45737 // Get an unchanging copy of a ThreadData list.
[email protected]84baeca2011-10-24 18:55:16738 ThreadData* my_list = ThreadData::first();
initial.commitd7cae122008-07-26 21:49:38739
[email protected]84b57952011-10-15 23:52:45740 // Gather data serially.
[email protected]75b79202009-12-30 07:31:45741 // This hackish approach *can* get some slighly corrupt tallies, as we are
742 // grabbing values without the protection of a lock, but it has the advantage
743 // of working even with threads that don't have message loops. If a user
744 // sees any strangeness, they can always just run their stats gathering a
745 // second time.
initial.commitd7cae122008-07-26 21:49:38746 for (ThreadData* thread_data = my_list;
747 thread_data;
748 thread_data = thread_data->next()) {
749 Append(*thread_data);
750 }
751}
752
[email protected]d4799a32010-09-28 22:54:58753DataCollector::~DataCollector() {
754}
755
initial.commitd7cae122008-07-26 21:49:38756void DataCollector::Append(const ThreadData& thread_data) {
[email protected]84b57952011-10-15 23:52:45757 // Get copy of data.
initial.commitd7cae122008-07-26 21:49:38758 ThreadData::BirthMap birth_map;
759 thread_data.SnapshotBirthMap(&birth_map);
760 ThreadData::DeathMap death_map;
761 thread_data.SnapshotDeathMap(&death_map);
762
initial.commitd7cae122008-07-26 21:49:38763 for (ThreadData::DeathMap::const_iterator it = death_map.begin();
764 it != death_map.end(); ++it) {
765 collection_.push_back(Snapshot(*it->first, thread_data, it->second));
766 global_birth_count_[it->first] -= it->first->birth_count();
767 }
768
769 for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
770 it != birth_map.end(); ++it) {
771 global_birth_count_[it->second] += it->second->birth_count();
772 }
initial.commitd7cae122008-07-26 21:49:38773}
774
775DataCollector::Collection* DataCollector::collection() {
initial.commitd7cae122008-07-26 21:49:38776 return &collection_;
777}
778
779void DataCollector::AddListOfLivingObjects() {
initial.commitd7cae122008-07-26 21:49:38780 for (BirthCount::iterator it = global_birth_count_.begin();
781 it != global_birth_count_.end(); ++it) {
782 if (it->second > 0)
783 collection_.push_back(Snapshot(*it->first, it->second));
784 }
785}
786
[email protected]84baeca2011-10-24 18:55:16787base::ListValue* DataCollector::ToValue() const {
788 base::ListValue* list = new base::ListValue;
789 for (size_t i = 0; i < collection_.size(); ++i) {
790 list->Append(collection_[i].ToValue());
791 }
792 return list;
793}
794
initial.commitd7cae122008-07-26 21:49:38795//------------------------------------------------------------------------------
796// Aggregation
797
[email protected]d4799a32010-09-28 22:54:58798Aggregation::Aggregation()
799 : birth_count_(0) {
800}
801
802Aggregation::~Aggregation() {
803}
804
initial.commitd7cae122008-07-26 21:49:38805void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
806 AddBirth(snapshot.birth());
807 death_threads_[snapshot.death_thread()]++;
808 AddDeathData(snapshot.death_data());
809}
810
811void Aggregation::AddBirths(const Births& births) {
812 AddBirth(births);
813 birth_count_ += births.birth_count();
814}
815void Aggregation::AddBirth(const BirthOnThread& birth) {
816 AddBirthPlace(birth.location());
817 birth_threads_[birth.birth_thread()]++;
818}
819
820void Aggregation::AddBirthPlace(const Location& location) {
821 locations_[location]++;
822 birth_files_[location.file_name()]++;
823}
824
[email protected]84baeca2011-10-24 18:55:16825void Aggregation::WriteHTML(std::string* output) const {
initial.commitd7cae122008-07-26 21:49:38826 if (locations_.size() == 1) {
827 locations_.begin()->first.Write(true, true, output);
828 } else {
[email protected]f1633932010-08-17 23:05:28829 base::StringAppendF(output, "%" PRIuS " Locations. ", locations_.size());
830 if (birth_files_.size() > 1) {
831 base::StringAppendF(output, "%" PRIuS " Files. ", birth_files_.size());
832 } else {
833 base::StringAppendF(output, "All born in %s. ",
834 birth_files_.begin()->first.c_str());
835 }
initial.commitd7cae122008-07-26 21:49:38836 }
837
[email protected]f1633932010-08-17 23:05:28838 if (birth_threads_.size() > 1) {
839 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ",
840 birth_threads_.size());
841 } else {
842 base::StringAppendF(output, "All born on %s. ",
[email protected]84b57952011-10-15 23:52:45843 birth_threads_.begin()->first->thread_name().c_str());
[email protected]f1633932010-08-17 23:05:28844 }
initial.commitd7cae122008-07-26 21:49:38845
846 if (death_threads_.size() > 1) {
[email protected]f1633932010-08-17 23:05:28847 base::StringAppendF(output, "%" PRIuS " DeathThreads. ",
848 death_threads_.size());
initial.commitd7cae122008-07-26 21:49:38849 } else {
[email protected]f1633932010-08-17 23:05:28850 if (death_threads_.begin()->first) {
851 base::StringAppendF(output, "All deleted on %s. ",
[email protected]84b57952011-10-15 23:52:45852 death_threads_.begin()->first->thread_name().c_str());
[email protected]f1633932010-08-17 23:05:28853 } else {
initial.commitd7cae122008-07-26 21:49:38854 output->append("All these objects are still alive.");
[email protected]f1633932010-08-17 23:05:28855 }
initial.commitd7cae122008-07-26 21:49:38856 }
857
858 if (birth_count_ > 1)
[email protected]f1633932010-08-17 23:05:28859 base::StringAppendF(output, "Births=%d ", birth_count_);
initial.commitd7cae122008-07-26 21:49:38860
[email protected]84baeca2011-10-24 18:55:16861 DeathData::WriteHTML(output);
initial.commitd7cae122008-07-26 21:49:38862}
863
864void Aggregation::Clear() {
865 birth_count_ = 0;
866 birth_files_.clear();
867 locations_.clear();
868 birth_threads_.clear();
869 DeathData::Clear();
870 death_threads_.clear();
871}
872
873//------------------------------------------------------------------------------
874// Comparison object for sorting.
875
876Comparator::Comparator()
877 : selector_(NIL),
878 tiebreaker_(NULL),
879 combined_selectors_(0),
880 use_tiebreaker_for_sort_only_(false) {}
881
882void Comparator::Clear() {
883 if (tiebreaker_) {
884 tiebreaker_->Clear();
885 delete tiebreaker_;
886 tiebreaker_ = NULL;
887 }
888 use_tiebreaker_for_sort_only_ = false;
889 selector_ = NIL;
890}
891
[email protected]b2a9bbd2011-10-31 22:36:21892// static
893Comparator::Selector Comparator::FindSelector(const std::string& keyword) {
894 // Sorting and aggretation keywords, which specify how to sort the data, or
895 // can specify a required match from the specified field in the record.
896 if (0 == keyword.compare("count"))
897 return COUNT;
898 if (0 == keyword.compare("totalduration"))
899 return TOTAL_RUN_DURATION;
900 if (0 == keyword.compare("duration"))
901 return AVERAGE_RUN_DURATION;
[email protected]63f5b0e2011-11-04 00:23:27902 if (0 == keyword.compare("maxduration"))
903 return MAX_RUN_DURATION;
[email protected]b2a9bbd2011-10-31 22:36:21904 if (0 == keyword.compare("totalqueueduration"))
905 return TOTAL_QUEUE_DURATION;
906 if (0 == keyword.compare("averagequeueduration"))
907 return AVERAGE_QUEUE_DURATION;
[email protected]63f5b0e2011-11-04 00:23:27908 if (0 == keyword.compare("maxqueueduration"))
909 return MAX_QUEUE_DURATION;
[email protected]b2a9bbd2011-10-31 22:36:21910 if (0 == keyword.compare("birth"))
911 return BIRTH_THREAD;
912 if (0 == keyword.compare("death"))
913 return DEATH_THREAD;
914 if (0 == keyword.compare("file"))
915 return BIRTH_FILE;
916 if (0 == keyword.compare("function"))
917 return BIRTH_FUNCTION;
918 if (0 == keyword.compare("line"))
919 return BIRTH_LINE;
920 if (0 == keyword.compare("reset"))
921 return RESET_ALL_DATA;
922 return UNKNOWN_KEYWORD;
923}
924
initial.commitd7cae122008-07-26 21:49:38925bool Comparator::operator()(const Snapshot& left,
926 const Snapshot& right) const {
927 switch (selector_) {
928 case BIRTH_THREAD:
929 if (left.birth_thread() != right.birth_thread() &&
[email protected]84b57952011-10-15 23:52:45930 left.birth_thread()->thread_name() !=
931 right.birth_thread()->thread_name())
932 return left.birth_thread()->thread_name() <
933 right.birth_thread()->thread_name();
initial.commitd7cae122008-07-26 21:49:38934 break;
935
936 case DEATH_THREAD:
937 if (left.death_thread() != right.death_thread() &&
938 left.DeathThreadName() !=
939 right.DeathThreadName()) {
940 if (!left.death_thread())
941 return true;
942 if (!right.death_thread())
943 return false;
944 return left.DeathThreadName() <
945 right.DeathThreadName();
946 }
947 break;
948
949 case BIRTH_FILE:
950 if (left.location().file_name() != right.location().file_name()) {
951 int comp = strcmp(left.location().file_name(),
952 right.location().file_name());
953 if (comp)
954 return 0 > comp;
955 }
956 break;
957
958 case BIRTH_FUNCTION:
959 if (left.location().function_name() != right.location().function_name()) {
960 int comp = strcmp(left.location().function_name(),
961 right.location().function_name());
962 if (comp)
963 return 0 > comp;
964 }
965 break;
966
967 case BIRTH_LINE:
968 if (left.location().line_number() != right.location().line_number())
969 return left.location().line_number() <
970 right.location().line_number();
971 break;
972
973 case COUNT:
974 if (left.count() != right.count())
975 return left.count() > right.count(); // Sort large at front of vector.
976 break;
977
[email protected]84b57952011-10-15 23:52:45978 case AVERAGE_RUN_DURATION:
[email protected]75b79202009-12-30 07:31:45979 if (!left.count() || !right.count())
980 break;
[email protected]84b57952011-10-15 23:52:45981 if (left.AverageMsRunDuration() != right.AverageMsRunDuration())
982 return left.AverageMsRunDuration() > right.AverageMsRunDuration();
983 break;
984
985 case TOTAL_RUN_DURATION:
986 if (!left.count() || !right.count())
987 break;
988 if (left.run_duration() != right.run_duration())
989 return left.run_duration() > right.run_duration();
990 break;
991
[email protected]63f5b0e2011-11-04 00:23:27992 case MAX_RUN_DURATION:
993 if (!left.count() || !right.count())
994 break;
995 if (left.run_duration_max() != right.run_duration_max())
996 return left.run_duration_max() > right.run_duration_max();
997 break;
998
[email protected]84b57952011-10-15 23:52:45999 case AVERAGE_QUEUE_DURATION:
1000 if (!left.count() || !right.count())
1001 break;
1002 if (left.AverageMsQueueDuration() != right.AverageMsQueueDuration())
1003 return left.AverageMsQueueDuration() > right.AverageMsQueueDuration();
1004 break;
1005
1006 case TOTAL_QUEUE_DURATION:
1007 if (!left.count() || !right.count())
1008 break;
1009 if (left.queue_duration() != right.queue_duration())
1010 return left.queue_duration() > right.queue_duration();
initial.commitd7cae122008-07-26 21:49:381011 break;
[email protected]95c796b2008-08-20 03:25:471012
[email protected]63f5b0e2011-11-04 00:23:271013 case MAX_QUEUE_DURATION:
1014 if (!left.count() || !right.count())
1015 break;
1016 if (left.queue_duration_max() != right.queue_duration_max())
1017 return left.queue_duration_max() > right.queue_duration_max();
1018 break;
1019
[email protected]95c796b2008-08-20 03:25:471020 default:
1021 break;
initial.commitd7cae122008-07-26 21:49:381022 }
1023 if (tiebreaker_)
1024 return tiebreaker_->operator()(left, right);
1025 return false;
1026}
1027
[email protected]eae9c062011-01-11 00:50:591028void Comparator::Sort(DataCollector::Collection* collection) const {
1029 std::sort(collection->begin(), collection->end(), *this);
1030}
1031
initial.commitd7cae122008-07-26 21:49:381032bool Comparator::Equivalent(const Snapshot& left,
[email protected]95c796b2008-08-20 03:25:471033 const Snapshot& right) const {
initial.commitd7cae122008-07-26 21:49:381034 switch (selector_) {
1035 case BIRTH_THREAD:
1036 if (left.birth_thread() != right.birth_thread() &&
[email protected]84b57952011-10-15 23:52:451037 left.birth_thread()->thread_name() !=
1038 right.birth_thread()->thread_name())
initial.commitd7cae122008-07-26 21:49:381039 return false;
1040 break;
1041
1042 case DEATH_THREAD:
1043 if (left.death_thread() != right.death_thread() &&
[email protected]95c796b2008-08-20 03:25:471044 left.DeathThreadName() != right.DeathThreadName())
initial.commitd7cae122008-07-26 21:49:381045 return false;
1046 break;
1047
1048 case BIRTH_FILE:
[email protected]63f5b0e2011-11-04 00:23:271049 if (!required_.empty())
1050 break; // No reason to aggregate when we've filtered out some.
initial.commitd7cae122008-07-26 21:49:381051 if (left.location().file_name() != right.location().file_name()) {
1052 int comp = strcmp(left.location().file_name(),
1053 right.location().file_name());
1054 if (comp)
1055 return false;
1056 }
1057 break;
1058
1059 case BIRTH_FUNCTION:
[email protected]63f5b0e2011-11-04 00:23:271060 if (!required_.empty())
1061 break; // No reason to aggregate when we've filtered out some.
initial.commitd7cae122008-07-26 21:49:381062 if (left.location().function_name() != right.location().function_name()) {
1063 int comp = strcmp(left.location().function_name(),
1064 right.location().function_name());
1065 if (comp)
1066 return false;
1067 }
1068 break;
1069
1070 case COUNT:
[email protected]84b57952011-10-15 23:52:451071 case AVERAGE_RUN_DURATION:
1072 case TOTAL_RUN_DURATION:
[email protected]63f5b0e2011-11-04 00:23:271073 case MAX_RUN_DURATION:
[email protected]84b57952011-10-15 23:52:451074 case AVERAGE_QUEUE_DURATION:
1075 case TOTAL_QUEUE_DURATION:
[email protected]63f5b0e2011-11-04 00:23:271076 case MAX_QUEUE_DURATION:
[email protected]84b57952011-10-15 23:52:451077 // We don't produce separate aggretation when only counts or times differ.
initial.commitd7cae122008-07-26 21:49:381078 break;
[email protected]95c796b2008-08-20 03:25:471079
1080 default:
1081 break;
initial.commitd7cae122008-07-26 21:49:381082 }
1083 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
1084 return tiebreaker_->Equivalent(left, right);
1085 return true;
1086}
1087
1088bool Comparator::Acceptable(const Snapshot& sample) const {
1089 if (required_.size()) {
1090 switch (selector_) {
1091 case BIRTH_THREAD:
[email protected]84b57952011-10-15 23:52:451092 if (sample.birth_thread()->thread_name().find(required_) ==
[email protected]764be58b2008-08-08 20:03:421093 std::string::npos)
initial.commitd7cae122008-07-26 21:49:381094 return false;
1095 break;
1096
1097 case DEATH_THREAD:
[email protected]764be58b2008-08-08 20:03:421098 if (sample.DeathThreadName().find(required_) == std::string::npos)
initial.commitd7cae122008-07-26 21:49:381099 return false;
1100 break;
1101
[email protected]95c796b2008-08-20 03:25:471102 case BIRTH_FILE:
initial.commitd7cae122008-07-26 21:49:381103 if (!strstr(sample.location().file_name(), required_.c_str()))
[email protected]95c796b2008-08-20 03:25:471104 return false;
initial.commitd7cae122008-07-26 21:49:381105 break;
1106
[email protected]95c796b2008-08-20 03:25:471107 case BIRTH_FUNCTION:
initial.commitd7cae122008-07-26 21:49:381108 if (!strstr(sample.location().function_name(), required_.c_str()))
[email protected]95c796b2008-08-20 03:25:471109 return false;
1110 break;
1111
1112 default:
initial.commitd7cae122008-07-26 21:49:381113 break;
1114 }
1115 }
1116 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
1117 return tiebreaker_->Acceptable(sample);
1118 return true;
1119}
1120
[email protected]2bce0352009-07-06 20:11:001121void Comparator::SetTiebreaker(Selector selector, const std::string& required) {
initial.commitd7cae122008-07-26 21:49:381122 if (selector == selector_ || NIL == selector)
1123 return;
1124 combined_selectors_ |= selector;
1125 if (NIL == selector_) {
1126 selector_ = selector;
1127 if (required.size())
1128 required_ = required;
1129 return;
1130 }
1131 if (tiebreaker_) {
1132 if (use_tiebreaker_for_sort_only_) {
1133 Comparator* temp = new Comparator;
1134 temp->tiebreaker_ = tiebreaker_;
1135 tiebreaker_ = temp;
1136 }
1137 } else {
1138 tiebreaker_ = new Comparator;
1139 DCHECK(!use_tiebreaker_for_sort_only_);
1140 }
1141 tiebreaker_->SetTiebreaker(selector, required);
1142}
1143
1144bool Comparator::IsGroupedBy(Selector selector) const {
1145 return 0 != (selector & combined_selectors_);
1146}
1147
1148void Comparator::SetSubgroupTiebreaker(Selector selector) {
1149 if (selector == selector_ || NIL == selector)
1150 return;
1151 if (!tiebreaker_) {
1152 use_tiebreaker_for_sort_only_ = true;
1153 tiebreaker_ = new Comparator;
1154 tiebreaker_->SetTiebreaker(selector, "");
1155 } else {
1156 tiebreaker_->SetSubgroupTiebreaker(selector);
1157 }
1158}
1159
[email protected]2bce0352009-07-06 20:11:001160void Comparator::ParseKeyphrase(const std::string& key_phrase) {
initial.commitd7cae122008-07-26 21:49:381161 std::string required;
[email protected]75b79202009-12-30 07:31:451162 // Watch for: "sort_key=value" as we parse.
initial.commitd7cae122008-07-26 21:49:381163 size_t equal_offset = key_phrase.find('=', 0);
[email protected]75b79202009-12-30 07:31:451164 if (key_phrase.npos != equal_offset) {
1165 // There is a value that must be matched for the data to display.
initial.commitd7cae122008-07-26 21:49:381166 required = key_phrase.substr(equal_offset + 1, key_phrase.npos);
[email protected]75b79202009-12-30 07:31:451167 }
initial.commitd7cae122008-07-26 21:49:381168 std::string keyword(key_phrase.substr(0, equal_offset));
1169 keyword = StringToLowerASCII(keyword);
[email protected]b2a9bbd2011-10-31 22:36:211170 Selector selector = FindSelector(keyword);
1171 if (selector == UNKNOWN_KEYWORD)
1172 return;
1173 if (selector == RESET_ALL_DATA) {
[email protected]75b79202009-12-30 07:31:451174 ThreadData::ResetAllThreadData();
[email protected]b2a9bbd2011-10-31 22:36:211175 return;
1176 }
1177 SetTiebreaker(selector, required);
initial.commitd7cae122008-07-26 21:49:381178}
1179
[email protected]2bce0352009-07-06 20:11:001180bool Comparator::ParseQuery(const std::string& query) {
[email protected]75b79202009-12-30 07:31:451181 // Parse each keyphrase between consecutive slashes.
initial.commitd7cae122008-07-26 21:49:381182 for (size_t i = 0; i < query.size();) {
1183 size_t slash_offset = query.find('/', i);
1184 ParseKeyphrase(query.substr(i, slash_offset - i));
1185 if (query.npos == slash_offset)
1186 break;
1187 i = slash_offset + 1;
1188 }
1189
1190 // Select subgroup ordering (if we want to display the subgroup)
1191 SetSubgroupTiebreaker(COUNT);
[email protected]84b57952011-10-15 23:52:451192 SetSubgroupTiebreaker(AVERAGE_RUN_DURATION);
1193 SetSubgroupTiebreaker(TOTAL_RUN_DURATION);
[email protected]63f5b0e2011-11-04 00:23:271194 SetSubgroupTiebreaker(MAX_RUN_DURATION);
1195 SetSubgroupTiebreaker(AVERAGE_QUEUE_DURATION);
1196 SetSubgroupTiebreaker(TOTAL_QUEUE_DURATION);
1197 SetSubgroupTiebreaker(MAX_QUEUE_DURATION);
initial.commitd7cae122008-07-26 21:49:381198 SetSubgroupTiebreaker(BIRTH_THREAD);
1199 SetSubgroupTiebreaker(DEATH_THREAD);
1200 SetSubgroupTiebreaker(BIRTH_FUNCTION);
1201 SetSubgroupTiebreaker(BIRTH_FILE);
1202 SetSubgroupTiebreaker(BIRTH_LINE);
1203
1204 return true;
1205}
1206
1207bool Comparator::WriteSortGrouping(const Snapshot& sample,
1208 std::string* output) const {
1209 bool wrote_data = false;
1210 switch (selector_) {
initial.commitd7cae122008-07-26 21:49:381211 case BIRTH_THREAD:
[email protected]f1633932010-08-17 23:05:281212 base::StringAppendF(output, "All new on %s ",
[email protected]84b57952011-10-15 23:52:451213 sample.birth_thread()->thread_name().c_str());
initial.commitd7cae122008-07-26 21:49:381214 wrote_data = true;
1215 break;
1216
1217 case DEATH_THREAD:
[email protected]f1633932010-08-17 23:05:281218 if (sample.death_thread()) {
1219 base::StringAppendF(output, "All deleted on %s ",
1220 sample.DeathThreadName().c_str());
1221 } else {
initial.commitd7cae122008-07-26 21:49:381222 output->append("All still alive ");
[email protected]f1633932010-08-17 23:05:281223 }
initial.commitd7cae122008-07-26 21:49:381224 wrote_data = true;
1225 break;
1226
1227 case BIRTH_FILE:
[email protected]f1633932010-08-17 23:05:281228 base::StringAppendF(output, "All born in %s ",
1229 sample.location().file_name());
initial.commitd7cae122008-07-26 21:49:381230 break;
1231
1232 case BIRTH_FUNCTION:
1233 output->append("All born in ");
1234 sample.location().WriteFunctionName(output);
1235 output->push_back(' ');
1236 break;
[email protected]95c796b2008-08-20 03:25:471237
1238 default:
1239 break;
initial.commitd7cae122008-07-26 21:49:381240 }
1241 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
1242 wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
1243 }
1244 return wrote_data;
1245}
1246
[email protected]84baeca2011-10-24 18:55:161247void Comparator::WriteSnapshotHTML(const Snapshot& sample,
1248 std::string* output) const {
1249 sample.death_data().WriteHTML(output);
initial.commitd7cae122008-07-26 21:49:381250 if (!(combined_selectors_ & BIRTH_THREAD) ||
1251 !(combined_selectors_ & DEATH_THREAD))
[email protected]f1633932010-08-17 23:05:281252 base::StringAppendF(output, "%s->%s ",
1253 (combined_selectors_ & BIRTH_THREAD) ? "*" :
[email protected]84b57952011-10-15 23:52:451254 sample.birth().birth_thread()->thread_name().c_str(),
[email protected]f1633932010-08-17 23:05:281255 (combined_selectors_ & DEATH_THREAD) ? "*" :
1256 sample.DeathThreadName().c_str());
initial.commitd7cae122008-07-26 21:49:381257 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
1258 !(combined_selectors_ & BIRTH_FUNCTION),
1259 output);
1260}
1261
1262} // namespace tracked_objects