WebKit merge 94748:94770
[chromium-blink-merge.git] / base / tracked_objects.cc
blob21ac0fcaf17e37fcec14fbe05d89b1b743aa1b62
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"
7 #include <math.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 {
19 // A TLS slot to the TrackRegistry for the current thread.
20 // static
21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED);
23 // A global state variable to prevent repeated initialization during tests.
24 // static
25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun;
27 //------------------------------------------------------------------------------
28 // Death data tallies durations when a death takes place.
30 void DeathData::RecordDeath(const TimeDelta& duration) {
31 ++count_;
32 life_duration_ += duration;
33 int64 milliseconds = duration.InMilliseconds();
34 square_duration_ += milliseconds * milliseconds;
37 int DeathData::AverageMsDuration() const {
38 return static_cast<int>(life_duration_.InMilliseconds() / count_);
41 double DeathData::StandardDeviation() const {
42 double average = AverageMsDuration();
43 double variance = static_cast<float>(square_duration_)/count_
44 - average * average;
45 return sqrt(variance);
49 void DeathData::AddDeathData(const DeathData& other) {
50 count_ += other.count_;
51 life_duration_ += other.life_duration_;
52 square_duration_ += other.square_duration_;
55 void DeathData::Write(std::string* output) const {
56 if (!count_)
57 return;
58 if (1 == count_) {
59 base::StringAppendF(output, "(1)Life in %dms ", AverageMsDuration());
60 } else {
61 base::StringAppendF(output, "(%d)Lives %dms/life ",
62 count_, AverageMsDuration());
66 void DeathData::Clear() {
67 count_ = 0;
68 life_duration_ = TimeDelta();
69 square_duration_ = 0;
72 //------------------------------------------------------------------------------
73 BirthOnThread::BirthOnThread(const Location& location)
74 : location_(location),
75 birth_thread_(ThreadData::current()) { }
77 //------------------------------------------------------------------------------
78 Births::Births(const Location& location)
79 : BirthOnThread(location),
80 birth_count_(1) { }
82 //------------------------------------------------------------------------------
83 // ThreadData maintains the central data for all births and death.
85 // static
86 ThreadData* ThreadData::first_ = NULL;
87 // static
88 base::Lock ThreadData::list_lock_;
90 // static
91 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
93 ThreadData::ThreadData() : next_(NULL) {
94 // This shouldn't use the MessageLoop::current() LazyInstance since this might
95 // be used on a non-joinable thread.
96 // http://crbug.com/62728
97 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton;
98 message_loop_ = MessageLoop::current();
101 ThreadData::~ThreadData() {}
103 // static
104 ThreadData* ThreadData::current() {
105 if (!tls_index_.initialized())
106 return NULL;
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;
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;
123 } // Release lock.
124 if (too_late_to_create) {
125 delete registry;
126 registry = NULL;
127 } else {
128 tls_index_.Set(registry);
131 return registry;
134 // static
135 void ThreadData::WriteHTML(const std::string& query, std::string* output) {
136 if (!ThreadData::IsActive())
137 return; // Not yet initialized.
139 DCHECK(ThreadData::current());
140 DataCollector collected_data; // Gather data.
141 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
143 // Data Gathering is complete. Now to sort/process/render.
144 DataCollector::Collection* collection = collected_data.collection();
146 // Create filtering and sort comparison object.
147 Comparator comparator;
148 comparator.ParseQuery(query);
150 // Filter out acceptable (matching) instances.
151 DataCollector::Collection match_array;
152 for (DataCollector::Collection::iterator it = collection->begin();
153 it != collection->end(); ++it) {
154 if (comparator.Acceptable(*it))
155 match_array.push_back(*it);
158 comparator.Sort(&match_array);
160 WriteHTMLTotalAndSubtotals(match_array, comparator, output);
162 comparator.Clear(); // Delete tiebreaker_ instances.
164 output->append("</pre>");
166 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>"
168 "<li><b>count</b> Number of instances seen."
169 "<li><b>duration</b> Duration in ms from construction to descrution."
170 "<li><b>birth</b> Thread on which the task was constructed."
171 "<li><b>death</b> Thread on which the task was run and deleted."
172 "<li><b>file</b> File in which the task was contructed."
173 "<li><b>function</b> Function in which the task was constructed."
174 "<li><b>line</b> Line number of the file in which the task was constructed."
175 "</ul><br>"
176 "As examples:<ul>"
177 "<li><b>about:tasks/file</b> would sort the above data by file, and"
178 " aggregate data on a per-file basis."
179 "<li><b>about:tasks/file=Dns</b> would only list data for tasks constructed"
180 " in a file containing the text |Dns|."
181 "<li><b>about:tasks/birth/death</b> would sort the above list by birth"
182 " thread, and then by death thread, and would aggregate data for each pair"
183 " of lifetime events."
184 "</ul>"
185 " The data can be reset to zero (discarding all births, deaths, etc.) using"
186 " <b>about:tasks/reset</b>. The existing stats will be displayed, but the"
187 " internal stats will be set to zero, and start accumulating afresh. This"
188 " option is very helpful if you only wish to consider tasks created after"
189 " some point in time.<br><br>"
190 "If you wish to monitor Renderer events, be sure to run in --single-process"
191 " mode.";
192 output->append(help_string);
195 // static
196 void ThreadData::WriteHTMLTotalAndSubtotals(
197 const DataCollector::Collection& match_array,
198 const Comparator& comparator,
199 std::string* output) {
200 if (!match_array.size()) {
201 output->append("There were no tracked matches.");
202 } else {
203 // Aggregate during printing
204 Aggregation totals;
205 for (size_t i = 0; i < match_array.size(); ++i) {
206 totals.AddDeathSnapshot(match_array[i]);
208 output->append("Aggregate Stats: ");
209 totals.Write(output);
210 output->append("<hr><hr>");
212 Aggregation subtotals;
213 for (size_t i = 0; i < match_array.size(); ++i) {
214 if (0 == i || !comparator.Equivalent(match_array[i - 1],
215 match_array[i])) {
216 // Print group's defining characteristics.
217 comparator.WriteSortGrouping(match_array[i], output);
218 output->append("<br><br>");
220 comparator.WriteSnapshot(match_array[i], output);
221 output->append("<br>");
222 subtotals.AddDeathSnapshot(match_array[i]);
223 if (i + 1 >= match_array.size() ||
224 !comparator.Equivalent(match_array[i],
225 match_array[i + 1])) {
226 // Print aggregate stats for the group.
227 output->append("<br>");
228 subtotals.Write(output);
229 output->append("<br><hr><br>");
230 subtotals.Clear();
236 Births* ThreadData::TallyABirth(const Location& location) {
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.
246 BirthMap::iterator it = birth_map_.find(location);
247 if (it != birth_map_.end()) {
248 it->second->RecordBirth();
249 return it->second;
252 Births* tracker = new Births(location);
253 // Lock since the map may get relocated now, and other threads sometimes
254 // snapshot it (but they lock before copying it).
255 base::AutoLock lock(lock_);
256 birth_map_[location] = tracker;
257 return tracker;
260 void ThreadData::TallyADeath(const Births& lifetimes,
261 const TimeDelta& duration) {
263 // http://crbug.com/62728
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.
269 DeathMap::iterator it = death_map_.find(&lifetimes);
270 if (it != death_map_.end()) {
271 it->second.RecordDeath(duration);
272 return;
275 base::AutoLock lock(lock_); // Lock since the map may get relocated now.
276 death_map_[&lifetimes].RecordDeath(duration);
279 // static
280 Births* ThreadData::TallyABirthIfActive(const Location& location) {
281 if (IsActive()) {
282 ThreadData* current_thread_data = current();
283 if (current_thread_data) {
284 return current_thread_data->TallyABirth(location);
288 return NULL;
291 // static
292 void ThreadData::TallyADeathIfActive(const Births* the_birth,
293 const base::TimeDelta& duration) {
294 if (IsActive() && the_birth) {
295 current()->TallyADeath(*the_birth, duration);
299 // static
300 ThreadData* ThreadData::first() {
301 base::AutoLock lock(list_lock_);
302 return first_;
305 const std::string ThreadData::ThreadName() const {
306 if (message_loop_)
307 return message_loop_->thread_name();
308 return "ThreadWithoutMessageLoop";
311 // This may be called from another thread.
312 void ThreadData::SnapshotBirthMap(BirthMap *output) const {
313 base::AutoLock lock(lock_);
314 for (BirthMap::const_iterator it = birth_map_.begin();
315 it != birth_map_.end(); ++it)
316 (*output)[it->first] = it->second;
319 // This may be called from another thread.
320 void ThreadData::SnapshotDeathMap(DeathMap *output) const {
321 base::AutoLock lock(lock_);
322 for (DeathMap::const_iterator it = death_map_.begin();
323 it != death_map_.end(); ++it)
324 (*output)[it->first] = it->second;
327 // static
328 void ThreadData::ResetAllThreadData() {
329 ThreadData* my_list = ThreadData::current()->first();
331 for (ThreadData* thread_data = my_list;
332 thread_data;
333 thread_data = thread_data->next())
334 thread_data->Reset();
337 void ThreadData::Reset() {
338 base::AutoLock lock(lock_);
339 for (DeathMap::iterator it = death_map_.begin();
340 it != death_map_.end(); ++it)
341 it->second.Clear();
342 for (BirthMap::iterator it = birth_map_.begin();
343 it != birth_map_.end(); ++it)
344 it->second->Clear();
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);
356 // Decrement the count, and return true if we hit zero. Also delete this
357 // instance automatically when we hit zero.
358 bool LastCaller();
360 private:
361 size_t remaining_count_;
362 base::Lock lock_; // protect access to remaining_count_.
365 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
366 : remaining_count_(count) {
367 DCHECK_GT(remaining_count_, 0u);
370 bool ThreadData::ThreadSafeDownCounter::LastCaller() {
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;
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();
392 private:
393 FunctionPointer function_;
394 HANDLE completion_handle_;
395 // Make sure enough tasks are called before completion is signaled.
396 ThreadSafeDownCounter* counter_;
398 DISALLOW_COPY_AND_ASSIGN(RunTheStatic);
401 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function,
402 HANDLE completion_handle,
403 ThreadSafeDownCounter* counter)
404 : function_(function),
405 completion_handle_(completion_handle),
406 counter_(counter) {
409 void ThreadData::RunTheStatic::Run() {
410 function_();
411 if (counter_->LastCaller())
412 SetEvent(completion_handle_);
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.
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());
425 ThreadSafeDownCounter* counter =
426 new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us!
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));
434 // Also run Task on our thread.
435 RunTheStatic local_task(function, completion_handle, counter);
436 local_task.Run();
438 WaitForSingleObject(completion_handle, INFINITE);
439 int ret_val = CloseHandle(completion_handle);
440 DCHECK(ret_val);
442 #endif // OS_WIN
444 // static
445 bool ThreadData::StartTracking(bool status) {
446 #ifndef TRACK_ALL_TASK_OBJECTS
447 return false; // Not compiled in.
448 #endif
450 if (!status) {
451 base::AutoLock lock(list_lock_);
452 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN);
453 status_ = SHUTDOWN;
454 return true;
456 base::AutoLock lock(list_lock_);
457 DCHECK_EQ(UNINITIALIZED, status_);
458 CHECK(tls_index_.Initialize(NULL));
459 status_ = ACTIVE;
460 return true;
463 // static
464 bool ThreadData::IsActive() {
465 return status_ == ACTIVE;
468 #ifdef OS_WIN
469 // static
470 void ThreadData::ShutdownMultiThreadTracking() {
471 // Using lock, guarantee that no new ThreadData instances will be created.
472 if (!StartTracking(false))
473 return;
475 RunOnAllThreads(ShutdownDisablingFurtherTracking);
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;
485 #endif
487 // static
488 void ThreadData::ShutdownSingleThreadedCleanup() {
489 // We must be single threaded... but be careful anyway.
490 if (!StartTracking(false))
491 return;
492 ThreadData* thread_data_list;
494 base::AutoLock lock(list_lock_);
495 thread_data_list = first_;
496 first_ = NULL;
499 while (thread_data_list) {
500 ThreadData* next_thread_data = thread_data_list;
501 thread_data_list = thread_data_list->next();
503 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
504 next_thread_data->birth_map_.end() != it; ++it)
505 delete it->second; // Delete the Birth Records.
506 next_thread_data->birth_map_.clear();
507 next_thread_data->death_map_.clear();
508 delete next_thread_data; // Includes all Death Records.
511 CHECK(tls_index_.initialized());
512 tls_index_.Free();
513 DCHECK(!tls_index_.initialized());
514 status_ = UNINITIALIZED;
517 // static
518 void ThreadData::ShutdownDisablingFurtherTracking() {
519 // Redundantly set status SHUTDOWN on this thread.
520 if (!StartTracking(false))
521 return;
524 //------------------------------------------------------------------------------
525 // Individual 3-tuple of birth (place and thread) along with death thread, and
526 // the accumulated stats for instances (DeathData).
528 Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
529 const ThreadData& death_thread,
530 const DeathData& death_data)
531 : birth_(&birth_on_thread),
532 death_thread_(&death_thread),
533 death_data_(death_data) {
536 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
537 : birth_(&birth_on_thread),
538 death_thread_(NULL),
539 death_data_(DeathData(count)) {
542 const std::string Snapshot::DeathThreadName() const {
543 if (death_thread_)
544 return death_thread_->ThreadName();
545 return "Still_Alive";
548 void Snapshot::Write(std::string* output) const {
549 death_data_.Write(output);
550 base::StringAppendF(output, "%s->%s ",
551 birth_->birth_thread()->ThreadName().c_str(),
552 death_thread_->ThreadName().c_str());
553 birth_->location().Write(true, true, output);
556 void Snapshot::Add(const Snapshot& other) {
557 death_data_.AddDeathData(other.death_data_);
560 //------------------------------------------------------------------------------
561 // DataCollector
563 DataCollector::DataCollector() {
564 DCHECK(ThreadData::IsActive());
566 // Get an unchanging copy of a ThreadData list.
567 ThreadData* my_list = ThreadData::current()->first();
569 count_of_contributing_threads_ = 0;
570 for (ThreadData* thread_data = my_list;
571 thread_data;
572 thread_data = thread_data->next()) {
573 ++count_of_contributing_threads_;
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
579 // 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
581 // sees any strangeness, they can always just run their stats gathering a
582 // 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;
587 thread_data;
588 thread_data = thread_data->next()) {
589 Append(*thread_data);
593 DataCollector::~DataCollector() {
596 void DataCollector::Append(const ThreadData& thread_data) {
597 // Get copy of data (which is done under ThreadData's lock).
598 ThreadData::BirthMap birth_map;
599 thread_data.SnapshotBirthMap(&birth_map);
600 ThreadData::DeathMap death_map;
601 thread_data.SnapshotDeathMap(&death_map);
603 // Use our lock to protect our accumulation activity.
604 base::AutoLock lock(accumulation_lock_);
606 DCHECK(count_of_contributing_threads_);
608 for (ThreadData::DeathMap::const_iterator it = death_map.begin();
609 it != death_map.end(); ++it) {
610 collection_.push_back(Snapshot(*it->first, thread_data, it->second));
611 global_birth_count_[it->first] -= it->first->birth_count();
614 for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
615 it != birth_map.end(); ++it) {
616 global_birth_count_[it->second] += it->second->birth_count();
619 --count_of_contributing_threads_;
622 DataCollector::Collection* DataCollector::collection() {
623 DCHECK(!count_of_contributing_threads_);
624 return &collection_;
627 void DataCollector::AddListOfLivingObjects() {
628 DCHECK(!count_of_contributing_threads_);
629 for (BirthCount::iterator it = global_birth_count_.begin();
630 it != global_birth_count_.end(); ++it) {
631 if (it->second > 0)
632 collection_.push_back(Snapshot(*it->first, it->second));
636 //------------------------------------------------------------------------------
637 // Aggregation
639 Aggregation::Aggregation()
640 : birth_count_(0) {
643 Aggregation::~Aggregation() {
646 void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
647 AddBirth(snapshot.birth());
648 death_threads_[snapshot.death_thread()]++;
649 AddDeathData(snapshot.death_data());
652 void Aggregation::AddBirths(const Births& births) {
653 AddBirth(births);
654 birth_count_ += births.birth_count();
656 void Aggregation::AddBirth(const BirthOnThread& birth) {
657 AddBirthPlace(birth.location());
658 birth_threads_[birth.birth_thread()]++;
661 void Aggregation::AddBirthPlace(const Location& location) {
662 locations_[location]++;
663 birth_files_[location.file_name()]++;
666 void Aggregation::Write(std::string* output) const {
667 if (locations_.size() == 1) {
668 locations_.begin()->first.Write(true, true, output);
669 } else {
670 base::StringAppendF(output, "%" PRIuS " Locations. ", locations_.size());
671 if (birth_files_.size() > 1) {
672 base::StringAppendF(output, "%" PRIuS " Files. ", birth_files_.size());
673 } else {
674 base::StringAppendF(output, "All born in %s. ",
675 birth_files_.begin()->first.c_str());
679 if (birth_threads_.size() > 1) {
680 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ",
681 birth_threads_.size());
682 } else {
683 base::StringAppendF(output, "All born on %s. ",
684 birth_threads_.begin()->first->ThreadName().c_str());
687 if (death_threads_.size() > 1) {
688 base::StringAppendF(output, "%" PRIuS " DeathThreads. ",
689 death_threads_.size());
690 } else {
691 if (death_threads_.begin()->first) {
692 base::StringAppendF(output, "All deleted on %s. ",
693 death_threads_.begin()->first->ThreadName().c_str());
694 } else {
695 output->append("All these objects are still alive.");
699 if (birth_count_ > 1)
700 base::StringAppendF(output, "Births=%d ", birth_count_);
702 DeathData::Write(output);
705 void Aggregation::Clear() {
706 birth_count_ = 0;
707 birth_files_.clear();
708 locations_.clear();
709 birth_threads_.clear();
710 DeathData::Clear();
711 death_threads_.clear();
714 //------------------------------------------------------------------------------
715 // Comparison object for sorting.
717 Comparator::Comparator()
718 : selector_(NIL),
719 tiebreaker_(NULL),
720 combined_selectors_(0),
721 use_tiebreaker_for_sort_only_(false) {}
723 void Comparator::Clear() {
724 if (tiebreaker_) {
725 tiebreaker_->Clear();
726 delete tiebreaker_;
727 tiebreaker_ = NULL;
729 use_tiebreaker_for_sort_only_ = false;
730 selector_ = NIL;
733 bool Comparator::operator()(const Snapshot& left,
734 const Snapshot& right) const {
735 switch (selector_) {
736 case BIRTH_THREAD:
737 if (left.birth_thread() != right.birth_thread() &&
738 left.birth_thread()->ThreadName() !=
739 right.birth_thread()->ThreadName())
740 return left.birth_thread()->ThreadName() <
741 right.birth_thread()->ThreadName();
742 break;
744 case DEATH_THREAD:
745 if (left.death_thread() != right.death_thread() &&
746 left.DeathThreadName() !=
747 right.DeathThreadName()) {
748 if (!left.death_thread())
749 return true;
750 if (!right.death_thread())
751 return false;
752 return left.DeathThreadName() <
753 right.DeathThreadName();
755 break;
757 case BIRTH_FILE:
758 if (left.location().file_name() != right.location().file_name()) {
759 int comp = strcmp(left.location().file_name(),
760 right.location().file_name());
761 if (comp)
762 return 0 > comp;
764 break;
766 case BIRTH_FUNCTION:
767 if (left.location().function_name() != right.location().function_name()) {
768 int comp = strcmp(left.location().function_name(),
769 right.location().function_name());
770 if (comp)
771 return 0 > comp;
773 break;
775 case BIRTH_LINE:
776 if (left.location().line_number() != right.location().line_number())
777 return left.location().line_number() <
778 right.location().line_number();
779 break;
781 case COUNT:
782 if (left.count() != right.count())
783 return left.count() > right.count(); // Sort large at front of vector.
784 break;
786 case AVERAGE_DURATION:
787 if (!left.count() || !right.count())
788 break;
789 if (left.AverageMsDuration() != right.AverageMsDuration())
790 return left.AverageMsDuration() > right.AverageMsDuration();
791 break;
793 default:
794 break;
796 if (tiebreaker_)
797 return tiebreaker_->operator()(left, right);
798 return false;
801 void Comparator::Sort(DataCollector::Collection* collection) const {
802 std::sort(collection->begin(), collection->end(), *this);
805 bool Comparator::Equivalent(const Snapshot& left,
806 const Snapshot& right) const {
807 switch (selector_) {
808 case BIRTH_THREAD:
809 if (left.birth_thread() != right.birth_thread() &&
810 left.birth_thread()->ThreadName() !=
811 right.birth_thread()->ThreadName())
812 return false;
813 break;
815 case DEATH_THREAD:
816 if (left.death_thread() != right.death_thread() &&
817 left.DeathThreadName() != right.DeathThreadName())
818 return false;
819 break;
821 case BIRTH_FILE:
822 if (left.location().file_name() != right.location().file_name()) {
823 int comp = strcmp(left.location().file_name(),
824 right.location().file_name());
825 if (comp)
826 return false;
828 break;
830 case BIRTH_FUNCTION:
831 if (left.location().function_name() != right.location().function_name()) {
832 int comp = strcmp(left.location().function_name(),
833 right.location().function_name());
834 if (comp)
835 return false;
837 break;
839 case COUNT:
840 if (left.count() != right.count())
841 return false;
842 break;
844 case AVERAGE_DURATION:
845 if (left.life_duration() != right.life_duration())
846 return false;
847 break;
849 default:
850 break;
852 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
853 return tiebreaker_->Equivalent(left, right);
854 return true;
857 bool Comparator::Acceptable(const Snapshot& sample) const {
858 if (required_.size()) {
859 switch (selector_) {
860 case BIRTH_THREAD:
861 if (sample.birth_thread()->ThreadName().find(required_) ==
862 std::string::npos)
863 return false;
864 break;
866 case DEATH_THREAD:
867 if (sample.DeathThreadName().find(required_) == std::string::npos)
868 return false;
869 break;
871 case BIRTH_FILE:
872 if (!strstr(sample.location().file_name(), required_.c_str()))
873 return false;
874 break;
876 case BIRTH_FUNCTION:
877 if (!strstr(sample.location().function_name(), required_.c_str()))
878 return false;
879 break;
881 default:
882 break;
885 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
886 return tiebreaker_->Acceptable(sample);
887 return true;
890 void Comparator::SetTiebreaker(Selector selector, const std::string& required) {
891 if (selector == selector_ || NIL == selector)
892 return;
893 combined_selectors_ |= selector;
894 if (NIL == selector_) {
895 selector_ = selector;
896 if (required.size())
897 required_ = required;
898 return;
900 if (tiebreaker_) {
901 if (use_tiebreaker_for_sort_only_) {
902 Comparator* temp = new Comparator;
903 temp->tiebreaker_ = tiebreaker_;
904 tiebreaker_ = temp;
906 } else {
907 tiebreaker_ = new Comparator;
908 DCHECK(!use_tiebreaker_for_sort_only_);
910 tiebreaker_->SetTiebreaker(selector, required);
913 bool Comparator::IsGroupedBy(Selector selector) const {
914 return 0 != (selector & combined_selectors_);
917 void Comparator::SetSubgroupTiebreaker(Selector selector) {
918 if (selector == selector_ || NIL == selector)
919 return;
920 if (!tiebreaker_) {
921 use_tiebreaker_for_sort_only_ = true;
922 tiebreaker_ = new Comparator;
923 tiebreaker_->SetTiebreaker(selector, "");
924 } else {
925 tiebreaker_->SetSubgroupTiebreaker(selector);
929 void Comparator::ParseKeyphrase(const std::string& key_phrase) {
930 typedef std::map<const std::string, Selector> KeyMap;
931 static KeyMap key_map;
932 static bool initialized = false;
933 if (!initialized) {
934 initialized = true;
935 // 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.
937 key_map["count"] = COUNT;
938 key_map["duration"] = AVERAGE_DURATION;
939 key_map["birth"] = BIRTH_THREAD;
940 key_map["death"] = DEATH_THREAD;
941 key_map["file"] = BIRTH_FILE;
942 key_map["function"] = BIRTH_FUNCTION;
943 key_map["line"] = BIRTH_LINE;
945 // Immediate commands that do not involve setting sort order.
946 key_map["reset"] = RESET_ALL_DATA;
949 std::string required;
950 // Watch for: "sort_key=value" as we parse.
951 size_t equal_offset = key_phrase.find('=', 0);
952 if (key_phrase.npos != equal_offset) {
953 // There is a value that must be matched for the data to display.
954 required = key_phrase.substr(equal_offset + 1, key_phrase.npos);
956 std::string keyword(key_phrase.substr(0, equal_offset));
957 keyword = StringToLowerASCII(keyword);
958 KeyMap::iterator it = key_map.find(keyword);
959 if (key_map.end() == it)
960 return; // Unknown keyword.
961 if (it->second == RESET_ALL_DATA)
962 ThreadData::ResetAllThreadData();
963 else
964 SetTiebreaker(key_map[keyword], required);
967 bool Comparator::ParseQuery(const std::string& query) {
968 // Parse each keyphrase between consecutive slashes.
969 for (size_t i = 0; i < query.size();) {
970 size_t slash_offset = query.find('/', i);
971 ParseKeyphrase(query.substr(i, slash_offset - i));
972 if (query.npos == slash_offset)
973 break;
974 i = slash_offset + 1;
977 // Select subgroup ordering (if we want to display the subgroup)
978 SetSubgroupTiebreaker(COUNT);
979 SetSubgroupTiebreaker(AVERAGE_DURATION);
980 SetSubgroupTiebreaker(BIRTH_THREAD);
981 SetSubgroupTiebreaker(DEATH_THREAD);
982 SetSubgroupTiebreaker(BIRTH_FUNCTION);
983 SetSubgroupTiebreaker(BIRTH_FILE);
984 SetSubgroupTiebreaker(BIRTH_LINE);
986 return true;
989 bool Comparator::WriteSortGrouping(const Snapshot& sample,
990 std::string* output) const {
991 bool wrote_data = false;
992 switch (selector_) {
993 case BIRTH_THREAD:
994 base::StringAppendF(output, "All new on %s ",
995 sample.birth_thread()->ThreadName().c_str());
996 wrote_data = true;
997 break;
999 case DEATH_THREAD:
1000 if (sample.death_thread()) {
1001 base::StringAppendF(output, "All deleted on %s ",
1002 sample.DeathThreadName().c_str());
1003 } else {
1004 output->append("All still alive ");
1006 wrote_data = true;
1007 break;
1009 case BIRTH_FILE:
1010 base::StringAppendF(output, "All born in %s ",
1011 sample.location().file_name());
1012 break;
1014 case BIRTH_FUNCTION:
1015 output->append("All born in ");
1016 sample.location().WriteFunctionName(output);
1017 output->push_back(' ');
1018 break;
1020 default:
1021 break;
1023 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
1024 wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
1026 return wrote_data;
1029 void Comparator::WriteSnapshot(const Snapshot& sample,
1030 std::string* output) const {
1031 sample.death_data().Write(output);
1032 if (!(combined_selectors_ & BIRTH_THREAD) ||
1033 !(combined_selectors_ & DEATH_THREAD))
1034 base::StringAppendF(output, "%s->%s ",
1035 (combined_selectors_ & BIRTH_THREAD) ? "*" :
1036 sample.birth().birth_thread()->ThreadName().c_str(),
1037 (combined_selectors_ & DEATH_THREAD) ? "*" :
1038 sample.DeathThreadName().c_str());
1039 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
1040 !(combined_selectors_ & BIRTH_FUNCTION),
1041 output);
1044 } // namespace tracked_objects