1 // Copyright (c) 2010 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.
4
5 #include "base/tracked_objects.h"
6
7 #include <math.h>
8
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"
14
15 using base::TimeDelta;
16
17 namespace tracked_objects {
18
19 // A TLS slot to the TrackRegistry for the current thread.
20 // static
21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED);
22
23 // A global state variable to prevent repeated initialization during tests.
24 // static
25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun;
26
27 //------------------------------------------------------------------------------
28 // Death data tallies durations when a death takes place.
29
RecordDeath(const TimeDelta & duration)30 void DeathData::RecordDeath(const TimeDelta& duration) {
31 ++count_;
32 life_duration_ += duration;
33 int64 milliseconds = duration.InMilliseconds();
34 square_duration_ += milliseconds * milliseconds;
35 }
36
AverageMsDuration() const37 int DeathData::AverageMsDuration() const {
38 return static_cast<int>(life_duration_.InMilliseconds() / count_);
39 }
40
StandardDeviation() const41 double DeathData::StandardDeviation() const {
42 double average = AverageMsDuration();
43 double variance = static_cast<float>(square_duration_)/count_
44 - average * average;
45 return sqrt(variance);
46 }
47
48
AddDeathData(const DeathData & other)49 void DeathData::AddDeathData(const DeathData& other) {
50 count_ += other.count_;
51 life_duration_ += other.life_duration_;
52 square_duration_ += other.square_duration_;
53 }
54
Write(std::string * output) const55 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());
63 }
64 }
65
Clear()66 void DeathData::Clear() {
67 count_ = 0;
68 life_duration_ = TimeDelta();
69 square_duration_ = 0;
70 }
71
72 //------------------------------------------------------------------------------
BirthOnThread(const Location & location)73 BirthOnThread::BirthOnThread(const Location& location)
74 : location_(location),
75 birth_thread_(ThreadData::current()) { }
76
77 //------------------------------------------------------------------------------
Births(const Location & location)78 Births::Births(const Location& location)
79 : BirthOnThread(location),
80 birth_count_(1) { }
81
82 //------------------------------------------------------------------------------
83 // ThreadData maintains the central data for all births and death.
84
85 // static
86 ThreadData* ThreadData::first_ = NULL;
87 // static
88 base::Lock ThreadData::list_lock_;
89
90 // static
91 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
92
ThreadData()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();
99 }
100
~ThreadData()101 ThreadData::~ThreadData() {}
102
103 // static
current()104 ThreadData* ThreadData::current() {
105 if (!tls_index_.initialized())
106 return NULL;
107
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;
112 {
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;
122 }
123 } // Release lock.
124 if (too_late_to_create) {
125 delete registry;
126 registry = NULL;
127 } else {
128 tls_index_.Set(registry);
129 }
130 }
131 return registry;
132 }
133
134 // Do mininimal fixups for searching function names.
UnescapeQuery(const std::string & query)135 static std::string UnescapeQuery(const std::string& query) {
136 std::string result;
137 for (size_t i = 0; i < query.size(); i++) {
138 char next = query[i];
139 if ('%' == next && i + 2 < query.size()) {
140 std::string hex = query.substr(i + 1, 2);
141 char replacement = '\0';
142 // Only bother with "<", ">", and " ".
143 if (LowerCaseEqualsASCII(hex, "3c"))
144 replacement ='<';
145 else if (LowerCaseEqualsASCII(hex, "3e"))
146 replacement = '>';
147 else if (hex == "20")
148 replacement = ' ';
149 if (replacement) {
150 next = replacement;
151 i += 2;
152 }
153 }
154 result.push_back(next);
155 }
156 return result;
157 }
158
159 // static
WriteHTML(const std::string & query,std::string * output)160 void ThreadData::WriteHTML(const std::string& query, std::string* output) {
161 if (!ThreadData::IsActive())
162 return; // Not yet initialized.
163
164 DCHECK(ThreadData::current());
165
166 output->append("<html><head><title>About Tasks");
167 std::string escaped_query = UnescapeQuery(query);
168 if (!escaped_query.empty())
169 output->append(" - " + escaped_query);
170 output->append("</title></head><body><pre>");
171
172 DataCollector collected_data; // Gather data.
173 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
174
175 // Data Gathering is complete. Now to sort/process/render.
176 DataCollector::Collection* collection = collected_data.collection();
177
178 // Create filtering and sort comparison object.
179 Comparator comparator;
180 comparator.ParseQuery(escaped_query);
181
182 // Filter out acceptable (matching) instances.
183 DataCollector::Collection match_array;
184 for (DataCollector::Collection::iterator it = collection->begin();
185 it != collection->end(); ++it) {
186 if (comparator.Acceptable(*it))
187 match_array.push_back(*it);
188 }
189
190 comparator.Sort(&match_array);
191
192 WriteHTMLTotalAndSubtotals(match_array, comparator, output);
193
194 comparator.Clear(); // Delete tiebreaker_ instances.
195
196 output->append("</pre>");
197
198 const char* help_string = "The following are the keywords that can be used to"
199 "sort and aggregate the data, or to select data.<br><ul>"
200 "<li><b>count</b> Number of instances seen."
201 "<li><b>duration</b> Duration in ms from construction to descrution."
202 "<li><b>birth</b> Thread on which the task was constructed."
203 "<li><b>death</b> Thread on which the task was run and deleted."
204 "<li><b>file</b> File in which the task was contructed."
205 "<li><b>function</b> Function in which the task was constructed."
206 "<li><b>line</b> Line number of the file in which the task was constructed."
207 "</ul><br>"
208 "As examples:<ul>"
209 "<li><b>about:tasks/file</b> would sort the above data by file, and"
210 " aggregate data on a per-file basis."
211 "<li><b>about:tasks/file=Dns</b> would only list data for tasks constructed"
212 " in a file containing the text |Dns|."
213 "<li><b>about:tasks/birth/death</b> would sort the above list by birth"
214 " thread, and then by death thread, and would aggregate data for each pair"
215 " of lifetime events."
216 "</ul>"
217 " The data can be reset to zero (discarding all births, deaths, etc.) using"
218 " <b>about:tasks/reset</b>. The existing stats will be displayed, but the"
219 " internal stats will be set to zero, and start accumulating afresh. This"
220 " option is very helpful if you only wish to consider tasks created after"
221 " some point in time.<br><br>"
222 "If you wish to monitor Renderer events, be sure to run in --single-process"
223 " mode.";
224 output->append(help_string);
225 output->append("</body></html>");
226 }
227
228 // static
WriteHTMLTotalAndSubtotals(const DataCollector::Collection & match_array,const Comparator & comparator,std::string * output)229 void ThreadData::WriteHTMLTotalAndSubtotals(
230 const DataCollector::Collection& match_array,
231 const Comparator& comparator,
232 std::string* output) {
233 if (!match_array.size()) {
234 output->append("There were no tracked matches.");
235 } else {
236 // Aggregate during printing
237 Aggregation totals;
238 for (size_t i = 0; i < match_array.size(); ++i) {
239 totals.AddDeathSnapshot(match_array[i]);
240 }
241 output->append("Aggregate Stats: ");
242 totals.Write(output);
243 output->append("<hr><hr>");
244
245 Aggregation subtotals;
246 for (size_t i = 0; i < match_array.size(); ++i) {
247 if (0 == i || !comparator.Equivalent(match_array[i - 1],
248 match_array[i])) {
249 // Print group's defining characteristics.
250 comparator.WriteSortGrouping(match_array[i], output);
251 output->append("<br><br>");
252 }
253 comparator.WriteSnapshot(match_array[i], output);
254 output->append("<br>");
255 subtotals.AddDeathSnapshot(match_array[i]);
256 if (i + 1 >= match_array.size() ||
257 !comparator.Equivalent(match_array[i],
258 match_array[i + 1])) {
259 // Print aggregate stats for the group.
260 output->append("<br>");
261 subtotals.Write(output);
262 output->append("<br><hr><br>");
263 subtotals.Clear();
264 }
265 }
266 }
267 }
268
TallyABirth(const Location & location)269 Births* ThreadData::TallyABirth(const Location& location) {
270 {
271 // This shouldn't use the MessageLoop::current() LazyInstance since this
272 // might be used on a non-joinable thread.
273 // http://crbug.com/62728
274 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton;
275 if (!message_loop_) // In case message loop wasn't yet around...
276 message_loop_ = MessageLoop::current(); // Find it now.
277 }
278
279 BirthMap::iterator it = birth_map_.find(location);
280 if (it != birth_map_.end()) {
281 it->second->RecordBirth();
282 return it->second;
283 }
284
285 Births* tracker = new Births(location);
286 // Lock since the map may get relocated now, and other threads sometimes
287 // snapshot it (but they lock before copying it).
288 base::AutoLock lock(lock_);
289 birth_map_[location] = tracker;
290 return tracker;
291 }
292
TallyADeath(const Births & lifetimes,const TimeDelta & duration)293 void ThreadData::TallyADeath(const Births& lifetimes,
294 const TimeDelta& duration) {
295 {
296 // http://crbug.com/62728
297 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton;
298 if (!message_loop_) // In case message loop wasn't yet around...
299 message_loop_ = MessageLoop::current(); // Find it now.
300 }
301
302 DeathMap::iterator it = death_map_.find(&lifetimes);
303 if (it != death_map_.end()) {
304 it->second.RecordDeath(duration);
305 return;
306 }
307
308 base::AutoLock lock(lock_); // Lock since the map may get relocated now.
309 death_map_[&lifetimes].RecordDeath(duration);
310 }
311
312 // static
first()313 ThreadData* ThreadData::first() {
314 base::AutoLock lock(list_lock_);
315 return first_;
316 }
317
ThreadName() const318 const std::string ThreadData::ThreadName() const {
319 if (message_loop_)
320 return message_loop_->thread_name();
321 return "ThreadWithoutMessageLoop";
322 }
323
324 // This may be called from another thread.
SnapshotBirthMap(BirthMap * output) const325 void ThreadData::SnapshotBirthMap(BirthMap *output) const {
326 base::AutoLock lock(lock_);
327 for (BirthMap::const_iterator it = birth_map_.begin();
328 it != birth_map_.end(); ++it)
329 (*output)[it->first] = it->second;
330 }
331
332 // This may be called from another thread.
SnapshotDeathMap(DeathMap * output) const333 void ThreadData::SnapshotDeathMap(DeathMap *output) const {
334 base::AutoLock lock(lock_);
335 for (DeathMap::const_iterator it = death_map_.begin();
336 it != death_map_.end(); ++it)
337 (*output)[it->first] = it->second;
338 }
339
340 // static
ResetAllThreadData()341 void ThreadData::ResetAllThreadData() {
342 ThreadData* my_list = ThreadData::current()->first();
343
344 for (ThreadData* thread_data = my_list;
345 thread_data;
346 thread_data = thread_data->next())
347 thread_data->Reset();
348 }
349
Reset()350 void ThreadData::Reset() {
351 base::AutoLock lock(lock_);
352 for (DeathMap::iterator it = death_map_.begin();
353 it != death_map_.end(); ++it)
354 it->second.Clear();
355 for (BirthMap::iterator it = birth_map_.begin();
356 it != birth_map_.end(); ++it)
357 it->second->Clear();
358 }
359
360 #ifdef OS_WIN
361 // A class used to count down which is accessed by several threads. This is
362 // used to make sure RunOnAllThreads() actually runs a task on the expected
363 // count of threads.
364 class ThreadData::ThreadSafeDownCounter {
365 public:
366 // Constructor sets the count, once and for all.
367 explicit ThreadSafeDownCounter(size_t count);
368
369 // Decrement the count, and return true if we hit zero. Also delete this
370 // instance automatically when we hit zero.
371 bool LastCaller();
372
373 private:
374 size_t remaining_count_;
375 base::Lock lock_; // protect access to remaining_count_.
376 };
377
ThreadSafeDownCounter(size_t count)378 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
379 : remaining_count_(count) {
380 DCHECK_GT(remaining_count_, 0u);
381 }
382
LastCaller()383 bool ThreadData::ThreadSafeDownCounter::LastCaller() {
384 {
385 base::AutoLock lock(lock_);
386 if (--remaining_count_)
387 return false;
388 } // Release lock, so we can delete everything in this instance.
389 delete this;
390 return true;
391 }
392
393 // A Task class that runs a static method supplied, and checks to see if this
394 // is the last tasks instance (on last thread) that will run the method.
395 // IF this is the last run, then the supplied event is signalled.
396 class ThreadData::RunTheStatic : public Task {
397 public:
398 typedef void (*FunctionPointer)();
399 RunTheStatic(FunctionPointer function,
400 HANDLE completion_handle,
401 ThreadSafeDownCounter* counter);
402 // Run the supplied static method, and optionally set the event.
403 void Run();
404
405 private:
406 FunctionPointer function_;
407 HANDLE completion_handle_;
408 // Make sure enough tasks are called before completion is signaled.
409 ThreadSafeDownCounter* counter_;
410
411 DISALLOW_COPY_AND_ASSIGN(RunTheStatic);
412 };
413
RunTheStatic(FunctionPointer function,HANDLE completion_handle,ThreadSafeDownCounter * counter)414 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function,
415 HANDLE completion_handle,
416 ThreadSafeDownCounter* counter)
417 : function_(function),
418 completion_handle_(completion_handle),
419 counter_(counter) {
420 }
421
Run()422 void ThreadData::RunTheStatic::Run() {
423 function_();
424 if (counter_->LastCaller())
425 SetEvent(completion_handle_);
426 }
427
428 // TODO(jar): This should use condition variables, and be cross platform.
RunOnAllThreads(void (* function)())429 void ThreadData::RunOnAllThreads(void (*function)()) {
430 ThreadData* list = first(); // Get existing list.
431
432 std::vector<MessageLoop*> message_loops;
433 for (ThreadData* it = list; it; it = it->next()) {
434 if (current() != it && it->message_loop())
435 message_loops.push_back(it->message_loop());
436 }
437
438 ThreadSafeDownCounter* counter =
439 new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us!
440
441 HANDLE completion_handle = CreateEvent(NULL, false, false, NULL);
442 // Tell all other threads to run.
443 for (size_t i = 0; i < message_loops.size(); ++i)
444 message_loops[i]->PostTask(
445 FROM_HERE, new RunTheStatic(function, completion_handle, counter));
446
447 // Also run Task on our thread.
448 RunTheStatic local_task(function, completion_handle, counter);
449 local_task.Run();
450
451 WaitForSingleObject(completion_handle, INFINITE);
452 int ret_val = CloseHandle(completion_handle);
453 DCHECK(ret_val);
454 }
455 #endif // OS_WIN
456
457 // static
StartTracking(bool status)458 bool ThreadData::StartTracking(bool status) {
459 #ifndef TRACK_ALL_TASK_OBJECTS
460 return false; // Not compiled in.
461 #endif
462
463 if (!status) {
464 base::AutoLock lock(list_lock_);
465 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN);
466 status_ = SHUTDOWN;
467 return true;
468 }
469 base::AutoLock lock(list_lock_);
470 DCHECK_EQ(UNINITIALIZED, status_);
471 CHECK(tls_index_.Initialize(NULL));
472 status_ = ACTIVE;
473 return true;
474 }
475
476 // static
IsActive()477 bool ThreadData::IsActive() {
478 return status_ == ACTIVE;
479 }
480
481 #ifdef OS_WIN
482 // static
ShutdownMultiThreadTracking()483 void ThreadData::ShutdownMultiThreadTracking() {
484 // Using lock, guarantee that no new ThreadData instances will be created.
485 if (!StartTracking(false))
486 return;
487
488 RunOnAllThreads(ShutdownDisablingFurtherTracking);
489
490 // Now the *only* threads that might change the database are the threads with
491 // no messages loops. They might still be adding data to their birth records,
492 // but since no objects are deleted on those threads, there will be no further
493 // access to to cross-thread data.
494 // We could do a cleanup on all threads except for the ones without
495 // MessageLoops, but we won't bother doing cleanup (destruction of data) yet.
496 return;
497 }
498 #endif
499
500 // static
ShutdownSingleThreadedCleanup()501 void ThreadData::ShutdownSingleThreadedCleanup() {
502 // We must be single threaded... but be careful anyway.
503 if (!StartTracking(false))
504 return;
505 ThreadData* thread_data_list;
506 {
507 base::AutoLock lock(list_lock_);
508 thread_data_list = first_;
509 first_ = NULL;
510 }
511
512 while (thread_data_list) {
513 ThreadData* next_thread_data = thread_data_list;
514 thread_data_list = thread_data_list->next();
515
516 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
517 next_thread_data->birth_map_.end() != it; ++it)
518 delete it->second; // Delete the Birth Records.
519 next_thread_data->birth_map_.clear();
520 next_thread_data->death_map_.clear();
521 delete next_thread_data; // Includes all Death Records.
522 }
523
524 CHECK(tls_index_.initialized());
525 tls_index_.Free();
526 DCHECK(!tls_index_.initialized());
527 status_ = UNINITIALIZED;
528 }
529
530 // static
ShutdownDisablingFurtherTracking()531 void ThreadData::ShutdownDisablingFurtherTracking() {
532 // Redundantly set status SHUTDOWN on this thread.
533 if (!StartTracking(false))
534 return;
535 }
536
537 //------------------------------------------------------------------------------
538 // Individual 3-tuple of birth (place and thread) along with death thread, and
539 // the accumulated stats for instances (DeathData).
540
Snapshot(const BirthOnThread & birth_on_thread,const ThreadData & death_thread,const DeathData & death_data)541 Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
542 const ThreadData& death_thread,
543 const DeathData& death_data)
544 : birth_(&birth_on_thread),
545 death_thread_(&death_thread),
546 death_data_(death_data) {
547 }
548
Snapshot(const BirthOnThread & birth_on_thread,int count)549 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
550 : birth_(&birth_on_thread),
551 death_thread_(NULL),
552 death_data_(DeathData(count)) {
553 }
554
DeathThreadName() const555 const std::string Snapshot::DeathThreadName() const {
556 if (death_thread_)
557 return death_thread_->ThreadName();
558 return "Still_Alive";
559 }
560
Write(std::string * output) const561 void Snapshot::Write(std::string* output) const {
562 death_data_.Write(output);
563 base::StringAppendF(output, "%s->%s ",
564 birth_->birth_thread()->ThreadName().c_str(),
565 death_thread_->ThreadName().c_str());
566 birth_->location().Write(true, true, output);
567 }
568
Add(const Snapshot & other)569 void Snapshot::Add(const Snapshot& other) {
570 death_data_.AddDeathData(other.death_data_);
571 }
572
573 //------------------------------------------------------------------------------
574 // DataCollector
575
DataCollector()576 DataCollector::DataCollector() {
577 DCHECK(ThreadData::IsActive());
578
579 // Get an unchanging copy of a ThreadData list.
580 ThreadData* my_list = ThreadData::current()->first();
581
582 count_of_contributing_threads_ = 0;
583 for (ThreadData* thread_data = my_list;
584 thread_data;
585 thread_data = thread_data->next()) {
586 ++count_of_contributing_threads_;
587 }
588
589 // Gather data serially. A different constructor could be used to do in
590 // parallel, and then invoke an OnCompletion task.
591 // This hackish approach *can* get some slighly corrupt tallies, as we are
592 // grabbing values without the protection of a lock, but it has the advantage
593 // of working even with threads that don't have message loops. If a user
594 // sees any strangeness, they can always just run their stats gathering a
595 // second time.
596 // TODO(jar): Provide version that gathers stats safely via PostTask in all
597 // cases where thread_data supplies a message_loop to post to. Be careful to
598 // handle message_loops that are destroyed!?!
599 for (ThreadData* thread_data = my_list;
600 thread_data;
601 thread_data = thread_data->next()) {
602 Append(*thread_data);
603 }
604 }
605
~DataCollector()606 DataCollector::~DataCollector() {
607 }
608
Append(const ThreadData & thread_data)609 void DataCollector::Append(const ThreadData& thread_data) {
610 // Get copy of data (which is done under ThreadData's lock).
611 ThreadData::BirthMap birth_map;
612 thread_data.SnapshotBirthMap(&birth_map);
613 ThreadData::DeathMap death_map;
614 thread_data.SnapshotDeathMap(&death_map);
615
616 // Use our lock to protect our accumulation activity.
617 base::AutoLock lock(accumulation_lock_);
618
619 DCHECK(count_of_contributing_threads_);
620
621 for (ThreadData::DeathMap::const_iterator it = death_map.begin();
622 it != death_map.end(); ++it) {
623 collection_.push_back(Snapshot(*it->first, thread_data, it->second));
624 global_birth_count_[it->first] -= it->first->birth_count();
625 }
626
627 for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
628 it != birth_map.end(); ++it) {
629 global_birth_count_[it->second] += it->second->birth_count();
630 }
631
632 --count_of_contributing_threads_;
633 }
634
collection()635 DataCollector::Collection* DataCollector::collection() {
636 DCHECK(!count_of_contributing_threads_);
637 return &collection_;
638 }
639
AddListOfLivingObjects()640 void DataCollector::AddListOfLivingObjects() {
641 DCHECK(!count_of_contributing_threads_);
642 for (BirthCount::iterator it = global_birth_count_.begin();
643 it != global_birth_count_.end(); ++it) {
644 if (it->second > 0)
645 collection_.push_back(Snapshot(*it->first, it->second));
646 }
647 }
648
649 //------------------------------------------------------------------------------
650 // Aggregation
651
Aggregation()652 Aggregation::Aggregation()
653 : birth_count_(0) {
654 }
655
~Aggregation()656 Aggregation::~Aggregation() {
657 }
658
AddDeathSnapshot(const Snapshot & snapshot)659 void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
660 AddBirth(snapshot.birth());
661 death_threads_[snapshot.death_thread()]++;
662 AddDeathData(snapshot.death_data());
663 }
664
AddBirths(const Births & births)665 void Aggregation::AddBirths(const Births& births) {
666 AddBirth(births);
667 birth_count_ += births.birth_count();
668 }
AddBirth(const BirthOnThread & birth)669 void Aggregation::AddBirth(const BirthOnThread& birth) {
670 AddBirthPlace(birth.location());
671 birth_threads_[birth.birth_thread()]++;
672 }
673
AddBirthPlace(const Location & location)674 void Aggregation::AddBirthPlace(const Location& location) {
675 locations_[location]++;
676 birth_files_[location.file_name()]++;
677 }
678
Write(std::string * output) const679 void Aggregation::Write(std::string* output) const {
680 if (locations_.size() == 1) {
681 locations_.begin()->first.Write(true, true, output);
682 } else {
683 base::StringAppendF(output, "%" PRIuS " Locations. ", locations_.size());
684 if (birth_files_.size() > 1) {
685 base::StringAppendF(output, "%" PRIuS " Files. ", birth_files_.size());
686 } else {
687 base::StringAppendF(output, "All born in %s. ",
688 birth_files_.begin()->first.c_str());
689 }
690 }
691
692 if (birth_threads_.size() > 1) {
693 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ",
694 birth_threads_.size());
695 } else {
696 base::StringAppendF(output, "All born on %s. ",
697 birth_threads_.begin()->first->ThreadName().c_str());
698 }
699
700 if (death_threads_.size() > 1) {
701 base::StringAppendF(output, "%" PRIuS " DeathThreads. ",
702 death_threads_.size());
703 } else {
704 if (death_threads_.begin()->first) {
705 base::StringAppendF(output, "All deleted on %s. ",
706 death_threads_.begin()->first->ThreadName().c_str());
707 } else {
708 output->append("All these objects are still alive.");
709 }
710 }
711
712 if (birth_count_ > 1)
713 base::StringAppendF(output, "Births=%d ", birth_count_);
714
715 DeathData::Write(output);
716 }
717
Clear()718 void Aggregation::Clear() {
719 birth_count_ = 0;
720 birth_files_.clear();
721 locations_.clear();
722 birth_threads_.clear();
723 DeathData::Clear();
724 death_threads_.clear();
725 }
726
727 //------------------------------------------------------------------------------
728 // Comparison object for sorting.
729
Comparator()730 Comparator::Comparator()
731 : selector_(NIL),
732 tiebreaker_(NULL),
733 combined_selectors_(0),
734 use_tiebreaker_for_sort_only_(false) {}
735
Clear()736 void Comparator::Clear() {
737 if (tiebreaker_) {
738 tiebreaker_->Clear();
739 delete tiebreaker_;
740 tiebreaker_ = NULL;
741 }
742 use_tiebreaker_for_sort_only_ = false;
743 selector_ = NIL;
744 }
745
operator ()(const Snapshot & left,const Snapshot & right) const746 bool Comparator::operator()(const Snapshot& left,
747 const Snapshot& right) const {
748 switch (selector_) {
749 case BIRTH_THREAD:
750 if (left.birth_thread() != right.birth_thread() &&
751 left.birth_thread()->ThreadName() !=
752 right.birth_thread()->ThreadName())
753 return left.birth_thread()->ThreadName() <
754 right.birth_thread()->ThreadName();
755 break;
756
757 case DEATH_THREAD:
758 if (left.death_thread() != right.death_thread() &&
759 left.DeathThreadName() !=
760 right.DeathThreadName()) {
761 if (!left.death_thread())
762 return true;
763 if (!right.death_thread())
764 return false;
765 return left.DeathThreadName() <
766 right.DeathThreadName();
767 }
768 break;
769
770 case BIRTH_FILE:
771 if (left.location().file_name() != right.location().file_name()) {
772 int comp = strcmp(left.location().file_name(),
773 right.location().file_name());
774 if (comp)
775 return 0 > comp;
776 }
777 break;
778
779 case BIRTH_FUNCTION:
780 if (left.location().function_name() != right.location().function_name()) {
781 int comp = strcmp(left.location().function_name(),
782 right.location().function_name());
783 if (comp)
784 return 0 > comp;
785 }
786 break;
787
788 case BIRTH_LINE:
789 if (left.location().line_number() != right.location().line_number())
790 return left.location().line_number() <
791 right.location().line_number();
792 break;
793
794 case COUNT:
795 if (left.count() != right.count())
796 return left.count() > right.count(); // Sort large at front of vector.
797 break;
798
799 case AVERAGE_DURATION:
800 if (!left.count() || !right.count())
801 break;
802 if (left.AverageMsDuration() != right.AverageMsDuration())
803 return left.AverageMsDuration() > right.AverageMsDuration();
804 break;
805
806 default:
807 break;
808 }
809 if (tiebreaker_)
810 return tiebreaker_->operator()(left, right);
811 return false;
812 }
813
Sort(DataCollector::Collection * collection) const814 void Comparator::Sort(DataCollector::Collection* collection) const {
815 std::sort(collection->begin(), collection->end(), *this);
816 }
817
Equivalent(const Snapshot & left,const Snapshot & right) const818 bool Comparator::Equivalent(const Snapshot& left,
819 const Snapshot& right) const {
820 switch (selector_) {
821 case BIRTH_THREAD:
822 if (left.birth_thread() != right.birth_thread() &&
823 left.birth_thread()->ThreadName() !=
824 right.birth_thread()->ThreadName())
825 return false;
826 break;
827
828 case DEATH_THREAD:
829 if (left.death_thread() != right.death_thread() &&
830 left.DeathThreadName() != right.DeathThreadName())
831 return false;
832 break;
833
834 case BIRTH_FILE:
835 if (left.location().file_name() != right.location().file_name()) {
836 int comp = strcmp(left.location().file_name(),
837 right.location().file_name());
838 if (comp)
839 return false;
840 }
841 break;
842
843 case BIRTH_FUNCTION:
844 if (left.location().function_name() != right.location().function_name()) {
845 int comp = strcmp(left.location().function_name(),
846 right.location().function_name());
847 if (comp)
848 return false;
849 }
850 break;
851
852 case COUNT:
853 if (left.count() != right.count())
854 return false;
855 break;
856
857 case AVERAGE_DURATION:
858 if (left.life_duration() != right.life_duration())
859 return false;
860 break;
861
862 default:
863 break;
864 }
865 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
866 return tiebreaker_->Equivalent(left, right);
867 return true;
868 }
869
Acceptable(const Snapshot & sample) const870 bool Comparator::Acceptable(const Snapshot& sample) const {
871 if (required_.size()) {
872 switch (selector_) {
873 case BIRTH_THREAD:
874 if (sample.birth_thread()->ThreadName().find(required_) ==
875 std::string::npos)
876 return false;
877 break;
878
879 case DEATH_THREAD:
880 if (sample.DeathThreadName().find(required_) == std::string::npos)
881 return false;
882 break;
883
884 case BIRTH_FILE:
885 if (!strstr(sample.location().file_name(), required_.c_str()))
886 return false;
887 break;
888
889 case BIRTH_FUNCTION:
890 if (!strstr(sample.location().function_name(), required_.c_str()))
891 return false;
892 break;
893
894 default:
895 break;
896 }
897 }
898 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
899 return tiebreaker_->Acceptable(sample);
900 return true;
901 }
902
SetTiebreaker(Selector selector,const std::string & required)903 void Comparator::SetTiebreaker(Selector selector, const std::string& required) {
904 if (selector == selector_ || NIL == selector)
905 return;
906 combined_selectors_ |= selector;
907 if (NIL == selector_) {
908 selector_ = selector;
909 if (required.size())
910 required_ = required;
911 return;
912 }
913 if (tiebreaker_) {
914 if (use_tiebreaker_for_sort_only_) {
915 Comparator* temp = new Comparator;
916 temp->tiebreaker_ = tiebreaker_;
917 tiebreaker_ = temp;
918 }
919 } else {
920 tiebreaker_ = new Comparator;
921 DCHECK(!use_tiebreaker_for_sort_only_);
922 }
923 tiebreaker_->SetTiebreaker(selector, required);
924 }
925
IsGroupedBy(Selector selector) const926 bool Comparator::IsGroupedBy(Selector selector) const {
927 return 0 != (selector & combined_selectors_);
928 }
929
SetSubgroupTiebreaker(Selector selector)930 void Comparator::SetSubgroupTiebreaker(Selector selector) {
931 if (selector == selector_ || NIL == selector)
932 return;
933 if (!tiebreaker_) {
934 use_tiebreaker_for_sort_only_ = true;
935 tiebreaker_ = new Comparator;
936 tiebreaker_->SetTiebreaker(selector, "");
937 } else {
938 tiebreaker_->SetSubgroupTiebreaker(selector);
939 }
940 }
941
ParseKeyphrase(const std::string & key_phrase)942 void Comparator::ParseKeyphrase(const std::string& key_phrase) {
943 typedef std::map<const std::string, Selector> KeyMap;
944 static KeyMap key_map;
945 static bool initialized = false;
946 if (!initialized) {
947 initialized = true;
948 // Sorting and aggretation keywords, which specify how to sort the data, or
949 // can specify a required match from the specified field in the record.
950 key_map["count"] = COUNT;
951 key_map["duration"] = AVERAGE_DURATION;
952 key_map["birth"] = BIRTH_THREAD;
953 key_map["death"] = DEATH_THREAD;
954 key_map["file"] = BIRTH_FILE;
955 key_map["function"] = BIRTH_FUNCTION;
956 key_map["line"] = BIRTH_LINE;
957
958 // Immediate commands that do not involve setting sort order.
959 key_map["reset"] = RESET_ALL_DATA;
960 }
961
962 std::string required;
963 // Watch for: "sort_key=value" as we parse.
964 size_t equal_offset = key_phrase.find('=', 0);
965 if (key_phrase.npos != equal_offset) {
966 // There is a value that must be matched for the data to display.
967 required = key_phrase.substr(equal_offset + 1, key_phrase.npos);
968 }
969 std::string keyword(key_phrase.substr(0, equal_offset));
970 keyword = StringToLowerASCII(keyword);
971 KeyMap::iterator it = key_map.find(keyword);
972 if (key_map.end() == it)
973 return; // Unknown keyword.
974 if (it->second == RESET_ALL_DATA)
975 ThreadData::ResetAllThreadData();
976 else
977 SetTiebreaker(key_map[keyword], required);
978 }
979
ParseQuery(const std::string & query)980 bool Comparator::ParseQuery(const std::string& query) {
981 // Parse each keyphrase between consecutive slashes.
982 for (size_t i = 0; i < query.size();) {
983 size_t slash_offset = query.find('/', i);
984 ParseKeyphrase(query.substr(i, slash_offset - i));
985 if (query.npos == slash_offset)
986 break;
987 i = slash_offset + 1;
988 }
989
990 // Select subgroup ordering (if we want to display the subgroup)
991 SetSubgroupTiebreaker(COUNT);
992 SetSubgroupTiebreaker(AVERAGE_DURATION);
993 SetSubgroupTiebreaker(BIRTH_THREAD);
994 SetSubgroupTiebreaker(DEATH_THREAD);
995 SetSubgroupTiebreaker(BIRTH_FUNCTION);
996 SetSubgroupTiebreaker(BIRTH_FILE);
997 SetSubgroupTiebreaker(BIRTH_LINE);
998
999 return true;
1000 }
1001
WriteSortGrouping(const Snapshot & sample,std::string * output) const1002 bool Comparator::WriteSortGrouping(const Snapshot& sample,
1003 std::string* output) const {
1004 bool wrote_data = false;
1005 switch (selector_) {
1006 case BIRTH_THREAD:
1007 base::StringAppendF(output, "All new on %s ",
1008 sample.birth_thread()->ThreadName().c_str());
1009 wrote_data = true;
1010 break;
1011
1012 case DEATH_THREAD:
1013 if (sample.death_thread()) {
1014 base::StringAppendF(output, "All deleted on %s ",
1015 sample.DeathThreadName().c_str());
1016 } else {
1017 output->append("All still alive ");
1018 }
1019 wrote_data = true;
1020 break;
1021
1022 case BIRTH_FILE:
1023 base::StringAppendF(output, "All born in %s ",
1024 sample.location().file_name());
1025 break;
1026
1027 case BIRTH_FUNCTION:
1028 output->append("All born in ");
1029 sample.location().WriteFunctionName(output);
1030 output->push_back(' ');
1031 break;
1032
1033 default:
1034 break;
1035 }
1036 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
1037 wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
1038 }
1039 return wrote_data;
1040 }
1041
WriteSnapshot(const Snapshot & sample,std::string * output) const1042 void Comparator::WriteSnapshot(const Snapshot& sample,
1043 std::string* output) const {
1044 sample.death_data().Write(output);
1045 if (!(combined_selectors_ & BIRTH_THREAD) ||
1046 !(combined_selectors_ & DEATH_THREAD))
1047 base::StringAppendF(output, "%s->%s ",
1048 (combined_selectors_ & BIRTH_THREAD) ? "*" :
1049 sample.birth().birth_thread()->ThreadName().c_str(),
1050 (combined_selectors_ & DEATH_THREAD) ? "*" :
1051 sample.DeathThreadName().c_str());
1052 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
1053 !(combined_selectors_ & BIRTH_FUNCTION),
1054 output);
1055 }
1056
1057 } // namespace tracked_objects
1058