| 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& queue_duration, |
| 35 const TimeDelta& run_duration) { |
| 31 ++count_; | 36 ++count_; |
| 32 life_duration_ += duration; | 37 queue_duration_ += queue_duration; |
| 33 int64 milliseconds = duration.InMilliseconds(); | 38 run_duration_ += run_duration; |
| 34 square_duration_ += milliseconds * milliseconds; | |
| 35 } | 39 } |
| 36 | 40 |
| 37 int DeathData::AverageMsDuration() const { | 41 int DeathData::AverageMsRunDuration() const { |
| 38 return static_cast<int>(life_duration_.InMilliseconds() / count_); | 42 return static_cast<int>(run_duration_.InMilliseconds() / count_); |
| 39 } | 43 } |
| 40 | 44 |
| 41 double DeathData::StandardDeviation() const { | 45 int DeathData::AverageMsQueueDuration() const { |
| 42 double average = AverageMsDuration(); | 46 return static_cast<int>(queue_duration_.InMilliseconds() / count_); |
| 43 double variance = static_cast<float>(square_duration_)/count_ | |
| 44 - average * average; | |
| 45 return sqrt(variance); | |
| 46 } | 47 } |
| 47 | 48 |
| 48 | |
| 49 void DeathData::AddDeathData(const DeathData& other) { | 49 void DeathData::AddDeathData(const DeathData& other) { |
| 50 count_ += other.count_; | 50 count_ += other.count_; |
| 51 life_duration_ += other.life_duration_; | 51 queue_duration_ += other.queue_duration_; |
| 52 square_duration_ += other.square_duration_; | 52 run_duration_ += other.run_duration_; |
| 53 } | 53 } |
| 54 | 54 |
| 55 void DeathData::Write(std::string* output) const { | 55 void DeathData::Write(std::string* output) const { |
| 56 if (!count_) | 56 if (!count_) |
| 57 return; | 57 return; |
| 58 if (1 == count_) { | 58 base::StringAppendF(output, "%s:%d, ", |
| 59 base::StringAppendF(output, "(1)Life in %dms ", AverageMsDuration()); | 59 (count_ == 1) ? "Life" : "Lives", count_); |
| 60 } else { | 60 base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ", |
| 61 base::StringAppendF(output, "(%d)Lives %dms/life ", | 61 run_duration_.InMilliseconds(), |
| 62 count_, AverageMsDuration()); | 62 AverageMsRunDuration()); |
| 63 } | 63 base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ", |
| 64 queue_duration_.InMilliseconds(), |
| 65 AverageMsQueueDuration()); |
| 64 } | 66 } |
| 65 | 67 |
| 66 void DeathData::Clear() { | 68 void DeathData::Clear() { |
| 67 count_ = 0; | 69 count_ = 0; |
| 68 life_duration_ = TimeDelta(); | 70 queue_duration_ = TimeDelta(); |
| 69 square_duration_ = 0; | 71 run_duration_ = TimeDelta(); |
| 70 } | 72 } |
| 71 | 73 |
| 72 //------------------------------------------------------------------------------ | 74 //------------------------------------------------------------------------------ |
| 73 BirthOnThread::BirthOnThread(const Location& location) | 75 BirthOnThread::BirthOnThread(const Location& location) |
| 74 : location_(location), | 76 : location_(location), |
| 75 birth_thread_(ThreadData::current()) { } | 77 birth_thread_(ThreadData::Get()) { } |
| 76 | 78 |
| 77 //------------------------------------------------------------------------------ | 79 //------------------------------------------------------------------------------ |
| 78 Births::Births(const Location& location) | 80 Births::Births(const Location& location) |
| 79 : BirthOnThread(location), | 81 : BirthOnThread(location), |
| 80 birth_count_(1) { } | 82 birth_count_(1) { } |
| 81 | 83 |
| 82 //------------------------------------------------------------------------------ | 84 //------------------------------------------------------------------------------ |
| 83 // ThreadData maintains the central data for all births and death. | 85 // ThreadData maintains the central data for all births and death. |
| 84 | 86 |
| 85 // static | 87 // static |
| 86 ThreadData* ThreadData::first_ = NULL; | 88 ThreadData* ThreadData::first_ = NULL; |
| 87 // static | 89 // static |
| 88 base::Lock ThreadData::list_lock_; | 90 base::Lock ThreadData::list_lock_; |
| 89 | 91 |
| 90 // static | 92 // static |
| 91 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 93 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
| 92 | 94 |
| 95 ThreadData::ThreadData(const std::string& suggested_name) : next_(NULL) { |
| 96 DCHECK_GE(suggested_name.size(), 0u); |
| 97 thread_name_ = suggested_name; |
| 98 } |
| 99 |
| 93 ThreadData::ThreadData() : next_(NULL) { | 100 ThreadData::ThreadData() : next_(NULL) { |
| 94 // This shouldn't use the MessageLoop::current() LazyInstance since this might | 101 int thread_number; |
| 95 // be used on a non-joinable thread. | 102 { |
| 96 // http://crbug.com/62728 | 103 base::AutoLock lock(list_lock_); |
| 97 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | 104 thread_number = ++thread_number_counter; |
| 98 message_loop_ = MessageLoop::current(); | 105 } |
| 106 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); |
| 99 } | 107 } |
| 100 | 108 |
| 101 ThreadData::~ThreadData() {} | 109 ThreadData::~ThreadData() {} |
| 102 | 110 |
| 103 // static | 111 // static |
| 104 ThreadData* ThreadData::current() { | 112 void ThreadData::InitializeThreadContext(const std::string& suggested_name) { |
| 105 if (!tls_index_.initialized()) | 113 if (!tls_index_.initialized()) |
| 106 return NULL; | 114 return; // For unittests only. |
| 107 | 115 RegisterCurrentContext(new ThreadData(suggested_name)); |
| 108 ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get()); | |
| 109 if (!registry) { | |
| 110 // We have to create a new registry for ThreadData. | |
| 111 bool too_late_to_create = false; | |
| 112 { | |
| 113 registry = new ThreadData; | |
| 114 base::AutoLock lock(list_lock_); | |
| 115 // Use lock to insure we have most recent status. | |
| 116 if (!IsActive()) { | |
| 117 too_late_to_create = true; | |
| 118 } else { | |
| 119 // Use lock to insert into list. | |
| 120 registry->next_ = first_; | |
| 121 first_ = registry; | |
| 122 } | |
| 123 } // Release lock. | |
| 124 if (too_late_to_create) { | |
| 125 delete registry; | |
| 126 registry = NULL; | |
| 127 } else { | |
| 128 tls_index_.Set(registry); | |
| 129 } | |
| 130 } | |
| 131 return registry; | |
| 132 } | 116 } |
| 133 | 117 |
| 134 // static | 118 // static |
| 119 ThreadData* ThreadData::Get() { |
| 120 if (!tls_index_.initialized()) |
| 121 return NULL; // For unittests only. |
| 122 ThreadData* registered = static_cast<ThreadData*>(tls_index_.Get()); |
| 123 if (!registered) { |
| 124 // We have to create a new registry entry for this ThreadData. |
| 125 // TODO(jar): Host all unamed (Worker) threads in *one* ThreadData instance, |
| 126 // (with locking protection on that instance) or else recycle and re-use |
| 127 // worker thread ThreadData when the worker thread terminates. |
| 128 registered = RegisterCurrentContext(new ThreadData()); |
| 129 } |
| 130 return registered; |
| 131 } |
| 132 |
| 133 // static |
| 134 ThreadData* ThreadData::RegisterCurrentContext(ThreadData* unregistered) { |
| 135 DCHECK_EQ(tls_index_.Get(), static_cast<void*>(0)); |
| 136 bool too_late_to_register = false; |
| 137 { |
| 138 base::AutoLock lock(list_lock_); |
| 139 // Use lock to insure we have most recent status. |
| 140 if (!IsActive()) { |
| 141 too_late_to_register = true; |
| 142 } else { |
| 143 // Use list_lock_ to insert as new head of list. |
| 144 unregistered->next_ = first_; |
| 145 first_ = unregistered; |
| 146 } |
| 147 } // Release lock. |
| 148 if (too_late_to_register) { |
| 149 delete unregistered; |
| 150 unregistered = NULL; |
| 151 } else { |
| 152 tls_index_.Set(unregistered); |
| 153 } |
| 154 return unregistered; |
| 155 } |
| 156 |
| 157 // static |
| 135 void ThreadData::WriteHTML(const std::string& query, std::string* output) { | 158 void ThreadData::WriteHTML(const std::string& query, std::string* output) { |
| 136 if (!ThreadData::IsActive()) | 159 if (!ThreadData::IsActive()) |
| 137 return; // Not yet initialized. | 160 return; // Not yet initialized. |
| 138 | 161 |
| 139 DCHECK(ThreadData::current()); | |
| 140 DataCollector collected_data; // Gather data. | 162 DataCollector collected_data; // Gather data. |
| 141 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 163 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
| 142 | 164 |
| 143 // Data Gathering is complete. Now to sort/process/render. | 165 // Data Gathering is complete. Now to sort/process/render. |
| 144 DataCollector::Collection* collection = collected_data.collection(); | 166 DataCollector::Collection* collection = collected_data.collection(); |
| 145 | 167 |
| 146 // Create filtering and sort comparison object. | 168 // Create filtering and sort comparison object. |
| 147 Comparator comparator; | 169 Comparator comparator; |
| 148 comparator.ParseQuery(query); | 170 comparator.ParseQuery(query); |
| 149 | 171 |
| 150 // Filter out acceptable (matching) instances. | 172 // Filter out acceptable (matching) instances. |
| 151 DataCollector::Collection match_array; | 173 DataCollector::Collection match_array; |
| 152 for (DataCollector::Collection::iterator it = collection->begin(); | 174 for (DataCollector::Collection::iterator it = collection->begin(); |
| 153 it != collection->end(); ++it) { | 175 it != collection->end(); ++it) { |
| 154 if (comparator.Acceptable(*it)) | 176 if (comparator.Acceptable(*it)) |
| 155 match_array.push_back(*it); | 177 match_array.push_back(*it); |
| 156 } | 178 } |
| 157 | 179 |
| 158 comparator.Sort(&match_array); | 180 comparator.Sort(&match_array); |
| 159 | 181 |
| 160 WriteHTMLTotalAndSubtotals(match_array, comparator, output); | 182 WriteHTMLTotalAndSubtotals(match_array, comparator, output); |
| 161 | 183 |
| 162 comparator.Clear(); // Delete tiebreaker_ instances. | 184 comparator.Clear(); // Delete tiebreaker_ instances. |
| 163 | 185 |
| 164 output->append("</pre>"); | 186 output->append("</pre>"); |
| 165 | 187 |
| 166 const char* help_string = "The following are the keywords that can be used to" | 188 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>" | 189 " sort and aggregate the data, or to select data.<br><ul>" |
| 168 "<li><b>count</b> Number of instances seen." | 190 "<li><b>Count</b> Number of instances seen." |
| 169 "<li><b>duration</b> Duration in ms from construction to descrution." | 191 "<li><b>Duration</b> Average duration in ms of Run() time." |
| 170 "<li><b>birth</b> Thread on which the task was constructed." | 192 "<li><b>TotalDuration</b> Summed durations in ms of Run() times." |
| 171 "<li><b>death</b> Thread on which the task was run and deleted." | 193 "<li><b>AverageQueueDuration</b> Average duration in ms of queueing time." |
| 172 "<li><b>file</b> File in which the task was contructed." | 194 "<li><b>TotalQueueDuration</b> Summed durations in ms of Run() times." |
| 173 "<li><b>function</b> Function in which the task was constructed." | 195 "<li><b>Birth</b> Thread on which the task was constructed." |
| 174 "<li><b>line</b> Line number of the file in which the task was constructed." | 196 "<li><b>Death</b> Thread on which the task was run and deleted." |
| 197 "<li><b>File</b> File in which the task was contructed." |
| 198 "<li><b>Function</b> Function in which the task was constructed." |
| 199 "<li><b>Line</b> Line number of the file in which the task was constructed." |
| 175 "</ul><br>" | 200 "</ul><br>" |
| 176 "As examples:<ul>" | 201 "As examples:<ul>" |
| 177 "<li><b>about:tracking/file</b> would sort the above data by file, and" | 202 "<li><b>about:tracking/file</b> would sort the above data by file, and" |
| 178 " aggregate data on a per-file basis." | 203 " aggregate data on a per-file basis." |
| 179 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" | 204 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" |
| 180 " constructed in a file containing the text |Dns|." | 205 " constructed in a file containing the text |Dns|." |
| 206 "<li><b>about:tracking/death/duration</b> would sort the data by death" |
| 207 " thread(i.e., where tasks ran) and then by the average runtime for the" |
| 208 " tasks. Form an aggregation group, one per thread, showing the results on" |
| 209 " each thread." |
| 181 "<li><b>about:tracking/birth/death</b> would sort the above list by birth" | 210 "<li><b>about:tracking/birth/death</b> would sort the above list by birth" |
| 182 " thread, and then by death thread, and would aggregate data for each pair" | 211 " thread, and then by death thread, and would aggregate data for each pair" |
| 183 " of lifetime events." | 212 " of lifetime events." |
| 184 "</ul>" | 213 "</ul>" |
| 185 " The data can be reset to zero (discarding all births, deaths, etc.) using" | 214 " The data can be reset to zero (discarding all births, deaths, etc.) using" |
| 186 " <b>about:tracking/reset</b>. The existing stats will be displayed, but" | 215 " <b>about:tracking/reset</b>. The existing stats will be displayed, but" |
| 187 " the internal stats will be set to zero, and start accumulating afresh." | 216 " the internal stats will be set to zero, and start accumulating afresh." |
| 188 " This option is very helpful if you only wish to consider tasks created" | 217 " This option is very helpful if you only wish to consider tasks created" |
| 189 " after some point in time.<br><br>" | 218 " after some point in time.<br><br>" |
| 190 "If you wish to monitor Renderer events, be sure to run in --single-process" | 219 "If you wish to monitor Renderer events, be sure to run in --single-process" |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 output->append("<br>"); | 256 output->append("<br>"); |
| 228 subtotals.Write(output); | 257 subtotals.Write(output); |
| 229 output->append("<br><hr><br>"); | 258 output->append("<br><hr><br>"); |
| 230 subtotals.Clear(); | 259 subtotals.Clear(); |
| 231 } | 260 } |
| 232 } | 261 } |
| 233 } | 262 } |
| 234 } | 263 } |
| 235 | 264 |
| 236 Births* ThreadData::TallyABirth(const Location& location) { | 265 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 | |
| 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); | 266 BirthMap::iterator it = birth_map_.find(location); |
| 247 if (it != birth_map_.end()) { | 267 if (it != birth_map_.end()) { |
| 248 it->second->RecordBirth(); | 268 it->second->RecordBirth(); |
| 249 return it->second; | 269 return it->second; |
| 250 } | 270 } |
| 251 | 271 |
| 252 Births* tracker = new Births(location); | 272 Births* tracker = new Births(location); |
| 253 // Lock since the map may get relocated now, and other threads sometimes | 273 // Lock since the map may get relocated now, and other threads sometimes |
| 254 // snapshot it (but they lock before copying it). | 274 // snapshot it (but they lock before copying it). |
| 255 base::AutoLock lock(lock_); | 275 base::AutoLock lock(lock_); |
| 256 birth_map_[location] = tracker; | 276 birth_map_[location] = tracker; |
| 257 return tracker; | 277 return tracker; |
| 258 } | 278 } |
| 259 | 279 |
| 260 void ThreadData::TallyADeath(const Births& lifetimes, | 280 void ThreadData::TallyADeath(const Births& the_birth, |
| 261 const TimeDelta& duration) { | 281 const TimeDelta& queue_duration, |
| 262 { | 282 const TimeDelta& run_duration) { |
| 263 // http://crbug.com/62728 | 283 DeathMap::iterator it = death_map_.find(&the_birth); |
| 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()) { | 284 if (it != death_map_.end()) { |
| 271 it->second.RecordDeath(duration); | 285 it->second.RecordDeath(queue_duration, run_duration); |
| 272 return; | 286 return; |
| 273 } | 287 } |
| 274 | 288 |
| 275 base::AutoLock lock(lock_); // Lock since the map may get relocated now. | 289 base::AutoLock lock(lock_); // Lock since the map may get relocated now. |
| 276 death_map_[&lifetimes].RecordDeath(duration); | 290 death_map_[&the_birth].RecordDeath(queue_duration, run_duration); |
| 277 } | 291 } |
| 278 | 292 |
| 279 // static | 293 // static |
| 280 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 294 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
| 281 if (IsActive()) { | 295 #if !defined(TRACK_ALL_TASK_OBJECTS) |
| 282 ThreadData* current_thread_data = current(); | 296 return NULL; // Not compiled in. |
| 283 if (current_thread_data) { | 297 #else |
| 284 return current_thread_data->TallyABirth(location); | 298 if (!IsActive()) |
| 285 } | 299 return NULL; |
| 286 } | 300 ThreadData* current_thread_data = Get(); |
| 287 | 301 if (!current_thread_data) |
| 288 return NULL; | 302 return NULL; |
| 303 return current_thread_data->TallyABirth(location); |
| 304 #endif |
| 289 } | 305 } |
| 290 | 306 |
| 291 // static | 307 // static |
| 292 void ThreadData::TallyADeathIfActive(const Births* the_birth, | 308 void ThreadData::TallyADeathIfActive(const Births* the_birth, |
| 293 const base::TimeDelta& duration) { | 309 const base::TimeTicks& time_posted, |
| 294 if (IsActive() && the_birth) { | 310 const base::TimeTicks& delayed_start_time, |
| 295 current()->TallyADeath(*the_birth, duration); | 311 const base::TimeTicks& start_of_run) { |
| 296 } | 312 #if !defined(TRACK_ALL_TASK_OBJECTS) |
| 313 return; // Not compiled in. |
| 314 #else |
| 315 if (!IsActive() || !the_birth) |
| 316 return; |
| 317 ThreadData* current_thread_data = Get(); |
| 318 if (!current_thread_data) |
| 319 return; |
| 320 |
| 321 // To avoid conflating our stats with the delay duration in a PostDelayedTask, |
| 322 // we identify such tasks, and replace their post_time with the time they |
| 323 // were sechudled (requested?) to emerge from the delayed task queue. This |
| 324 // means that queueing delay for such tasks will show how long they went |
| 325 // unserviced, after they *could* be serviced. This is the same stat as we |
| 326 // have for non-delayed tasks, and we consistently call it queueing delay. |
| 327 base::TimeTicks effective_post_time = |
| 328 (delayed_start_time.is_null()) ? time_posted : delayed_start_time; |
| 329 base::TimeDelta queue_duration = start_of_run - effective_post_time; |
| 330 base::TimeDelta run_duration = Now() - start_of_run; |
| 331 current_thread_data->TallyADeath(*the_birth, queue_duration, run_duration); |
| 332 #endif |
| 297 } | 333 } |
| 298 | 334 |
| 299 // static | 335 // static |
| 300 ThreadData* ThreadData::first() { | 336 ThreadData* ThreadData::first() { |
| 301 base::AutoLock lock(list_lock_); | 337 base::AutoLock lock(list_lock_); |
| 302 return first_; | 338 return first_; |
| 303 } | 339 } |
| 304 | 340 |
| 305 const std::string ThreadData::ThreadName() const { | |
| 306 if (message_loop_) | |
| 307 return message_loop_->thread_name(); | |
| 308 return "ThreadWithoutMessageLoop"; | |
| 309 } | |
| 310 | |
| 311 // This may be called from another thread. | 341 // This may be called from another thread. |
| 312 void ThreadData::SnapshotBirthMap(BirthMap *output) const { | 342 void ThreadData::SnapshotBirthMap(BirthMap *output) const { |
| 313 base::AutoLock lock(lock_); | 343 base::AutoLock lock(lock_); |
| 314 for (BirthMap::const_iterator it = birth_map_.begin(); | 344 for (BirthMap::const_iterator it = birth_map_.begin(); |
| 315 it != birth_map_.end(); ++it) | 345 it != birth_map_.end(); ++it) |
| 316 (*output)[it->first] = it->second; | 346 (*output)[it->first] = it->second; |
| 317 } | 347 } |
| 318 | 348 |
| 319 // This may be called from another thread. | 349 // This may be called from another thread. |
| 320 void ThreadData::SnapshotDeathMap(DeathMap *output) const { | 350 void ThreadData::SnapshotDeathMap(DeathMap *output) const { |
| 321 base::AutoLock lock(lock_); | 351 base::AutoLock lock(lock_); |
| 322 for (DeathMap::const_iterator it = death_map_.begin(); | 352 for (DeathMap::const_iterator it = death_map_.begin(); |
| 323 it != death_map_.end(); ++it) | 353 it != death_map_.end(); ++it) |
| 324 (*output)[it->first] = it->second; | 354 (*output)[it->first] = it->second; |
| 325 } | 355 } |
| 326 | 356 |
| 327 // static | 357 // static |
| 328 void ThreadData::ResetAllThreadData() { | 358 void ThreadData::ResetAllThreadData() { |
| 329 ThreadData* my_list = ThreadData::current()->first(); | 359 ThreadData* my_list = Get()->first(); |
| 330 | 360 |
| 331 for (ThreadData* thread_data = my_list; | 361 for (ThreadData* thread_data = my_list; |
| 332 thread_data; | 362 thread_data; |
| 333 thread_data = thread_data->next()) | 363 thread_data = thread_data->next()) |
| 334 thread_data->Reset(); | 364 thread_data->Reset(); |
| 335 } | 365 } |
| 336 | 366 |
| 337 void ThreadData::Reset() { | 367 void ThreadData::Reset() { |
| 338 base::AutoLock lock(lock_); | 368 base::AutoLock lock(lock_); |
| 339 for (DeathMap::iterator it = death_map_.begin(); | 369 for (DeathMap::iterator it = death_map_.begin(); |
| 340 it != death_map_.end(); ++it) | 370 it != death_map_.end(); ++it) |
| 341 it->second.Clear(); | 371 it->second.Clear(); |
| 342 for (BirthMap::iterator it = birth_map_.begin(); | 372 for (BirthMap::iterator it = birth_map_.begin(); |
| 343 it != birth_map_.end(); ++it) | 373 it != birth_map_.end(); ++it) |
| 344 it->second->Clear(); | 374 it->second->Clear(); |
| 345 } | 375 } |
| 346 | 376 |
| 347 #ifdef OS_WIN | |
| 348 // A class used to count down which is accessed by several threads. This is | |
| 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()); | |
| 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 | 377 // static |
| 445 bool ThreadData::StartTracking(bool status) { | 378 bool ThreadData::StartTracking(bool status) { |
| 446 #if !defined(TRACK_ALL_TASK_OBJECTS) | 379 #if !defined(TRACK_ALL_TASK_OBJECTS) |
| 447 return false; // Not compiled in. | 380 return false; // Not compiled in. |
| 448 #endif | 381 #else |
| 449 | |
| 450 if (!status) { | 382 if (!status) { |
| 451 base::AutoLock lock(list_lock_); | 383 base::AutoLock lock(list_lock_); |
| 452 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); | 384 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); |
| 453 status_ = SHUTDOWN; | 385 status_ = SHUTDOWN; |
| 454 return true; | 386 return true; |
| 455 } | 387 } |
| 456 base::AutoLock lock(list_lock_); | 388 base::AutoLock lock(list_lock_); |
| 457 DCHECK_EQ(UNINITIALIZED, status_); | 389 DCHECK_EQ(UNINITIALIZED, status_); |
| 458 CHECK(tls_index_.Initialize(NULL)); | 390 CHECK(tls_index_.Initialize(NULL)); |
| 459 status_ = ACTIVE; | 391 status_ = ACTIVE; |
| 460 return true; | 392 return true; |
| 393 #endif |
| 461 } | 394 } |
| 462 | 395 |
| 463 // static | 396 // static |
| 464 bool ThreadData::IsActive() { | 397 bool ThreadData::IsActive() { |
| 465 return status_ == ACTIVE; | 398 return status_ == ACTIVE; |
| 466 } | 399 } |
| 467 | 400 |
| 468 #ifdef OS_WIN | |
| 469 // static | 401 // static |
| 470 void ThreadData::ShutdownMultiThreadTracking() { | 402 base::TimeTicks ThreadData::Now() { |
| 471 // Using lock, guarantee that no new ThreadData instances will be created. | 403 #if defined(TRACK_ALL_TASK_OBJECTS) |
| 472 if (!StartTracking(false)) | 404 if (status_ == ACTIVE) |
| 473 return; | 405 return base::TimeTicks::Now(); |
| 474 | 406 #endif |
| 475 RunOnAllThreads(ShutdownDisablingFurtherTracking); | 407 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 } | 408 } |
| 485 #endif | |
| 486 | 409 |
| 487 // static | 410 // static |
| 488 void ThreadData::ShutdownSingleThreadedCleanup() { | 411 void ThreadData::ShutdownSingleThreadedCleanup() { |
| 489 // We must be single threaded... but be careful anyway. | 412 // We must be single threaded... but be careful anyway. |
| 490 if (!StartTracking(false)) | 413 if (!StartTracking(false)) |
| 491 return; | 414 return; |
| 492 ThreadData* thread_data_list; | 415 ThreadData* thread_data_list; |
| 493 { | 416 { |
| 494 base::AutoLock lock(list_lock_); | 417 base::AutoLock lock(list_lock_); |
| 495 thread_data_list = first_; | 418 thread_data_list = first_; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 507 next_thread_data->death_map_.clear(); | 430 next_thread_data->death_map_.clear(); |
| 508 delete next_thread_data; // Includes all Death Records. | 431 delete next_thread_data; // Includes all Death Records. |
| 509 } | 432 } |
| 510 | 433 |
| 511 CHECK(tls_index_.initialized()); | 434 CHECK(tls_index_.initialized()); |
| 512 tls_index_.Free(); | 435 tls_index_.Free(); |
| 513 DCHECK(!tls_index_.initialized()); | 436 DCHECK(!tls_index_.initialized()); |
| 514 status_ = UNINITIALIZED; | 437 status_ = UNINITIALIZED; |
| 515 } | 438 } |
| 516 | 439 |
| 517 // static | |
| 518 void ThreadData::ShutdownDisablingFurtherTracking() { | |
| 519 // Redundantly set status SHUTDOWN on this thread. | |
| 520 if (!StartTracking(false)) | |
| 521 return; | |
| 522 } | |
| 523 | |
| 524 //------------------------------------------------------------------------------ | 440 //------------------------------------------------------------------------------ |
| 525 // Individual 3-tuple of birth (place and thread) along with death thread, and | 441 // Individual 3-tuple of birth (place and thread) along with death thread, and |
| 526 // the accumulated stats for instances (DeathData). | 442 // the accumulated stats for instances (DeathData). |
| 527 | 443 |
| 528 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, | 444 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, |
| 529 const ThreadData& death_thread, | 445 const ThreadData& death_thread, |
| 530 const DeathData& death_data) | 446 const DeathData& death_data) |
| 531 : birth_(&birth_on_thread), | 447 : birth_(&birth_on_thread), |
| 532 death_thread_(&death_thread), | 448 death_thread_(&death_thread), |
| 533 death_data_(death_data) { | 449 death_data_(death_data) { |
| 534 } | 450 } |
| 535 | 451 |
| 536 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) | 452 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) |
| 537 : birth_(&birth_on_thread), | 453 : birth_(&birth_on_thread), |
| 538 death_thread_(NULL), | 454 death_thread_(NULL), |
| 539 death_data_(DeathData(count)) { | 455 death_data_(DeathData(count)) { |
| 540 } | 456 } |
| 541 | 457 |
| 542 const std::string Snapshot::DeathThreadName() const { | 458 const std::string Snapshot::DeathThreadName() const { |
| 543 if (death_thread_) | 459 if (death_thread_) |
| 544 return death_thread_->ThreadName(); | 460 return death_thread_->thread_name(); |
| 545 return "Still_Alive"; | 461 return "Still_Alive"; |
| 546 } | 462 } |
| 547 | 463 |
| 548 void Snapshot::Write(std::string* output) const { | 464 void Snapshot::Write(std::string* output) const { |
| 549 death_data_.Write(output); | 465 death_data_.Write(output); |
| 550 base::StringAppendF(output, "%s->%s ", | 466 base::StringAppendF(output, "%s->%s ", |
| 551 birth_->birth_thread()->ThreadName().c_str(), | 467 birth_->birth_thread()->thread_name().c_str(), |
| 552 death_thread_->ThreadName().c_str()); | 468 death_thread_->thread_name().c_str()); |
| 553 birth_->location().Write(true, true, output); | 469 birth_->location().Write(true, true, output); |
| 554 } | 470 } |
| 555 | 471 |
| 556 void Snapshot::Add(const Snapshot& other) { | 472 void Snapshot::Add(const Snapshot& other) { |
| 557 death_data_.AddDeathData(other.death_data_); | 473 death_data_.AddDeathData(other.death_data_); |
| 558 } | 474 } |
| 559 | 475 |
| 560 //------------------------------------------------------------------------------ | 476 //------------------------------------------------------------------------------ |
| 561 // DataCollector | 477 // DataCollector |
| 562 | 478 |
| 563 DataCollector::DataCollector() { | 479 DataCollector::DataCollector() { |
| 564 DCHECK(ThreadData::IsActive()); | 480 DCHECK(ThreadData::IsActive()); |
| 565 | 481 |
| 566 // Get an unchanging copy of a ThreadData list. | 482 // Get an unchanging copy of a ThreadData list. |
| 567 ThreadData* my_list = ThreadData::current()->first(); | 483 ThreadData* my_list = ThreadData::Get()->first(); |
| 568 | 484 |
| 569 count_of_contributing_threads_ = 0; | 485 // Gather data serially. |
| 570 for (ThreadData* thread_data = my_list; | |
| 571 thread_data; | |
| 572 thread_data = thread_data->next()) { | |
| 573 ++count_of_contributing_threads_; | |
| 574 } | |
| 575 | |
| 576 // Gather data serially. A different constructor could be used to do in | |
| 577 // parallel, and then invoke an OnCompletion task. | |
| 578 // This hackish approach *can* get some slighly corrupt tallies, as we are | 486 // 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 | 487 // 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 | 488 // 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 | 489 // sees any strangeness, they can always just run their stats gathering a |
| 582 // second time. | 490 // 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; | 491 for (ThreadData* thread_data = my_list; |
| 587 thread_data; | 492 thread_data; |
| 588 thread_data = thread_data->next()) { | 493 thread_data = thread_data->next()) { |
| 589 Append(*thread_data); | 494 Append(*thread_data); |
| 590 } | 495 } |
| 591 } | 496 } |
| 592 | 497 |
| 593 DataCollector::~DataCollector() { | 498 DataCollector::~DataCollector() { |
| 594 } | 499 } |
| 595 | 500 |
| 596 void DataCollector::Append(const ThreadData& thread_data) { | 501 void DataCollector::Append(const ThreadData& thread_data) { |
| 597 // Get copy of data (which is done under ThreadData's lock). | 502 // Get copy of data. |
| 598 ThreadData::BirthMap birth_map; | 503 ThreadData::BirthMap birth_map; |
| 599 thread_data.SnapshotBirthMap(&birth_map); | 504 thread_data.SnapshotBirthMap(&birth_map); |
| 600 ThreadData::DeathMap death_map; | 505 ThreadData::DeathMap death_map; |
| 601 thread_data.SnapshotDeathMap(&death_map); | 506 thread_data.SnapshotDeathMap(&death_map); |
| 602 | 507 |
| 603 // Use our lock to protect our accumulation activity. | |
| 604 base::AutoLock lock(accumulation_lock_); | |
| 605 | |
| 606 DCHECK(count_of_contributing_threads_); | |
| 607 | |
| 608 for (ThreadData::DeathMap::const_iterator it = death_map.begin(); | 508 for (ThreadData::DeathMap::const_iterator it = death_map.begin(); |
| 609 it != death_map.end(); ++it) { | 509 it != death_map.end(); ++it) { |
| 610 collection_.push_back(Snapshot(*it->first, thread_data, it->second)); | 510 collection_.push_back(Snapshot(*it->first, thread_data, it->second)); |
| 611 global_birth_count_[it->first] -= it->first->birth_count(); | 511 global_birth_count_[it->first] -= it->first->birth_count(); |
| 612 } | 512 } |
| 613 | 513 |
| 614 for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); | 514 for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); |
| 615 it != birth_map.end(); ++it) { | 515 it != birth_map.end(); ++it) { |
| 616 global_birth_count_[it->second] += it->second->birth_count(); | 516 global_birth_count_[it->second] += it->second->birth_count(); |
| 617 } | 517 } |
| 618 | |
| 619 --count_of_contributing_threads_; | |
| 620 } | 518 } |
| 621 | 519 |
| 622 DataCollector::Collection* DataCollector::collection() { | 520 DataCollector::Collection* DataCollector::collection() { |
| 623 DCHECK(!count_of_contributing_threads_); | |
| 624 return &collection_; | 521 return &collection_; |
| 625 } | 522 } |
| 626 | 523 |
| 627 void DataCollector::AddListOfLivingObjects() { | 524 void DataCollector::AddListOfLivingObjects() { |
| 628 DCHECK(!count_of_contributing_threads_); | |
| 629 for (BirthCount::iterator it = global_birth_count_.begin(); | 525 for (BirthCount::iterator it = global_birth_count_.begin(); |
| 630 it != global_birth_count_.end(); ++it) { | 526 it != global_birth_count_.end(); ++it) { |
| 631 if (it->second > 0) | 527 if (it->second > 0) |
| 632 collection_.push_back(Snapshot(*it->first, it->second)); | 528 collection_.push_back(Snapshot(*it->first, it->second)); |
| 633 } | 529 } |
| 634 } | 530 } |
| 635 | 531 |
| 636 //------------------------------------------------------------------------------ | 532 //------------------------------------------------------------------------------ |
| 637 // Aggregation | 533 // Aggregation |
| 638 | 534 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 674 base::StringAppendF(output, "All born in %s. ", | 570 base::StringAppendF(output, "All born in %s. ", |
| 675 birth_files_.begin()->first.c_str()); | 571 birth_files_.begin()->first.c_str()); |
| 676 } | 572 } |
| 677 } | 573 } |
| 678 | 574 |
| 679 if (birth_threads_.size() > 1) { | 575 if (birth_threads_.size() > 1) { |
| 680 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", | 576 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", |
| 681 birth_threads_.size()); | 577 birth_threads_.size()); |
| 682 } else { | 578 } else { |
| 683 base::StringAppendF(output, "All born on %s. ", | 579 base::StringAppendF(output, "All born on %s. ", |
| 684 birth_threads_.begin()->first->ThreadName().c_str()); | 580 birth_threads_.begin()->first->thread_name().c_str()); |
| 685 } | 581 } |
| 686 | 582 |
| 687 if (death_threads_.size() > 1) { | 583 if (death_threads_.size() > 1) { |
| 688 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", | 584 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", |
| 689 death_threads_.size()); | 585 death_threads_.size()); |
| 690 } else { | 586 } else { |
| 691 if (death_threads_.begin()->first) { | 587 if (death_threads_.begin()->first) { |
| 692 base::StringAppendF(output, "All deleted on %s. ", | 588 base::StringAppendF(output, "All deleted on %s. ", |
| 693 death_threads_.begin()->first->ThreadName().c_str()); | 589 death_threads_.begin()->first->thread_name().c_str()); |
| 694 } else { | 590 } else { |
| 695 output->append("All these objects are still alive."); | 591 output->append("All these objects are still alive."); |
| 696 } | 592 } |
| 697 } | 593 } |
| 698 | 594 |
| 699 if (birth_count_ > 1) | 595 if (birth_count_ > 1) |
| 700 base::StringAppendF(output, "Births=%d ", birth_count_); | 596 base::StringAppendF(output, "Births=%d ", birth_count_); |
| 701 | 597 |
| 702 DeathData::Write(output); | 598 DeathData::Write(output); |
| 703 } | 599 } |
| (...skipping 24 matching lines...) Expand all Loading... |
| 728 } | 624 } |
| 729 use_tiebreaker_for_sort_only_ = false; | 625 use_tiebreaker_for_sort_only_ = false; |
| 730 selector_ = NIL; | 626 selector_ = NIL; |
| 731 } | 627 } |
| 732 | 628 |
| 733 bool Comparator::operator()(const Snapshot& left, | 629 bool Comparator::operator()(const Snapshot& left, |
| 734 const Snapshot& right) const { | 630 const Snapshot& right) const { |
| 735 switch (selector_) { | 631 switch (selector_) { |
| 736 case BIRTH_THREAD: | 632 case BIRTH_THREAD: |
| 737 if (left.birth_thread() != right.birth_thread() && | 633 if (left.birth_thread() != right.birth_thread() && |
| 738 left.birth_thread()->ThreadName() != | 634 left.birth_thread()->thread_name() != |
| 739 right.birth_thread()->ThreadName()) | 635 right.birth_thread()->thread_name()) |
| 740 return left.birth_thread()->ThreadName() < | 636 return left.birth_thread()->thread_name() < |
| 741 right.birth_thread()->ThreadName(); | 637 right.birth_thread()->thread_name(); |
| 742 break; | 638 break; |
| 743 | 639 |
| 744 case DEATH_THREAD: | 640 case DEATH_THREAD: |
| 745 if (left.death_thread() != right.death_thread() && | 641 if (left.death_thread() != right.death_thread() && |
| 746 left.DeathThreadName() != | 642 left.DeathThreadName() != |
| 747 right.DeathThreadName()) { | 643 right.DeathThreadName()) { |
| 748 if (!left.death_thread()) | 644 if (!left.death_thread()) |
| 749 return true; | 645 return true; |
| 750 if (!right.death_thread()) | 646 if (!right.death_thread()) |
| 751 return false; | 647 return false; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 776 if (left.location().line_number() != right.location().line_number()) | 672 if (left.location().line_number() != right.location().line_number()) |
| 777 return left.location().line_number() < | 673 return left.location().line_number() < |
| 778 right.location().line_number(); | 674 right.location().line_number(); |
| 779 break; | 675 break; |
| 780 | 676 |
| 781 case COUNT: | 677 case COUNT: |
| 782 if (left.count() != right.count()) | 678 if (left.count() != right.count()) |
| 783 return left.count() > right.count(); // Sort large at front of vector. | 679 return left.count() > right.count(); // Sort large at front of vector. |
| 784 break; | 680 break; |
| 785 | 681 |
| 786 case AVERAGE_DURATION: | 682 case AVERAGE_RUN_DURATION: |
| 787 if (!left.count() || !right.count()) | 683 if (!left.count() || !right.count()) |
| 788 break; | 684 break; |
| 789 if (left.AverageMsDuration() != right.AverageMsDuration()) | 685 if (left.AverageMsRunDuration() != right.AverageMsRunDuration()) |
| 790 return left.AverageMsDuration() > right.AverageMsDuration(); | 686 return left.AverageMsRunDuration() > right.AverageMsRunDuration(); |
| 687 break; |
| 688 |
| 689 case TOTAL_RUN_DURATION: |
| 690 if (!left.count() || !right.count()) |
| 691 break; |
| 692 if (left.run_duration() != right.run_duration()) |
| 693 return left.run_duration() > right.run_duration(); |
| 694 break; |
| 695 |
| 696 case AVERAGE_QUEUE_DURATION: |
| 697 if (!left.count() || !right.count()) |
| 698 break; |
| 699 if (left.AverageMsQueueDuration() != right.AverageMsQueueDuration()) |
| 700 return left.AverageMsQueueDuration() > right.AverageMsQueueDuration(); |
| 701 break; |
| 702 |
| 703 case TOTAL_QUEUE_DURATION: |
| 704 if (!left.count() || !right.count()) |
| 705 break; |
| 706 if (left.queue_duration() != right.queue_duration()) |
| 707 return left.queue_duration() > right.queue_duration(); |
| 791 break; | 708 break; |
| 792 | 709 |
| 793 default: | 710 default: |
| 794 break; | 711 break; |
| 795 } | 712 } |
| 796 if (tiebreaker_) | 713 if (tiebreaker_) |
| 797 return tiebreaker_->operator()(left, right); | 714 return tiebreaker_->operator()(left, right); |
| 798 return false; | 715 return false; |
| 799 } | 716 } |
| 800 | 717 |
| 801 void Comparator::Sort(DataCollector::Collection* collection) const { | 718 void Comparator::Sort(DataCollector::Collection* collection) const { |
| 802 std::sort(collection->begin(), collection->end(), *this); | 719 std::sort(collection->begin(), collection->end(), *this); |
| 803 } | 720 } |
| 804 | 721 |
| 805 bool Comparator::Equivalent(const Snapshot& left, | 722 bool Comparator::Equivalent(const Snapshot& left, |
| 806 const Snapshot& right) const { | 723 const Snapshot& right) const { |
| 807 switch (selector_) { | 724 switch (selector_) { |
| 808 case BIRTH_THREAD: | 725 case BIRTH_THREAD: |
| 809 if (left.birth_thread() != right.birth_thread() && | 726 if (left.birth_thread() != right.birth_thread() && |
| 810 left.birth_thread()->ThreadName() != | 727 left.birth_thread()->thread_name() != |
| 811 right.birth_thread()->ThreadName()) | 728 right.birth_thread()->thread_name()) |
| 812 return false; | 729 return false; |
| 813 break; | 730 break; |
| 814 | 731 |
| 815 case DEATH_THREAD: | 732 case DEATH_THREAD: |
| 816 if (left.death_thread() != right.death_thread() && | 733 if (left.death_thread() != right.death_thread() && |
| 817 left.DeathThreadName() != right.DeathThreadName()) | 734 left.DeathThreadName() != right.DeathThreadName()) |
| 818 return false; | 735 return false; |
| 819 break; | 736 break; |
| 820 | 737 |
| 821 case BIRTH_FILE: | 738 case BIRTH_FILE: |
| 822 if (left.location().file_name() != right.location().file_name()) { | 739 if (left.location().file_name() != right.location().file_name()) { |
| 823 int comp = strcmp(left.location().file_name(), | 740 int comp = strcmp(left.location().file_name(), |
| 824 right.location().file_name()); | 741 right.location().file_name()); |
| 825 if (comp) | 742 if (comp) |
| 826 return false; | 743 return false; |
| 827 } | 744 } |
| 828 break; | 745 break; |
| 829 | 746 |
| 830 case BIRTH_FUNCTION: | 747 case BIRTH_FUNCTION: |
| 831 if (left.location().function_name() != right.location().function_name()) { | 748 if (left.location().function_name() != right.location().function_name()) { |
| 832 int comp = strcmp(left.location().function_name(), | 749 int comp = strcmp(left.location().function_name(), |
| 833 right.location().function_name()); | 750 right.location().function_name()); |
| 834 if (comp) | 751 if (comp) |
| 835 return false; | 752 return false; |
| 836 } | 753 } |
| 837 break; | 754 break; |
| 838 | 755 |
| 839 case COUNT: | 756 case COUNT: |
| 840 if (left.count() != right.count()) | 757 case AVERAGE_RUN_DURATION: |
| 841 return false; | 758 case TOTAL_RUN_DURATION: |
| 842 break; | 759 case AVERAGE_QUEUE_DURATION: |
| 843 | 760 case TOTAL_QUEUE_DURATION: |
| 844 case AVERAGE_DURATION: | 761 // We don't produce separate aggretation when only counts or times differ. |
| 845 if (left.life_duration() != right.life_duration()) | |
| 846 return false; | |
| 847 break; | 762 break; |
| 848 | 763 |
| 849 default: | 764 default: |
| 850 break; | 765 break; |
| 851 } | 766 } |
| 852 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) | 767 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) |
| 853 return tiebreaker_->Equivalent(left, right); | 768 return tiebreaker_->Equivalent(left, right); |
| 854 return true; | 769 return true; |
| 855 } | 770 } |
| 856 | 771 |
| 857 bool Comparator::Acceptable(const Snapshot& sample) const { | 772 bool Comparator::Acceptable(const Snapshot& sample) const { |
| 858 if (required_.size()) { | 773 if (required_.size()) { |
| 859 switch (selector_) { | 774 switch (selector_) { |
| 860 case BIRTH_THREAD: | 775 case BIRTH_THREAD: |
| 861 if (sample.birth_thread()->ThreadName().find(required_) == | 776 if (sample.birth_thread()->thread_name().find(required_) == |
| 862 std::string::npos) | 777 std::string::npos) |
| 863 return false; | 778 return false; |
| 864 break; | 779 break; |
| 865 | 780 |
| 866 case DEATH_THREAD: | 781 case DEATH_THREAD: |
| 867 if (sample.DeathThreadName().find(required_) == std::string::npos) | 782 if (sample.DeathThreadName().find(required_) == std::string::npos) |
| 868 return false; | 783 return false; |
| 869 break; | 784 break; |
| 870 | 785 |
| 871 case BIRTH_FILE: | 786 case BIRTH_FILE: |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 927 } | 842 } |
| 928 | 843 |
| 929 void Comparator::ParseKeyphrase(const std::string& key_phrase) { | 844 void Comparator::ParseKeyphrase(const std::string& key_phrase) { |
| 930 typedef std::map<const std::string, Selector> KeyMap; | 845 typedef std::map<const std::string, Selector> KeyMap; |
| 931 static KeyMap key_map; | 846 static KeyMap key_map; |
| 932 static bool initialized = false; | 847 static bool initialized = false; |
| 933 if (!initialized) { | 848 if (!initialized) { |
| 934 initialized = true; | 849 initialized = true; |
| 935 // Sorting and aggretation keywords, which specify how to sort the data, or | 850 // 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. | 851 // can specify a required match from the specified field in the record. |
| 937 key_map["count"] = COUNT; | 852 key_map["count"] = COUNT; |
| 938 key_map["duration"] = AVERAGE_DURATION; | 853 key_map["totalduration"] = TOTAL_RUN_DURATION; |
| 939 key_map["birth"] = BIRTH_THREAD; | 854 key_map["duration"] = AVERAGE_RUN_DURATION; |
| 940 key_map["death"] = DEATH_THREAD; | 855 key_map["totalqueueduration"] = TOTAL_QUEUE_DURATION; |
| 941 key_map["file"] = BIRTH_FILE; | 856 key_map["averagequeueduration"] = AVERAGE_QUEUE_DURATION; |
| 942 key_map["function"] = BIRTH_FUNCTION; | 857 key_map["birth"] = BIRTH_THREAD; |
| 943 key_map["line"] = BIRTH_LINE; | 858 key_map["death"] = DEATH_THREAD; |
| 859 key_map["file"] = BIRTH_FILE; |
| 860 key_map["function"] = BIRTH_FUNCTION; |
| 861 key_map["line"] = BIRTH_LINE; |
| 944 | 862 |
| 945 // Immediate commands that do not involve setting sort order. | 863 // Immediate commands that do not involve setting sort order. |
| 946 key_map["reset"] = RESET_ALL_DATA; | 864 key_map["reset"] = RESET_ALL_DATA; |
| 947 } | 865 } |
| 948 | 866 |
| 949 std::string required; | 867 std::string required; |
| 950 // Watch for: "sort_key=value" as we parse. | 868 // Watch for: "sort_key=value" as we parse. |
| 951 size_t equal_offset = key_phrase.find('=', 0); | 869 size_t equal_offset = key_phrase.find('=', 0); |
| 952 if (key_phrase.npos != equal_offset) { | 870 if (key_phrase.npos != equal_offset) { |
| 953 // There is a value that must be matched for the data to display. | 871 // 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();) { | 887 for (size_t i = 0; i < query.size();) { |
| 970 size_t slash_offset = query.find('/', i); | 888 size_t slash_offset = query.find('/', i); |
| 971 ParseKeyphrase(query.substr(i, slash_offset - i)); | 889 ParseKeyphrase(query.substr(i, slash_offset - i)); |
| 972 if (query.npos == slash_offset) | 890 if (query.npos == slash_offset) |
| 973 break; | 891 break; |
| 974 i = slash_offset + 1; | 892 i = slash_offset + 1; |
| 975 } | 893 } |
| 976 | 894 |
| 977 // Select subgroup ordering (if we want to display the subgroup) | 895 // Select subgroup ordering (if we want to display the subgroup) |
| 978 SetSubgroupTiebreaker(COUNT); | 896 SetSubgroupTiebreaker(COUNT); |
| 979 SetSubgroupTiebreaker(AVERAGE_DURATION); | 897 SetSubgroupTiebreaker(AVERAGE_RUN_DURATION); |
| 898 SetSubgroupTiebreaker(TOTAL_RUN_DURATION); |
| 980 SetSubgroupTiebreaker(BIRTH_THREAD); | 899 SetSubgroupTiebreaker(BIRTH_THREAD); |
| 981 SetSubgroupTiebreaker(DEATH_THREAD); | 900 SetSubgroupTiebreaker(DEATH_THREAD); |
| 982 SetSubgroupTiebreaker(BIRTH_FUNCTION); | 901 SetSubgroupTiebreaker(BIRTH_FUNCTION); |
| 983 SetSubgroupTiebreaker(BIRTH_FILE); | 902 SetSubgroupTiebreaker(BIRTH_FILE); |
| 984 SetSubgroupTiebreaker(BIRTH_LINE); | 903 SetSubgroupTiebreaker(BIRTH_LINE); |
| 985 | 904 |
| 986 return true; | 905 return true; |
| 987 } | 906 } |
| 988 | 907 |
| 989 bool Comparator::WriteSortGrouping(const Snapshot& sample, | 908 bool Comparator::WriteSortGrouping(const Snapshot& sample, |
| 990 std::string* output) const { | 909 std::string* output) const { |
| 991 bool wrote_data = false; | 910 bool wrote_data = false; |
| 992 switch (selector_) { | 911 switch (selector_) { |
| 993 case BIRTH_THREAD: | 912 case BIRTH_THREAD: |
| 994 base::StringAppendF(output, "All new on %s ", | 913 base::StringAppendF(output, "All new on %s ", |
| 995 sample.birth_thread()->ThreadName().c_str()); | 914 sample.birth_thread()->thread_name().c_str()); |
| 996 wrote_data = true; | 915 wrote_data = true; |
| 997 break; | 916 break; |
| 998 | 917 |
| 999 case DEATH_THREAD: | 918 case DEATH_THREAD: |
| 1000 if (sample.death_thread()) { | 919 if (sample.death_thread()) { |
| 1001 base::StringAppendF(output, "All deleted on %s ", | 920 base::StringAppendF(output, "All deleted on %s ", |
| 1002 sample.DeathThreadName().c_str()); | 921 sample.DeathThreadName().c_str()); |
| 1003 } else { | 922 } else { |
| 1004 output->append("All still alive "); | 923 output->append("All still alive "); |
| 1005 } | 924 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1026 return wrote_data; | 945 return wrote_data; |
| 1027 } | 946 } |
| 1028 | 947 |
| 1029 void Comparator::WriteSnapshot(const Snapshot& sample, | 948 void Comparator::WriteSnapshot(const Snapshot& sample, |
| 1030 std::string* output) const { | 949 std::string* output) const { |
| 1031 sample.death_data().Write(output); | 950 sample.death_data().Write(output); |
| 1032 if (!(combined_selectors_ & BIRTH_THREAD) || | 951 if (!(combined_selectors_ & BIRTH_THREAD) || |
| 1033 !(combined_selectors_ & DEATH_THREAD)) | 952 !(combined_selectors_ & DEATH_THREAD)) |
| 1034 base::StringAppendF(output, "%s->%s ", | 953 base::StringAppendF(output, "%s->%s ", |
| 1035 (combined_selectors_ & BIRTH_THREAD) ? "*" : | 954 (combined_selectors_ & BIRTH_THREAD) ? "*" : |
| 1036 sample.birth().birth_thread()->ThreadName().c_str(), | 955 sample.birth().birth_thread()->thread_name().c_str(), |
| 1037 (combined_selectors_ & DEATH_THREAD) ? "*" : | 956 (combined_selectors_ & DEATH_THREAD) ? "*" : |
| 1038 sample.DeathThreadName().c_str()); | 957 sample.DeathThreadName().c_str()); |
| 1039 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), | 958 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), |
| 1040 !(combined_selectors_ & BIRTH_FUNCTION), | 959 !(combined_selectors_ & BIRTH_FUNCTION), |
| 1041 output); | 960 output); |
| 1042 } | 961 } |
| 1043 | 962 |
| 1044 } // namespace tracked_objects | 963 } // namespace tracked_objects |
| OLD | NEW |