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
{
19 // A TLS slot to the TrackRegistry for the current thread.
21 base::ThreadLocalStorage::Slot
ThreadData::tls_index_(base::LINKER_INITIALIZED
);
23 // A global state variable to prevent repeated initialization during tests.
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
) {
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_
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 {
59 base::StringAppendF(output
, "(1)Life in %dms ", AverageMsDuration());
61 base::StringAppendF(output
, "(%d)Lives %dms/life ",
62 count_
, AverageMsDuration());
66 void DeathData::Clear() {
68 life_duration_
= TimeDelta();
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
),
82 //------------------------------------------------------------------------------
83 // ThreadData maintains the central data for all births and death.
86 ThreadData
* ThreadData::first_
= NULL
;
88 base::Lock
ThreadData::list_lock_
;
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() {}
104 ThreadData
* ThreadData::current() {
105 if (!tls_index_
.initialized())
108 ThreadData
* registry
= static_cast<ThreadData
*>(tls_index_
.Get());
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.
117 too_late_to_create
= true;
119 // Use lock to insert into list.
120 registry
->next_
= first_
;
124 if (too_late_to_create
) {
128 tls_index_
.Set(registry
);
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."
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."
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"
192 output
->append(help_string
);
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.");
203 // Aggregate during printing
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],
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>");
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();
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
;
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
);
275 base::AutoLock
lock(lock_
); // Lock since the map may get relocated now.
276 death_map_
[&lifetimes
].RecordDeath(duration
);
280 Births
* ThreadData::TallyABirthIfActive(const Location
& location
) {
282 ThreadData
* current_thread_data
= current();
283 if (current_thread_data
) {
284 return current_thread_data
->TallyABirth(location
);
292 void ThreadData::TallyADeathIfActive(const Births
* the_birth
,
293 const base::TimeDelta
& duration
) {
294 if (IsActive() && the_birth
) {
295 current()->TallyADeath(*the_birth
, duration
);
300 ThreadData
* ThreadData::first() {
301 base::AutoLock
lock(list_lock_
);
305 const std::string
ThreadData::ThreadName() const {
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
;
328 void ThreadData::ResetAllThreadData() {
329 ThreadData
* my_list
= ThreadData::current()->first();
331 for (ThreadData
* thread_data
= my_list
;
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
)
342 for (BirthMap::iterator it
= birth_map_
.begin();
343 it
!= birth_map_
.end(); ++it
)
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
351 class ThreadData::ThreadSafeDownCounter
{
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.
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_
)
375 } // Release lock, so we can delete everything in this instance.
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
{
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.
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
),
409 void ThreadData::RunTheStatic::Run() {
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
);
438 WaitForSingleObject(completion_handle
, INFINITE
);
439 int ret_val
= CloseHandle(completion_handle
);
445 bool ThreadData::StartTracking(bool status
) {
446 #ifndef TRACK_ALL_TASK_OBJECTS
447 return false; // Not compiled in.
451 base::AutoLock
lock(list_lock_
);
452 DCHECK(status_
== ACTIVE
|| status_
== SHUTDOWN
);
456 base::AutoLock
lock(list_lock_
);
457 DCHECK_EQ(UNINITIALIZED
, status_
);
458 CHECK(tls_index_
.Initialize(NULL
));
464 bool ThreadData::IsActive() {
465 return status_
== ACTIVE
;
470 void ThreadData::ShutdownMultiThreadTracking() {
471 // Using lock, guarantee that no new ThreadData instances will be created.
472 if (!StartTracking(false))
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.
488 void ThreadData::ShutdownSingleThreadedCleanup() {
489 // We must be single threaded... but be careful anyway.
490 if (!StartTracking(false))
492 ThreadData
* thread_data_list
;
494 base::AutoLock
lock(list_lock_
);
495 thread_data_list
= first_
;
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());
513 DCHECK(!tls_index_
.initialized());
514 status_
= UNINITIALIZED
;
518 void ThreadData::ShutdownDisablingFurtherTracking() {
519 // Redundantly set status SHUTDOWN on this thread.
520 if (!StartTracking(false))
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
),
539 death_data_(DeathData(count
)) {
542 const std::string
Snapshot::DeathThreadName() const {
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 //------------------------------------------------------------------------------
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
;
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
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
;
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_
);
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
) {
632 collection_
.push_back(Snapshot(*it
->first
, it
->second
));
636 //------------------------------------------------------------------------------
639 Aggregation::Aggregation()
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
) {
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
);
670 base::StringAppendF(output
, "%" PRIuS
" Locations. ", locations_
.size());
671 if (birth_files_
.size() > 1) {
672 base::StringAppendF(output
, "%" PRIuS
" Files. ", birth_files_
.size());
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());
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());
691 if (death_threads_
.begin()->first
) {
692 base::StringAppendF(output
, "All deleted on %s. ",
693 death_threads_
.begin()->first
->ThreadName().c_str());
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() {
707 birth_files_
.clear();
709 birth_threads_
.clear();
711 death_threads_
.clear();
714 //------------------------------------------------------------------------------
715 // Comparison object for sorting.
717 Comparator::Comparator()
720 combined_selectors_(0),
721 use_tiebreaker_for_sort_only_(false) {}
723 void Comparator::Clear() {
725 tiebreaker_
->Clear();
729 use_tiebreaker_for_sort_only_
= false;
733 bool Comparator::operator()(const Snapshot
& left
,
734 const Snapshot
& right
) const {
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();
745 if (left
.death_thread() != right
.death_thread() &&
746 left
.DeathThreadName() !=
747 right
.DeathThreadName()) {
748 if (!left
.death_thread())
750 if (!right
.death_thread())
752 return left
.DeathThreadName() <
753 right
.DeathThreadName();
758 if (left
.location().file_name() != right
.location().file_name()) {
759 int comp
= strcmp(left
.location().file_name(),
760 right
.location().file_name());
767 if (left
.location().function_name() != right
.location().function_name()) {
768 int comp
= strcmp(left
.location().function_name(),
769 right
.location().function_name());
776 if (left
.location().line_number() != right
.location().line_number())
777 return left
.location().line_number() <
778 right
.location().line_number();
782 if (left
.count() != right
.count())
783 return left
.count() > right
.count(); // Sort large at front of vector.
786 case AVERAGE_DURATION
:
787 if (!left
.count() || !right
.count())
789 if (left
.AverageMsDuration() != right
.AverageMsDuration())
790 return left
.AverageMsDuration() > right
.AverageMsDuration();
797 return tiebreaker_
->operator()(left
, right
);
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 {
809 if (left
.birth_thread() != right
.birth_thread() &&
810 left
.birth_thread()->ThreadName() !=
811 right
.birth_thread()->ThreadName())
816 if (left
.death_thread() != right
.death_thread() &&
817 left
.DeathThreadName() != right
.DeathThreadName())
822 if (left
.location().file_name() != right
.location().file_name()) {
823 int comp
= strcmp(left
.location().file_name(),
824 right
.location().file_name());
831 if (left
.location().function_name() != right
.location().function_name()) {
832 int comp
= strcmp(left
.location().function_name(),
833 right
.location().function_name());
840 if (left
.count() != right
.count())
844 case AVERAGE_DURATION
:
845 if (left
.life_duration() != right
.life_duration())
852 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
)
853 return tiebreaker_
->Equivalent(left
, right
);
857 bool Comparator::Acceptable(const Snapshot
& sample
) const {
858 if (required_
.size()) {
861 if (sample
.birth_thread()->ThreadName().find(required_
) ==
867 if (sample
.DeathThreadName().find(required_
) == std::string::npos
)
872 if (!strstr(sample
.location().file_name(), required_
.c_str()))
877 if (!strstr(sample
.location().function_name(), required_
.c_str()))
885 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
)
886 return tiebreaker_
->Acceptable(sample
);
890 void Comparator::SetTiebreaker(Selector selector
, const std::string
& required
) {
891 if (selector
== selector_
|| NIL
== selector
)
893 combined_selectors_
|= selector
;
894 if (NIL
== selector_
) {
895 selector_
= selector
;
897 required_
= required
;
901 if (use_tiebreaker_for_sort_only_
) {
902 Comparator
* temp
= new Comparator
;
903 temp
->tiebreaker_
= tiebreaker_
;
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
)
921 use_tiebreaker_for_sort_only_
= true;
922 tiebreaker_
= new Comparator
;
923 tiebreaker_
->SetTiebreaker(selector
, "");
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;
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();
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
)
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
);
989 bool Comparator::WriteSortGrouping(const Snapshot
& sample
,
990 std::string
* output
) const {
991 bool wrote_data
= false;
994 base::StringAppendF(output
, "All new on %s ",
995 sample
.birth_thread()->ThreadName().c_str());
1000 if (sample
.death_thread()) {
1001 base::StringAppendF(output
, "All deleted on %s ",
1002 sample
.DeathThreadName().c_str());
1004 output
->append("All still alive ");
1010 base::StringAppendF(output
, "All born in %s ",
1011 sample
.location().file_name());
1014 case BIRTH_FUNCTION
:
1015 output
->append("All born in ");
1016 sample
.location().WriteFunctionName(output
);
1017 output
->push_back(' ');
1023 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
) {
1024 wrote_data
|= tiebreaker_
->WriteSortGrouping(sample
, output
);
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
),
1044 } // namespace tracked_objects