ipc/chromium/src/base/tracked_objects.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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.
     5 #include "base/tracked_objects.h"
     7 #include <math.h>
     9 #include "base/string_util.h"
    11 using base::TimeDelta;
    13 namespace tracked_objects {
    15 // A TLS slot to the TrackRegistry for the current thread.
    16 // static
    17 TLSSlot ThreadData::tls_index_(base::LINKER_INITIALIZED);
    19 //------------------------------------------------------------------------------
    20 // Death data tallies durations when a death takes place.
    22 void DeathData::RecordDeath(const TimeDelta& duration) {
    23   ++count_;
    24   life_duration_ += duration;
    25   int64_t milliseconds = duration.InMilliseconds();
    26   square_duration_ += milliseconds * milliseconds;
    27 }
    29 int DeathData::AverageMsDuration() const {
    30   return static_cast<int>(life_duration_.InMilliseconds() / count_);
    31 }
    33 double DeathData::StandardDeviation() const {
    34   double average = AverageMsDuration();
    35   double variance = static_cast<float>(square_duration_)/count_
    36                     - average * average;
    37   return sqrt(variance);
    38 }
    41 void DeathData::AddDeathData(const DeathData& other) {
    42   count_ += other.count_;
    43   life_duration_ += other.life_duration_;
    44   square_duration_ += other.square_duration_;
    45 }
    47 void DeathData::Write(std::string* output) const {
    48   if (!count_)
    49     return;
    50   if (1 == count_)
    51     StringAppendF(output, "(1)Life in %dms ", AverageMsDuration());
    52   else
    53     StringAppendF(output, "(%d)Lives %dms/life ", count_, AverageMsDuration());
    54 }
    56 void DeathData::Clear() {
    57   count_ = 0;
    58   life_duration_ = TimeDelta();
    59   square_duration_ = 0;
    60 }
    62 //------------------------------------------------------------------------------
    64 BirthOnThread::BirthOnThread(const Location& location)
    65     : location_(location),
    66       birth_thread_(ThreadData::current()) { }
    68 //------------------------------------------------------------------------------
    69 Births::Births(const Location& location)
    70     : BirthOnThread(location),
    71       birth_count_(0) { }
    73 //------------------------------------------------------------------------------
    74 // ThreadData maintains the central data for all births and death.
    76 // static
    77 ThreadData* ThreadData::first_ = NULL;
    78 // static
    79 Lock ThreadData::list_lock_;
    81 // static
    82 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
    84 ThreadData::ThreadData() : next_(NULL), message_loop_(MessageLoop::current()) {}
    86 // static
    87 ThreadData* ThreadData::current() {
    88   if (!tls_index_.initialized())
    89     return NULL;
    91   ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get());
    92   if (!registry) {
    93     // We have to create a new registry for ThreadData.
    94     bool too_late_to_create = false;
    95     {
    96       registry = new ThreadData;
    97       AutoLock lock(list_lock_);
    98       // Use lock to insure we have most recent status.
    99       if (!IsActive()) {
   100         too_late_to_create = true;
   101       } else {
   102         // Use lock to insert into list.
   103         registry->next_ = first_;
   104         first_ = registry;
   105       }
   106     }  // Release lock.
   107     if (too_late_to_create) {
   108       delete registry;
   109       registry = NULL;
   110     } else {
   111       tls_index_.Set(registry);
   112     }
   113   }
   114   return registry;
   115 }
   117 Births* ThreadData::FindLifetime(const Location& location) {
   118   if (!message_loop_)  // In case message loop wasn't yet around...
   119     message_loop_ = MessageLoop::current();  // Find it now.
   121   BirthMap::iterator it = birth_map_.find(location);
   122   if (it != birth_map_.end())
   123     return it->second;
   124   Births* tracker = new Births(location);
   126   // Lock since the map may get relocated now, and other threads sometimes
   127   // snapshot it (but they lock before copying it).
   128   AutoLock lock(lock_);
   129   birth_map_[location] = tracker;
   130   return tracker;
   131 }
   133 void ThreadData::TallyADeath(const Births& lifetimes,
   134                              const TimeDelta& duration) {
   135   if (!message_loop_)  // In case message loop wasn't yet around...
   136     message_loop_ = MessageLoop::current();  // Find it now.
   138   DeathMap::iterator it = death_map_.find(&lifetimes);
   139   if (it != death_map_.end()) {
   140     it->second.RecordDeath(duration);
   141     return;
   142   }
   144   AutoLock lock(lock_);  // Lock since the map may get relocated now.
   145   death_map_[&lifetimes].RecordDeath(duration);
   146 }
   148 // static
   149 ThreadData* ThreadData::first() {
   150   AutoLock lock(list_lock_);
   151   return first_;
   152 }
   154 const std::string ThreadData::ThreadName() const {
   155   if (message_loop_)
   156     return message_loop_->thread_name();
   157   return "ThreadWithoutMessageLoop";
   158 }
   160 // This may be called from another thread.
   161 void ThreadData::SnapshotBirthMap(BirthMap *output) const {
   162   AutoLock lock(*const_cast<Lock*>(&lock_));
   163   for (BirthMap::const_iterator it = birth_map_.begin();
   164        it != birth_map_.end(); ++it)
   165     (*output)[it->first] = it->second;
   166 }
   168 // This may be called from another thread.
   169 void ThreadData::SnapshotDeathMap(DeathMap *output) const {
   170   AutoLock lock(*const_cast<Lock*>(&lock_));
   171   for (DeathMap::const_iterator it = death_map_.begin();
   172        it != death_map_.end(); ++it)
   173     (*output)[it->first] = it->second;
   174 }
   176 #ifdef OS_WIN
   177 void ThreadData::RunOnAllThreads(void (*function)()) {
   178   ThreadData* list = first();  // Get existing list.
   180   std::vector<MessageLoop*> message_loops;
   181   for (ThreadData* it = list; it; it = it->next()) {
   182     if (current() != it && it->message_loop())
   183       message_loops.push_back(it->message_loop());
   184   }
   186   ThreadSafeDownCounter* counter =
   187     new ThreadSafeDownCounter(message_loops.size() + 1);  // Extra one for us!
   189   HANDLE completion_handle = CreateEvent(NULL, false, false, NULL);
   190   // Tell all other threads to run.
   191   for (size_t i = 0; i < message_loops.size(); ++i)
   192     message_loops[i]->PostTask(FROM_HERE,
   193         new RunTheStatic(function, completion_handle, counter));
   195   // Also run Task on our thread.
   196   RunTheStatic local_task(function, completion_handle, counter);
   197   local_task.Run();
   199   WaitForSingleObject(completion_handle, INFINITE);
   200   int ret_val = CloseHandle(completion_handle);
   201   DCHECK(ret_val);
   202 }
   203 #endif
   205 // static
   206 bool ThreadData::StartTracking(bool status) {
   207 #ifndef TRACK_ALL_TASK_OBJECTS
   208   return false;  // Not compiled in.
   209 #else
   210   if (!status) {
   211     AutoLock lock(list_lock_);
   212     DCHECK(status_ == ACTIVE || status_ == SHUTDOWN);
   213     status_ = SHUTDOWN;
   214     return true;
   215   }
   216   AutoLock lock(list_lock_);
   217   DCHECK(status_ == UNINITIALIZED);
   218   CHECK(tls_index_.Initialize(NULL));
   219   status_ = ACTIVE;
   220   return true;
   221 #endif
   222 }
   224 // static
   225 bool ThreadData::IsActive() {
   226   return status_ == ACTIVE;
   227 }
   229 #ifdef OS_WIN
   230 // static
   231 void ThreadData::ShutdownMultiThreadTracking() {
   232   // Using lock, guarantee that no new ThreadData instances will be created.
   233   if (!StartTracking(false))
   234     return;
   236   RunOnAllThreads(ShutdownDisablingFurtherTracking);
   238   // Now the *only* threads that might change the database are the threads with
   239   // no messages loops.  They might still be adding data to their birth records,
   240   // but since no objects are deleted on those threads, there will be no further
   241   // access to to cross-thread data.
   242   // We could do a cleanup on all threads except for the ones without
   243   // MessageLoops, but we won't bother doing cleanup (destruction of data) yet.
   244   return;
   245 }
   246 #endif
   248 // static
   249 void ThreadData::ShutdownSingleThreadedCleanup() {
   250   // We must be single threaded... but be careful anyway.
   251   if (!StartTracking(false))
   252     return;
   253   ThreadData* thread_data_list;
   254   {
   255     AutoLock lock(list_lock_);
   256     thread_data_list = first_;
   257     first_ = NULL;
   258   }
   260   while (thread_data_list) {
   261     ThreadData* next_thread_data = thread_data_list;
   262     thread_data_list = thread_data_list->next();
   264     for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
   265          next_thread_data->birth_map_.end() != it; ++it)
   266       delete it->second;  // Delete the Birth Records.
   267     next_thread_data->birth_map_.clear();
   268     next_thread_data->death_map_.clear();
   269     delete next_thread_data;  // Includes all Death Records.
   270   }
   272   CHECK(tls_index_.initialized());
   273   tls_index_.Free();
   274   DCHECK(!tls_index_.initialized());
   275   status_ = UNINITIALIZED;
   276 }
   278 // static
   279 void ThreadData::ShutdownDisablingFurtherTracking() {
   280   // Redundantly set status SHUTDOWN on this thread.
   281   if (!StartTracking(false))
   282     return;
   283 }
   286 //------------------------------------------------------------------------------
   288 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
   289     : remaining_count_(count) {
   290   DCHECK(remaining_count_ > 0);
   291 }
   293 bool ThreadData::ThreadSafeDownCounter::LastCaller() {
   294   {
   295     AutoLock lock(lock_);
   296     if (--remaining_count_)
   297       return false;
   298   }  // Release lock, so we can delete everything in this instance.
   299   delete this;
   300   return true;
   301 }
   303 //------------------------------------------------------------------------------
   304 #ifdef OS_WIN
   305 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function,
   306                                        HANDLE completion_handle,
   307                                        ThreadSafeDownCounter* counter)
   308     : function_(function),
   309       completion_handle_(completion_handle),
   310       counter_(counter) {
   311 }
   313 void ThreadData::RunTheStatic::Run() {
   314   function_();
   315   if (counter_->LastCaller())
   316     SetEvent(completion_handle_);
   317 }
   318 #endif
   320 //------------------------------------------------------------------------------
   321 // Individual 3-tuple of birth (place and thread) along with death thread, and
   322 // the accumulated stats for instances (DeathData).
   324 Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
   325                    const ThreadData& death_thread,
   326                    const DeathData& death_data)
   327     : birth_(&birth_on_thread),
   328       death_thread_(&death_thread),
   329       death_data_(death_data) {
   330 }
   332 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
   333     : birth_(&birth_on_thread),
   334       death_thread_(NULL),
   335       death_data_(DeathData(count)) {
   336 }
   338 const std::string Snapshot::DeathThreadName() const {
   339   if (death_thread_)
   340     return death_thread_->ThreadName();
   341   return "Still_Alive";
   342 }
   344 void Snapshot::Write(std::string* output) const {
   345   death_data_.Write(output);
   346   StringAppendF(output, "%s->%s ",
   347                 birth_->birth_thread()->ThreadName().c_str(),
   348                 death_thread_->ThreadName().c_str());
   349   birth_->location().Write(true, true, output);
   350 }
   352 void Snapshot::Add(const Snapshot& other) {
   353   death_data_.AddDeathData(other.death_data_);
   354 }
   356 //------------------------------------------------------------------------------
   357 // DataCollector
   359 DataCollector::DataCollector() {
   360   DCHECK(ThreadData::IsActive());
   362   ThreadData* my_list = ThreadData::current()->first();
   364   count_of_contributing_threads_ = 0;
   365   for (ThreadData* thread_data = my_list;
   366        thread_data;
   367        thread_data = thread_data->next()) {
   368     ++count_of_contributing_threads_;
   369   }
   371   // Gather data serially.  A different constructor could be used to do in
   372   // parallel, and then invoke an OnCompletion task.
   373   for (ThreadData* thread_data = my_list;
   374        thread_data;
   375        thread_data = thread_data->next()) {
   376     Append(*thread_data);
   377   }
   378 }
   380 void DataCollector::Append(const ThreadData& thread_data) {
   381   // Get copy of data (which is done under ThreadData's lock).
   382   ThreadData::BirthMap birth_map;
   383   thread_data.SnapshotBirthMap(&birth_map);
   384   ThreadData::DeathMap death_map;
   385   thread_data.SnapshotDeathMap(&death_map);
   387   // Use our lock to protect our accumulation activity.
   388   AutoLock lock(accumulation_lock_);
   390   DCHECK(count_of_contributing_threads_);
   392   for (ThreadData::DeathMap::const_iterator it = death_map.begin();
   393        it != death_map.end(); ++it) {
   394     collection_.push_back(Snapshot(*it->first, thread_data, it->second));
   395     global_birth_count_[it->first] -= it->first->birth_count();
   396   }
   398   for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
   399        it != birth_map.end(); ++it) {
   400     global_birth_count_[it->second] += it->second->birth_count();
   401   }
   403   --count_of_contributing_threads_;
   404 }
   406 DataCollector::Collection* DataCollector::collection() {
   407   DCHECK(!count_of_contributing_threads_);
   408   return &collection_;
   409 }
   411 void DataCollector::AddListOfLivingObjects() {
   412   DCHECK(!count_of_contributing_threads_);
   413   for (BirthCount::iterator it = global_birth_count_.begin();
   414        it != global_birth_count_.end(); ++it) {
   415     if (it->second > 0)
   416       collection_.push_back(Snapshot(*it->first, it->second));
   417   }
   418 }
   420 //------------------------------------------------------------------------------
   421 // Aggregation
   423 void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
   424   AddBirth(snapshot.birth());
   425   death_threads_[snapshot.death_thread()]++;
   426   AddDeathData(snapshot.death_data());
   427 }
   429 void Aggregation::AddBirths(const Births& births) {
   430   AddBirth(births);
   431   birth_count_ += births.birth_count();
   432 }
   433 void Aggregation::AddBirth(const BirthOnThread& birth) {
   434   AddBirthPlace(birth.location());
   435   birth_threads_[birth.birth_thread()]++;
   436 }
   438 void Aggregation::AddBirthPlace(const Location& location) {
   439   locations_[location]++;
   440   birth_files_[location.file_name()]++;
   441 }
   443 void Aggregation::Write(std::string* output) const {
   444   if (locations_.size() == 1) {
   445     locations_.begin()->first.Write(true, true, output);
   446   } else {
   447     StringAppendF(output, "%d Locations. ", locations_.size());
   448     if (birth_files_.size() > 1)
   449       StringAppendF(output, "%d Files. ", birth_files_.size());
   450     else
   451       StringAppendF(output, "All born in %s. ",
   452                     birth_files_.begin()->first.c_str());
   453   }
   455   if (birth_threads_.size() > 1)
   456     StringAppendF(output, "%d BirthingThreads. ", birth_threads_.size());
   457   else
   458     StringAppendF(output, "All born on %s. ",
   459                   birth_threads_.begin()->first->ThreadName().c_str());
   461   if (death_threads_.size() > 1) {
   462     StringAppendF(output, "%d DeathThreads. ", death_threads_.size());
   463   } else {
   464     if (death_threads_.begin()->first)
   465       StringAppendF(output, "All deleted on %s. ",
   466                   death_threads_.begin()->first->ThreadName().c_str());
   467     else
   468       output->append("All these objects are still alive.");
   469   }
   471   if (birth_count_ > 1)
   472     StringAppendF(output, "Births=%d ", birth_count_);
   474   DeathData::Write(output);
   475 }
   477 void Aggregation::Clear() {
   478   birth_count_ = 0;
   479   birth_files_.clear();
   480   locations_.clear();
   481   birth_threads_.clear();
   482   DeathData::Clear();
   483   death_threads_.clear();
   484 }
   486 //------------------------------------------------------------------------------
   487 // Comparison object for sorting.
   489 Comparator::Comparator()
   490     : selector_(NIL),
   491       tiebreaker_(NULL),
   492       combined_selectors_(0),
   493       use_tiebreaker_for_sort_only_(false) {}
   495 void Comparator::Clear() {
   496   if (tiebreaker_) {
   497     tiebreaker_->Clear();
   498     delete tiebreaker_;
   499     tiebreaker_ = NULL;
   500   }
   501   use_tiebreaker_for_sort_only_ = false;
   502   selector_ = NIL;
   503 }
   505 void Comparator::Sort(DataCollector::Collection* collection) const {
   506   std::sort(collection->begin(), collection->end(), *this);
   507 }
   510 bool Comparator::operator()(const Snapshot& left,
   511                             const Snapshot& right) const {
   512   switch (selector_) {
   513     case BIRTH_THREAD:
   514       if (left.birth_thread() != right.birth_thread() &&
   515           left.birth_thread()->ThreadName() !=
   516           right.birth_thread()->ThreadName())
   517         return left.birth_thread()->ThreadName() <
   518             right.birth_thread()->ThreadName();
   519       break;
   521     case DEATH_THREAD:
   522       if (left.death_thread() != right.death_thread() &&
   523           left.DeathThreadName() !=
   524           right.DeathThreadName()) {
   525         if (!left.death_thread())
   526           return true;
   527         if (!right.death_thread())
   528           return false;
   529         return left.DeathThreadName() <
   530              right.DeathThreadName();
   531       }
   532       break;
   534     case BIRTH_FILE:
   535       if (left.location().file_name() != right.location().file_name()) {
   536         int comp = strcmp(left.location().file_name(),
   537                           right.location().file_name());
   538         if (comp)
   539           return 0 > comp;
   540       }
   541       break;
   543     case BIRTH_FUNCTION:
   544       if (left.location().function_name() != right.location().function_name()) {
   545         int comp = strcmp(left.location().function_name(),
   546                           right.location().function_name());
   547         if (comp)
   548           return 0 > comp;
   549       }
   550       break;
   552     case BIRTH_LINE:
   553       if (left.location().line_number() != right.location().line_number())
   554         return left.location().line_number() <
   555             right.location().line_number();
   556       break;
   558     case COUNT:
   559       if (left.count() != right.count())
   560         return left.count() > right.count();  // Sort large at front of vector.
   561       break;
   563     case AVERAGE_DURATION:
   564       if (left.AverageMsDuration() != right.AverageMsDuration())
   565         return left.AverageMsDuration() > right.AverageMsDuration();
   566       break;
   568     default:
   569       break;
   570   }
   571   if (tiebreaker_)
   572     return tiebreaker_->operator()(left, right);
   573   return false;
   574 }
   576 bool Comparator::Equivalent(const Snapshot& left,
   577                             const Snapshot& right) const {
   578   switch (selector_) {
   579     case BIRTH_THREAD:
   580       if (left.birth_thread() != right.birth_thread() &&
   581           left.birth_thread()->ThreadName() !=
   582               right.birth_thread()->ThreadName())
   583         return false;
   584       break;
   586     case DEATH_THREAD:
   587       if (left.death_thread() != right.death_thread() &&
   588           left.DeathThreadName() != right.DeathThreadName())
   589         return false;
   590       break;
   592     case BIRTH_FILE:
   593       if (left.location().file_name() != right.location().file_name()) {
   594         int comp = strcmp(left.location().file_name(),
   595                           right.location().file_name());
   596         if (comp)
   597           return false;
   598       }
   599       break;
   601     case BIRTH_FUNCTION:
   602       if (left.location().function_name() != right.location().function_name()) {
   603         int comp = strcmp(left.location().function_name(),
   604                           right.location().function_name());
   605         if (comp)
   606           return false;
   607       }
   608       break;
   610     case COUNT:
   611       if (left.count() != right.count())
   612         return false;
   613       break;
   615     case AVERAGE_DURATION:
   616       if (left.life_duration() != right.life_duration())
   617         return false;
   618       break;
   620     default:
   621       break;
   622   }
   623   if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
   624     return tiebreaker_->Equivalent(left, right);
   625   return true;
   626 }
   628 bool Comparator::Acceptable(const Snapshot& sample) const {
   629   if (required_.size()) {
   630     switch (selector_) {
   631       case BIRTH_THREAD:
   632         if (sample.birth_thread()->ThreadName().find(required_) ==
   633             std::string::npos)
   634           return false;
   635         break;
   637       case DEATH_THREAD:
   638         if (sample.DeathThreadName().find(required_) == std::string::npos)
   639           return false;
   640         break;
   642       case BIRTH_FILE:
   643         if (!strstr(sample.location().file_name(), required_.c_str()))
   644           return false;
   645         break;
   647       case BIRTH_FUNCTION:
   648         if (!strstr(sample.location().function_name(), required_.c_str()))
   649           return false;
   650         break;
   652       default:
   653         break;
   654     }
   655   }
   656   if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
   657     return tiebreaker_->Acceptable(sample);
   658   return true;
   659 }
   661 void Comparator::SetTiebreaker(Selector selector, const std::string required) {
   662   if (selector == selector_ || NIL == selector)
   663     return;
   664   combined_selectors_ |= selector;
   665   if (NIL == selector_) {
   666     selector_ = selector;
   667     if (required.size())
   668       required_ = required;
   669     return;
   670   }
   671   if (tiebreaker_) {
   672     if (use_tiebreaker_for_sort_only_) {
   673       Comparator* temp = new Comparator;
   674       temp->tiebreaker_ = tiebreaker_;
   675       tiebreaker_ = temp;
   676     }
   677   } else {
   678     tiebreaker_ = new Comparator;
   679     DCHECK(!use_tiebreaker_for_sort_only_);
   680   }
   681   tiebreaker_->SetTiebreaker(selector, required);
   682 }
   684 bool Comparator::IsGroupedBy(Selector selector) const {
   685   return 0 != (selector & combined_selectors_);
   686 }
   688 void Comparator::SetSubgroupTiebreaker(Selector selector) {
   689   if (selector == selector_ || NIL == selector)
   690     return;
   691   if (!tiebreaker_) {
   692     use_tiebreaker_for_sort_only_ = true;
   693     tiebreaker_ = new Comparator;
   694     tiebreaker_->SetTiebreaker(selector, "");
   695   } else {
   696     tiebreaker_->SetSubgroupTiebreaker(selector);
   697   }
   698 }
   700 bool Comparator::WriteSortGrouping(const Snapshot& sample,
   701                                        std::string* output) const {
   702   bool wrote_data = false;
   703   switch (selector_) {
   704     case BIRTH_THREAD:
   705       StringAppendF(output, "All new on %s ",
   706                     sample.birth_thread()->ThreadName().c_str());
   707       wrote_data = true;
   708       break;
   710     case DEATH_THREAD:
   711       if (sample.death_thread())
   712         StringAppendF(output, "All deleted on %s ",
   713                       sample.DeathThreadName().c_str());
   714       else
   715         output->append("All still alive ");
   716       wrote_data = true;
   717       break;
   719     case BIRTH_FILE:
   720       StringAppendF(output, "All born in %s ",
   721                     sample.location().file_name());
   722       break;
   724     case BIRTH_FUNCTION:
   725       output->append("All born in ");
   726       sample.location().WriteFunctionName(output);
   727       output->push_back(' ');
   728       break;
   730     default:
   731       break;
   732   }
   733   if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
   734     wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
   735   }
   736   return wrote_data;
   737 }
   739 void Comparator::WriteSnapshot(const Snapshot& sample,
   740                                std::string* output) const {
   741   sample.death_data().Write(output);
   742   if (!(combined_selectors_ & BIRTH_THREAD) ||
   743       !(combined_selectors_ & DEATH_THREAD))
   744     StringAppendF(output, "%s->%s ",
   745                   (combined_selectors_ & BIRTH_THREAD) ? "*" :
   746                     sample.birth().birth_thread()->ThreadName().c_str(),
   747                   (combined_selectors_ & DEATH_THREAD) ? "*" :
   748                     sample.DeathThreadName().c_str());
   749   sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
   750                                   !(combined_selectors_ & BIRTH_FUNCTION),
   751                                   output);
   752 }
   754 }  // namespace tracked_objects

mercurial