• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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