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/third_party/valgrind/memcheck.h" | 12 #include "base/third_party/valgrind/memcheck.h" |
| 13 #include "base/threading/thread_restrictions.h" | 13 #include "base/threading/thread_restrictions.h" |
| 14 #include "build/build_config.h" | 14 #include "build/build_config.h" |
| 15 #include "base/port.h" | 15 #include "base/port.h" |
| 16 | 16 |
| 17 using base::TimeDelta; | 17 using base::TimeDelta; |
| 18 | 18 |
| 19 namespace tracked_objects { | 19 namespace tracked_objects { |
| 20 | 20 |
| 21 namespace { | 21 namespace { |
| 22 // Flag to compile out almost all of the task tracking code. | 22 // Flag to compile out almost all of the task tracking code. |
| 23 static const bool kTrackAllTaskObjects = true; | 23 static const bool kTrackAllTaskObjects = true; |
| 24 | 24 |
| 25 // Flag to compile out parent-child link recording. | |
| 26 static const bool kTrackParentChildLinks = false; | |
| 27 | |
| 25 // When ThreadData is first initialized, should we start in an ACTIVE state to | 28 // When ThreadData is first initialized, should we start in an ACTIVE state to |
| 26 // record all of the startup-time tasks, or should we start up DEACTIVATED, so | 29 // record all of the startup-time tasks, or should we start up DEACTIVATED, so |
| 27 // that we only record after parsing the command line flag --enable-tracking. | 30 // that we only record after parsing the command line flag --enable-tracking. |
| 28 // Note that the flag may force either state, so this really controls only the | 31 // Note that the flag may force either state, so this really controls only the |
| 29 // period of time up until that flag is parsed. If there is no flag seen, then | 32 // period of time up until that flag is parsed. If there is no flag seen, then |
| 30 // this state may prevail for much or all of the process lifetime. | 33 // this state may prevail for much or all of the process lifetime. |
| 31 static const ThreadData::Status kInitialStartupState = ThreadData::ACTIVE; | 34 static const ThreadData::Status kInitialStartupState = ThreadData::FULLY_ACTIVE; |
| 32 } // anonymous namespace. | 35 } // namespace |
| 33 | 36 |
| 34 //------------------------------------------------------------------------------ | 37 //------------------------------------------------------------------------------ |
| 35 // DeathData tallies durations when a death takes place. | 38 // DeathData tallies durations when a death takes place. |
| 36 | 39 |
| 37 DeathData::DeathData() { | 40 DeathData::DeathData() { |
| 38 Clear(); | 41 Clear(); |
| 39 } | 42 } |
| 40 | 43 |
| 41 DeathData::DeathData(int count) { | 44 DeathData::DeathData(int count) { |
| 42 Clear(); | 45 Clear(); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 135 //------------------------------------------------------------------------------ | 138 //------------------------------------------------------------------------------ |
| 136 BirthOnThread::BirthOnThread(const Location& location, | 139 BirthOnThread::BirthOnThread(const Location& location, |
| 137 const ThreadData& current) | 140 const ThreadData& current) |
| 138 : location_(location), | 141 : location_(location), |
| 139 birth_thread_(¤t) { | 142 birth_thread_(¤t) { |
| 140 } | 143 } |
| 141 | 144 |
| 142 const Location BirthOnThread::location() const { return location_; } | 145 const Location BirthOnThread::location() const { return location_; } |
| 143 const ThreadData* BirthOnThread::birth_thread() const { return birth_thread_; } | 146 const ThreadData* BirthOnThread::birth_thread() const { return birth_thread_; } |
| 144 | 147 |
| 148 void BirthOnThread::ToValue(const std::string& prefix, | |
| 149 base::DictionaryValue* dictionary) const { | |
| 150 dictionary->Set(prefix + "_location", location_.ToValue()); | |
| 151 dictionary->Set(prefix + "_thread", | |
| 152 base::Value::CreateStringValue(birth_thread_->thread_name())); | |
| 153 } | |
| 154 | |
| 145 //------------------------------------------------------------------------------ | 155 //------------------------------------------------------------------------------ |
| 146 Births::Births(const Location& location, const ThreadData& current) | 156 Births::Births(const Location& location, const ThreadData& current) |
| 147 : BirthOnThread(location, current), | 157 : BirthOnThread(location, current), |
| 148 birth_count_(1) { } | 158 birth_count_(1) { } |
| 149 | 159 |
| 150 int Births::birth_count() const { return birth_count_; } | 160 int Births::birth_count() const { return birth_count_; } |
| 151 | 161 |
| 152 void Births::RecordBirth() { ++birth_count_; } | 162 void Births::RecordBirth() { ++birth_count_; } |
| 153 | 163 |
| 154 void Births::ForgetBirth() { --birth_count_; } | 164 void Births::ForgetBirth() { --birth_count_; } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 | 196 |
| 187 // static | 197 // static |
| 188 base::LazyInstance<base::Lock, | 198 base::LazyInstance<base::Lock, |
| 189 base::LeakyLazyInstanceTraits<base::Lock> > | 199 base::LeakyLazyInstanceTraits<base::Lock> > |
| 190 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER; | 200 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER; |
| 191 | 201 |
| 192 // static | 202 // static |
| 193 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 203 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
| 194 | 204 |
| 195 ThreadData::ThreadData(const std::string& suggested_name) | 205 ThreadData::ThreadData(const std::string& suggested_name) |
| 196 : incarnation_count_for_pool_(-1), | 206 : next_(NULL), |
| 197 next_(NULL), | |
| 198 next_retired_worker_(NULL), | 207 next_retired_worker_(NULL), |
| 199 worker_thread_number_(0) { | 208 worker_thread_number_(0), |
| 209 incarnation_count_for_pool_(-1) { | |
| 200 DCHECK_GE(suggested_name.size(), 0u); | 210 DCHECK_GE(suggested_name.size(), 0u); |
| 201 thread_name_ = suggested_name; | 211 thread_name_ = suggested_name; |
| 202 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. | 212 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. |
| 203 } | 213 } |
| 204 | 214 |
| 205 ThreadData::ThreadData(int thread_number) | 215 ThreadData::ThreadData(int thread_number) |
| 206 : incarnation_count_for_pool_(-1), | 216 : next_(NULL), |
| 207 next_(NULL), | |
| 208 next_retired_worker_(NULL), | 217 next_retired_worker_(NULL), |
| 209 worker_thread_number_(thread_number) { | 218 worker_thread_number_(thread_number), |
| 219 incarnation_count_for_pool_(-1) { | |
| 210 CHECK_GT(thread_number, 0); | 220 CHECK_GT(thread_number, 0); |
| 211 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); | 221 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); |
| 212 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. | 222 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. |
| 213 } | 223 } |
| 214 | 224 |
| 215 ThreadData::~ThreadData() {} | 225 ThreadData::~ThreadData() {} |
| 216 | 226 |
| 217 void ThreadData::PushToHeadOfList() { | 227 void ThreadData::PushToHeadOfList() { |
| 218 // Toss in a hint of randomness (atop the uniniitalized value). | 228 // Toss in a hint of randomness (atop the uniniitalized value). |
| 219 (void)VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(&random_number_, | 229 (void)VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(&random_number_, |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 280 tls_index_.Set(worker_thread_data); | 290 tls_index_.Set(worker_thread_data); |
| 281 return worker_thread_data; | 291 return worker_thread_data; |
| 282 } | 292 } |
| 283 | 293 |
| 284 // static | 294 // static |
| 285 void ThreadData::OnThreadTermination(void* thread_data) { | 295 void ThreadData::OnThreadTermination(void* thread_data) { |
| 286 // We must NOT do any allocations during this callback. There is a chance | 296 // We must NOT do any allocations during this callback. There is a chance |
| 287 // that the allocator is no longer active on this thread. | 297 // that the allocator is no longer active on this thread. |
| 288 if (!kTrackAllTaskObjects) | 298 if (!kTrackAllTaskObjects) |
| 289 return; // Not compiled in. | 299 return; // Not compiled in. |
| 290 if (!thread_data) | |
|
jar (doing other things)
2011/12/13 07:31:41
Thread local store no longers call us with NULLs.
| |
| 291 return; | |
| 292 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); | 300 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); |
| 293 } | 301 } |
| 294 | 302 |
| 295 void ThreadData::OnThreadTerminationCleanup() { | 303 void ThreadData::OnThreadTerminationCleanup() { |
| 296 // The list_lock_ was created when we registered the callback, so it won't be | 304 // The list_lock_ was created when we registered the callback, so it won't be |
| 297 // allocated here despite the lazy reference. | 305 // allocated here despite the lazy reference. |
| 298 base::AutoLock lock(*list_lock_.Pointer()); | 306 base::AutoLock lock(*list_lock_.Pointer()); |
| 299 if (incarnation_counter_ != incarnation_count_for_pool_) | 307 if (incarnation_counter_ != incarnation_count_for_pool_) |
| 300 return; // ThreadData was constructed in an earlier unit test. | 308 return; // ThreadData was constructed in an earlier unit test. |
| 301 ++cleanup_count_; | 309 ++cleanup_count_; |
| 302 // Only worker threads need to be retired and reused. | 310 // Only worker threads need to be retired and reused. |
| 303 if (!worker_thread_number_) { | 311 if (!worker_thread_number_) { |
| 304 return; | 312 return; |
| 305 } | 313 } |
| 306 // We must NOT do any allocations during this callback. | 314 // We must NOT do any allocations during this callback. |
| 307 // Using the simple linked lists avoids all allocations. | 315 // Using the simple linked lists avoids all allocations. |
| 308 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL)); | 316 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL)); |
| 309 this->next_retired_worker_ = first_retired_worker_; | 317 this->next_retired_worker_ = first_retired_worker_; |
| 310 first_retired_worker_ = this; | 318 first_retired_worker_ = this; |
| 311 } | 319 } |
| 312 | 320 |
| 313 // static | 321 // static |
| 314 base::DictionaryValue* ThreadData::ToValue(bool reset_max) { | 322 base::DictionaryValue* ThreadData::ToValue(bool reset_max) { |
| 315 DataCollector collected_data; // Gather data. | 323 DataCollector collected_data; // Gather data. |
| 316 // Request multiple calls to collected_data.Append() for all threads. | 324 // Request multiple calls to collected_data.Append() for all threads. |
| 317 SendAllMaps(reset_max, &collected_data); | 325 SendAllMaps(reset_max, &collected_data); |
| 318 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 326 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
| 319 base::ListValue* list = collected_data.ToValue(); | |
| 320 base::DictionaryValue* dictionary = new base::DictionaryValue(); | 327 base::DictionaryValue* dictionary = new base::DictionaryValue(); |
| 321 dictionary->Set("list", list); | 328 collected_data.ToValue(dictionary); |
| 322 return dictionary; | 329 return dictionary; |
| 323 } | 330 } |
| 324 | 331 |
| 325 Births* ThreadData::TallyABirth(const Location& location) { | 332 Births* ThreadData::TallyABirth(const Location& location) { |
| 326 BirthMap::iterator it = birth_map_.find(location); | 333 BirthMap::iterator it = birth_map_.find(location); |
| 334 Births* child; | |
| 327 if (it != birth_map_.end()) { | 335 if (it != birth_map_.end()) { |
| 328 it->second->RecordBirth(); | 336 child = it->second; |
| 329 return it->second; | 337 child->RecordBirth(); |
| 338 } else { | |
| 339 child = new Births(location, *this); // Leak this. | |
| 340 // Lock since the map may get relocated now, and other threads sometimes | |
| 341 // snapshot it (but they lock before copying it). | |
| 342 base::AutoLock lock(map_lock_); | |
| 343 birth_map_[location] = child; | |
| 330 } | 344 } |
| 331 | 345 |
| 332 Births* tracker = new Births(location, *this); | 346 if (kTrackParentChildLinks && status_ > PROFILING_ACTIVE && |
| 333 // Lock since the map may get relocated now, and other threads sometimes | 347 !parent_stack_.empty()) { |
| 334 // snapshot it (but they lock before copying it). | 348 const Births* parent = parent_stack_.top(); |
| 335 base::AutoLock lock(map_lock_); | 349 ParentChildPair pair(parent, child); |
| 336 birth_map_[location] = tracker; | 350 if (parent_child_set_.find(pair) == parent_child_set_.end()) { |
| 337 return tracker; | 351 // Lock since the map may get relocated now, and other threads sometimes |
| 352 // snapshot it (but they lock before copying it). | |
| 353 base::AutoLock lock(map_lock_); | |
| 354 parent_child_set_.insert(pair); | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 return child; | |
| 338 } | 359 } |
| 339 | 360 |
| 340 void ThreadData::TallyADeath(const Births& birth, | 361 void ThreadData::TallyADeath(const Births& birth, |
| 341 DurationInt queue_duration, | 362 DurationInt queue_duration, |
| 342 DurationInt run_duration) { | 363 DurationInt run_duration) { |
| 343 // Stir in some randomness, plus add constant in case durations are zero. | 364 // Stir in some randomness, plus add constant in case durations are zero. |
| 344 const DurationInt kSomePrimeNumber = 5939; // To big is 4294967279; | 365 const DurationInt kSomePrimeNumber = 2147483647; |
| 345 random_number_ += queue_duration + run_duration + kSomePrimeNumber; | 366 random_number_ += queue_duration + run_duration + kSomePrimeNumber; |
| 346 // An address is going to have some randomness to it as well ;-). | 367 // An address is going to have some randomness to it as well ;-). |
| 347 random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0)); | 368 random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0)); |
| 348 | 369 |
| 349 DeathMap::iterator it = death_map_.find(&birth); | 370 DeathMap::iterator it = death_map_.find(&birth); |
| 350 DeathData* death_data; | 371 DeathData* death_data; |
| 351 if (it != death_map_.end()) { | 372 if (it != death_map_.end()) { |
| 352 death_data = &it->second; | 373 death_data = &it->second; |
| 353 } else { | 374 } else { |
| 354 base::AutoLock lock(map_lock_); // Lock as the map may get relocated now. | 375 base::AutoLock lock(map_lock_); // Lock as the map may get relocated now. |
| 355 death_data = &death_map_[&birth]; | 376 death_data = &death_map_[&birth]; |
| 356 } // Release lock ASAP. | 377 } // Release lock ASAP. |
| 357 death_data->RecordDeath(queue_duration, run_duration, random_number_); | 378 death_data->RecordDeath(queue_duration, run_duration, random_number_); |
| 379 | |
| 380 if (!kTrackParentChildLinks) | |
| 381 return; | |
| 382 if (!parent_stack_.empty()) { // We might get turned off. | |
| 383 DCHECK_EQ(parent_stack_.top(), &birth); | |
| 384 parent_stack_.pop(); | |
| 385 } | |
| 358 } | 386 } |
| 359 | 387 |
| 360 // static | 388 // static |
| 361 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 389 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
| 362 if (!kTrackAllTaskObjects) | 390 if (!kTrackAllTaskObjects) |
| 363 return NULL; // Not compiled in. | 391 return NULL; // Not compiled in. |
| 364 | 392 |
| 365 if (!tracking_status()) | 393 if (!tracking_status()) |
| 366 return NULL; | 394 return NULL; |
| 367 ThreadData* current_thread_data = Get(); | 395 ThreadData* current_thread_data = Get(); |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 392 // we identify such tasks, and replace their post_time with the time they | 420 // we identify such tasks, and replace their post_time with the time they |
| 393 // were scheduled (requested?) to emerge from the delayed task queue. This | 421 // were scheduled (requested?) to emerge from the delayed task queue. This |
| 394 // means that queueing delay for such tasks will show how long they went | 422 // means that queueing delay for such tasks will show how long they went |
| 395 // unserviced, after they *could* be serviced. This is the same stat as we | 423 // unserviced, after they *could* be serviced. This is the same stat as we |
| 396 // have for non-delayed tasks, and we consistently call it queueing delay. | 424 // have for non-delayed tasks, and we consistently call it queueing delay. |
| 397 TrackedTime effective_post_time = completed_task.delayed_run_time.is_null() | 425 TrackedTime effective_post_time = completed_task.delayed_run_time.is_null() |
| 398 ? tracked_objects::TrackedTime(completed_task.time_posted) | 426 ? tracked_objects::TrackedTime(completed_task.time_posted) |
| 399 : tracked_objects::TrackedTime(completed_task.delayed_run_time); | 427 : tracked_objects::TrackedTime(completed_task.delayed_run_time); |
| 400 | 428 |
| 401 // Watch out for a race where status_ is changing, and hence one or both | 429 // Watch out for a race where status_ is changing, and hence one or both |
| 402 // of start_of_run or end_of_run is zero. IN that case, we didn't bother to | 430 // of start_of_run or end_of_run is zero. In that case, we didn't bother to |
| 403 // get a time value since we "weren't tracking" and we were trying to be | 431 // get a time value since we "weren't tracking" and we were trying to be |
| 404 // efficient by not calling for a genuine time value. For simplicity, we'll | 432 // efficient by not calling for a genuine time value. For simplicity, we'll |
| 405 // use a default zero duration when we can't calculate a true value. | 433 // use a default zero duration when we can't calculate a true value. |
| 406 DurationInt queue_duration = 0; | 434 DurationInt queue_duration = 0; |
| 407 DurationInt run_duration = 0; | 435 DurationInt run_duration = 0; |
| 408 if (!start_of_run.is_null()) { | 436 if (!start_of_run.is_null()) { |
| 409 queue_duration = (start_of_run - effective_post_time).InMilliseconds(); | 437 queue_duration = (start_of_run - effective_post_time).InMilliseconds(); |
| 410 if (!end_of_run.is_null()) | 438 if (!end_of_run.is_null()) |
| 411 run_duration = (end_of_run - start_of_run).InMilliseconds(); | 439 run_duration = (end_of_run - start_of_run).InMilliseconds(); |
| 412 } | 440 } |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 474 if (!start_of_run.is_null() && !end_of_run.is_null()) | 502 if (!start_of_run.is_null() && !end_of_run.is_null()) |
| 475 run_duration = (end_of_run - start_of_run).InMilliseconds(); | 503 run_duration = (end_of_run - start_of_run).InMilliseconds(); |
| 476 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); | 504 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); |
| 477 } | 505 } |
| 478 | 506 |
| 479 const std::string ThreadData::thread_name() const { return thread_name_; } | 507 const std::string ThreadData::thread_name() const { return thread_name_; } |
| 480 | 508 |
| 481 // This may be called from another thread. | 509 // This may be called from another thread. |
| 482 void ThreadData::SnapshotMaps(bool reset_max, | 510 void ThreadData::SnapshotMaps(bool reset_max, |
| 483 BirthMap* birth_map, | 511 BirthMap* birth_map, |
| 484 DeathMap* death_map) { | 512 DeathMap* death_map, |
| 513 ParentChildSet* parent_child_set) { | |
| 485 base::AutoLock lock(map_lock_); | 514 base::AutoLock lock(map_lock_); |
| 486 for (BirthMap::const_iterator it = birth_map_.begin(); | 515 for (BirthMap::const_iterator it = birth_map_.begin(); |
| 487 it != birth_map_.end(); ++it) | 516 it != birth_map_.end(); ++it) |
| 488 (*birth_map)[it->first] = it->second; | 517 (*birth_map)[it->first] = it->second; |
| 489 for (DeathMap::iterator it = death_map_.begin(); | 518 for (DeathMap::iterator it = death_map_.begin(); |
| 490 it != death_map_.end(); ++it) { | 519 it != death_map_.end(); ++it) { |
| 491 (*death_map)[it->first] = it->second; | 520 (*death_map)[it->first] = it->second; |
| 492 if (reset_max) | 521 if (reset_max) |
| 493 it->second.ResetMax(); | 522 it->second.ResetMax(); |
| 494 } | 523 } |
| 524 if (kTrackParentChildLinks) { | |
| 525 for (ParentChildSet::iterator it = parent_child_set_.begin(); | |
| 526 it != parent_child_set_.end(); ++it) | |
| 527 parent_child_set->insert(*it); | |
| 528 } | |
| 495 } | 529 } |
| 496 | 530 |
| 497 // static | 531 // static |
| 498 void ThreadData::SendAllMaps(bool reset_max, class DataCollector* target) { | 532 void ThreadData::SendAllMaps(bool reset_max, class DataCollector* target) { |
| 499 if (!kTrackAllTaskObjects) | 533 if (!kTrackAllTaskObjects) |
| 500 return; // Not compiled in. | 534 return; // Not compiled in. |
| 501 // Get an unchanging copy of a ThreadData list. | 535 // Get an unchanging copy of a ThreadData list. |
| 502 ThreadData* my_list = ThreadData::first(); | 536 ThreadData* my_list = ThreadData::first(); |
| 503 | 537 |
| 504 // Gather data serially. | 538 // Gather data serially. |
| 505 // This hackish approach *can* get some slighly corrupt tallies, as we are | 539 // This hackish approach *can* get some slighly corrupt tallies, as we are |
| 506 // grabbing values without the protection of a lock, but it has the advantage | 540 // grabbing values without the protection of a lock, but it has the advantage |
| 507 // of working even with threads that don't have message loops. If a user | 541 // of working even with threads that don't have message loops. If a user |
| 508 // sees any strangeness, they can always just run their stats gathering a | 542 // sees any strangeness, they can always just run their stats gathering a |
| 509 // second time. | 543 // second time. |
| 510 for (ThreadData* thread_data = my_list; | 544 for (ThreadData* thread_data = my_list; |
| 511 thread_data; | 545 thread_data; |
| 512 thread_data = thread_data->next()) { | 546 thread_data = thread_data->next()) { |
| 513 // Get copy of data. | 547 // Get copy of data. |
| 514 ThreadData::BirthMap birth_map; | 548 ThreadData::BirthMap birth_map; |
| 515 ThreadData::DeathMap death_map; | 549 ThreadData::DeathMap death_map; |
| 516 thread_data->SnapshotMaps(reset_max, &birth_map, &death_map); | 550 ThreadData::ParentChildSet parent_child_set; |
| 517 target->Append(*thread_data, birth_map, death_map); | 551 thread_data->SnapshotMaps(reset_max, &birth_map, &death_map, |
| 552 &parent_child_set); | |
| 553 target->Append(*thread_data, birth_map, death_map, parent_child_set); | |
| 518 } | 554 } |
| 519 } | 555 } |
| 520 | 556 |
| 521 // static | 557 // static |
| 522 void ThreadData::ResetAllThreadData() { | 558 void ThreadData::ResetAllThreadData() { |
| 523 ThreadData* my_list = first(); | 559 ThreadData* my_list = first(); |
| 524 | 560 |
| 525 for (ThreadData* thread_data = my_list; | 561 for (ThreadData* thread_data = my_list; |
| 526 thread_data; | 562 thread_data; |
| 527 thread_data = thread_data->next()) | 563 thread_data = thread_data->next()) |
| 528 thread_data->Reset(); | 564 thread_data->Reset(); |
| 529 } | 565 } |
| 530 | 566 |
| 531 void ThreadData::Reset() { | 567 void ThreadData::Reset() { |
| 532 base::AutoLock lock(map_lock_); | 568 base::AutoLock lock(map_lock_); |
| 533 for (DeathMap::iterator it = death_map_.begin(); | 569 for (DeathMap::iterator it = death_map_.begin(); |
| 534 it != death_map_.end(); ++it) | 570 it != death_map_.end(); ++it) |
| 535 it->second.Clear(); | 571 it->second.Clear(); |
| 536 for (BirthMap::iterator it = birth_map_.begin(); | 572 for (BirthMap::iterator it = birth_map_.begin(); |
| 537 it != birth_map_.end(); ++it) | 573 it != birth_map_.end(); ++it) |
| 538 it->second->Clear(); | 574 it->second->Clear(); |
| 539 } | 575 } |
| 540 | 576 |
| 541 bool ThreadData::Initialize() { | 577 bool ThreadData::Initialize() { |
| 542 if (!kTrackAllTaskObjects) | 578 if (!kTrackAllTaskObjects) |
| 543 return false; // Not compiled in. | 579 return false; // Not compiled in. |
| 544 if (status_ != UNINITIALIZED) | 580 if (status_ >= DEACTIVATED) |
| 545 return true; // Someone else did the initialization. | 581 return true; // Someone else did the initialization. |
| 546 // Due to racy lazy initialization in tests, we'll need to recheck status_ | 582 // Due to racy lazy initialization in tests, we'll need to recheck status_ |
| 547 // after we acquire the lock. | 583 // after we acquire the lock. |
| 548 | 584 |
| 549 // Ensure that we don't double initialize tls. We are called when single | 585 // Ensure that we don't double initialize tls. We are called when single |
| 550 // threaded in the product, but some tests may be racy and lazy about our | 586 // threaded in the product, but some tests may be racy and lazy about our |
| 551 // initialization. | 587 // initialization. |
| 552 base::AutoLock lock(*list_lock_.Pointer()); | 588 base::AutoLock lock(*list_lock_.Pointer()); |
| 553 if (status_ != UNINITIALIZED) | 589 if (status_ >= DEACTIVATED) |
| 554 return true; // Someone raced in here and beat us. | 590 return true; // Someone raced in here and beat us. |
| 555 | 591 |
| 556 // Perform the "real" TLS initialization now, and leave it intact through | 592 // Perform the "real" TLS initialization now, and leave it intact through |
| 557 // process termination. | 593 // process termination. |
| 558 if (!tls_index_.initialized()) { // Testing may have initialized this. | 594 if (!tls_index_.initialized()) { // Testing may have initialized this. |
| 595 DCHECK_EQ(status_, UNINITIALIZED); | |
| 559 tls_index_.Initialize(&ThreadData::OnThreadTermination); | 596 tls_index_.Initialize(&ThreadData::OnThreadTermination); |
| 560 if (!tls_index_.initialized()) | 597 if (!tls_index_.initialized()) |
| 561 return false; | 598 return false; |
| 599 } else { | |
| 600 // TLS was initialzed for us earlier. | |
| 601 DCHECK_EQ(status_, DORMANT_DURING_TESTS); | |
| 562 } | 602 } |
| 563 | 603 |
| 564 // Incarnation counter is only significant to testing, as it otherwise will | 604 // Incarnation counter is only significant to testing, as it otherwise will |
| 565 // never again change in this process. | 605 // never again change in this process. |
| 566 ++incarnation_counter_; | 606 ++incarnation_counter_; |
| 567 | 607 |
| 568 // The lock is not critical for setting status_, but it doesn't hurt. It also | 608 // The lock is not critical for setting status_, but it doesn't hurt. It also |
| 569 // ensures that if we have a racy initialization, that we'll bail as soon as | 609 // ensures that if we have a racy initialization, that we'll bail as soon as |
| 570 // we get the lock earlier in this method. | 610 // we get the lock earlier in this method. |
| 571 status_ = kInitialStartupState; | 611 status_ = kInitialStartupState; |
| 572 DCHECK(status_ != UNINITIALIZED); | 612 DCHECK(status_ != UNINITIALIZED); |
| 573 return true; | 613 return true; |
| 574 } | 614 } |
| 575 | 615 |
| 576 // static | 616 // static |
| 577 bool ThreadData::InitializeAndSetTrackingStatus(bool status) { | 617 bool ThreadData::InitializeAndSetTrackingStatus(bool status) { |
| 578 if (!Initialize()) // No-op if already initialized. | 618 if (!Initialize()) // No-op if already initialized. |
| 579 return false; // Not compiled in. | 619 return false; // Not compiled in. |
| 580 | 620 |
| 581 status_ = status ? ACTIVE : DEACTIVATED; | 621 if (!status) { |
| 622 status_ = DEACTIVATED; | |
| 623 } else { | |
| 624 if (kTrackParentChildLinks) | |
| 625 status_ = FULLY_ACTIVE; | |
| 626 else | |
| 627 status_ = PROFILING_ACTIVE; | |
| 628 } | |
| 582 return true; | 629 return true; |
| 583 } | 630 } |
| 584 | 631 |
| 585 // static | 632 // static |
| 586 bool ThreadData::tracking_status() { | 633 bool ThreadData::tracking_status() { |
| 587 return status_ == ACTIVE; | 634 return status_ > DEACTIVATED; |
| 588 } | 635 } |
| 589 | 636 |
| 590 // static | 637 // static |
| 591 TrackedTime ThreadData::NowForStartOfRun() { | 638 bool ThreadData::tracking_parent_child_status() { |
| 639 return status_ >= FULLY_ACTIVE; | |
| 640 } | |
| 641 | |
| 642 // static | |
| 643 TrackedTime ThreadData::NowForStartOfRun(const Births* parent) { | |
| 644 if (kTrackParentChildLinks && parent && status_ > PROFILING_ACTIVE) { | |
| 645 ThreadData* current_thread_data = Get(); | |
| 646 current_thread_data->parent_stack_.push(parent); | |
| 647 } | |
| 592 return Now(); | 648 return Now(); |
| 593 } | 649 } |
| 594 | 650 |
| 595 // static | 651 // static |
| 596 TrackedTime ThreadData::NowForEndOfRun() { | 652 TrackedTime ThreadData::NowForEndOfRun() { |
| 597 return Now(); | 653 return Now(); |
| 598 } | 654 } |
| 599 | 655 |
| 600 // static | 656 // static |
| 601 TrackedTime ThreadData::Now() { | 657 TrackedTime ThreadData::Now() { |
| 602 if (kTrackAllTaskObjects && tracking_status()) | 658 if (kTrackAllTaskObjects && tracking_status()) |
| 603 return TrackedTime::Now(); | 659 return TrackedTime::Now(); |
| 604 return TrackedTime(); // Super fast when disabled, or not compiled. | 660 return TrackedTime(); // Super fast when disabled, or not compiled. |
| 605 } | 661 } |
| 606 | 662 |
| 607 // static | 663 // static |
| 608 void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) { | 664 void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) { |
| 609 base::AutoLock lock(*list_lock_.Pointer()); | 665 base::AutoLock lock(*list_lock_.Pointer()); |
| 610 if (worker_thread_data_creation_count_ == 0) | 666 if (worker_thread_data_creation_count_ == 0) |
| 611 return; // We haven't really run much, and couldn't have leaked. | 667 return; // We haven't really run much, and couldn't have leaked. |
| 612 // Verify that we've at least shutdown/cleanup the major namesd threads. The | 668 // Verify that we've at least shutdown/cleanup the major namesd threads. The |
| 613 // caller should tell us how many thread shutdowns should have taken place by | 669 // caller should tell us how many thread shutdowns should have taken place by |
| 614 // now. | 670 // now. |
| 615 return; // TODO(jar): until this is working on XP, don't run the real test. | |
| 616 CHECK_GT(cleanup_count_, major_threads_shutdown_count); | 671 CHECK_GT(cleanup_count_, major_threads_shutdown_count); |
| 617 } | 672 } |
| 618 | 673 |
| 619 // static | 674 // static |
| 620 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { | 675 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { |
| 621 // This is only called from test code, where we need to cleanup so that | 676 // This is only called from test code, where we need to cleanup so that |
| 622 // additional tests can be run. | 677 // additional tests can be run. |
| 623 // We must be single threaded... but be careful anyway. | 678 // We must be single threaded... but be careful anyway. |
| 624 if (!InitializeAndSetTrackingStatus(false)) | 679 if (!InitializeAndSetTrackingStatus(false)) |
| 625 return; | 680 return; |
| 626 ThreadData* thread_data_list; | 681 ThreadData* thread_data_list; |
| 627 { | 682 { |
| 628 base::AutoLock lock(*list_lock_.Pointer()); | 683 base::AutoLock lock(*list_lock_.Pointer()); |
| 629 thread_data_list = all_thread_data_list_head_; | 684 thread_data_list = all_thread_data_list_head_; |
| 630 all_thread_data_list_head_ = NULL; | 685 all_thread_data_list_head_ = NULL; |
| 631 ++incarnation_counter_; | 686 ++incarnation_counter_; |
| 632 // To be clean, break apart the retired worker list (though we leak them). | 687 // To be clean, break apart the retired worker list (though we leak them). |
| 633 while (first_retired_worker_) { | 688 while (first_retired_worker_) { |
| 634 ThreadData* worker = first_retired_worker_; | 689 ThreadData* worker = first_retired_worker_; |
| 635 CHECK_GT(worker->worker_thread_number_, 0); | 690 CHECK_GT(worker->worker_thread_number_, 0); |
| 636 first_retired_worker_ = worker->next_retired_worker_; | 691 first_retired_worker_ = worker->next_retired_worker_; |
| 637 worker->next_retired_worker_ = NULL; | 692 worker->next_retired_worker_ = NULL; |
| 638 } | 693 } |
| 639 } | 694 } |
| 640 | 695 |
| 641 // Put most global static back in pristine shape. | 696 // Put most global static back in pristine shape. |
| 642 worker_thread_data_creation_count_ = 0; | 697 worker_thread_data_creation_count_ = 0; |
| 643 cleanup_count_ = 0; | 698 cleanup_count_ = 0; |
| 644 tls_index_.Set(NULL); | 699 tls_index_.Set(NULL); |
| 645 status_ = UNINITIALIZED; | 700 status_ = DORMANT_DURING_TESTS; // Almost UNINITIALIZED |
| 646 | 701 |
| 647 // To avoid any chance of racing in unit tests, which is the only place we | 702 // To avoid any chance of racing in unit tests, which is the only place we |
| 648 // call this function, we may sometimes leak all the data structures we | 703 // call this function, we may sometimes leak all the data structures we |
| 649 // recovered, as they may still be in use on threads from prior tests! | 704 // recovered, as they may still be in use on threads from prior tests! |
| 650 if (leak) | 705 if (leak) |
| 651 return; | 706 return; |
| 652 | 707 |
| 653 // When we want to cleanup (on a single thread), here is what we do. | 708 // When we want to cleanup (on a single thread), here is what we do. |
| 654 | 709 |
| 655 // Do actual recursive delete in all ThreadData instances. | 710 // Do actual recursive delete in all ThreadData instances. |
| 656 while (thread_data_list) { | 711 while (thread_data_list) { |
| 657 ThreadData* next_thread_data = thread_data_list; | 712 ThreadData* next_thread_data = thread_data_list; |
| 658 thread_data_list = thread_data_list->next(); | 713 thread_data_list = thread_data_list->next(); |
| 659 | 714 |
| 660 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); | 715 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); |
| 661 next_thread_data->birth_map_.end() != it; ++it) | 716 next_thread_data->birth_map_.end() != it; ++it) |
| 662 delete it->second; // Delete the Birth Records. | 717 delete it->second; // Delete the Birth Records. |
| 663 next_thread_data->birth_map_.clear(); | |
| 664 next_thread_data->death_map_.clear(); | |
| 665 delete next_thread_data; // Includes all Death Records. | 718 delete next_thread_data; // Includes all Death Records. |
| 666 } | 719 } |
| 667 } | 720 } |
| 668 | 721 |
| 669 //------------------------------------------------------------------------------ | 722 //------------------------------------------------------------------------------ |
| 670 // Individual 3-tuple of birth (place and thread) along with death thread, and | 723 // Individual 3-tuple of birth (place and thread) along with death thread, and |
| 671 // the accumulated stats for instances (DeathData). | 724 // the accumulated stats for instances (DeathData). |
| 672 | 725 |
| 673 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, | 726 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, |
| 674 const ThreadData& death_thread, | 727 const ThreadData& death_thread, |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 685 } | 738 } |
| 686 | 739 |
| 687 const std::string Snapshot::DeathThreadName() const { | 740 const std::string Snapshot::DeathThreadName() const { |
| 688 if (death_thread_) | 741 if (death_thread_) |
| 689 return death_thread_->thread_name(); | 742 return death_thread_->thread_name(); |
| 690 return "Still_Alive"; | 743 return "Still_Alive"; |
| 691 } | 744 } |
| 692 | 745 |
| 693 base::DictionaryValue* Snapshot::ToValue() const { | 746 base::DictionaryValue* Snapshot::ToValue() const { |
| 694 base::DictionaryValue* dictionary = new base::DictionaryValue; | 747 base::DictionaryValue* dictionary = new base::DictionaryValue; |
| 695 dictionary->Set("death_data", death_data_.ToValue()); | 748 // TODO(jar): Switch the next two lines to: |
| 749 // birth_->ToValue("birth", dictionary); | |
| 750 // ...but that will require fixing unit tests, and JS to take | |
| 751 // "birth_location" rather than "location" | |
| 696 dictionary->Set("birth_thread", | 752 dictionary->Set("birth_thread", |
| 697 base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); | 753 base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); |
| 754 dictionary->Set("location", birth_->location().ToValue()); | |
| 755 | |
| 756 dictionary->Set("death_data", death_data_.ToValue()); | |
| 698 dictionary->Set("death_thread", | 757 dictionary->Set("death_thread", |
| 699 base::Value::CreateStringValue(DeathThreadName())); | 758 base::Value::CreateStringValue(DeathThreadName())); |
| 700 dictionary->Set("location", birth_->location().ToValue()); | |
| 701 return dictionary; | 759 return dictionary; |
| 702 } | 760 } |
| 703 | 761 |
| 704 //------------------------------------------------------------------------------ | 762 //------------------------------------------------------------------------------ |
| 705 // DataCollector | 763 // DataCollector |
| 706 | 764 |
| 707 DataCollector::DataCollector() {} | 765 DataCollector::DataCollector() {} |
| 708 | 766 |
| 709 DataCollector::~DataCollector() { | 767 DataCollector::~DataCollector() { |
| 710 } | 768 } |
| 711 | 769 |
| 712 void DataCollector::Append(const ThreadData &thread_data, | 770 void DataCollector::Append(const ThreadData &thread_data, |
| 713 const ThreadData::BirthMap &birth_map, | 771 const ThreadData::BirthMap& birth_map, |
| 714 const ThreadData::DeathMap &death_map) { | 772 const ThreadData::DeathMap& death_map, |
| 773 const ThreadData::ParentChildSet& parent_child_set) { | |
| 715 for (ThreadData::DeathMap::const_iterator it = death_map.begin(); | 774 for (ThreadData::DeathMap::const_iterator it = death_map.begin(); |
| 716 it != death_map.end(); ++it) { | 775 it != death_map.end(); ++it) { |
| 717 collection_.push_back(Snapshot(*it->first, thread_data, it->second)); | 776 collection_.push_back(Snapshot(*it->first, thread_data, it->second)); |
| 718 global_birth_count_[it->first] -= it->first->birth_count(); | 777 global_birth_count_[it->first] -= it->first->birth_count(); |
| 719 } | 778 } |
| 720 | 779 |
| 721 for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); | 780 for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); |
| 722 it != birth_map.end(); ++it) { | 781 it != birth_map.end(); ++it) { |
| 723 global_birth_count_[it->second] += it->second->birth_count(); | 782 global_birth_count_[it->second] += it->second->birth_count(); |
| 724 } | 783 } |
| 784 | |
| 785 for (ThreadData::ParentChildSet::const_iterator it = parent_child_set.begin(); | |
| 786 it != parent_child_set.end(); ++it) { | |
| 787 parent_child_set_.insert(*it); | |
| 788 } | |
| 725 } | 789 } |
| 726 | 790 |
| 727 DataCollector::Collection* DataCollector::collection() { | 791 DataCollector::Collection* DataCollector::collection() { |
| 728 return &collection_; | 792 return &collection_; |
| 729 } | 793 } |
| 730 | 794 |
| 731 void DataCollector::AddListOfLivingObjects() { | 795 void DataCollector::AddListOfLivingObjects() { |
| 732 for (BirthCount::iterator it = global_birth_count_.begin(); | 796 for (BirthCount::iterator it = global_birth_count_.begin(); |
| 733 it != global_birth_count_.end(); ++it) { | 797 it != global_birth_count_.end(); ++it) { |
| 734 if (it->second > 0) | 798 if (it->second > 0) |
| 735 collection_.push_back(Snapshot(*it->first, it->second)); | 799 collection_.push_back(Snapshot(*it->first, it->second)); |
| 736 } | 800 } |
| 737 } | 801 } |
| 738 | 802 |
| 739 base::ListValue* DataCollector::ToValue() const { | 803 void DataCollector::ToValue(base::DictionaryValue* dictionary) const { |
| 740 base::ListValue* list = new base::ListValue; | 804 base::ListValue* list = new base::ListValue; |
| 741 for (size_t i = 0; i < collection_.size(); ++i) { | 805 for (size_t i = 0; i < collection_.size(); ++i) { |
| 742 list->Append(collection_[i].ToValue()); | 806 list->Append(collection_[i].ToValue()); |
| 743 } | 807 } |
| 744 return list; | 808 dictionary->Set("list", list); |
| 809 | |
| 810 base::ListValue* descendants = new base::ListValue; | |
| 811 for (ThreadData::ParentChildSet::const_iterator it = | |
| 812 parent_child_set_.begin(); | |
| 813 it != parent_child_set_.end(); | |
| 814 ++it) { | |
| 815 base::DictionaryValue* parent_child = new base::DictionaryValue; | |
| 816 it->first->ToValue("parent", parent_child); | |
| 817 it->second->ToValue("child", parent_child); | |
| 818 descendants->Append(parent_child); | |
| 819 } | |
| 820 dictionary->Set("descendants", descendants); | |
| 745 } | 821 } |
| 746 | 822 |
| 747 } // namespace tracked_objects | 823 } // namespace tracked_objects |
| OLD | NEW |