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 ThreadData
* ThreadData::first() {
281 base::AutoLock
lock(list_lock_
);
285 const std::string
ThreadData::ThreadName() const {
287 return message_loop_
->thread_name();
288 return "ThreadWithoutMessageLoop";
291 // This may be called from another thread.
292 void ThreadData::SnapshotBirthMap(BirthMap
*output
) const {
293 base::AutoLock
lock(lock_
);
294 for (BirthMap::const_iterator it
= birth_map_
.begin();
295 it
!= birth_map_
.end(); ++it
)
296 (*output
)[it
->first
] = it
->second
;
299 // This may be called from another thread.
300 void ThreadData::SnapshotDeathMap(DeathMap
*output
) const {
301 base::AutoLock
lock(lock_
);
302 for (DeathMap::const_iterator it
= death_map_
.begin();
303 it
!= death_map_
.end(); ++it
)
304 (*output
)[it
->first
] = it
->second
;
308 void ThreadData::ResetAllThreadData() {
309 ThreadData
* my_list
= ThreadData::current()->first();
311 for (ThreadData
* thread_data
= my_list
;
313 thread_data
= thread_data
->next())
314 thread_data
->Reset();
317 void ThreadData::Reset() {
318 base::AutoLock
lock(lock_
);
319 for (DeathMap::iterator it
= death_map_
.begin();
320 it
!= death_map_
.end(); ++it
)
322 for (BirthMap::iterator it
= birth_map_
.begin();
323 it
!= birth_map_
.end(); ++it
)
328 // A class used to count down which is accessed by several threads. This is
329 // used to make sure RunOnAllThreads() actually runs a task on the expected
331 class ThreadData::ThreadSafeDownCounter
{
333 // Constructor sets the count, once and for all.
334 explicit ThreadSafeDownCounter(size_t count
);
336 // Decrement the count, and return true if we hit zero. Also delete this
337 // instance automatically when we hit zero.
341 size_t remaining_count_
;
342 base::Lock lock_
; // protect access to remaining_count_.
345 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count
)
346 : remaining_count_(count
) {
347 DCHECK_GT(remaining_count_
, 0u);
350 bool ThreadData::ThreadSafeDownCounter::LastCaller() {
352 base::AutoLock
lock(lock_
);
353 if (--remaining_count_
)
355 } // Release lock, so we can delete everything in this instance.
360 // A Task class that runs a static method supplied, and checks to see if this
361 // is the last tasks instance (on last thread) that will run the method.
362 // IF this is the last run, then the supplied event is signalled.
363 class ThreadData::RunTheStatic
: public Task
{
365 typedef void (*FunctionPointer
)();
366 RunTheStatic(FunctionPointer function
,
367 HANDLE completion_handle
,
368 ThreadSafeDownCounter
* counter
);
369 // Run the supplied static method, and optionally set the event.
373 FunctionPointer function_
;
374 HANDLE completion_handle_
;
375 // Make sure enough tasks are called before completion is signaled.
376 ThreadSafeDownCounter
* counter_
;
378 DISALLOW_COPY_AND_ASSIGN(RunTheStatic
);
381 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function
,
382 HANDLE completion_handle
,
383 ThreadSafeDownCounter
* counter
)
384 : function_(function
),
385 completion_handle_(completion_handle
),
389 void ThreadData::RunTheStatic::Run() {
391 if (counter_
->LastCaller())
392 SetEvent(completion_handle_
);
395 // TODO(jar): This should use condition variables, and be cross platform.
396 void ThreadData::RunOnAllThreads(void (*function
)()) {
397 ThreadData
* list
= first(); // Get existing list.
399 std::vector
<MessageLoop
*> message_loops
;
400 for (ThreadData
* it
= list
; it
; it
= it
->next()) {
401 if (current() != it
&& it
->message_loop())
402 message_loops
.push_back(it
->message_loop());
405 ThreadSafeDownCounter
* counter
=
406 new ThreadSafeDownCounter(message_loops
.size() + 1); // Extra one for us!
408 HANDLE completion_handle
= CreateEvent(NULL
, false, false, NULL
);
409 // Tell all other threads to run.
410 for (size_t i
= 0; i
< message_loops
.size(); ++i
)
411 message_loops
[i
]->PostTask(
412 FROM_HERE
, new RunTheStatic(function
, completion_handle
, counter
));
414 // Also run Task on our thread.
415 RunTheStatic
local_task(function
, completion_handle
, counter
);
418 WaitForSingleObject(completion_handle
, INFINITE
);
419 int ret_val
= CloseHandle(completion_handle
);
425 bool ThreadData::StartTracking(bool status
) {
426 #ifndef TRACK_ALL_TASK_OBJECTS
427 return false; // Not compiled in.
431 base::AutoLock
lock(list_lock_
);
432 DCHECK(status_
== ACTIVE
|| status_
== SHUTDOWN
);
436 base::AutoLock
lock(list_lock_
);
437 DCHECK_EQ(UNINITIALIZED
, status_
);
438 CHECK(tls_index_
.Initialize(NULL
));
444 bool ThreadData::IsActive() {
445 return status_
== ACTIVE
;
450 void ThreadData::ShutdownMultiThreadTracking() {
451 // Using lock, guarantee that no new ThreadData instances will be created.
452 if (!StartTracking(false))
455 RunOnAllThreads(ShutdownDisablingFurtherTracking
);
457 // Now the *only* threads that might change the database are the threads with
458 // no messages loops. They might still be adding data to their birth records,
459 // but since no objects are deleted on those threads, there will be no further
460 // access to to cross-thread data.
461 // We could do a cleanup on all threads except for the ones without
462 // MessageLoops, but we won't bother doing cleanup (destruction of data) yet.
468 void ThreadData::ShutdownSingleThreadedCleanup() {
469 // We must be single threaded... but be careful anyway.
470 if (!StartTracking(false))
472 ThreadData
* thread_data_list
;
474 base::AutoLock
lock(list_lock_
);
475 thread_data_list
= first_
;
479 while (thread_data_list
) {
480 ThreadData
* next_thread_data
= thread_data_list
;
481 thread_data_list
= thread_data_list
->next();
483 for (BirthMap::iterator it
= next_thread_data
->birth_map_
.begin();
484 next_thread_data
->birth_map_
.end() != it
; ++it
)
485 delete it
->second
; // Delete the Birth Records.
486 next_thread_data
->birth_map_
.clear();
487 next_thread_data
->death_map_
.clear();
488 delete next_thread_data
; // Includes all Death Records.
491 CHECK(tls_index_
.initialized());
493 DCHECK(!tls_index_
.initialized());
494 status_
= UNINITIALIZED
;
498 void ThreadData::ShutdownDisablingFurtherTracking() {
499 // Redundantly set status SHUTDOWN on this thread.
500 if (!StartTracking(false))
504 //------------------------------------------------------------------------------
505 // Individual 3-tuple of birth (place and thread) along with death thread, and
506 // the accumulated stats for instances (DeathData).
508 Snapshot::Snapshot(const BirthOnThread
& birth_on_thread
,
509 const ThreadData
& death_thread
,
510 const DeathData
& death_data
)
511 : birth_(&birth_on_thread
),
512 death_thread_(&death_thread
),
513 death_data_(death_data
) {
516 Snapshot::Snapshot(const BirthOnThread
& birth_on_thread
, int count
)
517 : birth_(&birth_on_thread
),
519 death_data_(DeathData(count
)) {
522 const std::string
Snapshot::DeathThreadName() const {
524 return death_thread_
->ThreadName();
525 return "Still_Alive";
528 void Snapshot::Write(std::string
* output
) const {
529 death_data_
.Write(output
);
530 base::StringAppendF(output
, "%s->%s ",
531 birth_
->birth_thread()->ThreadName().c_str(),
532 death_thread_
->ThreadName().c_str());
533 birth_
->location().Write(true, true, output
);
536 void Snapshot::Add(const Snapshot
& other
) {
537 death_data_
.AddDeathData(other
.death_data_
);
540 //------------------------------------------------------------------------------
543 DataCollector::DataCollector() {
544 DCHECK(ThreadData::IsActive());
546 // Get an unchanging copy of a ThreadData list.
547 ThreadData
* my_list
= ThreadData::current()->first();
549 count_of_contributing_threads_
= 0;
550 for (ThreadData
* thread_data
= my_list
;
552 thread_data
= thread_data
->next()) {
553 ++count_of_contributing_threads_
;
556 // Gather data serially. A different constructor could be used to do in
557 // parallel, and then invoke an OnCompletion task.
558 // This hackish approach *can* get some slighly corrupt tallies, as we are
559 // grabbing values without the protection of a lock, but it has the advantage
560 // of working even with threads that don't have message loops. If a user
561 // sees any strangeness, they can always just run their stats gathering a
563 // TODO(jar): Provide version that gathers stats safely via PostTask in all
564 // cases where thread_data supplies a message_loop to post to. Be careful to
565 // handle message_loops that are destroyed!?!
566 for (ThreadData
* thread_data
= my_list
;
568 thread_data
= thread_data
->next()) {
569 Append(*thread_data
);
573 DataCollector::~DataCollector() {
576 void DataCollector::Append(const ThreadData
& thread_data
) {
577 // Get copy of data (which is done under ThreadData's lock).
578 ThreadData::BirthMap birth_map
;
579 thread_data
.SnapshotBirthMap(&birth_map
);
580 ThreadData::DeathMap death_map
;
581 thread_data
.SnapshotDeathMap(&death_map
);
583 // Use our lock to protect our accumulation activity.
584 base::AutoLock
lock(accumulation_lock_
);
586 DCHECK(count_of_contributing_threads_
);
588 for (ThreadData::DeathMap::const_iterator it
= death_map
.begin();
589 it
!= death_map
.end(); ++it
) {
590 collection_
.push_back(Snapshot(*it
->first
, thread_data
, it
->second
));
591 global_birth_count_
[it
->first
] -= it
->first
->birth_count();
594 for (ThreadData::BirthMap::const_iterator it
= birth_map
.begin();
595 it
!= birth_map
.end(); ++it
) {
596 global_birth_count_
[it
->second
] += it
->second
->birth_count();
599 --count_of_contributing_threads_
;
602 DataCollector::Collection
* DataCollector::collection() {
603 DCHECK(!count_of_contributing_threads_
);
607 void DataCollector::AddListOfLivingObjects() {
608 DCHECK(!count_of_contributing_threads_
);
609 for (BirthCount::iterator it
= global_birth_count_
.begin();
610 it
!= global_birth_count_
.end(); ++it
) {
612 collection_
.push_back(Snapshot(*it
->first
, it
->second
));
616 //------------------------------------------------------------------------------
619 Aggregation::Aggregation()
623 Aggregation::~Aggregation() {
626 void Aggregation::AddDeathSnapshot(const Snapshot
& snapshot
) {
627 AddBirth(snapshot
.birth());
628 death_threads_
[snapshot
.death_thread()]++;
629 AddDeathData(snapshot
.death_data());
632 void Aggregation::AddBirths(const Births
& births
) {
634 birth_count_
+= births
.birth_count();
636 void Aggregation::AddBirth(const BirthOnThread
& birth
) {
637 AddBirthPlace(birth
.location());
638 birth_threads_
[birth
.birth_thread()]++;
641 void Aggregation::AddBirthPlace(const Location
& location
) {
642 locations_
[location
]++;
643 birth_files_
[location
.file_name()]++;
646 void Aggregation::Write(std::string
* output
) const {
647 if (locations_
.size() == 1) {
648 locations_
.begin()->first
.Write(true, true, output
);
650 base::StringAppendF(output
, "%" PRIuS
" Locations. ", locations_
.size());
651 if (birth_files_
.size() > 1) {
652 base::StringAppendF(output
, "%" PRIuS
" Files. ", birth_files_
.size());
654 base::StringAppendF(output
, "All born in %s. ",
655 birth_files_
.begin()->first
.c_str());
659 if (birth_threads_
.size() > 1) {
660 base::StringAppendF(output
, "%" PRIuS
" BirthingThreads. ",
661 birth_threads_
.size());
663 base::StringAppendF(output
, "All born on %s. ",
664 birth_threads_
.begin()->first
->ThreadName().c_str());
667 if (death_threads_
.size() > 1) {
668 base::StringAppendF(output
, "%" PRIuS
" DeathThreads. ",
669 death_threads_
.size());
671 if (death_threads_
.begin()->first
) {
672 base::StringAppendF(output
, "All deleted on %s. ",
673 death_threads_
.begin()->first
->ThreadName().c_str());
675 output
->append("All these objects are still alive.");
679 if (birth_count_
> 1)
680 base::StringAppendF(output
, "Births=%d ", birth_count_
);
682 DeathData::Write(output
);
685 void Aggregation::Clear() {
687 birth_files_
.clear();
689 birth_threads_
.clear();
691 death_threads_
.clear();
694 //------------------------------------------------------------------------------
695 // Comparison object for sorting.
697 Comparator::Comparator()
700 combined_selectors_(0),
701 use_tiebreaker_for_sort_only_(false) {}
703 void Comparator::Clear() {
705 tiebreaker_
->Clear();
709 use_tiebreaker_for_sort_only_
= false;
713 bool Comparator::operator()(const Snapshot
& left
,
714 const Snapshot
& right
) const {
717 if (left
.birth_thread() != right
.birth_thread() &&
718 left
.birth_thread()->ThreadName() !=
719 right
.birth_thread()->ThreadName())
720 return left
.birth_thread()->ThreadName() <
721 right
.birth_thread()->ThreadName();
725 if (left
.death_thread() != right
.death_thread() &&
726 left
.DeathThreadName() !=
727 right
.DeathThreadName()) {
728 if (!left
.death_thread())
730 if (!right
.death_thread())
732 return left
.DeathThreadName() <
733 right
.DeathThreadName();
738 if (left
.location().file_name() != right
.location().file_name()) {
739 int comp
= strcmp(left
.location().file_name(),
740 right
.location().file_name());
747 if (left
.location().function_name() != right
.location().function_name()) {
748 int comp
= strcmp(left
.location().function_name(),
749 right
.location().function_name());
756 if (left
.location().line_number() != right
.location().line_number())
757 return left
.location().line_number() <
758 right
.location().line_number();
762 if (left
.count() != right
.count())
763 return left
.count() > right
.count(); // Sort large at front of vector.
766 case AVERAGE_DURATION
:
767 if (!left
.count() || !right
.count())
769 if (left
.AverageMsDuration() != right
.AverageMsDuration())
770 return left
.AverageMsDuration() > right
.AverageMsDuration();
777 return tiebreaker_
->operator()(left
, right
);
781 void Comparator::Sort(DataCollector::Collection
* collection
) const {
782 std::sort(collection
->begin(), collection
->end(), *this);
785 bool Comparator::Equivalent(const Snapshot
& left
,
786 const Snapshot
& right
) const {
789 if (left
.birth_thread() != right
.birth_thread() &&
790 left
.birth_thread()->ThreadName() !=
791 right
.birth_thread()->ThreadName())
796 if (left
.death_thread() != right
.death_thread() &&
797 left
.DeathThreadName() != right
.DeathThreadName())
802 if (left
.location().file_name() != right
.location().file_name()) {
803 int comp
= strcmp(left
.location().file_name(),
804 right
.location().file_name());
811 if (left
.location().function_name() != right
.location().function_name()) {
812 int comp
= strcmp(left
.location().function_name(),
813 right
.location().function_name());
820 if (left
.count() != right
.count())
824 case AVERAGE_DURATION
:
825 if (left
.life_duration() != right
.life_duration())
832 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
)
833 return tiebreaker_
->Equivalent(left
, right
);
837 bool Comparator::Acceptable(const Snapshot
& sample
) const {
838 if (required_
.size()) {
841 if (sample
.birth_thread()->ThreadName().find(required_
) ==
847 if (sample
.DeathThreadName().find(required_
) == std::string::npos
)
852 if (!strstr(sample
.location().file_name(), required_
.c_str()))
857 if (!strstr(sample
.location().function_name(), required_
.c_str()))
865 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
)
866 return tiebreaker_
->Acceptable(sample
);
870 void Comparator::SetTiebreaker(Selector selector
, const std::string
& required
) {
871 if (selector
== selector_
|| NIL
== selector
)
873 combined_selectors_
|= selector
;
874 if (NIL
== selector_
) {
875 selector_
= selector
;
877 required_
= required
;
881 if (use_tiebreaker_for_sort_only_
) {
882 Comparator
* temp
= new Comparator
;
883 temp
->tiebreaker_
= tiebreaker_
;
887 tiebreaker_
= new Comparator
;
888 DCHECK(!use_tiebreaker_for_sort_only_
);
890 tiebreaker_
->SetTiebreaker(selector
, required
);
893 bool Comparator::IsGroupedBy(Selector selector
) const {
894 return 0 != (selector
& combined_selectors_
);
897 void Comparator::SetSubgroupTiebreaker(Selector selector
) {
898 if (selector
== selector_
|| NIL
== selector
)
901 use_tiebreaker_for_sort_only_
= true;
902 tiebreaker_
= new Comparator
;
903 tiebreaker_
->SetTiebreaker(selector
, "");
905 tiebreaker_
->SetSubgroupTiebreaker(selector
);
909 void Comparator::ParseKeyphrase(const std::string
& key_phrase
) {
910 typedef std::map
<const std::string
, Selector
> KeyMap
;
911 static KeyMap key_map
;
912 static bool initialized
= false;
915 // Sorting and aggretation keywords, which specify how to sort the data, or
916 // can specify a required match from the specified field in the record.
917 key_map
["count"] = COUNT
;
918 key_map
["duration"] = AVERAGE_DURATION
;
919 key_map
["birth"] = BIRTH_THREAD
;
920 key_map
["death"] = DEATH_THREAD
;
921 key_map
["file"] = BIRTH_FILE
;
922 key_map
["function"] = BIRTH_FUNCTION
;
923 key_map
["line"] = BIRTH_LINE
;
925 // Immediate commands that do not involve setting sort order.
926 key_map
["reset"] = RESET_ALL_DATA
;
929 std::string required
;
930 // Watch for: "sort_key=value" as we parse.
931 size_t equal_offset
= key_phrase
.find('=', 0);
932 if (key_phrase
.npos
!= equal_offset
) {
933 // There is a value that must be matched for the data to display.
934 required
= key_phrase
.substr(equal_offset
+ 1, key_phrase
.npos
);
936 std::string
keyword(key_phrase
.substr(0, equal_offset
));
937 keyword
= StringToLowerASCII(keyword
);
938 KeyMap::iterator it
= key_map
.find(keyword
);
939 if (key_map
.end() == it
)
940 return; // Unknown keyword.
941 if (it
->second
== RESET_ALL_DATA
)
942 ThreadData::ResetAllThreadData();
944 SetTiebreaker(key_map
[keyword
], required
);
947 bool Comparator::ParseQuery(const std::string
& query
) {
948 // Parse each keyphrase between consecutive slashes.
949 for (size_t i
= 0; i
< query
.size();) {
950 size_t slash_offset
= query
.find('/', i
);
951 ParseKeyphrase(query
.substr(i
, slash_offset
- i
));
952 if (query
.npos
== slash_offset
)
954 i
= slash_offset
+ 1;
957 // Select subgroup ordering (if we want to display the subgroup)
958 SetSubgroupTiebreaker(COUNT
);
959 SetSubgroupTiebreaker(AVERAGE_DURATION
);
960 SetSubgroupTiebreaker(BIRTH_THREAD
);
961 SetSubgroupTiebreaker(DEATH_THREAD
);
962 SetSubgroupTiebreaker(BIRTH_FUNCTION
);
963 SetSubgroupTiebreaker(BIRTH_FILE
);
964 SetSubgroupTiebreaker(BIRTH_LINE
);
969 bool Comparator::WriteSortGrouping(const Snapshot
& sample
,
970 std::string
* output
) const {
971 bool wrote_data
= false;
974 base::StringAppendF(output
, "All new on %s ",
975 sample
.birth_thread()->ThreadName().c_str());
980 if (sample
.death_thread()) {
981 base::StringAppendF(output
, "All deleted on %s ",
982 sample
.DeathThreadName().c_str());
984 output
->append("All still alive ");
990 base::StringAppendF(output
, "All born in %s ",
991 sample
.location().file_name());
995 output
->append("All born in ");
996 sample
.location().WriteFunctionName(output
);
997 output
->push_back(' ');
1003 if (tiebreaker_
&& !use_tiebreaker_for_sort_only_
) {
1004 wrote_data
|= tiebreaker_
->WriteSortGrouping(sample
, output
);
1009 void Comparator::WriteSnapshot(const Snapshot
& sample
,
1010 std::string
* output
) const {
1011 sample
.death_data().Write(output
);
1012 if (!(combined_selectors_
& BIRTH_THREAD
) ||
1013 !(combined_selectors_
& DEATH_THREAD
))
1014 base::StringAppendF(output
, "%s->%s ",
1015 (combined_selectors_
& BIRTH_THREAD
) ? "*" :
1016 sample
.birth().birth_thread()->ThreadName().c_str(),
1017 (combined_selectors_
& DEATH_THREAD
) ? "*" :
1018 sample
.DeathThreadName().c_str());
1019 sample
.birth().location().Write(!(combined_selectors_
& BIRTH_FILE
),
1020 !(combined_selectors_
& BIRTH_FUNCTION
),
1024 } // namespace tracked_objects