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