Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/tracked_objects.h" | 5 #include "base/tracked_objects.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 | 8 |
| 9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
| 10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
| 11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 12 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
| 13 #include "base/threading/thread_restrictions.h" | 13 #include "base/threading/thread_restrictions.h" |
| 14 | 14 |
| 15 using base::TimeDelta; | 15 using base::TimeDelta; |
| 16 | 16 |
| 17 namespace tracked_objects { | 17 namespace tracked_objects { |
| 18 | 18 |
| 19 // A TLS slot to the TrackRegistry for the current thread. | 19 // A TLS slot to the TrackRegistry for the current thread. |
| 20 // static | 20 // static |
| 21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); | 21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); |
| 22 | 22 |
| 23 // A global state variable to prevent repeated initialization during tests. | 23 // A global state variable to prevent repeated initialization during tests. |
| 24 // static | 24 // static |
| 25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; | 25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; |
| 26 | 26 |
| 27 // A locked protected counter to assign sequence number to threads. | |
| 28 // static | |
| 29 int ThreadData::thread_number_counter = 0; | |
| 30 | |
| 27 //------------------------------------------------------------------------------ | 31 //------------------------------------------------------------------------------ |
| 28 // Death data tallies durations when a death takes place. | 32 // Death data tallies durations when a death takes place. |
| 29 | 33 |
| 30 void DeathData::RecordDeath(const TimeDelta& duration) { | 34 void DeathData::RecordDeath(const TimeDelta& duration) { |
| 31 ++count_; | 35 ++count_; |
| 32 life_duration_ += duration; | 36 life_duration_ += duration; |
| 33 int64 milliseconds = duration.InMilliseconds(); | |
| 34 square_duration_ += milliseconds * milliseconds; | |
| 35 } | 37 } |
| 36 | 38 |
| 37 int DeathData::AverageMsDuration() const { | 39 int DeathData::AverageMsDuration() const { |
| 38 return static_cast<int>(life_duration_.InMilliseconds() / count_); | 40 return static_cast<int>(life_duration_.InMilliseconds() / count_); |
| 39 } | 41 } |
| 40 | 42 |
| 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) { | 43 void DeathData::AddDeathData(const DeathData& other) { |
| 50 count_ += other.count_; | 44 count_ += other.count_; |
| 51 life_duration_ += other.life_duration_; | 45 life_duration_ += other.life_duration_; |
| 52 square_duration_ += other.square_duration_; | |
| 53 } | 46 } |
| 54 | 47 |
| 55 void DeathData::Write(std::string* output) const { | 48 void DeathData::Write(std::string* output) const { |
| 56 if (!count_) | 49 if (!count_) |
| 57 return; | 50 return; |
| 58 if (1 == count_) { | 51 base::StringAppendF(output, "Lives:%d, Total:%dms Avg:%dms/life ", |
| 59 base::StringAppendF(output, "(1)Life in %dms ", AverageMsDuration()); | 52 count_, |
| 60 } else { | 53 static_cast<int>(life_duration_.InMilliseconds()), |
| 61 base::StringAppendF(output, "(%d)Lives %dms/life ", | 54 AverageMsDuration()); |
| 62 count_, AverageMsDuration()); | |
| 63 } | |
| 64 } | 55 } |
| 65 | 56 |
| 66 void DeathData::Clear() { | 57 void DeathData::Clear() { |
| 67 count_ = 0; | 58 count_ = 0; |
| 68 life_duration_ = TimeDelta(); | 59 life_duration_ = TimeDelta(); |
| 69 square_duration_ = 0; | |
| 70 } | 60 } |
| 71 | 61 |
| 72 //------------------------------------------------------------------------------ | 62 //------------------------------------------------------------------------------ |
| 73 BirthOnThread::BirthOnThread(const Location& location) | 63 BirthOnThread::BirthOnThread(const Location& location) |
| 74 : location_(location), | 64 : location_(location), |
| 75 birth_thread_(ThreadData::current()) { } | 65 birth_thread_(ThreadData::FactoryGet(NULL)) { } |
| 76 | 66 |
| 77 //------------------------------------------------------------------------------ | 67 //------------------------------------------------------------------------------ |
| 78 Births::Births(const Location& location) | 68 Births::Births(const Location& location) |
| 79 : BirthOnThread(location), | 69 : BirthOnThread(location), |
| 80 birth_count_(1) { } | 70 birth_count_(1) { } |
| 81 | 71 |
| 82 //------------------------------------------------------------------------------ | 72 //------------------------------------------------------------------------------ |
| 83 // ThreadData maintains the central data for all births and death. | 73 // ThreadData maintains the central data for all births and death. |
| 84 | 74 |
| 85 // static | 75 // static |
| 86 ThreadData* ThreadData::first_ = NULL; | 76 ThreadData* ThreadData::first_ = NULL; |
| 87 // static | 77 // static |
| 88 base::Lock ThreadData::list_lock_; | 78 base::Lock ThreadData::list_lock_; |
| 89 | 79 |
| 90 // static | 80 // static |
| 91 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 81 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
| 92 | 82 |
| 93 ThreadData::ThreadData() : next_(NULL) { | 83 ThreadData::ThreadData(const char* suggested_name) |
| 94 // This shouldn't use the MessageLoop::current() LazyInstance since this might | 84 : next_(NULL), |
| 95 // be used on a non-joinable thread. | 85 thread_number_(0) { |
| 96 // http://crbug.com/62728 | 86 if (suggested_name) { |
| 97 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | 87 DCHECK_GE(strlen(suggested_name), 0u); |
| 98 message_loop_ = MessageLoop::current(); | 88 thread_name_ = suggested_name; |
| 89 return; | |
| 90 } | |
| 91 { | |
| 92 base::AutoLock lock(list_lock_); | |
| 93 thread_number_ = ++thread_number_counter; | |
| 94 } | |
| 95 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number_); | |
| 99 } | 96 } |
| 100 | 97 |
| 101 ThreadData::~ThreadData() {} | 98 ThreadData::~ThreadData() {} |
| 102 | 99 |
| 103 // static | 100 // static |
| 104 ThreadData* ThreadData::current() { | 101 ThreadData* ThreadData::FactoryGet(const char* suggested_name) { |
| 105 if (!tls_index_.initialized()) | 102 if (!tls_index_.initialized()) |
| 106 return NULL; | 103 return NULL; |
| 107 | 104 |
| 108 ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get()); | 105 ThreadData* registered = static_cast<ThreadData*>(tls_index_.Get()); |
| 109 if (!registry) { | 106 if (!registered) { |
| 110 // We have to create a new registry for ThreadData. | 107 // We have to create a new registry entry for this ThreadData. |
| 111 bool too_late_to_create = false; | 108 bool too_late_to_create = false; |
| 109 // TODO(jar): Host all unamed (Worker) threads in *one* ThreadData instance, | |
| 110 // (with locking protection on that instance) or else recycle and re-use | |
| 111 // worker thread ThreadData when the worker thread terminates. | |
| 112 registered = new ThreadData(suggested_name); | |
| 112 { | 113 { |
| 113 registry = new ThreadData; | |
| 114 base::AutoLock lock(list_lock_); | 114 base::AutoLock lock(list_lock_); |
| 115 // Use lock to insure we have most recent status. | 115 // Use lock to insure we have most recent status. |
| 116 if (!IsActive()) { | 116 if (!IsActive()) { |
| 117 too_late_to_create = true; | 117 too_late_to_create = true; |
| 118 } else { | 118 } else { |
| 119 // Use lock to insert into list. | 119 // Use list_lock_ to insert as new head of list. |
| 120 registry->next_ = first_; | 120 registered->next_ = first_; |
| 121 first_ = registry; | 121 first_ = registered; |
| 122 } | 122 } |
| 123 } // Release lock. | 123 } // Release lock. |
| 124 if (too_late_to_create) { | 124 if (too_late_to_create) { |
| 125 delete registry; | 125 delete registered; |
| 126 registry = NULL; | 126 registered = NULL; |
| 127 } else { | 127 } else { |
| 128 tls_index_.Set(registry); | 128 tls_index_.Set(registered); |
| 129 } | 129 } |
| 130 } | 130 } |
| 131 return registry; | 131 DCHECK_GT(registered->thread_name_.size(), 0u); |
| 132 // If this DCHECK fails, then a task was *probabably* created on this thread | |
| 133 // before the thread object was initialized with a name etc. | |
| 134 DCHECK(!suggested_name || | |
| 135 registered->thread_name_.compare(suggested_name) == 0); | |
| 136 | |
| 137 return registered; | |
| 132 } | 138 } |
| 133 | 139 |
| 134 // static | 140 // static |
| 135 void ThreadData::WriteHTML(const std::string& query, std::string* output) { | 141 void ThreadData::WriteHTML(const std::string& query, std::string* output) { |
| 136 if (!ThreadData::IsActive()) | 142 if (!ThreadData::IsActive()) |
| 137 return; // Not yet initialized. | 143 return; // Not yet initialized. |
| 138 | 144 |
| 139 DCHECK(ThreadData::current()); | |
| 140 DataCollector collected_data; // Gather data. | 145 DataCollector collected_data; // Gather data. |
| 141 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 146 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
| 142 | 147 |
| 143 // Data Gathering is complete. Now to sort/process/render. | 148 // Data Gathering is complete. Now to sort/process/render. |
| 144 DataCollector::Collection* collection = collected_data.collection(); | 149 DataCollector::Collection* collection = collected_data.collection(); |
| 145 | 150 |
| 146 // Create filtering and sort comparison object. | 151 // Create filtering and sort comparison object. |
| 147 Comparator comparator; | 152 Comparator comparator; |
| 148 comparator.ParseQuery(query); | 153 comparator.ParseQuery(query); |
| 149 | 154 |
| 150 // Filter out acceptable (matching) instances. | 155 // Filter out acceptable (matching) instances. |
| 151 DataCollector::Collection match_array; | 156 DataCollector::Collection match_array; |
| 152 for (DataCollector::Collection::iterator it = collection->begin(); | 157 for (DataCollector::Collection::iterator it = collection->begin(); |
| 153 it != collection->end(); ++it) { | 158 it != collection->end(); ++it) { |
| 154 if (comparator.Acceptable(*it)) | 159 if (comparator.Acceptable(*it)) |
| 155 match_array.push_back(*it); | 160 match_array.push_back(*it); |
| 156 } | 161 } |
| 157 | 162 |
| 158 comparator.Sort(&match_array); | 163 comparator.Sort(&match_array); |
| 159 | 164 |
| 160 WriteHTMLTotalAndSubtotals(match_array, comparator, output); | 165 WriteHTMLTotalAndSubtotals(match_array, comparator, output); |
| 161 | 166 |
| 162 comparator.Clear(); // Delete tiebreaker_ instances. | 167 comparator.Clear(); // Delete tiebreaker_ instances. |
| 163 | 168 |
| 164 output->append("</pre>"); | 169 output->append("</pre>"); |
| 165 | 170 |
| 166 const char* help_string = "The following are the keywords that can be used to" | 171 const char* help_string = "The following are the keywords that can be used to" |
| 167 " sort and aggregate the data, or to select data.<br><ul>" | 172 " sort and aggregate the data, or to select data.<br><ul>" |
| 168 "<li><b>count</b> Number of instances seen." | 173 "<li><b>count</b> Number of instances seen." |
| 169 "<li><b>duration</b> Duration in ms from construction to descrution." | 174 "<li><b>duration</b> Average duration in ms of Run() time." |
| 175 "<li><b>totalduration</b> Summed durations in ms of Run() times." | |
| 170 "<li><b>birth</b> Thread on which the task was constructed." | 176 "<li><b>birth</b> Thread on which the task was constructed." |
| 171 "<li><b>death</b> Thread on which the task was run and deleted." | 177 "<li><b>death</b> Thread on which the task was run and deleted." |
| 172 "<li><b>file</b> File in which the task was contructed." | 178 "<li><b>file</b> File in which the task was contructed." |
| 173 "<li><b>function</b> Function in which the task was constructed." | 179 "<li><b>function</b> Function in which the task was constructed." |
| 174 "<li><b>line</b> Line number of the file in which the task was constructed." | 180 "<li><b>line</b> Line number of the file in which the task was constructed." |
| 175 "</ul><br>" | 181 "</ul><br>" |
| 176 "As examples:<ul>" | 182 "As examples:<ul>" |
| 177 "<li><b>about:tracking/file</b> would sort the above data by file, and" | 183 "<li><b>about:tracking/file</b> would sort the above data by file, and" |
| 178 " aggregate data on a per-file basis." | 184 " aggregate data on a per-file basis." |
| 179 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" | 185 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 227 output->append("<br>"); | 233 output->append("<br>"); |
| 228 subtotals.Write(output); | 234 subtotals.Write(output); |
| 229 output->append("<br><hr><br>"); | 235 output->append("<br><hr><br>"); |
| 230 subtotals.Clear(); | 236 subtotals.Clear(); |
| 231 } | 237 } |
| 232 } | 238 } |
| 233 } | 239 } |
| 234 } | 240 } |
| 235 | 241 |
| 236 Births* ThreadData::TallyABirth(const Location& location) { | 242 Births* ThreadData::TallyABirth(const Location& location) { |
| 237 { | |
| 238 // This shouldn't use the MessageLoop::current() LazyInstance since this | |
| 239 // might be used on a non-joinable thread. | |
| 240 // http://crbug.com/62728 | |
|
jar (doing other things)
2011/10/14 02:29:53
This was the primary motivation for this cleanup C
| |
| 241 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | |
| 242 if (!message_loop_) // In case message loop wasn't yet around... | |
| 243 message_loop_ = MessageLoop::current(); // Find it now. | |
| 244 } | |
| 245 | |
| 246 BirthMap::iterator it = birth_map_.find(location); | 243 BirthMap::iterator it = birth_map_.find(location); |
| 247 if (it != birth_map_.end()) { | 244 if (it != birth_map_.end()) { |
| 248 it->second->RecordBirth(); | 245 it->second->RecordBirth(); |
| 249 return it->second; | 246 return it->second; |
| 250 } | 247 } |
| 251 | 248 |
| 252 Births* tracker = new Births(location); | 249 Births* tracker = new Births(location); |
| 253 // Lock since the map may get relocated now, and other threads sometimes | 250 // Lock since the map may get relocated now, and other threads sometimes |
| 254 // snapshot it (but they lock before copying it). | 251 // snapshot it (but they lock before copying it). |
| 255 base::AutoLock lock(lock_); | 252 base::AutoLock lock(lock_); |
| 256 birth_map_[location] = tracker; | 253 birth_map_[location] = tracker; |
| 257 return tracker; | 254 return tracker; |
| 258 } | 255 } |
| 259 | 256 |
| 260 void ThreadData::TallyADeath(const Births& lifetimes, | 257 void ThreadData::TallyADeath(const Births& the_birth, |
| 261 const TimeDelta& duration) { | 258 const TimeDelta& duration) { |
| 262 { | 259 DeathMap::iterator it = death_map_.find(&the_birth); |
| 263 // http://crbug.com/62728 | |
|
jar (doing other things)
2011/10/14 02:29:53
Again... the motivation for the CL.
| |
| 264 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | |
| 265 if (!message_loop_) // In case message loop wasn't yet around... | |
| 266 message_loop_ = MessageLoop::current(); // Find it now. | |
| 267 } | |
| 268 | |
| 269 DeathMap::iterator it = death_map_.find(&lifetimes); | |
| 270 if (it != death_map_.end()) { | 260 if (it != death_map_.end()) { |
| 271 it->second.RecordDeath(duration); | 261 it->second.RecordDeath(duration); |
| 272 return; | 262 return; |
| 273 } | 263 } |
| 274 | 264 |
| 275 base::AutoLock lock(lock_); // Lock since the map may get relocated now. | 265 base::AutoLock lock(lock_); // Lock since the map may get relocated now. |
| 276 death_map_[&lifetimes].RecordDeath(duration); | 266 death_map_[&the_birth].RecordDeath(duration); |
| 277 } | 267 } |
| 278 | 268 |
| 279 // static | 269 // static |
| 280 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 270 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
| 281 if (IsActive()) { | 271 #if !defined(TRACK_ALL_TASK_OBJECTS) |
| 282 ThreadData* current_thread_data = current(); | 272 return NULL; // Not compiled in. |
| 283 if (current_thread_data) { | 273 #endif |
| 284 return current_thread_data->TallyABirth(location); | 274 if (!IsActive()) |
| 285 } | 275 return NULL; |
| 286 } | 276 ThreadData* current_thread_data = FactoryGet(NULL); |
| 287 | 277 if (!current_thread_data) |
| 288 return NULL; | 278 return NULL; |
| 279 return current_thread_data->TallyABirth(location); | |
| 289 } | 280 } |
| 290 | 281 |
| 291 // static | 282 // static |
| 292 void ThreadData::TallyADeathIfActive(const Births* the_birth, | 283 void ThreadData::TallyADeathIfActive(const Births* the_birth, |
| 293 const base::TimeDelta& duration) { | 284 const base::TimeTicks& time_posted, |
| 294 if (IsActive() && the_birth) { | 285 const base::TimeTicks& start_of_run, |
| 295 current()->TallyADeath(*the_birth, duration); | 286 const MessageLoop* message_loop) { |
| 296 } | 287 #if !defined(TRACK_ALL_TASK_OBJECTS) |
| 288 return; // Not compiled in. | |
| 289 #endif | |
| 290 if (!IsActive() || !the_birth) | |
| 291 return; | |
| 292 ThreadData* current_thread_data = FactoryGet(NULL); | |
| 293 if (!current_thread_data) | |
| 294 return; | |
| 295 DCHECK(!message_loop || | |
| 296 message_loop->thread_name() == current_thread_data->thread_name_); | |
| 297 // We don't currently handle queueing time duration, since time_posted, so | |
| 298 // we discard time_posted. | |
| 299 current_thread_data->TallyADeath(*the_birth, | |
| 300 base::TimeTicks::Now() - start_of_run); | |
| 297 } | 301 } |
| 298 | 302 |
| 299 // static | 303 // static |
| 300 ThreadData* ThreadData::first() { | 304 ThreadData* ThreadData::first() { |
| 301 base::AutoLock lock(list_lock_); | 305 base::AutoLock lock(list_lock_); |
| 302 return first_; | 306 return first_; |
| 303 } | 307 } |
| 304 | 308 |
| 305 const std::string ThreadData::ThreadName() const { | |
| 306 if (message_loop_) | |
| 307 return message_loop_->thread_name(); | |
|
jar (doing other things)
2011/10/14 02:29:53
We now store the thread name, and don't need to ke
| |
| 308 return "ThreadWithoutMessageLoop"; | |
| 309 } | |
| 310 | |
| 311 // This may be called from another thread. | 309 // This may be called from another thread. |
| 312 void ThreadData::SnapshotBirthMap(BirthMap *output) const { | 310 void ThreadData::SnapshotBirthMap(BirthMap *output) const { |
| 313 base::AutoLock lock(lock_); | 311 base::AutoLock lock(lock_); |
| 314 for (BirthMap::const_iterator it = birth_map_.begin(); | 312 for (BirthMap::const_iterator it = birth_map_.begin(); |
| 315 it != birth_map_.end(); ++it) | 313 it != birth_map_.end(); ++it) |
| 316 (*output)[it->first] = it->second; | 314 (*output)[it->first] = it->second; |
| 317 } | 315 } |
| 318 | 316 |
| 319 // This may be called from another thread. | 317 // This may be called from another thread. |
| 320 void ThreadData::SnapshotDeathMap(DeathMap *output) const { | 318 void ThreadData::SnapshotDeathMap(DeathMap *output) const { |
| 321 base::AutoLock lock(lock_); | 319 base::AutoLock lock(lock_); |
| 322 for (DeathMap::const_iterator it = death_map_.begin(); | 320 for (DeathMap::const_iterator it = death_map_.begin(); |
| 323 it != death_map_.end(); ++it) | 321 it != death_map_.end(); ++it) |
| 324 (*output)[it->first] = it->second; | 322 (*output)[it->first] = it->second; |
| 325 } | 323 } |
| 326 | 324 |
| 327 // static | 325 // static |
| 328 void ThreadData::ResetAllThreadData() { | 326 void ThreadData::ResetAllThreadData() { |
| 329 ThreadData* my_list = ThreadData::current()->first(); | 327 ThreadData* my_list = ThreadData::FactoryGet(NULL)->first(); |
| 330 | 328 |
| 331 for (ThreadData* thread_data = my_list; | 329 for (ThreadData* thread_data = my_list; |
| 332 thread_data; | 330 thread_data; |
| 333 thread_data = thread_data->next()) | 331 thread_data = thread_data->next()) |
| 334 thread_data->Reset(); | 332 thread_data->Reset(); |
| 335 } | 333 } |
| 336 | 334 |
| 337 void ThreadData::Reset() { | 335 void ThreadData::Reset() { |
| 338 base::AutoLock lock(lock_); | 336 base::AutoLock lock(lock_); |
| 339 for (DeathMap::iterator it = death_map_.begin(); | 337 for (DeathMap::iterator it = death_map_.begin(); |
| 340 it != death_map_.end(); ++it) | 338 it != death_map_.end(); ++it) |
| 341 it->second.Clear(); | 339 it->second.Clear(); |
| 342 for (BirthMap::iterator it = birth_map_.begin(); | 340 for (BirthMap::iterator it = birth_map_.begin(); |
| 343 it != birth_map_.end(); ++it) | 341 it != birth_map_.end(); ++it) |
| 344 it->second->Clear(); | 342 it->second->Clear(); |
| 345 } | 343 } |
| 346 | 344 |
| 347 #ifdef OS_WIN | |
| 348 // A class used to count down which is accessed by several threads. This is | |
|
jar (doing other things)
2011/10/14 02:29:53
We no longer do any terminaition cleanup... so all
| |
| 349 // used to make sure RunOnAllThreads() actually runs a task on the expected | |
| 350 // count of threads. | |
| 351 class ThreadData::ThreadSafeDownCounter { | |
| 352 public: | |
| 353 // Constructor sets the count, once and for all. | |
| 354 explicit ThreadSafeDownCounter(size_t count); | |
| 355 | |
| 356 // Decrement the count, and return true if we hit zero. Also delete this | |
| 357 // instance automatically when we hit zero. | |
| 358 bool LastCaller(); | |
| 359 | |
| 360 private: | |
| 361 size_t remaining_count_; | |
| 362 base::Lock lock_; // protect access to remaining_count_. | |
| 363 }; | |
| 364 | |
| 365 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) | |
| 366 : remaining_count_(count) { | |
| 367 DCHECK_GT(remaining_count_, 0u); | |
| 368 } | |
| 369 | |
| 370 bool ThreadData::ThreadSafeDownCounter::LastCaller() { | |
| 371 { | |
| 372 base::AutoLock lock(lock_); | |
| 373 if (--remaining_count_) | |
| 374 return false; | |
| 375 } // Release lock, so we can delete everything in this instance. | |
| 376 delete this; | |
| 377 return true; | |
| 378 } | |
| 379 | |
| 380 // A Task class that runs a static method supplied, and checks to see if this | |
| 381 // is the last tasks instance (on last thread) that will run the method. | |
| 382 // IF this is the last run, then the supplied event is signalled. | |
| 383 class ThreadData::RunTheStatic : public Task { | |
| 384 public: | |
| 385 typedef void (*FunctionPointer)(); | |
| 386 RunTheStatic(FunctionPointer function, | |
| 387 HANDLE completion_handle, | |
| 388 ThreadSafeDownCounter* counter); | |
| 389 // Run the supplied static method, and optionally set the event. | |
| 390 void Run(); | |
| 391 | |
| 392 private: | |
| 393 FunctionPointer function_; | |
| 394 HANDLE completion_handle_; | |
| 395 // Make sure enough tasks are called before completion is signaled. | |
| 396 ThreadSafeDownCounter* counter_; | |
| 397 | |
| 398 DISALLOW_COPY_AND_ASSIGN(RunTheStatic); | |
| 399 }; | |
| 400 | |
| 401 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function, | |
| 402 HANDLE completion_handle, | |
| 403 ThreadSafeDownCounter* counter) | |
| 404 : function_(function), | |
| 405 completion_handle_(completion_handle), | |
| 406 counter_(counter) { | |
| 407 } | |
| 408 | |
| 409 void ThreadData::RunTheStatic::Run() { | |
| 410 function_(); | |
| 411 if (counter_->LastCaller()) | |
| 412 SetEvent(completion_handle_); | |
| 413 } | |
| 414 | |
| 415 // TODO(jar): This should use condition variables, and be cross platform. | |
| 416 void ThreadData::RunOnAllThreads(void (*function)()) { | |
| 417 ThreadData* list = first(); // Get existing list. | |
| 418 | |
| 419 std::vector<MessageLoop*> message_loops; | |
| 420 for (ThreadData* it = list; it; it = it->next()) { | |
| 421 if (current() != it && it->message_loop()) | |
| 422 message_loops.push_back(it->message_loop()); | |
|
jar (doing other things)
2011/10/14 02:29:53
Since we don't do this racy cleanup attempt, we do
| |
| 423 } | |
| 424 | |
| 425 ThreadSafeDownCounter* counter = | |
| 426 new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us! | |
| 427 | |
| 428 HANDLE completion_handle = CreateEvent(NULL, false, false, NULL); | |
| 429 // Tell all other threads to run. | |
| 430 for (size_t i = 0; i < message_loops.size(); ++i) | |
| 431 message_loops[i]->PostTask( | |
| 432 FROM_HERE, new RunTheStatic(function, completion_handle, counter)); | |
| 433 | |
| 434 // Also run Task on our thread. | |
| 435 RunTheStatic local_task(function, completion_handle, counter); | |
| 436 local_task.Run(); | |
| 437 | |
| 438 WaitForSingleObject(completion_handle, INFINITE); | |
| 439 int ret_val = CloseHandle(completion_handle); | |
| 440 DCHECK(ret_val); | |
| 441 } | |
| 442 #endif // OS_WIN | |
| 443 | |
| 444 // static | 345 // static |
| 445 bool ThreadData::StartTracking(bool status) { | 346 bool ThreadData::StartTracking(bool status) { |
| 446 #if !defined(TRACK_ALL_TASK_OBJECTS) | 347 #if !defined(TRACK_ALL_TASK_OBJECTS) |
| 447 return false; // Not compiled in. | 348 return false; // Not compiled in. |
| 448 #endif | 349 #endif |
| 449 | 350 |
| 450 if (!status) { | 351 if (!status) { |
| 451 base::AutoLock lock(list_lock_); | 352 base::AutoLock lock(list_lock_); |
| 452 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); | 353 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); |
| 453 status_ = SHUTDOWN; | 354 status_ = SHUTDOWN; |
| 454 return true; | 355 return true; |
| 455 } | 356 } |
| 456 base::AutoLock lock(list_lock_); | 357 base::AutoLock lock(list_lock_); |
| 457 DCHECK_EQ(UNINITIALIZED, status_); | 358 DCHECK_EQ(UNINITIALIZED, status_); |
| 458 CHECK(tls_index_.Initialize(NULL)); | 359 CHECK(tls_index_.Initialize(NULL)); |
| 459 status_ = ACTIVE; | 360 status_ = ACTIVE; |
| 460 return true; | 361 return true; |
| 461 } | 362 } |
| 462 | 363 |
| 463 // static | 364 // static |
| 464 bool ThreadData::IsActive() { | 365 bool ThreadData::IsActive() { |
| 465 return status_ == ACTIVE; | 366 return status_ == ACTIVE; |
| 466 } | 367 } |
| 467 | 368 |
| 468 #ifdef OS_WIN | |
| 469 // static | 369 // static |
| 470 void ThreadData::ShutdownMultiThreadTracking() { | 370 base::TimeTicks ThreadData::Now() { |
| 471 // Using lock, guarantee that no new ThreadData instances will be created. | 371 #if defined(TRACK_ALL_TASK_OBJECTS) |
| 472 if (!StartTracking(false)) | 372 if (status_ == ACTIVE) |
| 473 return; | 373 return base::TimeTicks::Now(); |
| 474 | 374 #endif |
| 475 RunOnAllThreads(ShutdownDisablingFurtherTracking); | 375 return base::TimeTicks(); // Super fast when disabled, or not compiled in. |
| 476 | |
| 477 // Now the *only* threads that might change the database are the threads with | |
| 478 // no messages loops. They might still be adding data to their birth records, | |
| 479 // but since no objects are deleted on those threads, there will be no further | |
| 480 // access to to cross-thread data. | |
| 481 // We could do a cleanup on all threads except for the ones without | |
| 482 // MessageLoops, but we won't bother doing cleanup (destruction of data) yet. | |
| 483 return; | |
| 484 } | 376 } |
| 485 #endif | |
| 486 | 377 |
| 487 // static | 378 // static |
| 488 void ThreadData::ShutdownSingleThreadedCleanup() { | 379 void ThreadData::ShutdownSingleThreadedCleanup() { |
| 380 NOTREACHED(); | |
|
jar (doing other things)
2011/10/14 15:48:42
This has to be removed.
This code is used is sing
| |
| 489 // We must be single threaded... but be careful anyway. | 381 // We must be single threaded... but be careful anyway. |
| 490 if (!StartTracking(false)) | 382 if (!StartTracking(false)) |
| 491 return; | 383 return; |
| 492 ThreadData* thread_data_list; | 384 ThreadData* thread_data_list; |
| 493 { | 385 { |
| 494 base::AutoLock lock(list_lock_); | 386 base::AutoLock lock(list_lock_); |
| 495 thread_data_list = first_; | 387 thread_data_list = first_; |
| 496 first_ = NULL; | 388 first_ = NULL; |
| 497 } | 389 } |
| 498 | 390 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 534 } | 426 } |
| 535 | 427 |
| 536 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) | 428 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) |
| 537 : birth_(&birth_on_thread), | 429 : birth_(&birth_on_thread), |
| 538 death_thread_(NULL), | 430 death_thread_(NULL), |
| 539 death_data_(DeathData(count)) { | 431 death_data_(DeathData(count)) { |
| 540 } | 432 } |
| 541 | 433 |
| 542 const std::string Snapshot::DeathThreadName() const { | 434 const std::string Snapshot::DeathThreadName() const { |
| 543 if (death_thread_) | 435 if (death_thread_) |
| 544 return death_thread_->ThreadName(); | 436 return death_thread_->thread_name(); |
| 545 return "Still_Alive"; | 437 return "Still_Alive"; |
| 546 } | 438 } |
| 547 | 439 |
| 548 void Snapshot::Write(std::string* output) const { | 440 void Snapshot::Write(std::string* output) const { |
| 549 death_data_.Write(output); | 441 death_data_.Write(output); |
| 550 base::StringAppendF(output, "%s->%s ", | 442 base::StringAppendF(output, "%s->%s ", |
| 551 birth_->birth_thread()->ThreadName().c_str(), | 443 birth_->birth_thread()->thread_name().c_str(), |
| 552 death_thread_->ThreadName().c_str()); | 444 death_thread_->thread_name().c_str()); |
| 553 birth_->location().Write(true, true, output); | 445 birth_->location().Write(true, true, output); |
| 554 } | 446 } |
| 555 | 447 |
| 556 void Snapshot::Add(const Snapshot& other) { | 448 void Snapshot::Add(const Snapshot& other) { |
| 557 death_data_.AddDeathData(other.death_data_); | 449 death_data_.AddDeathData(other.death_data_); |
| 558 } | 450 } |
| 559 | 451 |
| 560 //------------------------------------------------------------------------------ | 452 //------------------------------------------------------------------------------ |
| 561 // DataCollector | 453 // DataCollector |
| 562 | 454 |
| 563 DataCollector::DataCollector() { | 455 DataCollector::DataCollector() { |
| 564 DCHECK(ThreadData::IsActive()); | 456 DCHECK(ThreadData::IsActive()); |
| 565 | 457 |
| 566 // Get an unchanging copy of a ThreadData list. | 458 // Get an unchanging copy of a ThreadData list. |
| 567 ThreadData* my_list = ThreadData::current()->first(); | 459 ThreadData* my_list = ThreadData::FactoryGet(NULL)->first(); |
| 568 | 460 |
| 569 count_of_contributing_threads_ = 0; | 461 count_of_contributing_threads_ = 0; |
| 570 for (ThreadData* thread_data = my_list; | 462 for (ThreadData* thread_data = my_list; |
| 571 thread_data; | 463 thread_data; |
| 572 thread_data = thread_data->next()) { | 464 thread_data = thread_data->next()) { |
| 573 ++count_of_contributing_threads_; | 465 ++count_of_contributing_threads_; |
| 574 } | 466 } |
| 575 | 467 |
| 576 // Gather data serially. A different constructor could be used to do in | 468 // Gather data serially. |
| 577 // parallel, and then invoke an OnCompletion task. | |
| 578 // This hackish approach *can* get some slighly corrupt tallies, as we are | 469 // This hackish approach *can* get some slighly corrupt tallies, as we are |
| 579 // grabbing values without the protection of a lock, but it has the advantage | 470 // grabbing values without the protection of a lock, but it has the advantage |
| 580 // of working even with threads that don't have message loops. If a user | 471 // of working even with threads that don't have message loops. If a user |
| 581 // sees any strangeness, they can always just run their stats gathering a | 472 // sees any strangeness, they can always just run their stats gathering a |
| 582 // second time. | 473 // second time. |
| 583 // TODO(jar): Provide version that gathers stats safely via PostTask in all | |
| 584 // cases where thread_data supplies a message_loop to post to. Be careful to | |
| 585 // handle message_loops that are destroyed!?! | |
| 586 for (ThreadData* thread_data = my_list; | 474 for (ThreadData* thread_data = my_list; |
| 587 thread_data; | 475 thread_data; |
| 588 thread_data = thread_data->next()) { | 476 thread_data = thread_data->next()) { |
| 589 Append(*thread_data); | 477 Append(*thread_data); |
| 590 } | 478 } |
| 591 } | 479 } |
| 592 | 480 |
| 593 DataCollector::~DataCollector() { | 481 DataCollector::~DataCollector() { |
| 594 } | 482 } |
| 595 | 483 |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 674 base::StringAppendF(output, "All born in %s. ", | 562 base::StringAppendF(output, "All born in %s. ", |
| 675 birth_files_.begin()->first.c_str()); | 563 birth_files_.begin()->first.c_str()); |
| 676 } | 564 } |
| 677 } | 565 } |
| 678 | 566 |
| 679 if (birth_threads_.size() > 1) { | 567 if (birth_threads_.size() > 1) { |
| 680 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", | 568 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", |
| 681 birth_threads_.size()); | 569 birth_threads_.size()); |
| 682 } else { | 570 } else { |
| 683 base::StringAppendF(output, "All born on %s. ", | 571 base::StringAppendF(output, "All born on %s. ", |
| 684 birth_threads_.begin()->first->ThreadName().c_str()); | 572 birth_threads_.begin()->first->thread_name().c_str()); |
| 685 } | 573 } |
| 686 | 574 |
| 687 if (death_threads_.size() > 1) { | 575 if (death_threads_.size() > 1) { |
| 688 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", | 576 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", |
| 689 death_threads_.size()); | 577 death_threads_.size()); |
| 690 } else { | 578 } else { |
| 691 if (death_threads_.begin()->first) { | 579 if (death_threads_.begin()->first) { |
| 692 base::StringAppendF(output, "All deleted on %s. ", | 580 base::StringAppendF(output, "All deleted on %s. ", |
| 693 death_threads_.begin()->first->ThreadName().c_str()); | 581 death_threads_.begin()->first->thread_name().c_str()); |
| 694 } else { | 582 } else { |
| 695 output->append("All these objects are still alive."); | 583 output->append("All these objects are still alive."); |
| 696 } | 584 } |
| 697 } | 585 } |
| 698 | 586 |
| 699 if (birth_count_ > 1) | 587 if (birth_count_ > 1) |
| 700 base::StringAppendF(output, "Births=%d ", birth_count_); | 588 base::StringAppendF(output, "Births=%d ", birth_count_); |
| 701 | 589 |
| 702 DeathData::Write(output); | 590 DeathData::Write(output); |
| 703 } | 591 } |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 728 } | 616 } |
| 729 use_tiebreaker_for_sort_only_ = false; | 617 use_tiebreaker_for_sort_only_ = false; |
| 730 selector_ = NIL; | 618 selector_ = NIL; |
| 731 } | 619 } |
| 732 | 620 |
| 733 bool Comparator::operator()(const Snapshot& left, | 621 bool Comparator::operator()(const Snapshot& left, |
| 734 const Snapshot& right) const { | 622 const Snapshot& right) const { |
| 735 switch (selector_) { | 623 switch (selector_) { |
| 736 case BIRTH_THREAD: | 624 case BIRTH_THREAD: |
| 737 if (left.birth_thread() != right.birth_thread() && | 625 if (left.birth_thread() != right.birth_thread() && |
| 738 left.birth_thread()->ThreadName() != | 626 left.birth_thread()->thread_name() != |
| 739 right.birth_thread()->ThreadName()) | 627 right.birth_thread()->thread_name()) |
| 740 return left.birth_thread()->ThreadName() < | 628 return left.birth_thread()->thread_name() < |
| 741 right.birth_thread()->ThreadName(); | 629 right.birth_thread()->thread_name(); |
| 742 break; | 630 break; |
| 743 | 631 |
| 744 case DEATH_THREAD: | 632 case DEATH_THREAD: |
| 745 if (left.death_thread() != right.death_thread() && | 633 if (left.death_thread() != right.death_thread() && |
| 746 left.DeathThreadName() != | 634 left.DeathThreadName() != |
| 747 right.DeathThreadName()) { | 635 right.DeathThreadName()) { |
| 748 if (!left.death_thread()) | 636 if (!left.death_thread()) |
| 749 return true; | 637 return true; |
| 750 if (!right.death_thread()) | 638 if (!right.death_thread()) |
| 751 return false; | 639 return false; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 783 return left.count() > right.count(); // Sort large at front of vector. | 671 return left.count() > right.count(); // Sort large at front of vector. |
| 784 break; | 672 break; |
| 785 | 673 |
| 786 case AVERAGE_DURATION: | 674 case AVERAGE_DURATION: |
| 787 if (!left.count() || !right.count()) | 675 if (!left.count() || !right.count()) |
| 788 break; | 676 break; |
| 789 if (left.AverageMsDuration() != right.AverageMsDuration()) | 677 if (left.AverageMsDuration() != right.AverageMsDuration()) |
| 790 return left.AverageMsDuration() > right.AverageMsDuration(); | 678 return left.AverageMsDuration() > right.AverageMsDuration(); |
| 791 break; | 679 break; |
| 792 | 680 |
| 681 case TOTAL_DURATION: | |
| 682 if (!left.count() || !right.count()) | |
| 683 break; | |
| 684 if (left.life_duration() != right.life_duration()) | |
| 685 return left.life_duration() > right.life_duration(); | |
| 686 break; | |
| 687 | |
| 793 default: | 688 default: |
| 794 break; | 689 break; |
| 795 } | 690 } |
| 796 if (tiebreaker_) | 691 if (tiebreaker_) |
| 797 return tiebreaker_->operator()(left, right); | 692 return tiebreaker_->operator()(left, right); |
| 798 return false; | 693 return false; |
| 799 } | 694 } |
| 800 | 695 |
| 801 void Comparator::Sort(DataCollector::Collection* collection) const { | 696 void Comparator::Sort(DataCollector::Collection* collection) const { |
| 802 std::sort(collection->begin(), collection->end(), *this); | 697 std::sort(collection->begin(), collection->end(), *this); |
| 803 } | 698 } |
| 804 | 699 |
| 805 bool Comparator::Equivalent(const Snapshot& left, | 700 bool Comparator::Equivalent(const Snapshot& left, |
| 806 const Snapshot& right) const { | 701 const Snapshot& right) const { |
| 807 switch (selector_) { | 702 switch (selector_) { |
| 808 case BIRTH_THREAD: | 703 case BIRTH_THREAD: |
| 809 if (left.birth_thread() != right.birth_thread() && | 704 if (left.birth_thread() != right.birth_thread() && |
| 810 left.birth_thread()->ThreadName() != | 705 left.birth_thread()->thread_name() != |
| 811 right.birth_thread()->ThreadName()) | 706 right.birth_thread()->thread_name()) |
| 812 return false; | 707 return false; |
| 813 break; | 708 break; |
| 814 | 709 |
| 815 case DEATH_THREAD: | 710 case DEATH_THREAD: |
| 816 if (left.death_thread() != right.death_thread() && | 711 if (left.death_thread() != right.death_thread() && |
| 817 left.DeathThreadName() != right.DeathThreadName()) | 712 left.DeathThreadName() != right.DeathThreadName()) |
| 818 return false; | 713 return false; |
| 819 break; | 714 break; |
| 820 | 715 |
| 821 case BIRTH_FILE: | 716 case BIRTH_FILE: |
| 822 if (left.location().file_name() != right.location().file_name()) { | 717 if (left.location().file_name() != right.location().file_name()) { |
| 823 int comp = strcmp(left.location().file_name(), | 718 int comp = strcmp(left.location().file_name(), |
| 824 right.location().file_name()); | 719 right.location().file_name()); |
| 825 if (comp) | 720 if (comp) |
| 826 return false; | 721 return false; |
| 827 } | 722 } |
| 828 break; | 723 break; |
| 829 | 724 |
| 830 case BIRTH_FUNCTION: | 725 case BIRTH_FUNCTION: |
| 831 if (left.location().function_name() != right.location().function_name()) { | 726 if (left.location().function_name() != right.location().function_name()) { |
| 832 int comp = strcmp(left.location().function_name(), | 727 int comp = strcmp(left.location().function_name(), |
| 833 right.location().function_name()); | 728 right.location().function_name()); |
| 834 if (comp) | 729 if (comp) |
| 835 return false; | 730 return false; |
| 836 } | 731 } |
| 837 break; | 732 break; |
| 838 | 733 |
| 839 case COUNT: | 734 case COUNT: |
| 840 if (left.count() != right.count()) | |
|
jar (doing other things)
2011/10/14 02:29:53
I decided it was stupid to aggregate groups of res
| |
| 841 return false; | |
| 842 break; | |
| 843 | |
| 844 case AVERAGE_DURATION: | 735 case AVERAGE_DURATION: |
| 845 if (left.life_duration() != right.life_duration()) | 736 case TOTAL_DURATION: |
| 846 return false; | 737 // We don't produce separate aggretation when only counts or times differ. |
| 847 break; | 738 break; |
| 848 | 739 |
| 849 default: | 740 default: |
| 850 break; | 741 break; |
| 851 } | 742 } |
| 852 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) | 743 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) |
| 853 return tiebreaker_->Equivalent(left, right); | 744 return tiebreaker_->Equivalent(left, right); |
| 854 return true; | 745 return true; |
| 855 } | 746 } |
| 856 | 747 |
| 857 bool Comparator::Acceptable(const Snapshot& sample) const { | 748 bool Comparator::Acceptable(const Snapshot& sample) const { |
| 858 if (required_.size()) { | 749 if (required_.size()) { |
| 859 switch (selector_) { | 750 switch (selector_) { |
| 860 case BIRTH_THREAD: | 751 case BIRTH_THREAD: |
| 861 if (sample.birth_thread()->ThreadName().find(required_) == | 752 if (sample.birth_thread()->thread_name().find(required_) == |
| 862 std::string::npos) | 753 std::string::npos) |
| 863 return false; | 754 return false; |
| 864 break; | 755 break; |
| 865 | 756 |
| 866 case DEATH_THREAD: | 757 case DEATH_THREAD: |
| 867 if (sample.DeathThreadName().find(required_) == std::string::npos) | 758 if (sample.DeathThreadName().find(required_) == std::string::npos) |
| 868 return false; | 759 return false; |
| 869 break; | 760 break; |
| 870 | 761 |
| 871 case BIRTH_FILE: | 762 case BIRTH_FILE: |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 927 } | 818 } |
| 928 | 819 |
| 929 void Comparator::ParseKeyphrase(const std::string& key_phrase) { | 820 void Comparator::ParseKeyphrase(const std::string& key_phrase) { |
| 930 typedef std::map<const std::string, Selector> KeyMap; | 821 typedef std::map<const std::string, Selector> KeyMap; |
| 931 static KeyMap key_map; | 822 static KeyMap key_map; |
| 932 static bool initialized = false; | 823 static bool initialized = false; |
| 933 if (!initialized) { | 824 if (!initialized) { |
| 934 initialized = true; | 825 initialized = true; |
| 935 // Sorting and aggretation keywords, which specify how to sort the data, or | 826 // Sorting and aggretation keywords, which specify how to sort the data, or |
| 936 // can specify a required match from the specified field in the record. | 827 // can specify a required match from the specified field in the record. |
| 937 key_map["count"] = COUNT; | 828 key_map["count"] = COUNT; |
| 938 key_map["duration"] = AVERAGE_DURATION; | 829 key_map["totalduration"] = TOTAL_DURATION; |
| 939 key_map["birth"] = BIRTH_THREAD; | 830 key_map["duration"] = AVERAGE_DURATION; |
| 940 key_map["death"] = DEATH_THREAD; | 831 key_map["birth"] = BIRTH_THREAD; |
| 941 key_map["file"] = BIRTH_FILE; | 832 key_map["death"] = DEATH_THREAD; |
| 942 key_map["function"] = BIRTH_FUNCTION; | 833 key_map["file"] = BIRTH_FILE; |
| 943 key_map["line"] = BIRTH_LINE; | 834 key_map["function"] = BIRTH_FUNCTION; |
| 835 key_map["line"] = BIRTH_LINE; | |
| 944 | 836 |
| 945 // Immediate commands that do not involve setting sort order. | 837 // Immediate commands that do not involve setting sort order. |
| 946 key_map["reset"] = RESET_ALL_DATA; | 838 key_map["reset"] = RESET_ALL_DATA; |
| 947 } | 839 } |
| 948 | 840 |
| 949 std::string required; | 841 std::string required; |
| 950 // Watch for: "sort_key=value" as we parse. | 842 // Watch for: "sort_key=value" as we parse. |
| 951 size_t equal_offset = key_phrase.find('=', 0); | 843 size_t equal_offset = key_phrase.find('=', 0); |
| 952 if (key_phrase.npos != equal_offset) { | 844 if (key_phrase.npos != equal_offset) { |
| 953 // There is a value that must be matched for the data to display. | 845 // There is a value that must be matched for the data to display. |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 969 for (size_t i = 0; i < query.size();) { | 861 for (size_t i = 0; i < query.size();) { |
| 970 size_t slash_offset = query.find('/', i); | 862 size_t slash_offset = query.find('/', i); |
| 971 ParseKeyphrase(query.substr(i, slash_offset - i)); | 863 ParseKeyphrase(query.substr(i, slash_offset - i)); |
| 972 if (query.npos == slash_offset) | 864 if (query.npos == slash_offset) |
| 973 break; | 865 break; |
| 974 i = slash_offset + 1; | 866 i = slash_offset + 1; |
| 975 } | 867 } |
| 976 | 868 |
| 977 // Select subgroup ordering (if we want to display the subgroup) | 869 // Select subgroup ordering (if we want to display the subgroup) |
| 978 SetSubgroupTiebreaker(COUNT); | 870 SetSubgroupTiebreaker(COUNT); |
| 871 SetSubgroupTiebreaker(TOTAL_DURATION); | |
| 979 SetSubgroupTiebreaker(AVERAGE_DURATION); | 872 SetSubgroupTiebreaker(AVERAGE_DURATION); |
| 980 SetSubgroupTiebreaker(BIRTH_THREAD); | 873 SetSubgroupTiebreaker(BIRTH_THREAD); |
| 981 SetSubgroupTiebreaker(DEATH_THREAD); | 874 SetSubgroupTiebreaker(DEATH_THREAD); |
| 982 SetSubgroupTiebreaker(BIRTH_FUNCTION); | 875 SetSubgroupTiebreaker(BIRTH_FUNCTION); |
| 983 SetSubgroupTiebreaker(BIRTH_FILE); | 876 SetSubgroupTiebreaker(BIRTH_FILE); |
| 984 SetSubgroupTiebreaker(BIRTH_LINE); | 877 SetSubgroupTiebreaker(BIRTH_LINE); |
| 985 | 878 |
| 986 return true; | 879 return true; |
| 987 } | 880 } |
| 988 | 881 |
| 989 bool Comparator::WriteSortGrouping(const Snapshot& sample, | 882 bool Comparator::WriteSortGrouping(const Snapshot& sample, |
| 990 std::string* output) const { | 883 std::string* output) const { |
| 991 bool wrote_data = false; | 884 bool wrote_data = false; |
| 992 switch (selector_) { | 885 switch (selector_) { |
| 993 case BIRTH_THREAD: | 886 case BIRTH_THREAD: |
| 994 base::StringAppendF(output, "All new on %s ", | 887 base::StringAppendF(output, "All new on %s ", |
| 995 sample.birth_thread()->ThreadName().c_str()); | 888 sample.birth_thread()->thread_name().c_str()); |
| 996 wrote_data = true; | 889 wrote_data = true; |
| 997 break; | 890 break; |
| 998 | 891 |
| 999 case DEATH_THREAD: | 892 case DEATH_THREAD: |
| 1000 if (sample.death_thread()) { | 893 if (sample.death_thread()) { |
| 1001 base::StringAppendF(output, "All deleted on %s ", | 894 base::StringAppendF(output, "All deleted on %s ", |
| 1002 sample.DeathThreadName().c_str()); | 895 sample.DeathThreadName().c_str()); |
| 1003 } else { | 896 } else { |
| 1004 output->append("All still alive "); | 897 output->append("All still alive "); |
| 1005 } | 898 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 1026 return wrote_data; | 919 return wrote_data; |
| 1027 } | 920 } |
| 1028 | 921 |
| 1029 void Comparator::WriteSnapshot(const Snapshot& sample, | 922 void Comparator::WriteSnapshot(const Snapshot& sample, |
| 1030 std::string* output) const { | 923 std::string* output) const { |
| 1031 sample.death_data().Write(output); | 924 sample.death_data().Write(output); |
| 1032 if (!(combined_selectors_ & BIRTH_THREAD) || | 925 if (!(combined_selectors_ & BIRTH_THREAD) || |
| 1033 !(combined_selectors_ & DEATH_THREAD)) | 926 !(combined_selectors_ & DEATH_THREAD)) |
| 1034 base::StringAppendF(output, "%s->%s ", | 927 base::StringAppendF(output, "%s->%s ", |
| 1035 (combined_selectors_ & BIRTH_THREAD) ? "*" : | 928 (combined_selectors_ & BIRTH_THREAD) ? "*" : |
| 1036 sample.birth().birth_thread()->ThreadName().c_str(), | 929 sample.birth().birth_thread()->thread_name().c_str(), |
| 1037 (combined_selectors_ & DEATH_THREAD) ? "*" : | 930 (combined_selectors_ & DEATH_THREAD) ? "*" : |
| 1038 sample.DeathThreadName().c_str()); | 931 sample.DeathThreadName().c_str()); |
| 1039 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), | 932 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), |
| 1040 !(combined_selectors_ & BIRTH_FUNCTION), | 933 !(combined_selectors_ & BIRTH_FUNCTION), |
| 1041 output); | 934 output); |
| 1042 } | 935 } |
| 1043 | 936 |
| 1044 } // namespace tracked_objects | 937 } // namespace tracked_objects |
| OLD | NEW |