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
3 // found in the LICENSE file.
5 #include "base/tracked_objects.h"
9 #include "base/format_macros.h"
10 #include "base/message_loop.h"
11 #include "base/string_util.h"
12 #include "base/stringprintf.h"
13 #include "base/threading/thread_restrictions.h"
15 using base::TimeDelta
;
17 namespace tracked_objects
{
20 #if defined(TRACK_ALL_TASK_OBJECTS)
21 static const bool kTrackAllTaskObjects
= true;
23 static const bool kTrackAllTaskObjects
= false;
26 // Can we count on thread termination to call for thread cleanup? If not, then
27 // we can't risk putting references to ThreadData in TLS, as it will leak on
28 // worker thread termination.
29 static const bool kWorkerThreadCleanupSupported
= true;
31 // A TLS slot which points to the ThreadData instance for the current thread. We
32 // do a fake initialization here (zeroing out data), and then the real in-place
33 // construction happens when we call tls_index_.Initialize().
35 base::ThreadLocalStorage::Slot
ThreadData::tls_index_(base::LINKER_INITIALIZED
);
37 // A global state variable to prevent repeated initialization during tests.
39 AutoTracking::State
AutoTracking::state_
= AutoTracking::kNeverBeenRun
;
41 // A locked protected counter to assign sequence number to threads.
43 int ThreadData::thread_number_counter_
= 0;
45 //------------------------------------------------------------------------------
46 // Death data tallies durations when a death takes place.
48 void DeathData::RecordDeath(const TimeDelta
& queue_duration
,
49 const TimeDelta
& run_duration
) {
51 queue_duration_
+= queue_duration
;
52 run_duration_
+= run_duration
;
55 int DeathData::AverageMsRunDuration() const {
56 if (run_duration_
== base::TimeDelta())
58 return static_cast<int>(run_duration_
.InMilliseconds() / count_
);
61 int DeathData::AverageMsQueueDuration() const {
62 if (queue_duration_
== base::TimeDelta())
64 return static_cast<int>(queue_duration_
.InMilliseconds() / count_
);
67 void DeathData::AddDeathData(const DeathData
& other
) {
68 count_
+= other
.count_
;
69 queue_duration_
+= other
.queue_duration_
;
70 run_duration_
+= other
.run_duration_
;
73 void DeathData::WriteHTML(std::string
* output
) const {
76 base::StringAppendF(output
, "%s:%d, ",
77 (count_
== 1) ? "Life" : "Lives", count_
);
78 base::StringAppendF(output
, "Run:%"PRId64
"ms(%dms/life) ",
79 run_duration_
.InMilliseconds(),
80 AverageMsRunDuration());
81 base::StringAppendF(output
, "Queue:%"PRId64
"ms(%dms/life) ",
82 queue_duration_
.InMilliseconds(),
83 AverageMsQueueDuration());
86 base::DictionaryValue
* DeathData::ToValue() const {
87 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
88 dictionary
->Set("count", base::Value::CreateIntegerValue(count_
));
89 dictionary
->Set("run_ms",
90 base::Value::CreateIntegerValue(run_duration_
.InMilliseconds()));
91 dictionary
->Set("queue_ms",
92 base::Value::CreateIntegerValue(queue_duration_
.InMilliseconds()));
96 void DeathData::Clear() {
98 queue_duration_
= TimeDelta();
99 run_duration_
= TimeDelta();
102 //------------------------------------------------------------------------------
103 BirthOnThread::BirthOnThread(const Location
& location
,
104 const ThreadData
& current
)
105 : location_(location
),
106 birth_thread_(¤t
) {}
108 //------------------------------------------------------------------------------
109 Births::Births(const Location
& location
, const ThreadData
& current
)
110 : BirthOnThread(location
, current
),
113 //------------------------------------------------------------------------------
114 // ThreadData maintains the central data for all births and deaths.
117 ThreadData
* ThreadData::all_thread_data_list_head_
= NULL
;
120 ThreadData::ThreadDataPool
* ThreadData::unregistered_thread_data_pool_
= NULL
;
123 base::Lock
ThreadData::list_lock_
;
126 ThreadData::Status
ThreadData::status_
= ThreadData::UNINITIALIZED
;
128 ThreadData::ThreadData(const std::string
& suggested_name
)
130 is_a_worker_thread_(false) {
131 DCHECK_GE(suggested_name
.size(), 0u);
132 thread_name_
= suggested_name
;
136 ThreadData::ThreadData() : next_(NULL
), is_a_worker_thread_(true) {
139 base::AutoLock
lock(list_lock_
);
140 thread_number
= ++thread_number_counter_
;
142 base::StringAppendF(&thread_name_
, "WorkerThread-%d", thread_number
);
146 ThreadData::~ThreadData() {}
148 void ThreadData::PushToHeadOfList() {
150 base::AutoLock
lock(list_lock_
);
151 next_
= all_thread_data_list_head_
;
152 all_thread_data_list_head_
= this;
156 void ThreadData::InitializeThreadContext(const std::string
& suggested_name
) {
157 if (!tls_index_
.initialized())
158 return; // For unittests only.
159 DCHECK_EQ(tls_index_
.Get(), reinterpret_cast<void*>(NULL
));
160 ThreadData
* current_thread_data
= new ThreadData(suggested_name
);
161 tls_index_
.Set(current_thread_data
);
165 ThreadData
* ThreadData::Get() {
166 if (!tls_index_
.initialized())
167 return NULL
; // For unittests only.
168 ThreadData
* registered
= reinterpret_cast<ThreadData
*>(tls_index_
.Get());
172 // We must be a worker thread, since we didn't pre-register.
173 ThreadData
* worker_thread_data
= NULL
;
175 base::AutoLock
lock(list_lock_
);
176 if (!unregistered_thread_data_pool_
->empty()) {
178 const_cast<ThreadData
*>(unregistered_thread_data_pool_
->top());
179 unregistered_thread_data_pool_
->pop();
183 // If we can't find a previously used instance, then we have to create one.
184 if (!worker_thread_data
)
185 worker_thread_data
= new ThreadData();
187 tls_index_
.Set(worker_thread_data
);
188 return worker_thread_data
;
192 void ThreadData::OnThreadTermination(void* thread_data
) {
193 if (!kTrackAllTaskObjects
)
194 return; // Not compiled in.
195 DCHECK(tls_index_
.initialized());
198 reinterpret_cast<ThreadData
*>(thread_data
)->OnThreadTerminationCleanup();
199 DCHECK_EQ(tls_index_
.Get(), reinterpret_cast<ThreadData
*>(NULL
));
202 void ThreadData::OnThreadTerminationCleanup() const {
203 tls_index_
.Set(NULL
);
204 if (!is_a_worker_thread_
)
206 base::AutoLock
lock(list_lock_
);
207 unregistered_thread_data_pool_
->push(this);
211 void ThreadData::WriteHTML(const std::string
& query
, std::string
* output
) {
212 if (!ThreadData::IsActive())
213 return; // Not yet initialized.
215 DataCollector collected_data
; // Gather data.
216 collected_data
.AddListOfLivingObjects(); // Add births that are still alive.
218 // Data Gathering is complete. Now to sort/process/render.
219 DataCollector::Collection
* collection
= collected_data
.collection();
221 // Create filtering and sort comparison object.
222 Comparator comparator
;
223 comparator
.ParseQuery(query
);
225 // Filter out acceptable (matching) instances.
226 DataCollector::Collection match_array
;
227 for (DataCollector::Collection::iterator it
= collection
->begin();
228 it
!= collection
->end(); ++it
) {
229 if (comparator
.Acceptable(*it
))
230 match_array
.push_back(*it
);
233 comparator
.Sort(&match_array
);
235 WriteHTMLTotalAndSubtotals(match_array
, comparator
, output
);
237 comparator
.Clear(); // Delete tiebreaker_ instances.
239 output
->append("</pre>");
241 const char* help_string
= "The following are the keywords that can be used to"
242 " sort and aggregate the data, or to select data.<br><ul>"
243 "<li><b>Count</b> Number of instances seen."
244 "<li><b>Duration</b> Average duration in ms of Run() time."
245 "<li><b>TotalDuration</b> Summed durations in ms of Run() times."
246 "<li><b>AverageQueueDuration</b> Average duration in ms of queueing time."
247 "<li><b>TotalQueueDuration</b> Summed durations in ms of Run() times."
248 "<li><b>Birth</b> Thread on which the task was constructed."
249 "<li><b>Death</b> Thread on which the task was run and deleted."
250 "<li><b>File</b> File in which the task was contructed."
251 "<li><b>Function</b> Function in which the task was constructed."
252 "<li><b>Line</b> Line number of the file in which the task was constructed."
255 "<li><b>about:tracking/file</b> would sort the above data by file, and"
256 " aggregate data on a per-file basis."
257 "<li><b>about:tracking/file=Dns</b> would only list data for tasks"
258 " constructed in a file containing the text |Dns|."
259 "<li><b>about:tracking/death/duration</b> would sort the data by death"
260 " thread(i.e., where tasks ran) and then by the average runtime for the"
261 " tasks. Form an aggregation group, one per thread, showing the results on"
263 "<li><b>about:tracking/birth/death</b> would sort the above list by birth"
264 " thread, and then by death thread, and would aggregate data for each pair"
265 " of lifetime events."
267 " The data can be reset to zero (discarding all births, deaths, etc.) using"
268 " <b>about:tracking/reset</b>. The existing stats will be displayed, but"
269 " the internal stats will be set to zero, and start accumulating afresh."
270 " This option is very helpful if you only wish to consider tasks created"
271 " after some point in time.<br><br>"
272 "If you wish to monitor Renderer events, be sure to run in --single-process"
274 output
->append(help_string
);
278 void ThreadData::WriteHTMLTotalAndSubtotals(
279 const DataCollector::Collection
& match_array
,
280 const Comparator
& comparator
,
281 std::string
* output
) {
282 if (match_array
.empty()) {
283 output
->append("There were no tracked matches.");
286 // Aggregate during printing
288 for (size_t i
= 0; i
< match_array
.size(); ++i
) {
289 totals
.AddDeathSnapshot(match_array
[i
]);
291 output
->append("Aggregate Stats: ");
292 totals
.WriteHTML(output
);
293 output
->append("<hr><hr>");
295 Aggregation subtotals
;
296 for (size_t i
= 0; i
< match_array
.size(); ++i
) {
297 if (0 == i
|| !comparator
.Equivalent(match_array
[i
- 1],
299 // Print group's defining characteristics.
300 comparator
.WriteSortGrouping(match_array
[i
], output
);
301 output
->append("<br><br>");
303 comparator
.WriteSnapshotHTML(match_array
[i
], output
);
304 output
->append("<br>");
305 subtotals
.AddDeathSnapshot(match_array
[i
]);
306 if (i
+ 1 >= match_array
.size() ||
307 !comparator
.Equivalent(match_array
[i
],
308 match_array
[i
+ 1])) {
309 // Print aggregate stats for the group.
310 output
->append("<br>");
311 subtotals
.WriteHTML(output
);
312 output
->append("<br><hr><br>");
319 base::Value
* ThreadData::ToValue(int process_type
) {
320 DataCollector collected_data
; // Gather data.
321 collected_data
.AddListOfLivingObjects(); // Add births that are still alive.
322 base::ListValue
* list
= collected_data
.ToValue();
323 base::DictionaryValue
* dictionary
= new base::DictionaryValue();
324 dictionary
->Set("list", list
);
325 dictionary
->SetInteger("process", process_type
);
329 Births
* ThreadData::TallyABirth(const Location
& location
) {
330 BirthMap::iterator it
= birth_map_
.find(location
);
331 if (it
!= birth_map_
.end()) {
332 it
->second
->RecordBirth();
336 Births
* tracker
= new Births(location
, *this);
337 // Lock since the map may get relocated now, and other threads sometimes
338 // snapshot it (but they lock before copying it).
339 base::AutoLock
lock(lock_
);
340 birth_map_
[location
] = tracker
;
344 void ThreadData::TallyADeath(const Births
& birth
,
345 const TimeDelta
& queue_duration
,
346 const TimeDelta
& run_duration
) {
347 DeathMap::iterator it
= death_map_
.find(&birth
);
348 DeathData
* death_data
;
349 if (it
!= death_map_
.end()) {
350 death_data
= &it
->second
;
352 base::AutoLock
lock(lock_
); // Lock since the map may get relocated now.
353 death_data
= &death_map_
[&birth
];
354 } // Release lock ASAP.
355 death_data
->RecordDeath(queue_duration
, run_duration
);
359 Births
* ThreadData::TallyABirthIfActive(const Location
& location
) {
360 if (!kTrackAllTaskObjects
)
361 return NULL
; // Not compiled in.
365 ThreadData
* current_thread_data
= Get();
366 if (!current_thread_data
)
368 return current_thread_data
->TallyABirth(location
);
372 void ThreadData::TallyADeathIfActive(const Births
* birth
,
373 const base::TimeTicks
& time_posted
,
374 const base::TimeTicks
& delayed_start_time
,
375 const base::TimeTicks
& start_of_run
,
376 const base::TimeTicks
& end_of_run
) {
377 if (!kTrackAllTaskObjects
)
378 return; // Not compiled in.
380 if (!IsActive() || !birth
)
383 ThreadData
* current_thread_data
= Get();
384 if (!current_thread_data
)
387 // To avoid conflating our stats with the delay duration in a PostDelayedTask,
388 // we identify such tasks, and replace their post_time with the time they
389 // were sechudled (requested?) to emerge from the delayed task queue. This
390 // means that queueing delay for such tasks will show how long they went
391 // unserviced, after they *could* be serviced. This is the same stat as we
392 // have for non-delayed tasks, and we consistently call it queueing delay.
393 base::TimeTicks effective_post_time
=
394 (delayed_start_time
.is_null()) ? time_posted
: delayed_start_time
;
395 base::TimeDelta queue_duration
= start_of_run
- effective_post_time
;
396 base::TimeDelta run_duration
= end_of_run
- start_of_run
;
397 current_thread_data
->TallyADeath(*birth
, queue_duration
, run_duration
);
401 ThreadData
* ThreadData::first() {
402 base::AutoLock
lock(list_lock_
);
403 return all_thread_data_list_head_
;
406 // This may be called from another thread.
407 void ThreadData::SnapshotBirthMap(BirthMap
*output
) const {
408 base::AutoLock
lock(lock_
);
409 for (BirthMap::const_iterator it
= birth_map_
.begin();
410 it
!= birth_map_
.end(); ++it
)
411 (*output
)[it
->first
] = it
->second
;
414 // This may be called from another thread.
415 void ThreadData::SnapshotDeathMap(DeathMap
*output
) const {
416 base::AutoLock
lock(lock_
);
417 for (DeathMap::const_iterator it
= death_map_
.begin();
418 it
!= death_map_
.end(); ++it
)
419 (*output
)[it
->first
] = it
->second
;
423 void ThreadData::ResetAllThreadData() {
424 ThreadData
* my_list
= first();
426 for (ThreadData
* thread_data
= my_list
;
428 thread_data
= thread_data
->next())
429 thread_data
->Reset();
432 void ThreadData::Reset() {
433 base::AutoLock
lock(lock_
);
434 for (DeathMap::iterator it
= death_map_
.begin();
435 it
!= death_map_
.end(); ++it
)
437 for (BirthMap::iterator it
= birth_map_
.begin();
438 it
!= birth_map_
.end(); ++it
)
443 bool ThreadData::StartTracking(bool status
) {
444 if (!kTrackAllTaskObjects
)
445 return false; // Not compiled in.
447 // Do a bit of class initialization.
448 if (!unregistered_thread_data_pool_
) {
449 ThreadDataPool
* initial_pool
= new ThreadDataPool
;
451 base::AutoLock
lock(list_lock_
);
452 if (!unregistered_thread_data_pool_
) {
453 unregistered_thread_data_pool_
= initial_pool
;
457 delete initial_pool
; // In case it was not used.
460 // Perform the "real" initialization now, and leave it intact through
461 // process termination.
462 if (!tls_index_
.initialized())
463 tls_index_
.Initialize(&ThreadData::OnThreadTermination
);
464 DCHECK(tls_index_
.initialized());
467 base::AutoLock
lock(list_lock_
);
468 DCHECK(status_
== ACTIVE
|| status_
== SHUTDOWN
);
472 base::AutoLock
lock(list_lock_
);
473 DCHECK_EQ(UNINITIALIZED
, status_
);
479 bool ThreadData::IsActive() {
480 return status_
== ACTIVE
;
484 base::TimeTicks
ThreadData::Now() {
485 if (kTrackAllTaskObjects
&& status_
== ACTIVE
)
486 return base::TimeTicks::Now();
487 return base::TimeTicks(); // Super fast when disabled, or not compiled in.
491 void ThreadData::ShutdownSingleThreadedCleanup() {
492 // This is only called from test code, where we need to cleanup so that
493 // additional tests can be run.
494 // We must be single threaded... but be careful anyway.
495 if (!StartTracking(false))
497 ThreadData
* thread_data_list
;
498 ThreadDataPool
* final_pool
;
500 base::AutoLock
lock(list_lock_
);
501 thread_data_list
= all_thread_data_list_head_
;
502 all_thread_data_list_head_
= NULL
;
503 final_pool
= unregistered_thread_data_pool_
;
504 unregistered_thread_data_pool_
= NULL
;
508 // The thread_data_list contains *all* the instances, and we'll use it to
509 // delete them. This pool has pointers to some instances, and we just
510 // have to drop those pointers (and not do the deletes here).
511 while (!final_pool
->empty())
516 // Do actual recursive delete in all ThreadData instances.
517 while (thread_data_list
) {
518 ThreadData
* next_thread_data
= thread_data_list
;
519 thread_data_list
= thread_data_list
->next();
521 for (BirthMap::iterator it
= next_thread_data
->birth_map_
.begin();
522 next_thread_data
->birth_map_
.end() != it
; ++it
)
523 delete it
->second
; // Delete the Birth Records.
524 next_thread_data
->birth_map_
.clear();
525 next_thread_data
->death_map_
.clear();
526 delete next_thread_data
; // Includes all Death Records.
528 // Put most global static back in pristine shape.
529 thread_number_counter_
= 0;
530 tls_index_
.Set(NULL
);
531 status_
= UNINITIALIZED
;
534 //------------------------------------------------------------------------------
535 // Individual 3-tuple of birth (place and thread) along with death thread, and
536 // the accumulated stats for instances (DeathData).
538 Snapshot::Snapshot(const BirthOnThread
& birth_on_thread
,
539 const ThreadData
& death_thread
,
540 const DeathData
& death_data
)
541 : birth_(&birth_on_thread
),
542 death_thread_(&death_thread
),
543 death_data_(death_data
) {
546 Snapshot::Snapshot(const BirthOnThread
& birth_on_thread
, int count
)
547 : birth_(&birth_on_thread
),
549 death_data_(DeathData(count
)) {
552 const std::string
Snapshot::DeathThreadName() const {
554 return death_thread_
->thread_name();
555 return "Still_Alive";
558 void Snapshot::WriteHTML(std::string
* output
) const {
559 death_data_
.WriteHTML(output
);
560 base::StringAppendF(output
, "%s->%s ",
561 birth_
->birth_thread()->thread_name().c_str(),
562 DeathThreadName().c_str());
563 birth_
->location().Write(true, true, output
);
566 base::DictionaryValue
* Snapshot::ToValue() const {
567 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
568 dictionary
->Set("death_data", death_data_
.ToValue());
569 dictionary
->Set("birth_thread",
570 base::Value::CreateStringValue(birth_
->birth_thread()->thread_name()));
571 dictionary
->Set("death_thread",
572 base::Value::CreateStringValue(DeathThreadName()));
573 dictionary
->Set("location", birth_
->location().ToValue());
577 void Snapshot::Add(const Snapshot
& other
) {
578 death_data_
.AddDeathData(other
.death_data_
);
581 //------------------------------------------------------------------------------
584 DataCollector::DataCollector() {
585 if (!ThreadData::IsActive())
588 // Get an unchanging copy of a ThreadData list.
589 ThreadData
* my_list
= ThreadData::first();
591 // Gather data serially.
592 // This hackish approach *can* get some slighly corrupt tallies, as we are
593 // grabbing values without the protection of a lock, but it has the advantage
594 // of working even with threads that don't have message loops. If a user
595 // sees any strangeness, they can always just run their stats gathering a
597 for (ThreadData
* thread_data
= my_list
;
599 thread_data
= thread_data
->next()) {
600 Append(*thread_data
);
604 DataCollector::~DataCollector() {
607 void DataCollector::Append(const ThreadData
& thread_data
) {
609 ThreadData::BirthMap birth_map
;
610 thread_data
.SnapshotBirthMap(&birth_map
);
611 ThreadData::DeathMap death_map
;
612 thread_data
.SnapshotDeathMap(&death_map
);
614 for (ThreadData::DeathMap::const_iterator it
= death_map
.begin();
615 it
!= death_map
.end(); ++it
) {
616 collection_
.push_back(Snapshot(*it
->first
, thread_data
, it
->second
));
617 global_birth_count_
[it
->first
] -= it
->first
->birth_count();
620 for (ThreadData::BirthMap::const_iterator it
= birth_map
.begin();
621 it
!= birth_map
.end(); ++it
) {
622 global_birth_count_
[it
->second
] += it
->second
->birth_count();
626 DataCollector::Collection
* DataCollector::collection() {
630 void DataCollector::AddListOfLivingObjects() {
631 for (BirthCount::iterator it
= global_birth_count_
.begin();
632 it
!= global_birth_count_
.end(); ++it
) {
634 collection_
.push_back(Snapshot(*it
->first
, it
->second
));
638 base::ListValue
* DataCollector::ToValue() const {
639 base::ListValue
* list
= new base::ListValue
;
640 for (size_t i
= 0; i
< collection_
.size(); ++i
) {
641 list
->Append(collection_
[i
].ToValue());
646 //------------------------------------------------------------------------------
649 Aggregation::Aggregation()
653 Aggregation::~Aggregation() {
656 void Aggregation::AddDeathSnapshot(const Snapshot
& snapshot
) {
657 AddBirth(snapshot
.birth());
658 death_threads_
[snapshot
.death_thread()]++;
659 AddDeathData(snapshot
.death_data());
662 void Aggregation::AddBirths(const Births
& births
) {
664 birth_count_
+= births
.birth_count();
666 void Aggregation::AddBirth(const BirthOnThread
& birth
) {
667 AddBirthPlace(birth
.location());
668 birth_threads_
[birth
.birth_thread()]++;
671 void Aggregation::AddBirthPlace(const Location
& location
) {
672 locations_
[location
]++;
673 birth_files_
[location
.file_name()]++;
676 void Aggregation::WriteHTML(std::string
* output
) const {
677 if (locations_
.size() == 1) {
678 locations_
.begin()->first
.Write(true, true, output
);
680 base::StringAppendF(output
, "%" PRIuS
" Locations. ", locations_
.size());
681 if (birth_files_
.size() > 1) {
682 base::StringAppendF(output
, "%" PRIuS
" Files. ", birth_files_
.size());
684 base::StringAppendF(output
, "All born in %s. ",
685 birth_files_
.begin()->first
.c_str());
689 if (birth_threads_
.size() > 1) {
690 base::StringAppendF(output
, "%" PRIuS
" BirthingThreads. ",
691 birth_threads_
.size());
693 base::StringAppendF(output
, "All born on %s. ",
694 birth_threads_
.begin()->first
->thread_name().c_str());
697 if (death_threads_
.size() > 1) {
698 base::StringAppendF(output
, "%" PRIuS
" DeathThreads. ",
699 death_threads_
.size());
701 if (death_threads_
.begin()->first
) {
702 base::StringAppendF(output
, "All deleted on %s. ",
703 death_threads_
.begin()->first
->thread_name().c_str());
705 output
->append("All these objects are still alive.");
709 if (birth_count_
> 1)
710 base::StringAppendF(output
, "Births=%d ", birth_count_
);
712 DeathData::WriteHTML(output
);
715 void Aggregation::Clear() {
717 birth_files_
.clear();
719 birth_threads_
.clear();
721 death_threads_
.clear();
724 //------------------------------------------------------------------------------
725 // Comparison object for sorting.
727 Comparator::Comparator()
730 combined_selectors_(0),
731 use_tiebreaker_for_sort_only_(false) {}
733 void Comparator::Clear() {
735 tiebreaker_
->Clear();
739 use_tiebreaker_for_sort_only_
= false;
743 bool Comparator::operator()(const Snapshot
& left
,
744 const Snapshot
& right
) const {
747 if (left
.birth_thread() != right
.birth_thread() &&
748 left
.birth_thread()->thread_name() !=
749 right
.birth_thread()->thread_name())
750 return left
.birth_thread()->thread_name() <
751 right
.birth_thread()->thread_name();
755 if (left
.death_thread() != right
.death_thread() &&
756 left
.DeathThreadName() !=
757 right
.DeathThreadName()) {
758 if (!left
.death_thread())
760 if (!right
.death_thread())
762 return left
.DeathThreadName() <
763 right
.DeathThreadName();
768 if (left
.location().file_name() != right
.location().file_name()) {
769 int comp
= strcmp(left
.location().file_name(),
770 right
.location().file_name());
777 if (left
.location().function_name() != right
.location().function_name()) {
778 int comp
= strcmp(left
.location().function_name(),
779 right
.location().function_name());
786 if (left
.location().line_number() != right
.location().line_number())
787 return left
.location().line_number() <
788 right
.location().line_number();
792 if (left
.count() != right
.count())
793 return left
.count() > right
.count(); // Sort large at front of vector.
796 case AVERAGE_RUN_DURATION
:
797 if (!left
.count() || !right
.count())
799 if (left
.AverageMsRunDuration() != right
.AverageMsRunDuration())
800 return left
.AverageMsRunDuration() > right
.AverageMsRunDuration();
803 case TOTAL_RUN_DURATION
:
804 if (!left
.count() || !right
.count())
806 if (left
.run_duration() != right
.run_duration())
807 return left
.run_duration() > right
.run_duration();
810 case AVERAGE_QUEUE_DURATION
:
811 if (!left
.count() || !right
.count())
813 if (left
.AverageMsQueueDuration() != right
.AverageMsQueueDuration())
814 return left
.AverageMsQueueDuration() > right
.AverageMsQueueDuration();
817 case TOTAL_QUEUE_DURATION
:
818 if (!left
.count() || !right
.count())
820 if (left
.queue_duration() != right
.queue_duration())
821 return left
.queue_duration() > right
.queue_duration();
828 return tiebreaker_
->operator()(left
, right
);
832 void Comparator::Sort(DataCollector::Collection
* collection
) const {
833 std::sort(collection
->begin(), collection
->end(), *this);
836 bool Comparator::Equivalent(const Snapshot
& left
,
837 const Snapshot
& right
) const {
840 if (left
.birth_thread() != right
.birth_thread() &&
841 left
.birth_thread()->thread_name() !=
842 right
.birth_thread()->thread_name())
847 if (left
.death_thread() != right
.death_thread() &&
848 left
.DeathThreadName() != right
.DeathThreadName())
853 if (left
.location().file_name() != right
.location().file_name()) {
854 int comp
= strcmp(left
.location().file_name(),
855 right
.location().file_name());
862 if (left
.location().function_name() != right
.location().function_name()) {
863 int comp
= strcmp(left
.location().function_name(),
864 right
.location().function_name());
871 case AVERAGE_RUN_DURATION
:
872 case TOTAL_RUN_DURATION
:
873 case AVERAGE_QUEUE_DURATION
:
874 case TOTAL_QUEUE_DURATION
:
875 // We don't produce separate aggretation when only counts or times differ.
881 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
)
882 return tiebreaker_
->Equivalent(left
, right
);
886 bool Comparator::Acceptable(const Snapshot
& sample
) const {
887 if (required_
.size()) {
890 if (sample
.birth_thread()->thread_name().find(required_
) ==
896 if (sample
.DeathThreadName().find(required_
) == std::string::npos
)
901 if (!strstr(sample
.location().file_name(), required_
.c_str()))
906 if (!strstr(sample
.location().function_name(), required_
.c_str()))
914 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
)
915 return tiebreaker_
->Acceptable(sample
);
919 void Comparator::SetTiebreaker(Selector selector
, const std::string
& required
) {
920 if (selector
== selector_
|| NIL
== selector
)
922 combined_selectors_
|= selector
;
923 if (NIL
== selector_
) {
924 selector_
= selector
;
926 required_
= required
;
930 if (use_tiebreaker_for_sort_only_
) {
931 Comparator
* temp
= new Comparator
;
932 temp
->tiebreaker_
= tiebreaker_
;
936 tiebreaker_
= new Comparator
;
937 DCHECK(!use_tiebreaker_for_sort_only_
);
939 tiebreaker_
->SetTiebreaker(selector
, required
);
942 bool Comparator::IsGroupedBy(Selector selector
) const {
943 return 0 != (selector
& combined_selectors_
);
946 void Comparator::SetSubgroupTiebreaker(Selector selector
) {
947 if (selector
== selector_
|| NIL
== selector
)
950 use_tiebreaker_for_sort_only_
= true;
951 tiebreaker_
= new Comparator
;
952 tiebreaker_
->SetTiebreaker(selector
, "");
954 tiebreaker_
->SetSubgroupTiebreaker(selector
);
958 void Comparator::ParseKeyphrase(const std::string
& key_phrase
) {
959 typedef std::map
<const std::string
, Selector
> KeyMap
;
960 static KeyMap key_map
;
961 static bool initialized
= false;
964 // Sorting and aggretation keywords, which specify how to sort the data, or
965 // can specify a required match from the specified field in the record.
966 key_map
["count"] = COUNT
;
967 key_map
["totalduration"] = TOTAL_RUN_DURATION
;
968 key_map
["duration"] = AVERAGE_RUN_DURATION
;
969 key_map
["totalqueueduration"] = TOTAL_QUEUE_DURATION
;
970 key_map
["averagequeueduration"] = AVERAGE_QUEUE_DURATION
;
971 key_map
["birth"] = BIRTH_THREAD
;
972 key_map
["death"] = DEATH_THREAD
;
973 key_map
["file"] = BIRTH_FILE
;
974 key_map
["function"] = BIRTH_FUNCTION
;
975 key_map
["line"] = BIRTH_LINE
;
977 // Immediate commands that do not involve setting sort order.
978 key_map
["reset"] = RESET_ALL_DATA
;
981 std::string required
;
982 // Watch for: "sort_key=value" as we parse.
983 size_t equal_offset
= key_phrase
.find('=', 0);
984 if (key_phrase
.npos
!= equal_offset
) {
985 // There is a value that must be matched for the data to display.
986 required
= key_phrase
.substr(equal_offset
+ 1, key_phrase
.npos
);
988 std::string
keyword(key_phrase
.substr(0, equal_offset
));
989 keyword
= StringToLowerASCII(keyword
);
990 KeyMap::iterator it
= key_map
.find(keyword
);
991 if (key_map
.end() == it
)
992 return; // Unknown keyword.
993 if (it
->second
== RESET_ALL_DATA
)
994 ThreadData::ResetAllThreadData();
996 SetTiebreaker(key_map
[keyword
], required
);
999 bool Comparator::ParseQuery(const std::string
& query
) {
1000 // Parse each keyphrase between consecutive slashes.
1001 for (size_t i
= 0; i
< query
.size();) {
1002 size_t slash_offset
= query
.find('/', i
);
1003 ParseKeyphrase(query
.substr(i
, slash_offset
- i
));
1004 if (query
.npos
== slash_offset
)
1006 i
= slash_offset
+ 1;
1009 // Select subgroup ordering (if we want to display the subgroup)
1010 SetSubgroupTiebreaker(COUNT
);
1011 SetSubgroupTiebreaker(AVERAGE_RUN_DURATION
);
1012 SetSubgroupTiebreaker(TOTAL_RUN_DURATION
);
1013 SetSubgroupTiebreaker(BIRTH_THREAD
);
1014 SetSubgroupTiebreaker(DEATH_THREAD
);
1015 SetSubgroupTiebreaker(BIRTH_FUNCTION
);
1016 SetSubgroupTiebreaker(BIRTH_FILE
);
1017 SetSubgroupTiebreaker(BIRTH_LINE
);
1022 bool Comparator::WriteSortGrouping(const Snapshot
& sample
,
1023 std::string
* output
) const {
1024 bool wrote_data
= false;
1025 switch (selector_
) {
1027 base::StringAppendF(output
, "All new on %s ",
1028 sample
.birth_thread()->thread_name().c_str());
1033 if (sample
.death_thread()) {
1034 base::StringAppendF(output
, "All deleted on %s ",
1035 sample
.DeathThreadName().c_str());
1037 output
->append("All still alive ");
1043 base::StringAppendF(output
, "All born in %s ",
1044 sample
.location().file_name());
1047 case BIRTH_FUNCTION
:
1048 output
->append("All born in ");
1049 sample
.location().WriteFunctionName(output
);
1050 output
->push_back(' ');
1056 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
) {
1057 wrote_data
|= tiebreaker_
->WriteSortGrouping(sample
, output
);
1062 void Comparator::WriteSnapshotHTML(const Snapshot
& sample
,
1063 std::string
* output
) const {
1064 sample
.death_data().WriteHTML(output
);
1065 if (!(combined_selectors_
& BIRTH_THREAD
) ||
1066 !(combined_selectors_
& DEATH_THREAD
))
1067 base::StringAppendF(output
, "%s->%s ",
1068 (combined_selectors_
& BIRTH_THREAD
) ? "*" :
1069 sample
.birth().birth_thread()->thread_name().c_str(),
1070 (combined_selectors_
& DEATH_THREAD
) ? "*" :
1071 sample
.DeathThreadName().c_str());
1072 sample
.birth().location().Write(!(combined_selectors_
& BIRTH_FILE
),
1073 !(combined_selectors_
& BIRTH_FUNCTION
),
1077 } // namespace tracked_objects