| 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 #include "build/build_config.h" |
| 14 | 15 |
| 15 using base::TimeDelta; | 16 using base::TimeDelta; |
| 16 | 17 |
| 17 namespace tracked_objects { | 18 namespace tracked_objects { |
| 18 | 19 |
| 19 | 20 namespace { |
| 20 #if defined(TRACK_ALL_TASK_OBJECTS) | 21 // Flag to compile out almost all of the task tracking code. |
| 22 #if defined(NDEBUG) && defined(OS_MAC) |
| 23 // Avoid problems with base_unittest crashes in Mac for now. |
| 24 static const bool kTrackAllTaskObjects = false; |
| 25 #else |
| 21 static const bool kTrackAllTaskObjects = true; | 26 static const bool kTrackAllTaskObjects = true; |
| 22 #else | |
| 23 static const bool kTrackAllTaskObjects = false; | |
| 24 #endif | 27 #endif |
| 25 | 28 |
| 26 // Can we count on thread termination to call for thread cleanup? If not, then | 29 // When ThreadData is first initialized, should we start in an ACTIVE state to |
| 27 // we can't risk putting references to ThreadData in TLS, as it will leak on | 30 // record all of the startup-time tasks, or should we start up DEACTIVATED, so |
| 28 // worker thread termination. | 31 // that we only record after parsing the command line flag --enable-tracking. |
| 29 static const bool kWorkerThreadCleanupSupported = true; | 32 // Note that the flag may force either state, so this really controls only the |
| 30 | 33 // period of time up until that flag is parsed. If there is no flag seen, then |
| 31 // A TLS slot which points to the ThreadData instance for the current thread. We | 34 // this state may prevail for much or all of the process lifetime. |
| 32 // do a fake initialization here (zeroing out data), and then the real in-place | 35 static const ThreadData::Status kInitialStartupState = ThreadData::ACTIVE; |
| 33 // construction happens when we call tls_index_.Initialize(). | 36 } // anonymous namespace. |
| 34 // static | |
| 35 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); | |
| 36 | |
| 37 // A global state variable to prevent repeated initialization during tests. | |
| 38 // static | |
| 39 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; | |
| 40 | |
| 41 // A locked protected counter to assign sequence number to threads. | |
| 42 // static | |
| 43 int ThreadData::thread_number_counter_ = 0; | |
| 44 | 37 |
| 45 //------------------------------------------------------------------------------ | 38 //------------------------------------------------------------------------------ |
| 46 // Death data tallies durations when a death takes place. | 39 // Death data tallies durations when a death takes place. |
| 47 | 40 |
| 48 void DeathData::RecordDeath(const TimeDelta& queue_duration, | 41 void DeathData::RecordDeath(const Duration& queue_duration, |
| 49 const TimeDelta& run_duration) { | 42 const Duration& run_duration) { |
| 50 ++count_; | 43 ++count_; |
| 51 queue_duration_ += queue_duration; | 44 queue_duration_ += queue_duration; |
| 52 run_duration_ += run_duration; | 45 run_duration_ += run_duration; |
| 53 } | 46 } |
| 54 | 47 |
| 55 int DeathData::AverageMsRunDuration() const { | 48 int DeathData::AverageMsRunDuration() const { |
| 56 if (run_duration_ == base::TimeDelta()) | 49 if (run_duration_ == Duration() || !count_) |
| 57 return 0; | 50 return 0; |
| 58 return static_cast<int>(run_duration_.InMilliseconds() / count_); | 51 // Add half of denominator to achieve rounding. |
| 52 return static_cast<int>(run_duration_.InMilliseconds() + count_ / 2) / |
| 53 count_; |
| 59 } | 54 } |
| 60 | 55 |
| 61 int DeathData::AverageMsQueueDuration() const { | 56 int DeathData::AverageMsQueueDuration() const { |
| 62 if (queue_duration_ == base::TimeDelta()) | 57 if (queue_duration_ == Duration() || !count_) |
| 63 return 0; | 58 return 0; |
| 64 return static_cast<int>(queue_duration_.InMilliseconds() / count_); | 59 // Add half of denominator to achieve rounding. |
| 60 return (static_cast<int>(queue_duration_.InMilliseconds() + count_ / 2) / |
| 61 count_); |
| 65 } | 62 } |
| 66 | 63 |
| 67 void DeathData::AddDeathData(const DeathData& other) { | 64 void DeathData::AddDeathData(const DeathData& other) { |
| 68 count_ += other.count_; | 65 count_ += other.count_; |
| 69 queue_duration_ += other.queue_duration_; | 66 queue_duration_ += other.queue_duration_; |
| 70 run_duration_ += other.run_duration_; | 67 run_duration_ += other.run_duration_; |
| 71 } | 68 } |
| 72 | 69 |
| 73 void DeathData::WriteHTML(std::string* output) const { | 70 void DeathData::WriteHTML(std::string* output) const { |
| 74 if (!count_) | 71 if (!count_) |
| 75 return; | 72 return; |
| 76 base::StringAppendF(output, "%s:%d, ", | 73 base::StringAppendF(output, "%s:%d, ", |
| 77 (count_ == 1) ? "Life" : "Lives", count_); | 74 (count_ == 1) ? "Life" : "Lives", count_); |
| 78 base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ", | 75 // Be careful to leave static_casts intact, as the type returned by |
| 79 run_duration_.InMilliseconds(), | 76 // InMilliseconds() may not always be an int, even if it can generally fit |
| 77 // into an int. |
| 78 base::StringAppendF(output, "Run:%dms(%dms/life) ", |
| 79 static_cast<int>(run_duration_.InMilliseconds()), |
| 80 AverageMsRunDuration()); | 80 AverageMsRunDuration()); |
| 81 base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ", | 81 base::StringAppendF(output, "Queue:%dms(%dms/life) ", |
| 82 queue_duration_.InMilliseconds(), | 82 static_cast<int>(queue_duration_.InMilliseconds()), |
| 83 AverageMsQueueDuration()); | 83 AverageMsQueueDuration()); |
| 84 } | 84 } |
| 85 | 85 |
| 86 base::DictionaryValue* DeathData::ToValue() const { | 86 base::DictionaryValue* DeathData::ToValue() const { |
| 87 base::DictionaryValue* dictionary = new base::DictionaryValue; | 87 base::DictionaryValue* dictionary = new base::DictionaryValue; |
| 88 dictionary->Set("count", base::Value::CreateIntegerValue(count_)); | 88 dictionary->Set("count", base::Value::CreateIntegerValue(count_)); |
| 89 dictionary->Set("run_ms", | 89 dictionary->Set("run_ms", |
| 90 base::Value::CreateIntegerValue(run_duration_.InMilliseconds())); | 90 base::Value::CreateIntegerValue(run_duration_.InMilliseconds())); |
| 91 dictionary->Set("queue_ms", | 91 dictionary->Set("queue_ms", |
| 92 base::Value::CreateIntegerValue(queue_duration_.InMilliseconds())); | 92 base::Value::CreateIntegerValue(queue_duration_.InMilliseconds())); |
| 93 return dictionary; | 93 return dictionary; |
| 94 } | 94 } |
| 95 | 95 |
| 96 void DeathData::Clear() { | 96 void DeathData::Clear() { |
| 97 count_ = 0; | 97 count_ = 0; |
| 98 queue_duration_ = TimeDelta(); | 98 queue_duration_ = Duration(); |
| 99 run_duration_ = TimeDelta(); | 99 run_duration_ = Duration(); |
| 100 } | 100 } |
| 101 | 101 |
| 102 //------------------------------------------------------------------------------ | 102 //------------------------------------------------------------------------------ |
| 103 BirthOnThread::BirthOnThread(const Location& location, | 103 BirthOnThread::BirthOnThread(const Location& location, |
| 104 const ThreadData& current) | 104 const ThreadData& current) |
| 105 : location_(location), | 105 : location_(location), |
| 106 birth_thread_(¤t) {} | 106 birth_thread_(¤t) {} |
| 107 | 107 |
| 108 //------------------------------------------------------------------------------ | 108 //------------------------------------------------------------------------------ |
| 109 Births::Births(const Location& location, const ThreadData& current) | 109 Births::Births(const Location& location, const ThreadData& current) |
| 110 : BirthOnThread(location, current), | 110 : BirthOnThread(location, current), |
| 111 birth_count_(1) { } | 111 birth_count_(1) { } |
| 112 | 112 |
| 113 //------------------------------------------------------------------------------ | 113 //------------------------------------------------------------------------------ |
| 114 // ThreadData maintains the central data for all births and deaths. | 114 // ThreadData maintains the central data for all births and deaths. |
| 115 | 115 |
| 116 // TODO(jar): We should pull all these static vars together, into a struct, and |
| 117 // optimize layout so that we benefit from locality of reference during accesses |
| 118 // to them. |
| 119 |
| 120 // A TLS slot which points to the ThreadData instance for the current thread. We |
| 121 // do a fake initialization here (zeroing out data), and then the real in-place |
| 122 // construction happens when we call tls_index_.Initialize(). |
| 123 // static |
| 124 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); |
| 125 |
| 126 // A lock-protected counter to assign sequence number to threads. |
| 127 // static |
| 128 int ThreadData::thread_number_counter_ = 0; |
| 129 |
| 130 // static |
| 131 int ThreadData::incarnation_counter_ = 0; |
| 132 |
| 116 // static | 133 // static |
| 117 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; | 134 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; |
| 118 | 135 |
| 119 // static | 136 // static |
| 120 ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL; | 137 ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL; |
| 121 | 138 |
| 122 // static | 139 // static |
| 123 base::Lock ThreadData::list_lock_; | 140 base::Lock* ThreadData::list_lock_; |
| 124 | 141 |
| 125 // static | 142 // static |
| 126 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 143 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
| 127 | 144 |
| 128 ThreadData::ThreadData(const std::string& suggested_name) | 145 ThreadData::ThreadData(const std::string& suggested_name) |
| 129 : next_(NULL), | 146 : incarnation_count_for_pool_(-1), |
| 147 next_(NULL), |
| 130 is_a_worker_thread_(false) { | 148 is_a_worker_thread_(false) { |
| 131 DCHECK_GE(suggested_name.size(), 0u); | 149 DCHECK_GE(suggested_name.size(), 0u); |
| 132 thread_name_ = suggested_name; | 150 thread_name_ = suggested_name; |
| 133 PushToHeadOfList(); | 151 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. |
| 134 } | 152 } |
| 135 | 153 |
| 136 ThreadData::ThreadData() : next_(NULL), is_a_worker_thread_(true) { | 154 ThreadData::ThreadData() |
| 155 : incarnation_count_for_pool_(-1), |
| 156 next_(NULL), |
| 157 is_a_worker_thread_(true) { |
| 137 int thread_number; | 158 int thread_number; |
| 138 { | 159 { |
| 139 base::AutoLock lock(list_lock_); | 160 base::AutoLock lock(*list_lock_); |
| 140 thread_number = ++thread_number_counter_; | 161 thread_number = ++thread_number_counter_; |
| 141 } | 162 } |
| 142 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); | 163 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); |
| 143 PushToHeadOfList(); | 164 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. |
| 144 } | 165 } |
| 145 | 166 |
| 146 ThreadData::~ThreadData() {} | 167 ThreadData::~ThreadData() {} |
| 147 | 168 |
| 148 void ThreadData::PushToHeadOfList() { | 169 void ThreadData::PushToHeadOfList() { |
| 149 DCHECK(!next_); | 170 DCHECK(!next_); |
| 150 base::AutoLock lock(list_lock_); | 171 base::AutoLock lock(*list_lock_); |
| 172 incarnation_count_for_pool_ = incarnation_counter_; |
| 151 next_ = all_thread_data_list_head_; | 173 next_ = all_thread_data_list_head_; |
| 152 all_thread_data_list_head_ = this; | 174 all_thread_data_list_head_ = this; |
| 153 } | 175 } |
| 154 | 176 |
| 155 // static | 177 // static |
| 156 void ThreadData::InitializeThreadContext(const std::string& suggested_name) { | 178 void ThreadData::InitializeThreadContext(const std::string& suggested_name) { |
| 157 if (!tls_index_.initialized()) | 179 if (!Initialize()) // Always initialize if needed. |
| 158 return; // For unittests only. | 180 return; |
| 159 DCHECK_EQ(tls_index_.Get(), reinterpret_cast<void*>(NULL)); | 181 ThreadData* current_thread_data = |
| 160 ThreadData* current_thread_data = new ThreadData(suggested_name); | 182 reinterpret_cast<ThreadData*>(tls_index_.Get()); |
| 183 if (current_thread_data) |
| 184 return; // Browser tests instigate this. |
| 185 current_thread_data = new ThreadData(suggested_name); |
| 161 tls_index_.Set(current_thread_data); | 186 tls_index_.Set(current_thread_data); |
| 162 } | 187 } |
| 163 | 188 |
| 164 // static | 189 // static |
| 165 ThreadData* ThreadData::Get() { | 190 ThreadData* ThreadData::Get() { |
| 166 if (!tls_index_.initialized()) | 191 if (!tls_index_.initialized()) |
| 167 return NULL; // For unittests only. | 192 return NULL; // For unittests only. |
| 168 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); | 193 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); |
| 169 if (registered) | 194 if (registered) |
| 170 return registered; | 195 return registered; |
| 171 | 196 |
| 172 // We must be a worker thread, since we didn't pre-register. | 197 // We must be a worker thread, since we didn't pre-register. |
| 173 ThreadData* worker_thread_data = NULL; | 198 ThreadData* worker_thread_data = NULL; |
| 174 { | 199 { |
| 175 base::AutoLock lock(list_lock_); | 200 base::AutoLock lock(*list_lock_); |
| 176 if (!unregistered_thread_data_pool_->empty()) { | 201 if (!unregistered_thread_data_pool_->empty()) { |
| 177 worker_thread_data = | 202 worker_thread_data = |
| 178 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); | 203 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); |
| 179 unregistered_thread_data_pool_->pop(); | 204 unregistered_thread_data_pool_->pop(); |
| 180 } | 205 } |
| 181 } | 206 } |
| 182 | 207 |
| 183 // If we can't find a previously used instance, then we have to create one. | 208 // If we can't find a previously used instance, then we have to create one. |
| 184 if (!worker_thread_data) | 209 if (!worker_thread_data) |
| 185 worker_thread_data = new ThreadData(); | 210 worker_thread_data = new ThreadData(); |
| 186 | 211 |
| 187 tls_index_.Set(worker_thread_data); | 212 tls_index_.Set(worker_thread_data); |
| 188 return worker_thread_data; | 213 return worker_thread_data; |
| 189 } | 214 } |
| 190 | 215 |
| 191 // static | 216 // static |
| 192 void ThreadData::OnThreadTermination(void* thread_data) { | 217 void ThreadData::OnThreadTermination(void* thread_data) { |
| 193 if (!kTrackAllTaskObjects) | 218 if (!kTrackAllTaskObjects) |
| 194 return; // Not compiled in. | 219 return; // Not compiled in. |
| 195 DCHECK(tls_index_.initialized()); | |
| 196 if (!thread_data) | 220 if (!thread_data) |
| 197 return; | 221 return; |
| 198 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); | 222 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); |
| 199 DCHECK_EQ(tls_index_.Get(), reinterpret_cast<ThreadData*>(NULL)); | |
| 200 } | 223 } |
| 201 | 224 |
| 202 void ThreadData::OnThreadTerminationCleanup() const { | 225 void ThreadData::OnThreadTerminationCleanup() const { |
| 203 tls_index_.Set(NULL); | |
| 204 if (!is_a_worker_thread_) | 226 if (!is_a_worker_thread_) |
| 205 return; | 227 return; |
| 206 base::AutoLock lock(list_lock_); | 228 base::AutoLock lock(*list_lock_); |
| 207 unregistered_thread_data_pool_->push(this); | 229 if (incarnation_counter_ != incarnation_count_for_pool_) |
| 230 return; // ThreadData was constructed in an earlier unit test. |
| 231 |
| 232 // Handle case where we are in unit tests, and have become UNINITIALIZED. |
| 233 // In that case, the pool might be NULL. We really should detect this via the |
| 234 // incarnation_counter_, but this call is rarely made, so we can afford to |
| 235 // code defensively. |
| 236 if (unregistered_thread_data_pool_) |
| 237 unregistered_thread_data_pool_->push(this); |
| 208 } | 238 } |
| 209 | 239 |
| 210 // static | 240 // static |
| 211 void ThreadData::WriteHTML(const std::string& query, std::string* output) { | 241 void ThreadData::WriteHTML(const std::string& query, std::string* output) { |
| 212 if (!ThreadData::IsActive()) | 242 if (status_ == UNINITIALIZED) |
| 213 return; // Not yet initialized. | 243 return; // Not yet initialized. |
| 214 | 244 |
| 215 DataCollector collected_data; // Gather data. | 245 DataCollector collected_data; // Gather data. |
| 216 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 246 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
| 217 | 247 |
| 218 // Data Gathering is complete. Now to sort/process/render. | 248 // Data Gathering is complete. Now to sort/process/render. |
| 219 DataCollector::Collection* collection = collected_data.collection(); | 249 DataCollector::Collection* collection = collected_data.collection(); |
| 220 | 250 |
| 221 // Create filtering and sort comparison object. | 251 // Create filtering and sort comparison object. |
| 222 Comparator comparator; | 252 Comparator comparator; |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 // Print aggregate stats for the group. | 339 // Print aggregate stats for the group. |
| 310 output->append("<br>"); | 340 output->append("<br>"); |
| 311 subtotals.WriteHTML(output); | 341 subtotals.WriteHTML(output); |
| 312 output->append("<br><hr><br>"); | 342 output->append("<br><hr><br>"); |
| 313 subtotals.Clear(); | 343 subtotals.Clear(); |
| 314 } | 344 } |
| 315 } | 345 } |
| 316 } | 346 } |
| 317 | 347 |
| 318 // static | 348 // static |
| 319 base::Value* ThreadData::ToValue(int process_type) { | 349 base::DictionaryValue* ThreadData::ToValue() { |
| 320 DataCollector collected_data; // Gather data. | 350 DataCollector collected_data; // Gather data. |
| 321 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 351 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
| 322 base::ListValue* list = collected_data.ToValue(); | 352 base::ListValue* list = collected_data.ToValue(); |
| 323 base::DictionaryValue* dictionary = new base::DictionaryValue(); | 353 base::DictionaryValue* dictionary = new base::DictionaryValue(); |
| 324 dictionary->Set("list", list); | 354 dictionary->Set("list", list); |
| 325 dictionary->SetInteger("process", process_type); | |
| 326 return dictionary; | 355 return dictionary; |
| 327 } | 356 } |
| 328 | 357 |
| 329 Births* ThreadData::TallyABirth(const Location& location) { | 358 Births* ThreadData::TallyABirth(const Location& location) { |
| 330 BirthMap::iterator it = birth_map_.find(location); | 359 BirthMap::iterator it = birth_map_.find(location); |
| 331 if (it != birth_map_.end()) { | 360 if (it != birth_map_.end()) { |
| 332 it->second->RecordBirth(); | 361 it->second->RecordBirth(); |
| 333 return it->second; | 362 return it->second; |
| 334 } | 363 } |
| 335 | 364 |
| 336 Births* tracker = new Births(location, *this); | 365 Births* tracker = new Births(location, *this); |
| 337 // Lock since the map may get relocated now, and other threads sometimes | 366 // Lock since the map may get relocated now, and other threads sometimes |
| 338 // snapshot it (but they lock before copying it). | 367 // snapshot it (but they lock before copying it). |
| 339 base::AutoLock lock(lock_); | 368 base::AutoLock lock(lock_); |
| 340 birth_map_[location] = tracker; | 369 birth_map_[location] = tracker; |
| 341 return tracker; | 370 return tracker; |
| 342 } | 371 } |
| 343 | 372 |
| 344 void ThreadData::TallyADeath(const Births& birth, | 373 void ThreadData::TallyADeath(const Births& birth, |
| 345 const TimeDelta& queue_duration, | 374 const Duration& queue_duration, |
| 346 const TimeDelta& run_duration) { | 375 const Duration& run_duration) { |
| 347 DeathMap::iterator it = death_map_.find(&birth); | 376 DeathMap::iterator it = death_map_.find(&birth); |
| 348 DeathData* death_data; | 377 DeathData* death_data; |
| 349 if (it != death_map_.end()) { | 378 if (it != death_map_.end()) { |
| 350 death_data = &it->second; | 379 death_data = &it->second; |
| 351 } else { | 380 } else { |
| 352 base::AutoLock lock(lock_); // Lock since the map may get relocated now. | 381 base::AutoLock lock(lock_); // Lock since the map may get relocated now. |
| 353 death_data = &death_map_[&birth]; | 382 death_data = &death_map_[&birth]; |
| 354 } // Release lock ASAP. | 383 } // Release lock ASAP. |
| 355 death_data->RecordDeath(queue_duration, run_duration); | 384 death_data->RecordDeath(queue_duration, run_duration); |
| 356 } | 385 } |
| 357 | 386 |
| 358 // static | 387 // static |
| 359 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 388 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
| 360 if (!kTrackAllTaskObjects) | 389 if (!kTrackAllTaskObjects) |
| 361 return NULL; // Not compiled in. | 390 return NULL; // Not compiled in. |
| 362 | 391 |
| 363 if (!IsActive()) | 392 if (!tracking_status()) |
| 364 return NULL; | 393 return NULL; |
| 365 ThreadData* current_thread_data = Get(); | 394 ThreadData* current_thread_data = Get(); |
| 366 if (!current_thread_data) | 395 if (!current_thread_data) |
| 367 return NULL; | 396 return NULL; |
| 368 return current_thread_data->TallyABirth(location); | 397 return current_thread_data->TallyABirth(location); |
| 369 } | 398 } |
| 370 | 399 |
| 371 // static | 400 // static |
| 372 void ThreadData::TallyADeathIfActive(const Births* birth, | 401 void ThreadData::TallyRunOnNamedThreadIfTracking( |
| 373 const base::TimeTicks& time_posted, | 402 const base::TrackingInfo& completed_task, |
| 374 const base::TimeTicks& delayed_start_time, | 403 const TrackedTime& start_of_run, |
| 375 const base::TimeTicks& start_of_run, | 404 const TrackedTime& end_of_run) { |
| 376 const base::TimeTicks& end_of_run) { | |
| 377 if (!kTrackAllTaskObjects) | 405 if (!kTrackAllTaskObjects) |
| 378 return; // Not compiled in. | 406 return; // Not compiled in. |
| 379 | 407 |
| 380 if (!IsActive() || !birth) | 408 // Even if we have been DEACTIVATED, we will process any pending births so |
| 409 // that our data structures (which counted the outstanding births) remain |
| 410 // consistent. |
| 411 const Births* birth = completed_task.birth_tally; |
| 412 if (!birth) |
| 381 return; | 413 return; |
| 382 | |
| 383 ThreadData* current_thread_data = Get(); | 414 ThreadData* current_thread_data = Get(); |
| 384 if (!current_thread_data) | 415 if (!current_thread_data) |
| 385 return; | 416 return; |
| 386 | 417 |
| 387 // To avoid conflating our stats with the delay duration in a PostDelayedTask, | 418 // To avoid conflating our stats with the delay duration in a PostDelayedTask, |
| 388 // we identify such tasks, and replace their post_time with the time they | 419 // we identify such tasks, and replace their post_time with the time they |
| 389 // were sechudled (requested?) to emerge from the delayed task queue. This | 420 // were scheduled (requested?) to emerge from the delayed task queue. This |
| 390 // means that queueing delay for such tasks will show how long they went | 421 // means that queueing delay for such tasks will show how long they went |
| 391 // unserviced, after they *could* be serviced. This is the same stat as we | 422 // unserviced, after they *could* be serviced. This is the same stat as we |
| 392 // have for non-delayed tasks, and we consistently call it queueing delay. | 423 // have for non-delayed tasks, and we consistently call it queueing delay. |
| 393 base::TimeTicks effective_post_time = | 424 TrackedTime effective_post_time = completed_task.delayed_run_time.is_null() |
| 394 (delayed_start_time.is_null()) ? time_posted : delayed_start_time; | 425 ? tracked_objects::TrackedTime(completed_task.time_posted) |
| 395 base::TimeDelta queue_duration = start_of_run - effective_post_time; | 426 : tracked_objects::TrackedTime(completed_task.delayed_run_time); |
| 396 base::TimeDelta run_duration = end_of_run - start_of_run; | 427 |
| 428 // Watch out for a race where status_ is changing, and hence one or both |
| 429 // of start_of_run or end_of_run is zero. IN that case, we didn't bother to |
| 430 // get a time value since we "weren't tracking" and we were trying to be |
| 431 // efficient by not calling for a genuine time value. For simplicity, we'll |
| 432 // use a default zero duration when we can't calculate a true value. |
| 433 Duration queue_duration; |
| 434 Duration run_duration; |
| 435 if (!start_of_run.is_null()) { |
| 436 queue_duration = start_of_run - effective_post_time; |
| 437 if (!end_of_run.is_null()) |
| 438 run_duration = end_of_run - start_of_run; |
| 439 } |
| 440 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); |
| 441 } |
| 442 |
| 443 // static |
| 444 void ThreadData::TallyRunOnWorkerThreadIfTracking( |
| 445 const Births* birth, |
| 446 const TrackedTime& time_posted, |
| 447 const TrackedTime& start_of_run, |
| 448 const TrackedTime& end_of_run) { |
| 449 if (!kTrackAllTaskObjects) |
| 450 return; // Not compiled in. |
| 451 |
| 452 // Even if we have been DEACTIVATED, we will process any pending births so |
| 453 // that our data structures (which counted the outstanding births) remain |
| 454 // consistent. |
| 455 if (!birth) |
| 456 return; |
| 457 |
| 458 // TODO(jar): Support the option to coalesce all worker-thread activity under |
| 459 // one ThreadData instance that uses locks to protect *all* access. This will |
| 460 // reduce memory (making it provably bounded), but run incrementally slower |
| 461 // (since we'll use locks on TallyBirth and TallyDeath). The good news is |
| 462 // that the locks on TallyDeath will be *after* the worker thread has run, and |
| 463 // hence nothing will be waiting for the completion (... besides some other |
| 464 // thread that might like to run). Also, the worker threads tasks are |
| 465 // generally longer, and hence the cost of the lock may perchance be amortized |
| 466 // over the long task's lifetime. |
| 467 ThreadData* current_thread_data = Get(); |
| 468 if (!current_thread_data) |
| 469 return; |
| 470 |
| 471 Duration queue_duration; |
| 472 Duration run_duration; |
| 473 if (!start_of_run.is_null()) { |
| 474 queue_duration = start_of_run - time_posted; |
| 475 if (!end_of_run.is_null()) |
| 476 run_duration = end_of_run - start_of_run; |
| 477 } |
| 397 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); | 478 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); |
| 398 } | 479 } |
| 399 | 480 |
| 400 // static | 481 // static |
| 401 ThreadData* ThreadData::first() { | 482 ThreadData* ThreadData::first() { |
| 402 base::AutoLock lock(list_lock_); | 483 base::AutoLock lock(*list_lock_); |
| 403 return all_thread_data_list_head_; | 484 return all_thread_data_list_head_; |
| 404 } | 485 } |
| 405 | 486 |
| 406 // This may be called from another thread. | 487 // This may be called from another thread. |
| 407 void ThreadData::SnapshotBirthMap(BirthMap *output) const { | 488 void ThreadData::SnapshotBirthMap(BirthMap *output) const { |
| 408 base::AutoLock lock(lock_); | 489 base::AutoLock lock(lock_); |
| 409 for (BirthMap::const_iterator it = birth_map_.begin(); | 490 for (BirthMap::const_iterator it = birth_map_.begin(); |
| 410 it != birth_map_.end(); ++it) | 491 it != birth_map_.end(); ++it) |
| 411 (*output)[it->first] = it->second; | 492 (*output)[it->first] = it->second; |
| 412 } | 493 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 432 void ThreadData::Reset() { | 513 void ThreadData::Reset() { |
| 433 base::AutoLock lock(lock_); | 514 base::AutoLock lock(lock_); |
| 434 for (DeathMap::iterator it = death_map_.begin(); | 515 for (DeathMap::iterator it = death_map_.begin(); |
| 435 it != death_map_.end(); ++it) | 516 it != death_map_.end(); ++it) |
| 436 it->second.Clear(); | 517 it->second.Clear(); |
| 437 for (BirthMap::iterator it = birth_map_.begin(); | 518 for (BirthMap::iterator it = birth_map_.begin(); |
| 438 it != birth_map_.end(); ++it) | 519 it != birth_map_.end(); ++it) |
| 439 it->second->Clear(); | 520 it->second->Clear(); |
| 440 } | 521 } |
| 441 | 522 |
| 442 // static | 523 bool ThreadData::Initialize() { |
| 443 bool ThreadData::StartTracking(bool status) { | |
| 444 if (!kTrackAllTaskObjects) | 524 if (!kTrackAllTaskObjects) |
| 445 return false; // Not compiled in. | 525 return false; // Not compiled in. |
| 446 | 526 if (status_ != UNINITIALIZED) |
| 447 // Do a bit of class initialization. | 527 return true; |
| 448 if (!unregistered_thread_data_pool_) { | 528 // Initialize all leaking constants that are difficult to toggle in and out |
| 449 ThreadDataPool* initial_pool = new ThreadDataPool; | 529 // of existance. |
| 450 { | 530 // First call must be made when single threaded at startup. |
| 451 base::AutoLock lock(list_lock_); | 531 // Perform the "real" TLS initialization now, and leave it intact through |
| 452 if (!unregistered_thread_data_pool_) { | |
| 453 unregistered_thread_data_pool_ = initial_pool; | |
| 454 initial_pool = NULL; | |
| 455 } | |
| 456 } | |
| 457 delete initial_pool; // In case it was not used. | |
| 458 } | |
| 459 | |
| 460 // Perform the "real" initialization now, and leave it intact through | |
| 461 // process termination. | 532 // process termination. |
| 462 if (!tls_index_.initialized()) | 533 if (!tls_index_.initialized()) // Testing may have initialized this. |
| 463 tls_index_.Initialize(&ThreadData::OnThreadTermination); | 534 tls_index_.Initialize(&ThreadData::OnThreadTermination); |
| 464 DCHECK(tls_index_.initialized()); | 535 DCHECK(tls_index_.initialized()); |
| 536 ThreadDataPool* pool = new ThreadDataPool; |
| 537 // TODO(jar): A linker initialized spin lock would be much safer than this |
| 538 // allocation, which relies on being called while single threaded. |
| 539 if (!list_lock_) // In case testing deleted this. |
| 540 list_lock_ = new base::Lock; |
| 541 status_ = kInitialStartupState; |
| 465 | 542 |
| 466 if (!status) { | 543 base::AutoLock lock(*list_lock_); |
| 467 base::AutoLock lock(list_lock_); | 544 DCHECK_EQ(unregistered_thread_data_pool_, |
| 468 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); | 545 reinterpret_cast<ThreadDataPool*>(NULL)); |
| 469 status_ = SHUTDOWN; | 546 unregistered_thread_data_pool_ = pool; |
| 470 return true; | 547 ++incarnation_counter_; |
| 471 } | |
| 472 base::AutoLock lock(list_lock_); | |
| 473 DCHECK_EQ(UNINITIALIZED, status_); | |
| 474 status_ = ACTIVE; | |
| 475 return true; | 548 return true; |
| 476 } | 549 } |
| 477 | 550 |
| 478 // static | 551 // static |
| 479 bool ThreadData::IsActive() { | 552 bool ThreadData::InitializeAndSetTrackingStatus(bool status) { |
| 553 if (!Initialize()) // No-op if already initialized. |
| 554 return false; // Not compiled in. |
| 555 |
| 556 status_ = status ? ACTIVE : DEACTIVATED; |
| 557 return true; |
| 558 } |
| 559 |
| 560 // static |
| 561 bool ThreadData::tracking_status() { |
| 480 return status_ == ACTIVE; | 562 return status_ == ACTIVE; |
| 481 } | 563 } |
| 482 | 564 |
| 483 // static | 565 // static |
| 484 base::TimeTicks ThreadData::Now() { | 566 TrackedTime ThreadData::Now() { |
| 485 if (kTrackAllTaskObjects && status_ == ACTIVE) | 567 if (kTrackAllTaskObjects && tracking_status()) |
| 486 return base::TimeTicks::Now(); | 568 return TrackedTime::Now(); |
| 487 return base::TimeTicks(); // Super fast when disabled, or not compiled in. | 569 return TrackedTime(); // Super fast when disabled, or not compiled. |
| 488 } | 570 } |
| 489 | 571 |
| 490 // static | 572 // static |
| 491 void ThreadData::ShutdownSingleThreadedCleanup() { | 573 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { |
| 492 // This is only called from test code, where we need to cleanup so that | 574 // This is only called from test code, where we need to cleanup so that |
| 493 // additional tests can be run. | 575 // additional tests can be run. |
| 494 // We must be single threaded... but be careful anyway. | 576 // We must be single threaded... but be careful anyway. |
| 495 if (!StartTracking(false)) | 577 if (!InitializeAndSetTrackingStatus(false)) |
| 496 return; | 578 return; |
| 497 ThreadData* thread_data_list; | 579 ThreadData* thread_data_list; |
| 498 ThreadDataPool* final_pool; | 580 ThreadDataPool* final_pool; |
| 499 { | 581 { |
| 500 base::AutoLock lock(list_lock_); | 582 base::AutoLock lock(*list_lock_); |
| 501 thread_data_list = all_thread_data_list_head_; | 583 thread_data_list = all_thread_data_list_head_; |
| 502 all_thread_data_list_head_ = NULL; | 584 all_thread_data_list_head_ = NULL; |
| 503 final_pool = unregistered_thread_data_pool_; | 585 final_pool = unregistered_thread_data_pool_; |
| 504 unregistered_thread_data_pool_ = NULL; | 586 unregistered_thread_data_pool_ = NULL; |
| 587 ++incarnation_counter_; |
| 505 } | 588 } |
| 506 | 589 |
| 590 // Put most global static back in pristine shape. |
| 591 thread_number_counter_ = 0; |
| 592 tls_index_.Set(NULL); |
| 593 status_ = UNINITIALIZED; |
| 594 |
| 595 // To avoid any chance of racing in unit tests, which is the only place we |
| 596 // call this function, we may sometimes leak all the data structures we |
| 597 // recovered, as they may still be in use on threads from prior tests! |
| 598 if (leak) |
| 599 return; |
| 600 |
| 601 // When we want to cleanup (on a single thread), here is what we do. |
| 602 |
| 507 if (final_pool) { | 603 if (final_pool) { |
| 508 // The thread_data_list contains *all* the instances, and we'll use it to | 604 // The thread_data_list contains *all* the instances, and we'll use it to |
| 509 // delete them. This pool has pointers to some instances, and we just | 605 // delete them. This pool has pointers to some instances, and we just |
| 510 // have to drop those pointers (and not do the deletes here). | 606 // have to drop those pointers (and not do the deletes here). |
| 511 while (!final_pool->empty()) | 607 while (!final_pool->empty()) |
| 512 final_pool->pop(); | 608 final_pool->pop(); |
| 513 delete final_pool; | 609 delete final_pool; |
| 514 } | 610 } |
| 515 | 611 |
| 516 // Do actual recursive delete in all ThreadData instances. | 612 // Do actual recursive delete in all ThreadData instances. |
| 517 while (thread_data_list) { | 613 while (thread_data_list) { |
| 518 ThreadData* next_thread_data = thread_data_list; | 614 ThreadData* next_thread_data = thread_data_list; |
| 519 thread_data_list = thread_data_list->next(); | 615 thread_data_list = thread_data_list->next(); |
| 520 | 616 |
| 521 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); | 617 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); |
| 522 next_thread_data->birth_map_.end() != it; ++it) | 618 next_thread_data->birth_map_.end() != it; ++it) |
| 523 delete it->second; // Delete the Birth Records. | 619 delete it->second; // Delete the Birth Records. |
| 524 next_thread_data->birth_map_.clear(); | 620 next_thread_data->birth_map_.clear(); |
| 525 next_thread_data->death_map_.clear(); | 621 next_thread_data->death_map_.clear(); |
| 526 delete next_thread_data; // Includes all Death Records. | 622 delete next_thread_data; // Includes all Death Records. |
| 527 } | 623 } |
| 528 // Put most global static back in pristine shape. | |
| 529 thread_number_counter_ = 0; | |
| 530 tls_index_.Set(NULL); | |
| 531 status_ = UNINITIALIZED; | |
| 532 } | 624 } |
| 533 | 625 |
| 534 //------------------------------------------------------------------------------ | 626 //------------------------------------------------------------------------------ |
| 535 // Individual 3-tuple of birth (place and thread) along with death thread, and | 627 // Individual 3-tuple of birth (place and thread) along with death thread, and |
| 536 // the accumulated stats for instances (DeathData). | 628 // the accumulated stats for instances (DeathData). |
| 537 | 629 |
| 538 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, | 630 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, |
| 539 const ThreadData& death_thread, | 631 const ThreadData& death_thread, |
| 540 const DeathData& death_data) | 632 const DeathData& death_data) |
| 541 : birth_(&birth_on_thread), | 633 : birth_(&birth_on_thread), |
| (...skipping 25 matching lines...) Expand all Loading... |
| 567 base::DictionaryValue* dictionary = new base::DictionaryValue; | 659 base::DictionaryValue* dictionary = new base::DictionaryValue; |
| 568 dictionary->Set("death_data", death_data_.ToValue()); | 660 dictionary->Set("death_data", death_data_.ToValue()); |
| 569 dictionary->Set("birth_thread", | 661 dictionary->Set("birth_thread", |
| 570 base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); | 662 base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); |
| 571 dictionary->Set("death_thread", | 663 dictionary->Set("death_thread", |
| 572 base::Value::CreateStringValue(DeathThreadName())); | 664 base::Value::CreateStringValue(DeathThreadName())); |
| 573 dictionary->Set("location", birth_->location().ToValue()); | 665 dictionary->Set("location", birth_->location().ToValue()); |
| 574 return dictionary; | 666 return dictionary; |
| 575 } | 667 } |
| 576 | 668 |
| 577 void Snapshot::Add(const Snapshot& other) { | |
| 578 death_data_.AddDeathData(other.death_data_); | |
| 579 } | |
| 580 | |
| 581 //------------------------------------------------------------------------------ | 669 //------------------------------------------------------------------------------ |
| 582 // DataCollector | 670 // DataCollector |
| 583 | 671 |
| 584 DataCollector::DataCollector() { | 672 DataCollector::DataCollector() { |
| 585 if (!ThreadData::IsActive()) | 673 if (!kTrackAllTaskObjects) |
| 586 return; | 674 return; // Not compiled in. |
| 587 | 675 |
| 588 // Get an unchanging copy of a ThreadData list. | 676 // Get an unchanging copy of a ThreadData list. |
| 589 ThreadData* my_list = ThreadData::first(); | 677 ThreadData* my_list = ThreadData::first(); |
| 590 | 678 |
| 591 // Gather data serially. | 679 // Gather data serially. |
| 592 // This hackish approach *can* get some slighly corrupt tallies, as we are | 680 // This hackish approach *can* get some slighly corrupt tallies, as we are |
| 593 // grabbing values without the protection of a lock, but it has the advantage | 681 // grabbing values without the protection of a lock, but it has the advantage |
| 594 // of working even with threads that don't have message loops. If a user | 682 // of working even with threads that don't have message loops. If a user |
| 595 // sees any strangeness, they can always just run their stats gathering a | 683 // sees any strangeness, they can always just run their stats gathering a |
| 596 // second time. | 684 // second time. |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 733 void Comparator::Clear() { | 821 void Comparator::Clear() { |
| 734 if (tiebreaker_) { | 822 if (tiebreaker_) { |
| 735 tiebreaker_->Clear(); | 823 tiebreaker_->Clear(); |
| 736 delete tiebreaker_; | 824 delete tiebreaker_; |
| 737 tiebreaker_ = NULL; | 825 tiebreaker_ = NULL; |
| 738 } | 826 } |
| 739 use_tiebreaker_for_sort_only_ = false; | 827 use_tiebreaker_for_sort_only_ = false; |
| 740 selector_ = NIL; | 828 selector_ = NIL; |
| 741 } | 829 } |
| 742 | 830 |
| 831 // static |
| 832 Comparator::Selector Comparator::FindSelector(const std::string& keyword) { |
| 833 // Sorting and aggretation keywords, which specify how to sort the data, or |
| 834 // can specify a required match from the specified field in the record. |
| 835 if (0 == keyword.compare("count")) |
| 836 return COUNT; |
| 837 if (0 == keyword.compare("totalduration")) |
| 838 return TOTAL_RUN_DURATION; |
| 839 if (0 == keyword.compare("duration")) |
| 840 return AVERAGE_RUN_DURATION; |
| 841 if (0 == keyword.compare("totalqueueduration")) |
| 842 return TOTAL_QUEUE_DURATION; |
| 843 if (0 == keyword.compare("averagequeueduration")) |
| 844 return AVERAGE_QUEUE_DURATION; |
| 845 if (0 == keyword.compare("birth")) |
| 846 return BIRTH_THREAD; |
| 847 if (0 == keyword.compare("death")) |
| 848 return DEATH_THREAD; |
| 849 if (0 == keyword.compare("file")) |
| 850 return BIRTH_FILE; |
| 851 if (0 == keyword.compare("function")) |
| 852 return BIRTH_FUNCTION; |
| 853 if (0 == keyword.compare("line")) |
| 854 return BIRTH_LINE; |
| 855 if (0 == keyword.compare("reset")) |
| 856 return RESET_ALL_DATA; |
| 857 return UNKNOWN_KEYWORD; |
| 858 } |
| 859 |
| 743 bool Comparator::operator()(const Snapshot& left, | 860 bool Comparator::operator()(const Snapshot& left, |
| 744 const Snapshot& right) const { | 861 const Snapshot& right) const { |
| 745 switch (selector_) { | 862 switch (selector_) { |
| 746 case BIRTH_THREAD: | 863 case BIRTH_THREAD: |
| 747 if (left.birth_thread() != right.birth_thread() && | 864 if (left.birth_thread() != right.birth_thread() && |
| 748 left.birth_thread()->thread_name() != | 865 left.birth_thread()->thread_name() != |
| 749 right.birth_thread()->thread_name()) | 866 right.birth_thread()->thread_name()) |
| 750 return left.birth_thread()->thread_name() < | 867 return left.birth_thread()->thread_name() < |
| 751 right.birth_thread()->thread_name(); | 868 right.birth_thread()->thread_name(); |
| 752 break; | 869 break; |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 949 if (!tiebreaker_) { | 1066 if (!tiebreaker_) { |
| 950 use_tiebreaker_for_sort_only_ = true; | 1067 use_tiebreaker_for_sort_only_ = true; |
| 951 tiebreaker_ = new Comparator; | 1068 tiebreaker_ = new Comparator; |
| 952 tiebreaker_->SetTiebreaker(selector, ""); | 1069 tiebreaker_->SetTiebreaker(selector, ""); |
| 953 } else { | 1070 } else { |
| 954 tiebreaker_->SetSubgroupTiebreaker(selector); | 1071 tiebreaker_->SetSubgroupTiebreaker(selector); |
| 955 } | 1072 } |
| 956 } | 1073 } |
| 957 | 1074 |
| 958 void Comparator::ParseKeyphrase(const std::string& key_phrase) { | 1075 void Comparator::ParseKeyphrase(const std::string& key_phrase) { |
| 959 typedef std::map<const std::string, Selector> KeyMap; | |
| 960 static KeyMap key_map; | |
| 961 static bool initialized = false; | |
| 962 if (!initialized) { | |
| 963 initialized = true; | |
| 964 // Sorting and aggretation keywords, which specify how to sort the data, or | |
| 965 // can specify a required match from the specified field in the record. | |
| 966 key_map["count"] = COUNT; | |
| 967 key_map["totalduration"] = TOTAL_RUN_DURATION; | |
| 968 key_map["duration"] = AVERAGE_RUN_DURATION; | |
| 969 key_map["totalqueueduration"] = TOTAL_QUEUE_DURATION; | |
| 970 key_map["averagequeueduration"] = AVERAGE_QUEUE_DURATION; | |
| 971 key_map["birth"] = BIRTH_THREAD; | |
| 972 key_map["death"] = DEATH_THREAD; | |
| 973 key_map["file"] = BIRTH_FILE; | |
| 974 key_map["function"] = BIRTH_FUNCTION; | |
| 975 key_map["line"] = BIRTH_LINE; | |
| 976 | |
| 977 // Immediate commands that do not involve setting sort order. | |
| 978 key_map["reset"] = RESET_ALL_DATA; | |
| 979 } | |
| 980 | |
| 981 std::string required; | 1076 std::string required; |
| 982 // Watch for: "sort_key=value" as we parse. | 1077 // Watch for: "sort_key=value" as we parse. |
| 983 size_t equal_offset = key_phrase.find('=', 0); | 1078 size_t equal_offset = key_phrase.find('=', 0); |
| 984 if (key_phrase.npos != equal_offset) { | 1079 if (key_phrase.npos != equal_offset) { |
| 985 // There is a value that must be matched for the data to display. | 1080 // There is a value that must be matched for the data to display. |
| 986 required = key_phrase.substr(equal_offset + 1, key_phrase.npos); | 1081 required = key_phrase.substr(equal_offset + 1, key_phrase.npos); |
| 987 } | 1082 } |
| 988 std::string keyword(key_phrase.substr(0, equal_offset)); | 1083 std::string keyword(key_phrase.substr(0, equal_offset)); |
| 989 keyword = StringToLowerASCII(keyword); | 1084 keyword = StringToLowerASCII(keyword); |
| 990 KeyMap::iterator it = key_map.find(keyword); | 1085 Selector selector = FindSelector(keyword); |
| 991 if (key_map.end() == it) | 1086 if (selector == UNKNOWN_KEYWORD) |
| 992 return; // Unknown keyword. | 1087 return; |
| 993 if (it->second == RESET_ALL_DATA) | 1088 if (selector == RESET_ALL_DATA) { |
| 994 ThreadData::ResetAllThreadData(); | 1089 ThreadData::ResetAllThreadData(); |
| 995 else | 1090 return; |
| 996 SetTiebreaker(key_map[keyword], required); | 1091 } |
| 1092 SetTiebreaker(selector, required); |
| 997 } | 1093 } |
| 998 | 1094 |
| 999 bool Comparator::ParseQuery(const std::string& query) { | 1095 bool Comparator::ParseQuery(const std::string& query) { |
| 1000 // Parse each keyphrase between consecutive slashes. | 1096 // Parse each keyphrase between consecutive slashes. |
| 1001 for (size_t i = 0; i < query.size();) { | 1097 for (size_t i = 0; i < query.size();) { |
| 1002 size_t slash_offset = query.find('/', i); | 1098 size_t slash_offset = query.find('/', i); |
| 1003 ParseKeyphrase(query.substr(i, slash_offset - i)); | 1099 ParseKeyphrase(query.substr(i, slash_offset - i)); |
| 1004 if (query.npos == slash_offset) | 1100 if (query.npos == slash_offset) |
| 1005 break; | 1101 break; |
| 1006 i = slash_offset + 1; | 1102 i = slash_offset + 1; |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1068 (combined_selectors_ & BIRTH_THREAD) ? "*" : | 1164 (combined_selectors_ & BIRTH_THREAD) ? "*" : |
| 1069 sample.birth().birth_thread()->thread_name().c_str(), | 1165 sample.birth().birth_thread()->thread_name().c_str(), |
| 1070 (combined_selectors_ & DEATH_THREAD) ? "*" : | 1166 (combined_selectors_ & DEATH_THREAD) ? "*" : |
| 1071 sample.DeathThreadName().c_str()); | 1167 sample.DeathThreadName().c_str()); |
| 1072 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), | 1168 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), |
| 1073 !(combined_selectors_ & BIRTH_FUNCTION), | 1169 !(combined_selectors_ & BIRTH_FUNCTION), |
| 1074 output); | 1170 output); |
| 1075 } | 1171 } |
| 1076 | 1172 |
| 1077 } // namespace tracked_objects | 1173 } // namespace tracked_objects |
| OLD | NEW |