Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/profiler/alternate_timer.h" | |
| 11 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
| 12 #include "base/third_party/valgrind/memcheck.h" | 13 #include "base/third_party/valgrind/memcheck.h" |
| 13 #include "base/threading/thread_restrictions.h" | 14 #include "base/threading/thread_restrictions.h" |
| 14 #include "build/build_config.h" | 15 #include "build/build_config.h" |
| 15 #include "base/port.h" | 16 #include "base/port.h" |
| 16 | 17 |
| 17 using base::TimeDelta; | 18 using base::TimeDelta; |
| 18 | 19 |
| 19 namespace tracked_objects { | 20 namespace tracked_objects { |
| 20 | 21 |
| 21 namespace { | 22 namespace { |
| 22 | 23 |
| 23 // Flag to compile out almost all of the task tracking code. | 24 // Flag to compile out almost all of the task tracking code. |
| 24 const bool kTrackAllTaskObjects = true; | 25 const bool kTrackAllTaskObjects = true; |
| 25 | 26 |
| 26 // Flag to compile out parent-child link recording. | 27 // Flag to compile out parent-child link recording. |
| 27 const bool kTrackParentChildLinks = false; | 28 const bool kTrackParentChildLinks = false; |
| 28 | 29 |
| 29 // When ThreadData is first initialized, should we start in an ACTIVE state to | 30 // When ThreadData is first initialized, should we start in an ACTIVE state to |
| 30 // record all of the startup-time tasks, or should we start up DEACTIVATED, so | 31 // record all of the startup-time tasks, or should we start up DEACTIVATED, so |
| 31 // that we only record after parsing the command line flag --enable-tracking. | 32 // that we only record after parsing the command line flag --enable-tracking. |
| 32 // Note that the flag may force either state, so this really controls only the | 33 // Note that the flag may force either state, so this really controls only the |
| 33 // period of time up until that flag is parsed. If there is no flag seen, then | 34 // period of time up until that flag is parsed. If there is no flag seen, then |
| 34 // this state may prevail for much or all of the process lifetime. | 35 // this state may prevail for much or all of the process lifetime. |
| 35 const ThreadData::Status kInitialStartupState = | 36 const ThreadData::Status kInitialStartupState = |
| 36 ThreadData::PROFILING_CHILDREN_ACTIVE; | 37 ThreadData::PROFILING_CHILDREN_ACTIVE; |
| 37 | 38 |
| 39 // Control whether an alternate time source (Now() function) is supported by | |
| 40 // the ThreadData class. This compile time flag should be set to try if we want | |
|
ramant (doing other things)
2012/02/15 18:33:46
nit: try -> true
jar (doing other things)
2012/02/15 20:26:32
Done. I added a lot of text to try to be even cle
| |
| 41 // other modules, such as an allocator, to call with a new thread-specific | |
| 42 // Now() function to use (instead of of calling into tracked_time). | |
| 43 static const bool kAllowAlternateTimeSourceHandling = true; | |
| 44 | |
| 45 // Environment variable name that is used to activate alternate timer profiling | |
| 46 // (such as using TCMalloc allocations to provide a pseudo-timer) for tasks | |
| 47 // instead of wall clock profiling. | |
| 48 static const char k_alternate_profile_timer[] = "CHROME_PROFILER_TIME"; | |
|
ramant (doing other things)
2012/02/15 18:33:46
nit: should we consider commenting that "k_alterna
jar (doing other things)
2012/02/15 20:26:32
I went back and tried to move it to shared place.
| |
| 49 | |
| 38 } // namespace | 50 } // namespace |
| 39 | 51 |
| 40 //------------------------------------------------------------------------------ | 52 //------------------------------------------------------------------------------ |
| 41 // DeathData tallies durations when a death takes place. | 53 // DeathData tallies durations when a death takes place. |
| 42 | 54 |
| 43 DeathData::DeathData() { | 55 DeathData::DeathData() { |
| 44 Clear(); | 56 Clear(); |
| 45 } | 57 } |
| 46 | 58 |
| 47 DeathData::DeathData(int count) { | 59 DeathData::DeathData(int count) { |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 169 void Births::Clear() { birth_count_ = 0; } | 181 void Births::Clear() { birth_count_ = 0; } |
| 170 | 182 |
| 171 //------------------------------------------------------------------------------ | 183 //------------------------------------------------------------------------------ |
| 172 // ThreadData maintains the central data for all births and deaths on a single | 184 // ThreadData maintains the central data for all births and deaths on a single |
| 173 // thread. | 185 // thread. |
| 174 | 186 |
| 175 // TODO(jar): We should pull all these static vars together, into a struct, and | 187 // TODO(jar): We should pull all these static vars together, into a struct, and |
| 176 // optimize layout so that we benefit from locality of reference during accesses | 188 // optimize layout so that we benefit from locality of reference during accesses |
| 177 // to them. | 189 // to them. |
| 178 | 190 |
| 191 // static | |
| 192 NowFunction* ThreadData::now_function_ = NULL; | |
| 193 | |
| 179 // A TLS slot which points to the ThreadData instance for the current thread. We | 194 // A TLS slot which points to the ThreadData instance for the current thread. We |
| 180 // do a fake initialization here (zeroing out data), and then the real in-place | 195 // do a fake initialization here (zeroing out data), and then the real in-place |
| 181 // construction happens when we call tls_index_.Initialize(). | 196 // construction happens when we call tls_index_.Initialize(). |
| 182 // static | 197 // static |
| 183 base::ThreadLocalStorage::StaticSlot ThreadData::tls_index_ = TLS_INITIALIZER; | 198 base::ThreadLocalStorage::StaticSlot ThreadData::tls_index_ = TLS_INITIALIZER; |
| 184 | 199 |
| 185 // static | 200 // static |
| 186 int ThreadData::worker_thread_data_creation_count_ = 0; | 201 int ThreadData::worker_thread_data_creation_count_ = 0; |
| 187 | 202 |
| 188 // static | 203 // static |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 363 | 378 |
| 364 void ThreadData::TallyADeath(const Births& birth, | 379 void ThreadData::TallyADeath(const Births& birth, |
| 365 DurationInt queue_duration, | 380 DurationInt queue_duration, |
| 366 DurationInt run_duration) { | 381 DurationInt run_duration) { |
| 367 // Stir in some randomness, plus add constant in case durations are zero. | 382 // Stir in some randomness, plus add constant in case durations are zero. |
| 368 const DurationInt kSomePrimeNumber = 2147483647; | 383 const DurationInt kSomePrimeNumber = 2147483647; |
| 369 random_number_ += queue_duration + run_duration + kSomePrimeNumber; | 384 random_number_ += queue_duration + run_duration + kSomePrimeNumber; |
| 370 // An address is going to have some randomness to it as well ;-). | 385 // An address is going to have some randomness to it as well ;-). |
| 371 random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0)); | 386 random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0)); |
| 372 | 387 |
| 388 // We don't have queue durations without OS timer. OS timer is automatically | |
| 389 // used for task-post-timing, so the use of an alternate timer implies all | |
| 390 // queue times are invalid. | |
| 391 if (kAllowAlternateTimeSourceHandling && now_function_) | |
| 392 queue_duration = 0; | |
| 393 | |
| 373 DeathMap::iterator it = death_map_.find(&birth); | 394 DeathMap::iterator it = death_map_.find(&birth); |
| 374 DeathData* death_data; | 395 DeathData* death_data; |
| 375 if (it != death_map_.end()) { | 396 if (it != death_map_.end()) { |
| 376 death_data = &it->second; | 397 death_data = &it->second; |
| 377 } else { | 398 } else { |
| 378 base::AutoLock lock(map_lock_); // Lock as the map may get relocated now. | 399 base::AutoLock lock(map_lock_); // Lock as the map may get relocated now. |
| 379 death_data = &death_map_[&birth]; | 400 death_data = &death_map_[&birth]; |
| 380 } // Release lock ASAP. | 401 } // Release lock ASAP. |
| 381 death_data->RecordDeath(queue_duration, run_duration, random_number_); | 402 death_data->RecordDeath(queue_duration, run_duration, random_number_); |
| 382 | 403 |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 572 void ThreadData::Reset() { | 593 void ThreadData::Reset() { |
| 573 base::AutoLock lock(map_lock_); | 594 base::AutoLock lock(map_lock_); |
| 574 for (DeathMap::iterator it = death_map_.begin(); | 595 for (DeathMap::iterator it = death_map_.begin(); |
| 575 it != death_map_.end(); ++it) | 596 it != death_map_.end(); ++it) |
| 576 it->second.Clear(); | 597 it->second.Clear(); |
| 577 for (BirthMap::iterator it = birth_map_.begin(); | 598 for (BirthMap::iterator it = birth_map_.begin(); |
| 578 it != birth_map_.end(); ++it) | 599 it != birth_map_.end(); ++it) |
| 579 it->second->Clear(); | 600 it->second->Clear(); |
| 580 } | 601 } |
| 581 | 602 |
| 603 static void OptionallyInitializeAlternateTimer() { | |
| 604 char* alternate_selector = getenv(k_alternate_profile_timer); | |
| 605 if (!alternate_selector) | |
| 606 return; | |
| 607 switch (*alternate_selector) { | |
| 608 case '0': // This is the default value, and uses the wall clock time. | |
| 609 break; | |
| 610 case '1': { | |
| 611 // Use the TCMalloc allocations-on-thread as a pseudo-time. | |
| 612 NowFunction* now_function = GetAlternateTimeSource(); | |
| 613 if (!now_function) | |
| 614 break; | |
| 615 ThreadData::SetAlternateTimeSource(now_function); | |
|
ramant (doing other things)
2012/02/15 18:33:46
nit: should we consider moving the following check
jar (doing other things)
2012/02/15 20:26:32
Done.
| |
| 616 break; | |
| 617 } | |
| 618 | |
| 619 default: | |
| 620 NOTREACHED(); | |
| 621 break; | |
| 622 } | |
| 623 } | |
| 624 | |
| 582 bool ThreadData::Initialize() { | 625 bool ThreadData::Initialize() { |
| 583 if (!kTrackAllTaskObjects) | 626 if (!kTrackAllTaskObjects) |
| 584 return false; // Not compiled in. | 627 return false; // Not compiled in. |
| 585 if (status_ >= DEACTIVATED) | 628 if (status_ >= DEACTIVATED) |
| 586 return true; // Someone else did the initialization. | 629 return true; // Someone else did the initialization. |
| 587 // Due to racy lazy initialization in tests, we'll need to recheck status_ | 630 // Due to racy lazy initialization in tests, we'll need to recheck status_ |
| 588 // after we acquire the lock. | 631 // after we acquire the lock. |
| 589 | 632 |
| 590 // Ensure that we don't double initialize tls. We are called when single | 633 // Ensure that we don't double initialize tls. We are called when single |
| 591 // threaded in the product, but some tests may be racy and lazy about our | 634 // threaded in the product, but some tests may be racy and lazy about our |
| 592 // initialization. | 635 // initialization. |
| 593 base::AutoLock lock(*list_lock_.Pointer()); | 636 base::AutoLock lock(*list_lock_.Pointer()); |
| 594 if (status_ >= DEACTIVATED) | 637 if (status_ >= DEACTIVATED) |
| 595 return true; // Someone raced in here and beat us. | 638 return true; // Someone raced in here and beat us. |
| 596 | 639 |
| 640 // Put an alternate timer in place if the environment calls for it, such as | |
| 641 // for tracking TCMalloc allocations. This insertion is idempotent, so we | |
| 642 // don't mind if there is a race, and we'd prefer not to be in a lock while | |
| 643 // doing this work. | |
|
ramant (doing other things)
2012/02/15 18:33:46
Wondered if "_heap_init" happens before ThreadData
jar (doing other things)
2012/02/15 20:26:32
heap_init is run super-duper-early, before *any* a
| |
| 644 if (kAllowAlternateTimeSourceHandling) | |
| 645 OptionallyInitializeAlternateTimer(); | |
| 646 | |
| 597 // Perform the "real" TLS initialization now, and leave it intact through | 647 // Perform the "real" TLS initialization now, and leave it intact through |
| 598 // process termination. | 648 // process termination. |
| 599 if (!tls_index_.initialized()) { // Testing may have initialized this. | 649 if (!tls_index_.initialized()) { // Testing may have initialized this. |
| 600 DCHECK_EQ(status_, UNINITIALIZED); | 650 DCHECK_EQ(status_, UNINITIALIZED); |
| 601 tls_index_.Initialize(&ThreadData::OnThreadTermination); | 651 tls_index_.Initialize(&ThreadData::OnThreadTermination); |
| 602 if (!tls_index_.initialized()) | 652 if (!tls_index_.initialized()) |
| 603 return false; | 653 return false; |
| 604 } else { | 654 } else { |
| 605 // TLS was initialzed for us earlier. | 655 // TLS was initialzed for us earlier. |
| 606 DCHECK_EQ(status_, DORMANT_DURING_TESTS); | 656 DCHECK_EQ(status_, DORMANT_DURING_TESTS); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 659 } | 709 } |
| 660 return Now(); | 710 return Now(); |
| 661 } | 711 } |
| 662 | 712 |
| 663 // static | 713 // static |
| 664 TrackedTime ThreadData::NowForEndOfRun() { | 714 TrackedTime ThreadData::NowForEndOfRun() { |
| 665 return Now(); | 715 return Now(); |
| 666 } | 716 } |
| 667 | 717 |
| 668 // static | 718 // static |
| 719 void ThreadData::SetAlternateTimeSource(NowFunction* now_function) { | |
| 720 if (kAllowAlternateTimeSourceHandling) | |
| 721 now_function_ = now_function; | |
| 722 } | |
| 723 | |
| 724 // static | |
| 669 TrackedTime ThreadData::Now() { | 725 TrackedTime ThreadData::Now() { |
| 726 if (kAllowAlternateTimeSourceHandling && now_function_) | |
| 727 return TrackedTime::FromMilliseconds((*now_function_)()); | |
| 670 if (kTrackAllTaskObjects && TrackingStatus()) | 728 if (kTrackAllTaskObjects && TrackingStatus()) |
| 671 return TrackedTime::Now(); | 729 return TrackedTime::Now(); |
| 672 return TrackedTime(); // Super fast when disabled, or not compiled. | 730 return TrackedTime(); // Super fast when disabled, or not compiled. |
| 673 } | 731 } |
| 674 | 732 |
| 675 // static | 733 // static |
| 676 void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) { | 734 void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) { |
| 677 base::AutoLock lock(*list_lock_.Pointer()); | 735 base::AutoLock lock(*list_lock_.Pointer()); |
| 678 if (worker_thread_data_creation_count_ == 0) | 736 if (worker_thread_data_creation_count_ == 0) |
| 679 return; // We haven't really run much, and couldn't have leaked. | 737 return; // We haven't really run much, and couldn't have leaked. |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 830 ++it) { | 888 ++it) { |
| 831 base::DictionaryValue* parent_child = new base::DictionaryValue; | 889 base::DictionaryValue* parent_child = new base::DictionaryValue; |
| 832 it->first->ToValue("parent", parent_child); | 890 it->first->ToValue("parent", parent_child); |
| 833 it->second->ToValue("child", parent_child); | 891 it->second->ToValue("child", parent_child); |
| 834 descendants->Append(parent_child); | 892 descendants->Append(parent_child); |
| 835 } | 893 } |
| 836 dictionary->Set("descendants", descendants); | 894 dictionary->Set("descendants", descendants); |
| 837 } | 895 } |
| 838 | 896 |
| 839 } // namespace tracked_objects | 897 } // namespace tracked_objects |
| OLD | NEW |