Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/tracked_objects.h" | 5 #include "base/tracked_objects.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 | 8 |
| 9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
| 10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
| 11 #include "base/stringprintf.h" | 11 #include "base/stringprintf.h" |
| 12 #include "base/threading/thread_restrictions.h" | 12 #include "base/threading/thread_restrictions.h" |
| 13 #include "build/build_config.h" | 13 #include "build/build_config.h" |
| 14 #include "base/port.h" | |
| 14 | 15 |
| 15 using base::TimeDelta; | 16 using base::TimeDelta; |
| 16 | 17 |
| 17 namespace tracked_objects { | 18 namespace tracked_objects { |
| 18 | 19 |
| 19 namespace { | 20 namespace { |
| 20 // Flag to compile out almost all of the task tracking code. | 21 // Flag to compile out almost all of the task tracking code. |
| 21 static const bool kTrackAllTaskObjects = true; | 22 static const bool kTrackAllTaskObjects = true; |
| 22 | 23 |
| 23 // When ThreadData is first initialized, should we start in an ACTIVE state to | 24 // When ThreadData is first initialized, should we start in an ACTIVE state to |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 140 base::LazyInstance<base::Lock, | 141 base::LazyInstance<base::Lock, |
| 141 base::LeakyLazyInstanceTraits<base::Lock> > | 142 base::LeakyLazyInstanceTraits<base::Lock> > |
| 142 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER; | 143 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER; |
| 143 | 144 |
| 144 // static | 145 // static |
| 145 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 146 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
| 146 | 147 |
| 147 ThreadData::ThreadData(const std::string& suggested_name) | 148 ThreadData::ThreadData(const std::string& suggested_name) |
| 148 : incarnation_count_for_pool_(-1), | 149 : incarnation_count_for_pool_(-1), |
| 149 next_(NULL), | 150 next_(NULL), |
| 150 is_a_worker_thread_(false) { | 151 worker_thread_number_(0) { |
| 151 DCHECK_GE(suggested_name.size(), 0u); | 152 DCHECK_GE(suggested_name.size(), 0u); |
| 152 thread_name_ = suggested_name; | 153 thread_name_ = suggested_name; |
| 153 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. | 154 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. |
| 154 } | 155 } |
| 155 | 156 |
| 156 ThreadData::ThreadData() | 157 ThreadData::ThreadData(size_t thread_number) |
| 157 : incarnation_count_for_pool_(-1), | 158 : incarnation_count_for_pool_(-1), |
| 158 next_(NULL), | 159 next_(NULL), |
| 159 is_a_worker_thread_(true) { | 160 worker_thread_number_(thread_number) { |
| 160 int thread_number; | 161 base::StringAppendF(&thread_name_, "WorkerThread-%"PRIuS, thread_number); |
| 161 { | |
| 162 base::AutoLock lock(*list_lock_.Pointer()); | |
| 163 thread_number = ++thread_number_counter_; | |
| 164 } | |
| 165 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); | |
| 166 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. | 162 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. |
| 167 } | 163 } |
| 168 | 164 |
| 169 ThreadData::~ThreadData() {} | 165 ThreadData::~ThreadData() {} |
| 170 | 166 |
| 171 void ThreadData::PushToHeadOfList() { | 167 void ThreadData::PushToHeadOfList() { |
| 172 DCHECK(!next_); | 168 DCHECK(!next_); |
| 173 base::AutoLock lock(*list_lock_.Pointer()); | 169 base::AutoLock lock(*list_lock_.Pointer()); |
| 174 incarnation_count_for_pool_ = incarnation_counter_; | 170 incarnation_count_for_pool_ = incarnation_counter_; |
| 175 next_ = all_thread_data_list_head_; | 171 next_ = all_thread_data_list_head_; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 191 // static | 187 // static |
| 192 ThreadData* ThreadData::Get() { | 188 ThreadData* ThreadData::Get() { |
| 193 if (!tls_index_.initialized()) | 189 if (!tls_index_.initialized()) |
| 194 return NULL; // For unittests only. | 190 return NULL; // For unittests only. |
| 195 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); | 191 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); |
| 196 if (registered) | 192 if (registered) |
| 197 return registered; | 193 return registered; |
| 198 | 194 |
| 199 // We must be a worker thread, since we didn't pre-register. | 195 // We must be a worker thread, since we didn't pre-register. |
| 200 ThreadData* worker_thread_data = NULL; | 196 ThreadData* worker_thread_data = NULL; |
| 197 size_t thread_number = 0; | |
| 201 { | 198 { |
| 202 base::AutoLock lock(*list_lock_.Pointer()); | 199 base::AutoLock lock(*list_lock_.Pointer()); |
| 203 if (unregistered_thread_data_pool_ && | 200 if (!unregistered_thread_data_pool_) |
| 204 !unregistered_thread_data_pool_->empty()) { | 201 unregistered_thread_data_pool_ = new ThreadDataPool; |
| 202 if (!unregistered_thread_data_pool_->empty()) { | |
| 205 worker_thread_data = | 203 worker_thread_data = |
| 206 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); | 204 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); |
| 207 unregistered_thread_data_pool_->pop(); | 205 unregistered_thread_data_pool_->pop(); |
|
ramant (doing other things)
2011/11/17 22:59:36
nit: Should we consider adding a DCHECK for worker
jar (doing other things)
2011/11/18 01:06:42
Line 205 would access lower memory if the instance
| |
| 206 } else { | |
| 207 thread_number = ++thread_number_counter_; | |
| 208 unregistered_thread_data_pool_->reserve(thread_number); | |
| 208 } | 209 } |
| 209 } | 210 } |
| 210 | 211 |
| 211 // If we can't find a previously used instance, then we have to create one. | 212 // If we can't find a previously used instance, then we have to create one. |
| 212 if (!worker_thread_data) | 213 if (!worker_thread_data) |
| 213 worker_thread_data = new ThreadData(); | 214 worker_thread_data = new ThreadData(thread_number); |
|
ramant (doing other things)
2011/11/17 22:59:36
nit: can thread_number be zero? If not should we c
jar (doing other things)
2011/11/18 01:06:42
Line 215 checks that via the DCHECK(). We're load
| |
| 215 DCHECK_GT(worker_thread_data->worker_thread_number_, 0u); | |
| 214 | 216 |
| 215 tls_index_.Set(worker_thread_data); | 217 tls_index_.Set(worker_thread_data); |
| 216 return worker_thread_data; | 218 return worker_thread_data; |
| 217 } | 219 } |
| 218 | 220 |
| 219 // static | 221 // static |
| 220 void ThreadData::OnThreadTermination(void* thread_data) { | 222 void ThreadData::OnThreadTermination(void* thread_data) { |
| 221 if (!kTrackAllTaskObjects) | 223 if (!kTrackAllTaskObjects) |
| 222 return; // Not compiled in. | 224 return; // Not compiled in. |
| 223 if (!thread_data) | 225 if (!thread_data) |
| 224 return; | 226 return; |
| 225 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); | 227 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); |
| 226 } | 228 } |
| 227 | 229 |
| 228 void ThreadData::OnThreadTerminationCleanup() const { | 230 void ThreadData::OnThreadTerminationCleanup() const { |
| 229 if (!is_a_worker_thread_) | 231 if (!worker_thread_number_) |
| 230 return; | 232 return; |
| 231 base::AutoLock lock(*list_lock_.Pointer()); | 233 base::AutoLock lock(*list_lock_.Pointer()); |
| 232 if (incarnation_counter_ != incarnation_count_for_pool_) | 234 if (incarnation_counter_ != incarnation_count_for_pool_) |
| 233 return; // ThreadData was constructed in an earlier unit test. | 235 return; // ThreadData was constructed in an earlier unit test. |
| 234 | 236 // The following will never have to do an allocation. |
| 235 // Handle case where we are in unit tests, and have become UNINITIALIZED. | |
| 236 // In that case, the pool might be NULL. We really should detect this via the | |
| 237 // incarnation_counter_, but this call is rarely made, so we can afford to | |
| 238 // code defensively. | |
| 239 if (!unregistered_thread_data_pool_) | |
| 240 unregistered_thread_data_pool_ = new ThreadDataPool; | |
| 241 unregistered_thread_data_pool_->push(this); | 237 unregistered_thread_data_pool_->push(this); |
|
ramant (doing other things)
2011/11/17 22:59:36
nit: Can we add a DCHECK for unregistered_thread_d
jar (doing other things)
2011/11/18 01:06:42
If unregistered_thread_data_pool_ is null, then we
| |
| 242 } | 238 } |
| 243 | 239 |
| 244 // static | 240 // static |
| 245 base::DictionaryValue* ThreadData::ToValue() { | 241 base::DictionaryValue* ThreadData::ToValue() { |
| 246 DataCollector collected_data; // Gather data. | 242 DataCollector collected_data; // Gather data. |
| 247 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 243 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
| 248 base::ListValue* list = collected_data.ToValue(); | 244 base::ListValue* list = collected_data.ToValue(); |
| 249 base::DictionaryValue* dictionary = new base::DictionaryValue(); | 245 base::DictionaryValue* dictionary = new base::DictionaryValue(); |
| 250 dictionary->Set("list", list); | 246 dictionary->Set("list", list); |
| 251 return dictionary; | 247 return dictionary; |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 555 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); | 551 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); |
| 556 next_thread_data->birth_map_.end() != it; ++it) | 552 next_thread_data->birth_map_.end() != it; ++it) |
| 557 delete it->second; // Delete the Birth Records. | 553 delete it->second; // Delete the Birth Records. |
| 558 next_thread_data->birth_map_.clear(); | 554 next_thread_data->birth_map_.clear(); |
| 559 next_thread_data->death_map_.clear(); | 555 next_thread_data->death_map_.clear(); |
| 560 delete next_thread_data; // Includes all Death Records. | 556 delete next_thread_data; // Includes all Death Records. |
| 561 } | 557 } |
| 562 } | 558 } |
| 563 | 559 |
| 564 //------------------------------------------------------------------------------ | 560 //------------------------------------------------------------------------------ |
| 561 // Small partial implementation of a stack that never has to allocate during a | |
| 562 // push() operation, because it is always prepared to accept the maximum number | |
| 563 // of ThreadData instances (all the worker thread related instances). | |
| 564 | |
| 565 ThreadData::ThreadDataPool::ThreadDataPool() : empty_slot_(0) {}; | |
| 566 ThreadData::ThreadDataPool::~ThreadDataPool() {}; | |
| 567 | |
| 568 bool ThreadData::ThreadDataPool::empty() const { return empty_slot_ == 0; } | |
| 569 | |
| 570 void ThreadData::ThreadDataPool::reserve(size_t largest_worker_pool_number) { | |
| 571 // Worker pool numbers start at 1, and exclude 0, so the number is exactly | |
| 572 // the least size needed. | |
| 573 // Due to asynchronous construction of worker-pool numbers (and associated | |
| 574 // ThreadData), we might not hear about the numbers sequentially. | |
| 575 if (largest_worker_pool_number > stack_.size()) | |
| 576 stack_.resize(largest_worker_pool_number); | |
| 577 } | |
| 578 | |
| 579 const ThreadData* ThreadData::ThreadDataPool::top() const { | |
| 580 if (empty_slot_ > 0) | |
| 581 return stack_[empty_slot_ - 1]; | |
| 582 NOTREACHED(); | |
| 583 return NULL; | |
| 584 } | |
| 585 | |
| 586 void ThreadData::ThreadDataPool::push(const ThreadData* thread_data) { | |
| 587 if (empty_slot_ < stack_.size()) { | |
|
ramant (doing other things)
2011/11/17 22:59:36
nit: is there a value to check if thread_data->wor
jar (doing other things)
2011/11/18 01:06:42
If that violation takes place, then I'll fall thro
| |
| 588 stack_[empty_slot_] = thread_data; | |
| 589 ++empty_slot_; | |
| 590 return; | |
| 591 } | |
| 592 NOTREACHED(); | |
| 593 } | |
| 594 | |
| 595 void ThreadData::ThreadDataPool::pop() { | |
| 596 if (empty_slot_ > 0) { | |
| 597 --empty_slot_; | |
| 598 return; | |
| 599 } | |
| 600 NOTREACHED(); | |
| 601 } | |
| 602 | |
| 603 //------------------------------------------------------------------------------ | |
| 565 // Individual 3-tuple of birth (place and thread) along with death thread, and | 604 // Individual 3-tuple of birth (place and thread) along with death thread, and |
| 566 // the accumulated stats for instances (DeathData). | 605 // the accumulated stats for instances (DeathData). |
| 567 | 606 |
| 568 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, | 607 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, |
| 569 const ThreadData& death_thread, | 608 const ThreadData& death_thread, |
| 570 const DeathData& death_data) | 609 const DeathData& death_data) |
| 571 : birth_(&birth_on_thread), | 610 : birth_(&birth_on_thread), |
| 572 death_thread_(&death_thread), | 611 death_thread_(&death_thread), |
| 573 death_data_(death_data) { | 612 death_data_(death_data) { |
| 574 } | 613 } |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 655 | 694 |
| 656 base::ListValue* DataCollector::ToValue() const { | 695 base::ListValue* DataCollector::ToValue() const { |
| 657 base::ListValue* list = new base::ListValue; | 696 base::ListValue* list = new base::ListValue; |
| 658 for (size_t i = 0; i < collection_.size(); ++i) { | 697 for (size_t i = 0; i < collection_.size(); ++i) { |
| 659 list->Append(collection_[i].ToValue()); | 698 list->Append(collection_[i].ToValue()); |
| 660 } | 699 } |
| 661 return list; | 700 return list; |
| 662 } | 701 } |
| 663 | 702 |
| 664 } // namespace tracked_objects | 703 } // namespace tracked_objects |
| OLD | NEW |