Home | History | Annotate | Download | only in base
      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 
     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 
     37 int DeathData::AverageMsDuration() const {
     38   return static_cast<int>(life_duration_.InMilliseconds() / count_);
     39 }
     40 
     41 double DeathData::StandardDeviation() const {
     42   double average = AverageMsDuration();
     43   double variance = static_cast<float>(square_duration_)/count_
     44                     - average * average;
     45   return sqrt(variance);
     46 }
     47 
     48 
     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 
     55 void DeathData::Write(std::string* output) const {
     56   if (!count_)
     57     return;
     58   if (1 == count_) {
     59     base::StringAppendF(output, "(1)Life in %dms ", AverageMsDuration());
     60   } else {
     61     base::StringAppendF(output, "(%d)Lives %dms/life ",
     62                         count_, AverageMsDuration());
     63   }
     64 }
     65 
     66 void DeathData::Clear() {
     67   count_ = 0;
     68   life_duration_ = TimeDelta();
     69   square_duration_ = 0;
     70 }
     71 
     72 //------------------------------------------------------------------------------
     73 BirthOnThread::BirthOnThread(const Location& location)
     74     : location_(location),
     75       birth_thread_(ThreadData::current()) { }
     76 
     77 //------------------------------------------------------------------------------
     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 
     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 
    101 ThreadData::~ThreadData() {}
    102 
    103 // static
    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.
    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
    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
    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 
    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 
    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
    313 ThreadData* ThreadData::first() {
    314   base::AutoLock lock(list_lock_);
    315   return first_;
    316 }
    317 
    318 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.
    325 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.
    333 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
    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 
    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 
    378 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
    379     : remaining_count_(count) {
    380   DCHECK_GT(remaining_count_, 0u);
    381 }
    382 
    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 
    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 
    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.
    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
    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
    477 bool ThreadData::IsActive() {
    478   return status_ == ACTIVE;
    479 }
    480 
    481 #ifdef OS_WIN
    482 // static
    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
    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
    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 
    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 
    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 
    555 const std::string Snapshot::DeathThreadName() const {
    556   if (death_thread_)
    557     return death_thread_->ThreadName();
    558   return "Still_Alive";
    559 }
    560 
    561 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 
    569 void Snapshot::Add(const Snapshot& other) {
    570   death_data_.AddDeathData(other.death_data_);
    571 }
    572 
    573 //------------------------------------------------------------------------------
    574 // DataCollector
    575 
    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 
    606 DataCollector::~DataCollector() {
    607 }
    608 
    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 
    635 DataCollector::Collection* DataCollector::collection() {
    636   DCHECK(!count_of_contributing_threads_);
    637   return &collection_;
    638 }
    639 
    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 
    652 Aggregation::Aggregation()
    653     : birth_count_(0) {
    654 }
    655 
    656 Aggregation::~Aggregation() {
    657 }
    658 
    659 void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
    660   AddBirth(snapshot.birth());
    661   death_threads_[snapshot.death_thread()]++;
    662   AddDeathData(snapshot.death_data());
    663 }
    664 
    665 void Aggregation::AddBirths(const Births& births) {
    666   AddBirth(births);
    667   birth_count_ += births.birth_count();
    668 }
    669 void Aggregation::AddBirth(const BirthOnThread& birth) {
    670   AddBirthPlace(birth.location());
    671   birth_threads_[birth.birth_thread()]++;
    672 }
    673 
    674 void Aggregation::AddBirthPlace(const Location& location) {
    675   locations_[location]++;
    676   birth_files_[location.file_name()]++;
    677 }
    678 
    679 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 
    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 
    730 Comparator::Comparator()
    731     : selector_(NIL),
    732       tiebreaker_(NULL),
    733       combined_selectors_(0),
    734       use_tiebreaker_for_sort_only_(false) {}
    735 
    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 
    746 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 
    814 void Comparator::Sort(DataCollector::Collection* collection) const {
    815   std::sort(collection->begin(), collection->end(), *this);
    816 }
    817 
    818 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 
    870 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 
    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 
    926 bool Comparator::IsGroupedBy(Selector selector) const {
    927   return 0 != (selector & combined_selectors_);
    928 }
    929 
    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 
    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 
    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 
   1002 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 
   1042 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