diff options
author | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-15 23:52:45 +0000 |
---|---|---|
committer | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-15 23:52:45 +0000 |
commit | 84b57956feca7dc25c01c5b5004f6e7eba99bdcc (patch) | |
tree | 013be8da7bbfc600cfde22cb03c11439278fd473 | |
parent | ea8bc98be00e84fc16c30e0fbc440fe9c283c56c (diff) | |
download | chromium_src-84b57956feca7dc25c01c5b5004f6e7eba99bdcc.zip chromium_src-84b57956feca7dc25c01c5b5004f6e7eba99bdcc.tar.gz chromium_src-84b57956feca7dc25c01c5b5004f6e7eba99bdcc.tar.bz2 |
Update task tracking to not depend on message_loop_ singleton
I also did a bunch of cleanup, and transitioned to tracking
the duration of a run, rather than the time from posting
(construction of a task) to completion of the run. It is
less interesting for now to track queueing delay, and we
need a focus on task execution time. I left in the hook
(API) with the expectation that I'll be extending the
tracked_objects code to include this as well.
I also landed changes to run in Linux/Mac. The fact that
I've punted on shutdown made this landing easy (all
code support was previously lost during migration to
some flavor of bind support).
r=willchan,jam,viettrungluu,ajwong
BUG=62728
Review URL: http://codereview.chromium.org/8233037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@105694 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/message_loop.cc | 14 | ||||
-rw-r--r-- | base/threading/worker_pool_posix.cc | 12 | ||||
-rw-r--r-- | base/threading/worker_pool_posix.h | 14 | ||||
-rw-r--r-- | base/threading/worker_pool_win.cc | 12 | ||||
-rw-r--r-- | base/tracked_objects.cc | 471 | ||||
-rw-r--r-- | base/tracked_objects.h | 205 | ||||
-rw-r--r-- | base/tracked_objects_unittest.cc | 14 | ||||
-rw-r--r-- | content/browser/browser_main.cc | 10 |
8 files changed, 356 insertions, 396 deletions
diff --git a/base/message_loop.cc b/base/message_loop.cc index cadb944..ce5e900 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -476,23 +476,25 @@ void MessageLoop::RunTask(const PendingTask& pending_task) { base::debug::Alias(&program_counter); HistogramEvent(kTaskRunEvent); + +#if defined(TRACK_ALL_TASK_OBJECTS) + TimeTicks start_of_run = tracked_objects::ThreadData::Now(); +#endif // defined(TRACK_ALL_TASK_OBJECTS) + FOR_EACH_OBSERVER(TaskObserver, task_observers_, WillProcessTask(pending_task.time_posted)); pending_task.task.Run(); FOR_EACH_OBSERVER(TaskObserver, task_observers_, DidProcessTask(pending_task.time_posted)); - #if defined(TRACK_ALL_TASK_OBJECTS) - tracked_objects::ThreadData::TallyADeathIfActive( - pending_task.post_births, - TimeTicks::Now() - pending_task.time_posted); + tracked_objects::ThreadData::TallyADeathIfActive(pending_task.post_births, + pending_task.time_posted, pending_task.delayed_run_time, start_of_run); #endif // defined(TRACK_ALL_TASK_OBJECTS) nestable_tasks_allowed_ = true; } -bool MessageLoop::DeferOrRunPendingTask( - const PendingTask& pending_task) { +bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) { if (pending_task.nestable || state_->run_depth == 1) { RunTask(pending_task); // Show that we ran a task (Note: a new one might arrive as a diff --git a/base/threading/worker_pool_posix.cc b/base/threading/worker_pool_posix.cc index 3a4408c..b6c30e5 100644 --- a/base/threading/worker_pool_posix.cc +++ b/base/threading/worker_pool_posix.cc @@ -87,7 +87,15 @@ void WorkerThread::ThreadMain() { UNSHIPPED_TRACE_EVENT2("task", "WorkerThread::ThreadMain::Run", "src_file", pending_task.posted_from.file_name(), "src_func", pending_task.posted_from.function_name()); + +#if defined(TRACK_ALL_TASK_OBJECTS) + TimeTicks start_of_run = tracked_objects::ThreadData::Now(); +#endif // defined(TRACK_ALL_TASK_OBJECTS) pending_task.task.Run(); +#if defined(TRACK_ALL_TASK_OBJECTS) + tracked_objects::ThreadData::TallyADeathIfActive(pending_task.post_births, + pending_task.time_posted, TimeTicks::TimeTicks(), start_of_run); +#endif // defined(TRACK_ALL_TASK_OBJECTS) } // The WorkerThread is non-joinable, so it deletes itself. @@ -113,6 +121,10 @@ PosixDynamicThreadPool::PendingTask::PendingTask( const base::Closure& task) : posted_from(posted_from), task(task) { +#if defined(TRACK_ALL_TASK_OBJECTS) + post_births = tracked_objects::ThreadData::TallyABirthIfActive(posted_from); + time_posted = tracked_objects::ThreadData::Now(); +#endif // defined(TRACK_ALL_TASK_OBJECTS) } PosixDynamicThreadPool::PendingTask::~PendingTask() { diff --git a/base/threading/worker_pool_posix.h b/base/threading/worker_pool_posix.h index 59593e2..c0a60cc 100644 --- a/base/threading/worker_pool_posix.h +++ b/base/threading/worker_pool_posix.h @@ -31,11 +31,13 @@ #include "base/basictypes.h" #include "base/callback.h" #include "base/location.h" +#include "base/time.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" +#include "base/tracked_objects.h" class Task; @@ -50,10 +52,14 @@ class BASE_EXPORT PosixDynamicThreadPool PendingTask(const tracked_objects::Location& posted_from, const base::Closure& task); ~PendingTask(); - // TODO(ajwong): After we figure out why Mac's ~AtExitManager dies when - // destructing the lock, add in extra info so we can call - // tracked_objects::TallyADeathIfActive() and - // tracked_objects::TallyABirthIfActive correctly. + +#if defined(TRACK_ALL_TASK_OBJECTS) + // Counter for location where the Closure was posted from. + tracked_objects::Births* post_births; + + // Time the task was posted. + TimeTicks time_posted; +#endif // defined(TRACK_ALL_TASK_OBJECTS) const tracked_objects::Location posted_from; diff --git a/base/threading/worker_pool_win.cc b/base/threading/worker_pool_win.cc index b7eaad1..671e004 100644 --- a/base/threading/worker_pool_win.cc +++ b/base/threading/worker_pool_win.cc @@ -22,7 +22,7 @@ struct PendingTask { task(task) { #if defined(TRACK_ALL_TASK_OBJECTS) post_births = tracked_objects::ThreadData::TallyABirthIfActive(posted_from); - time_posted = TimeTicks::Now(); + time_posted = tracked_objects::ThreadData::Now(); #endif // defined(TRACK_ALL_TASK_OBJECTS) } @@ -46,12 +46,16 @@ DWORD CALLBACK WorkItemCallback(void* param) { UNSHIPPED_TRACE_EVENT2("task", "WorkItemCallback::Run", "src_file", pending_task->posted_from.file_name(), "src_func", pending_task->posted_from.function_name()); + +#if defined(TRACK_ALL_TASK_OBJECTS) + TimeTicks start_of_run = tracked_objects::ThreadData::Now(); +#endif // defined(TRACK_ALL_TASK_OBJECTS) pending_task->task.Run(); #if defined(TRACK_ALL_TASK_OBJECTS) - tracked_objects::ThreadData::TallyADeathIfActive( - pending_task->post_births, - TimeTicks::Now() - pending_task->time_posted); + tracked_objects::ThreadData::TallyADeathIfActive(pending_task->post_births, + pending_task->time_posted, TimeTicks::TimeTicks(), start_of_run); #endif // defined(TRACK_ALL_TASK_OBJECTS) + delete pending_task; return 0; } diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index 5df017b..58a9afd 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc @@ -24,55 +24,57 @@ base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); // static AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; +// A locked protected counter to assign sequence number to threads. +// static +int ThreadData::thread_number_counter = 0; + //------------------------------------------------------------------------------ // Death data tallies durations when a death takes place. -void DeathData::RecordDeath(const TimeDelta& duration) { +void DeathData::RecordDeath(const TimeDelta& queue_duration, + const TimeDelta& run_duration) { ++count_; - life_duration_ += duration; - int64 milliseconds = duration.InMilliseconds(); - square_duration_ += milliseconds * milliseconds; + queue_duration_ += queue_duration; + run_duration_ += run_duration; } -int DeathData::AverageMsDuration() const { - return static_cast<int>(life_duration_.InMilliseconds() / count_); +int DeathData::AverageMsRunDuration() const { + return static_cast<int>(run_duration_.InMilliseconds() / count_); } -double DeathData::StandardDeviation() const { - double average = AverageMsDuration(); - double variance = static_cast<float>(square_duration_)/count_ - - average * average; - return sqrt(variance); +int DeathData::AverageMsQueueDuration() const { + return static_cast<int>(queue_duration_.InMilliseconds() / count_); } - void DeathData::AddDeathData(const DeathData& other) { count_ += other.count_; - life_duration_ += other.life_duration_; - square_duration_ += other.square_duration_; + queue_duration_ += other.queue_duration_; + run_duration_ += other.run_duration_; } void DeathData::Write(std::string* output) const { if (!count_) return; - if (1 == count_) { - base::StringAppendF(output, "(1)Life in %dms ", AverageMsDuration()); - } else { - base::StringAppendF(output, "(%d)Lives %dms/life ", - count_, AverageMsDuration()); - } + base::StringAppendF(output, "%s:%d, ", + (count_ == 1) ? "Life" : "Lives", count_); + base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ", + run_duration_.InMilliseconds(), + AverageMsRunDuration()); + base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ", + queue_duration_.InMilliseconds(), + AverageMsQueueDuration()); } void DeathData::Clear() { count_ = 0; - life_duration_ = TimeDelta(); - square_duration_ = 0; + queue_duration_ = TimeDelta(); + run_duration_ = TimeDelta(); } //------------------------------------------------------------------------------ BirthOnThread::BirthOnThread(const Location& location) : location_(location), - birth_thread_(ThreadData::current()) { } + birth_thread_(ThreadData::Get()) { } //------------------------------------------------------------------------------ Births::Births(const Location& location) @@ -90,45 +92,66 @@ base::Lock ThreadData::list_lock_; // static ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; +ThreadData::ThreadData(const std::string& suggested_name) : next_(NULL) { + DCHECK_GE(suggested_name.size(), 0u); + thread_name_ = suggested_name; +} + ThreadData::ThreadData() : next_(NULL) { - // This shouldn't use the MessageLoop::current() LazyInstance since this might - // be used on a non-joinable thread. - // http://crbug.com/62728 - base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; - message_loop_ = MessageLoop::current(); + int thread_number; + { + base::AutoLock lock(list_lock_); + thread_number = ++thread_number_counter; + } + base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); } ThreadData::~ThreadData() {} // static -ThreadData* ThreadData::current() { +void ThreadData::InitializeThreadContext(const std::string& suggested_name) { if (!tls_index_.initialized()) - return NULL; + return; // For unittests only. + RegisterCurrentContext(new ThreadData(suggested_name)); +} - ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get()); - if (!registry) { - // We have to create a new registry for ThreadData. - bool too_late_to_create = false; - { - registry = new ThreadData; - base::AutoLock lock(list_lock_); - // Use lock to insure we have most recent status. - if (!IsActive()) { - too_late_to_create = true; - } else { - // Use lock to insert into list. - registry->next_ = first_; - first_ = registry; - } - } // Release lock. - if (too_late_to_create) { - delete registry; - registry = NULL; +// static +ThreadData* ThreadData::Get() { + if (!tls_index_.initialized()) + return NULL; // For unittests only. + ThreadData* registered = static_cast<ThreadData*>(tls_index_.Get()); + if (!registered) { + // We have to create a new registry entry for this ThreadData. + // TODO(jar): Host all unamed (Worker) threads in *one* ThreadData instance, + // (with locking protection on that instance) or else recycle and re-use + // worker thread ThreadData when the worker thread terminates. + registered = RegisterCurrentContext(new ThreadData()); + } + return registered; +} + +// static +ThreadData* ThreadData::RegisterCurrentContext(ThreadData* unregistered) { + DCHECK_EQ(tls_index_.Get(), static_cast<void*>(0)); + bool too_late_to_register = false; + { + base::AutoLock lock(list_lock_); + // Use lock to insure we have most recent status. + if (!IsActive()) { + too_late_to_register = true; } else { - tls_index_.Set(registry); + // Use list_lock_ to insert as new head of list. + unregistered->next_ = first_; + first_ = unregistered; } + } // Release lock. + if (too_late_to_register) { + delete unregistered; + unregistered = NULL; + } else { + tls_index_.Set(unregistered); } - return registry; + return unregistered; } // static @@ -136,7 +159,6 @@ void ThreadData::WriteHTML(const std::string& query, std::string* output) { if (!ThreadData::IsActive()) return; // Not yet initialized. - DCHECK(ThreadData::current()); DataCollector collected_data; // Gather data. collected_data.AddListOfLivingObjects(); // Add births that are still alive. @@ -165,19 +187,26 @@ void ThreadData::WriteHTML(const std::string& query, std::string* output) { const char* help_string = "The following are the keywords that can be used to" " sort and aggregate the data, or to select data.<br><ul>" - "<li><b>count</b> Number of instances seen." - "<li><b>duration</b> Duration in ms from construction to descrution." - "<li><b>birth</b> Thread on which the task was constructed." - "<li><b>death</b> Thread on which the task was run and deleted." - "<li><b>file</b> File in which the task was contructed." - "<li><b>function</b> Function in which the task was constructed." - "<li><b>line</b> Line number of the file in which the task was constructed." + "<li><b>Count</b> Number of instances seen." + "<li><b>Duration</b> Average duration in ms of Run() time." + "<li><b>TotalDuration</b> Summed durations in ms of Run() times." + "<li><b>AverageQueueDuration</b> Average duration in ms of queueing time." + "<li><b>TotalQueueDuration</b> Summed durations in ms of Run() times." + "<li><b>Birth</b> Thread on which the task was constructed." + "<li><b>Death</b> Thread on which the task was run and deleted." + "<li><b>File</b> File in which the task was contructed." + "<li><b>Function</b> Function in which the task was constructed." + "<li><b>Line</b> Line number of the file in which the task was constructed." "</ul><br>" "As examples:<ul>" "<li><b>about:tracking/file</b> would sort the above data by file, and" " aggregate data on a per-file basis." "<li><b>about:tracking/file=Dns</b> would only list data for tasks" " constructed in a file containing the text |Dns|." + "<li><b>about:tracking/death/duration</b> would sort the data by death" + " thread(i.e., where tasks ran) and then by the average runtime for the" + " tasks. Form an aggregation group, one per thread, showing the results on" + " each thread." "<li><b>about:tracking/birth/death</b> would sort the above list by birth" " thread, and then by death thread, and would aggregate data for each pair" " of lifetime events." @@ -234,15 +263,6 @@ void ThreadData::WriteHTMLTotalAndSubtotals( } Births* ThreadData::TallyABirth(const Location& location) { - { - // This shouldn't use the MessageLoop::current() LazyInstance since this - // might be used on a non-joinable thread. - // http://crbug.com/62728 - base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; - if (!message_loop_) // In case message loop wasn't yet around... - message_loop_ = MessageLoop::current(); // Find it now. - } - BirthMap::iterator it = birth_map_.find(location); if (it != birth_map_.end()) { it->second->RecordBirth(); @@ -257,43 +277,59 @@ Births* ThreadData::TallyABirth(const Location& location) { return tracker; } -void ThreadData::TallyADeath(const Births& lifetimes, - const TimeDelta& duration) { - { - // http://crbug.com/62728 - base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; - if (!message_loop_) // In case message loop wasn't yet around... - message_loop_ = MessageLoop::current(); // Find it now. - } - - DeathMap::iterator it = death_map_.find(&lifetimes); +void ThreadData::TallyADeath(const Births& the_birth, + const TimeDelta& queue_duration, + const TimeDelta& run_duration) { + DeathMap::iterator it = death_map_.find(&the_birth); if (it != death_map_.end()) { - it->second.RecordDeath(duration); + it->second.RecordDeath(queue_duration, run_duration); return; } base::AutoLock lock(lock_); // Lock since the map may get relocated now. - death_map_[&lifetimes].RecordDeath(duration); + death_map_[&the_birth].RecordDeath(queue_duration, run_duration); } // static Births* ThreadData::TallyABirthIfActive(const Location& location) { - if (IsActive()) { - ThreadData* current_thread_data = current(); - if (current_thread_data) { - return current_thread_data->TallyABirth(location); - } - } - - return NULL; +#if !defined(TRACK_ALL_TASK_OBJECTS) + return NULL; // Not compiled in. +#else + if (!IsActive()) + return NULL; + ThreadData* current_thread_data = Get(); + if (!current_thread_data) + return NULL; + return current_thread_data->TallyABirth(location); +#endif } // static void ThreadData::TallyADeathIfActive(const Births* the_birth, - const base::TimeDelta& duration) { - if (IsActive() && the_birth) { - current()->TallyADeath(*the_birth, duration); - } + const base::TimeTicks& time_posted, + const base::TimeTicks& delayed_start_time, + const base::TimeTicks& start_of_run) { +#if !defined(TRACK_ALL_TASK_OBJECTS) + return; // Not compiled in. +#else + if (!IsActive() || !the_birth) + return; + ThreadData* current_thread_data = Get(); + if (!current_thread_data) + return; + + // To avoid conflating our stats with the delay duration in a PostDelayedTask, + // we identify such tasks, and replace their post_time with the time they + // were sechudled (requested?) to emerge from the delayed task queue. This + // means that queueing delay for such tasks will show how long they went + // unserviced, after they *could* be serviced. This is the same stat as we + // have for non-delayed tasks, and we consistently call it queueing delay. + base::TimeTicks effective_post_time = + (delayed_start_time.is_null()) ? time_posted : delayed_start_time; + base::TimeDelta queue_duration = start_of_run - effective_post_time; + base::TimeDelta run_duration = Now() - start_of_run; + current_thread_data->TallyADeath(*the_birth, queue_duration, run_duration); +#endif } // static @@ -302,12 +338,6 @@ ThreadData* ThreadData::first() { return first_; } -const std::string ThreadData::ThreadName() const { - if (message_loop_) - return message_loop_->thread_name(); - return "ThreadWithoutMessageLoop"; -} - // This may be called from another thread. void ThreadData::SnapshotBirthMap(BirthMap *output) const { base::AutoLock lock(lock_); @@ -326,7 +356,7 @@ void ThreadData::SnapshotDeathMap(DeathMap *output) const { // static void ThreadData::ResetAllThreadData() { - ThreadData* my_list = ThreadData::current()->first(); + ThreadData* my_list = Get()->first(); for (ThreadData* thread_data = my_list; thread_data; @@ -344,109 +374,11 @@ void ThreadData::Reset() { it->second->Clear(); } -#ifdef OS_WIN -// A class used to count down which is accessed by several threads. This is -// used to make sure RunOnAllThreads() actually runs a task on the expected -// count of threads. -class ThreadData::ThreadSafeDownCounter { - public: - // Constructor sets the count, once and for all. - explicit ThreadSafeDownCounter(size_t count); - - // Decrement the count, and return true if we hit zero. Also delete this - // instance automatically when we hit zero. - bool LastCaller(); - - private: - size_t remaining_count_; - base::Lock lock_; // protect access to remaining_count_. -}; - -ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) - : remaining_count_(count) { - DCHECK_GT(remaining_count_, 0u); -} - -bool ThreadData::ThreadSafeDownCounter::LastCaller() { - { - base::AutoLock lock(lock_); - if (--remaining_count_) - return false; - } // Release lock, so we can delete everything in this instance. - delete this; - return true; -} - -// A Task class that runs a static method supplied, and checks to see if this -// is the last tasks instance (on last thread) that will run the method. -// IF this is the last run, then the supplied event is signalled. -class ThreadData::RunTheStatic : public Task { - public: - typedef void (*FunctionPointer)(); - RunTheStatic(FunctionPointer function, - HANDLE completion_handle, - ThreadSafeDownCounter* counter); - // Run the supplied static method, and optionally set the event. - void Run(); - - private: - FunctionPointer function_; - HANDLE completion_handle_; - // Make sure enough tasks are called before completion is signaled. - ThreadSafeDownCounter* counter_; - - DISALLOW_COPY_AND_ASSIGN(RunTheStatic); -}; - -ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function, - HANDLE completion_handle, - ThreadSafeDownCounter* counter) - : function_(function), - completion_handle_(completion_handle), - counter_(counter) { -} - -void ThreadData::RunTheStatic::Run() { - function_(); - if (counter_->LastCaller()) - SetEvent(completion_handle_); -} - -// TODO(jar): This should use condition variables, and be cross platform. -void ThreadData::RunOnAllThreads(void (*function)()) { - ThreadData* list = first(); // Get existing list. - - std::vector<MessageLoop*> message_loops; - for (ThreadData* it = list; it; it = it->next()) { - if (current() != it && it->message_loop()) - message_loops.push_back(it->message_loop()); - } - - ThreadSafeDownCounter* counter = - new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us! - - HANDLE completion_handle = CreateEvent(NULL, false, false, NULL); - // Tell all other threads to run. - for (size_t i = 0; i < message_loops.size(); ++i) - message_loops[i]->PostTask( - FROM_HERE, new RunTheStatic(function, completion_handle, counter)); - - // Also run Task on our thread. - RunTheStatic local_task(function, completion_handle, counter); - local_task.Run(); - - WaitForSingleObject(completion_handle, INFINITE); - int ret_val = CloseHandle(completion_handle); - DCHECK(ret_val); -} -#endif // OS_WIN - // static bool ThreadData::StartTracking(bool status) { #if !defined(TRACK_ALL_TASK_OBJECTS) return false; // Not compiled in. -#endif - +#else if (!status) { base::AutoLock lock(list_lock_); DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); @@ -458,6 +390,7 @@ bool ThreadData::StartTracking(bool status) { CHECK(tls_index_.Initialize(NULL)); status_ = ACTIVE; return true; +#endif } // static @@ -465,24 +398,14 @@ bool ThreadData::IsActive() { return status_ == ACTIVE; } -#ifdef OS_WIN // static -void ThreadData::ShutdownMultiThreadTracking() { - // Using lock, guarantee that no new ThreadData instances will be created. - if (!StartTracking(false)) - return; - - RunOnAllThreads(ShutdownDisablingFurtherTracking); - - // Now the *only* threads that might change the database are the threads with - // no messages loops. They might still be adding data to their birth records, - // but since no objects are deleted on those threads, there will be no further - // access to to cross-thread data. - // We could do a cleanup on all threads except for the ones without - // MessageLoops, but we won't bother doing cleanup (destruction of data) yet. - return; -} +base::TimeTicks ThreadData::Now() { +#if defined(TRACK_ALL_TASK_OBJECTS) + if (status_ == ACTIVE) + return base::TimeTicks::Now(); #endif + return base::TimeTicks(); // Super fast when disabled, or not compiled in. +} // static void ThreadData::ShutdownSingleThreadedCleanup() { @@ -514,13 +437,6 @@ void ThreadData::ShutdownSingleThreadedCleanup() { status_ = UNINITIALIZED; } -// static -void ThreadData::ShutdownDisablingFurtherTracking() { - // Redundantly set status SHUTDOWN on this thread. - if (!StartTracking(false)) - return; -} - //------------------------------------------------------------------------------ // Individual 3-tuple of birth (place and thread) along with death thread, and // the accumulated stats for instances (DeathData). @@ -541,15 +457,15 @@ Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) const std::string Snapshot::DeathThreadName() const { if (death_thread_) - return death_thread_->ThreadName(); + return death_thread_->thread_name(); return "Still_Alive"; } void Snapshot::Write(std::string* output) const { death_data_.Write(output); base::StringAppendF(output, "%s->%s ", - birth_->birth_thread()->ThreadName().c_str(), - death_thread_->ThreadName().c_str()); + birth_->birth_thread()->thread_name().c_str(), + death_thread_->thread_name().c_str()); birth_->location().Write(true, true, output); } @@ -564,25 +480,14 @@ DataCollector::DataCollector() { DCHECK(ThreadData::IsActive()); // Get an unchanging copy of a ThreadData list. - ThreadData* my_list = ThreadData::current()->first(); + ThreadData* my_list = ThreadData::Get()->first(); - count_of_contributing_threads_ = 0; - for (ThreadData* thread_data = my_list; - thread_data; - thread_data = thread_data->next()) { - ++count_of_contributing_threads_; - } - - // Gather data serially. A different constructor could be used to do in - // parallel, and then invoke an OnCompletion task. + // Gather data serially. // This hackish approach *can* get some slighly corrupt tallies, as we are // grabbing values without the protection of a lock, but it has the advantage // of working even with threads that don't have message loops. If a user // sees any strangeness, they can always just run their stats gathering a // second time. - // TODO(jar): Provide version that gathers stats safely via PostTask in all - // cases where thread_data supplies a message_loop to post to. Be careful to - // handle message_loops that are destroyed!?! for (ThreadData* thread_data = my_list; thread_data; thread_data = thread_data->next()) { @@ -594,17 +499,12 @@ DataCollector::~DataCollector() { } void DataCollector::Append(const ThreadData& thread_data) { - // Get copy of data (which is done under ThreadData's lock). + // Get copy of data. ThreadData::BirthMap birth_map; thread_data.SnapshotBirthMap(&birth_map); ThreadData::DeathMap death_map; thread_data.SnapshotDeathMap(&death_map); - // Use our lock to protect our accumulation activity. - base::AutoLock lock(accumulation_lock_); - - DCHECK(count_of_contributing_threads_); - for (ThreadData::DeathMap::const_iterator it = death_map.begin(); it != death_map.end(); ++it) { collection_.push_back(Snapshot(*it->first, thread_data, it->second)); @@ -615,17 +515,13 @@ void DataCollector::Append(const ThreadData& thread_data) { it != birth_map.end(); ++it) { global_birth_count_[it->second] += it->second->birth_count(); } - - --count_of_contributing_threads_; } DataCollector::Collection* DataCollector::collection() { - DCHECK(!count_of_contributing_threads_); return &collection_; } void DataCollector::AddListOfLivingObjects() { - DCHECK(!count_of_contributing_threads_); for (BirthCount::iterator it = global_birth_count_.begin(); it != global_birth_count_.end(); ++it) { if (it->second > 0) @@ -681,7 +577,7 @@ void Aggregation::Write(std::string* output) const { birth_threads_.size()); } else { base::StringAppendF(output, "All born on %s. ", - birth_threads_.begin()->first->ThreadName().c_str()); + birth_threads_.begin()->first->thread_name().c_str()); } if (death_threads_.size() > 1) { @@ -690,7 +586,7 @@ void Aggregation::Write(std::string* output) const { } else { if (death_threads_.begin()->first) { base::StringAppendF(output, "All deleted on %s. ", - death_threads_.begin()->first->ThreadName().c_str()); + death_threads_.begin()->first->thread_name().c_str()); } else { output->append("All these objects are still alive."); } @@ -735,10 +631,10 @@ bool Comparator::operator()(const Snapshot& left, switch (selector_) { case BIRTH_THREAD: if (left.birth_thread() != right.birth_thread() && - left.birth_thread()->ThreadName() != - right.birth_thread()->ThreadName()) - return left.birth_thread()->ThreadName() < - right.birth_thread()->ThreadName(); + left.birth_thread()->thread_name() != + right.birth_thread()->thread_name()) + return left.birth_thread()->thread_name() < + right.birth_thread()->thread_name(); break; case DEATH_THREAD: @@ -783,11 +679,32 @@ bool Comparator::operator()(const Snapshot& left, return left.count() > right.count(); // Sort large at front of vector. break; - case AVERAGE_DURATION: + case AVERAGE_RUN_DURATION: + if (!left.count() || !right.count()) + break; + if (left.AverageMsRunDuration() != right.AverageMsRunDuration()) + return left.AverageMsRunDuration() > right.AverageMsRunDuration(); + break; + + case TOTAL_RUN_DURATION: if (!left.count() || !right.count()) break; - if (left.AverageMsDuration() != right.AverageMsDuration()) - return left.AverageMsDuration() > right.AverageMsDuration(); + if (left.run_duration() != right.run_duration()) + return left.run_duration() > right.run_duration(); + break; + + case AVERAGE_QUEUE_DURATION: + if (!left.count() || !right.count()) + break; + if (left.AverageMsQueueDuration() != right.AverageMsQueueDuration()) + return left.AverageMsQueueDuration() > right.AverageMsQueueDuration(); + break; + + case TOTAL_QUEUE_DURATION: + if (!left.count() || !right.count()) + break; + if (left.queue_duration() != right.queue_duration()) + return left.queue_duration() > right.queue_duration(); break; default: @@ -807,8 +724,8 @@ bool Comparator::Equivalent(const Snapshot& left, switch (selector_) { case BIRTH_THREAD: if (left.birth_thread() != right.birth_thread() && - left.birth_thread()->ThreadName() != - right.birth_thread()->ThreadName()) + left.birth_thread()->thread_name() != + right.birth_thread()->thread_name()) return false; break; @@ -837,13 +754,11 @@ bool Comparator::Equivalent(const Snapshot& left, break; case COUNT: - if (left.count() != right.count()) - return false; - break; - - case AVERAGE_DURATION: - if (left.life_duration() != right.life_duration()) - return false; + case AVERAGE_RUN_DURATION: + case TOTAL_RUN_DURATION: + case AVERAGE_QUEUE_DURATION: + case TOTAL_QUEUE_DURATION: + // We don't produce separate aggretation when only counts or times differ. break; default: @@ -858,7 +773,7 @@ bool Comparator::Acceptable(const Snapshot& sample) const { if (required_.size()) { switch (selector_) { case BIRTH_THREAD: - if (sample.birth_thread()->ThreadName().find(required_) == + if (sample.birth_thread()->thread_name().find(required_) == std::string::npos) return false; break; @@ -934,13 +849,16 @@ void Comparator::ParseKeyphrase(const std::string& key_phrase) { initialized = true; // Sorting and aggretation keywords, which specify how to sort the data, or // can specify a required match from the specified field in the record. - key_map["count"] = COUNT; - key_map["duration"] = AVERAGE_DURATION; - key_map["birth"] = BIRTH_THREAD; - key_map["death"] = DEATH_THREAD; - key_map["file"] = BIRTH_FILE; - key_map["function"] = BIRTH_FUNCTION; - key_map["line"] = BIRTH_LINE; + key_map["count"] = COUNT; + key_map["totalduration"] = TOTAL_RUN_DURATION; + key_map["duration"] = AVERAGE_RUN_DURATION; + key_map["totalqueueduration"] = TOTAL_QUEUE_DURATION; + key_map["averagequeueduration"] = AVERAGE_QUEUE_DURATION; + key_map["birth"] = BIRTH_THREAD; + key_map["death"] = DEATH_THREAD; + key_map["file"] = BIRTH_FILE; + key_map["function"] = BIRTH_FUNCTION; + key_map["line"] = BIRTH_LINE; // Immediate commands that do not involve setting sort order. key_map["reset"] = RESET_ALL_DATA; @@ -976,7 +894,8 @@ bool Comparator::ParseQuery(const std::string& query) { // Select subgroup ordering (if we want to display the subgroup) SetSubgroupTiebreaker(COUNT); - SetSubgroupTiebreaker(AVERAGE_DURATION); + SetSubgroupTiebreaker(AVERAGE_RUN_DURATION); + SetSubgroupTiebreaker(TOTAL_RUN_DURATION); SetSubgroupTiebreaker(BIRTH_THREAD); SetSubgroupTiebreaker(DEATH_THREAD); SetSubgroupTiebreaker(BIRTH_FUNCTION); @@ -992,7 +911,7 @@ bool Comparator::WriteSortGrouping(const Snapshot& sample, switch (selector_) { case BIRTH_THREAD: base::StringAppendF(output, "All new on %s ", - sample.birth_thread()->ThreadName().c_str()); + sample.birth_thread()->thread_name().c_str()); wrote_data = true; break; @@ -1033,7 +952,7 @@ void Comparator::WriteSnapshot(const Snapshot& sample, !(combined_selectors_ & DEATH_THREAD)) base::StringAppendF(output, "%s->%s ", (combined_selectors_ & BIRTH_THREAD) ? "*" : - sample.birth().birth_thread()->ThreadName().c_str(), + sample.birth().birth_thread()->thread_name().c_str(), (combined_selectors_ & DEATH_THREAD) ? "*" : sample.DeathThreadName().c_str()); sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), diff --git a/base/tracked_objects.h b/base/tracked_objects.h index 8e324a2..25b1771 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h @@ -31,9 +31,7 @@ // marginal allocation cost associated with construction or destruction of // tracked objects, no locks are generally employed, and probably the largest // computational cost is associated with obtaining start and stop times for -// instances as they are created and destroyed. The introduction of worker -// threads had a slight impact on this approach, and required use of some locks -// when accessing data from the worker threads. +// instances as they are created and destroyed. // // The following describes the lifecycle of tracking an instance. // @@ -54,9 +52,10 @@ // The derived Births class contains slots for recording statistics about all // instances born at the same location. Statistics currently include only the // count of instances constructed. +// // Since the base class BirthOnThread contains only constant data, it can be // freely accessed by any thread at any time (i.e., only the statistic needs to -// be handled carefully, and it is ONLY read or written by the birth thread). +// be handled carefully, and stats are updated exclusively on the birth thread). // // For Tasks, having now either constructed or found the Births instance // described above, a pointer to the Births instance is then recorded into the @@ -67,7 +66,7 @@ // can find out a Task's location of birth, and thread of birth, without using // any locks, as all that data is constant across the life of the process. // -// This can also be done for any other object as well by calling +// The above work *could* also be done for any other object as well by calling // TallyABirthIfActive() and TallyADeathIfActive() as appropriate. // // The amount of memory used in the above data structures depends on how many @@ -81,10 +80,11 @@ // carefully accumulated. That tallying wrties into slots (members) in a // collection of DeathData instances. For each birth place Location that is // destroyed on a thread, there is a DeathData instance to record the additional -// death count, as well as accumulate the lifetime duration of the instance as -// it is destroyed (dies). By maintaining a single place to aggregate this -// addition *only* for the given thread, we avoid the need to lock such -// DeathData instances. +// death count, as well as accumulate the run-time and queue-time durations for +// the instance as it is destroyed (dies). By maintaining a single place to +// aggregate this running sum *only* for the given thread, we avoid the need to +// lock such DeathData instances. (i.e., these accumulated stats in a DeathData +// instance are exclusively updated by the singular owning thread). // // With the above lifecycle description complete, the major remaining detail is // explaining how each thread maintains a list of DeathData instances, and of @@ -129,18 +129,26 @@ // birth and death datastructures, but have local (frozen) copies of the actual // statistics (birth count, durations, etc. etc.). // -// A DataCollector is a container object that holds a set of Snapshots. A -// DataCollector can be passed from thread to thread, and each thread -// contributes to it by adding or updating Snapshot instances. DataCollector -// instances are thread safe containers which are passed to various threads to -// accumulate all Snapshot instances. +// A DataCollector is a container object that holds a set of Snapshots. The +// statistics in a snapshot are gathered asynhcronously relative to their +// ongoing updates. It is possible, though highly unlikely, that stats such +// as a 64bit counter could incorrectly recorded by this process. The advantage +// to having fast (non-atomic) updates of the data outweighs the minimal risk +// of a singular corrupt statistic snapshot (only the snapshot could be corrupt, +// not the underlying and ongoing stistic). In constrast, pointer data that is +// accessed during snapshotting is completely invariant, and hence is perfectly +// acquired (i.e., no potential corruption, and no risk of a bad memory +// reference). // // After an array of Snapshots instances are colleted into a DataCollector, they -// need to be sorted, and possibly aggregated (example: how many threads are in -// a specific consecutive set of Snapshots? What was the total birth count for -// that set? etc.). Aggregation instances collect running sums of any set of -// snapshot instances, and are used to print sub-totals in an about:tracking -// page. +// need to be prepared for display our output. We currently implement a direct +// renderin to HTML, but we will soon also have a JSON serialization as well. + +// For direct HTML display, the data must be sorted, and possibly aggregated +// (example: how many threads are in a specific consecutive set of Snapshots? +// What was the total birth count for that set? etc.). Aggregation instances +// collect running sums of any set of snapshot instances, and are used to print +// sub-totals in an about:tracking page. // // TODO(jar): I need to store DataCollections, and provide facilities for taking // the difference between two gathered DataCollections. For now, I'm just @@ -167,7 +175,7 @@ class BASE_EXPORT BirthOnThread { const ThreadData* birth_thread() const { return birth_thread_; } private: - // File/lineno of birth. This defines the essence of the type, as the context + // File/lineno of birth. This defines the essence of the task, as the context // of the birth (construction) often tell what the item is for. This field // is const, and hence safe to access from any thread. const Location location_; @@ -213,35 +221,39 @@ class BASE_EXPORT Births: public BirthOnThread { class BASE_EXPORT DeathData { public: // Default initializer. - DeathData() : count_(0), square_duration_(0) {} + DeathData() : count_(0) {} // When deaths have not yet taken place, and we gather data from all the // threads, we create DeathData stats that tally the number of births without // a corrosponding death. - explicit DeathData(int count) : count_(count), square_duration_(0) {} + explicit DeathData(int count) : count_(count) {} - void RecordDeath(const base::TimeDelta& duration); + // Update stats for a task destruction (death) that had a Run() time of + // |duration|, and has had a queueing delay of |queue_duration|. + void RecordDeath(const base::TimeDelta& queue_duration, + const base::TimeDelta& run_duration); // Metrics accessors. int count() const { return count_; } - base::TimeDelta life_duration() const { return life_duration_; } - int64 square_duration() const { return square_duration_; } - int AverageMsDuration() const; - double StandardDeviation() const; + base::TimeDelta run_duration() const { return run_duration_; } + int AverageMsRunDuration() const; + base::TimeDelta queue_duration() const { return queue_duration_; } + int AverageMsQueueDuration() const; - // Accumulate metrics from other into this. + // Accumulate metrics from other into this. This method is never used on + // realtime statistics, and only used in snapshots and aggregatinos. void AddDeathData(const DeathData& other); // Simple print of internal state. void Write(std::string* output) const; - // Reset all tallies to zero. + // Reset all tallies to zero. This is used as a hack on realtime data. void Clear(); private: - int count_; // Number of destructions. - base::TimeDelta life_duration_; // Sum of all lifetime durations. - int64 square_duration_; // Sum of squares in milliseconds. + int count_; // Number of destructions. + base::TimeDelta run_duration_; // Sum of all Run()time durations. + base::TimeDelta queue_duration_; // Sum of all queue time durations. }; //------------------------------------------------------------------------------ @@ -260,7 +272,6 @@ class BASE_EXPORT Snapshot { // When snapshotting a birth, with no death yet, use this: Snapshot(const BirthOnThread& birth_on_thread, int count); - const ThreadData* birth_thread() const { return birth_->birth_thread(); } const Location location() const { return birth_->location(); } const BirthOnThread& birth() const { return *birth_; } @@ -269,9 +280,16 @@ class BASE_EXPORT Snapshot { const std::string DeathThreadName() const; int count() const { return death_data_.count(); } - base::TimeDelta life_duration() const { return death_data_.life_duration(); } - int64 square_duration() const { return death_data_.square_duration(); } - int AverageMsDuration() const { return death_data_.AverageMsDuration(); } + base::TimeDelta run_duration() const { return death_data_.run_duration(); } + int AverageMsRunDuration() const { + return death_data_.AverageMsRunDuration(); + } + base::TimeDelta queue_duration() const { + return death_data_.queue_duration(); + } + int AverageMsQueueDuration() const { + return death_data_.AverageMsQueueDuration(); + } void Write(std::string* output) const; @@ -282,10 +300,10 @@ class BASE_EXPORT Snapshot { const ThreadData* death_thread_; DeathData death_data_; }; + //------------------------------------------------------------------------------ // DataCollector is a container class for Snapshot and BirthOnThread count -// items. It protects the gathering under locks, so that it could be called via -// Posttask on any threads, or passed to all the target threads in parallel. +// items. class BASE_EXPORT DataCollector { public: @@ -312,12 +330,6 @@ class BASE_EXPORT DataCollector { private: typedef std::map<const BirthOnThread*, int> BirthCount; - // This instance may be provided to several threads to contribute data. The - // following counter tracks how many more threads will contribute. When it is - // zero, then all asynchronous contributions are complete, and locked access - // is no longer needed. - int count_of_contributing_threads_; - // The array that we collect data into. Collection collection_; @@ -325,8 +337,6 @@ class BASE_EXPORT DataCollector { // seen a death count. BirthCount global_birth_count_; - base::Lock accumulation_lock_; // Protects access during accumulation phase. - DISALLOW_COPY_AND_ASSIGN(DataCollector); }; @@ -381,8 +391,10 @@ class BASE_EXPORT Comparator { BIRTH_FUNCTION = 8, BIRTH_LINE = 16, COUNT = 32, - AVERAGE_DURATION = 64, - TOTAL_DURATION = 128, + AVERAGE_RUN_DURATION = 64, + TOTAL_RUN_DURATION = 128, + AVERAGE_QUEUE_DURATION = 256, + TOTAL_QUEUE_DURATION = 512, // Imediate action keywords. RESET_ALL_DATA = -1, @@ -418,7 +430,7 @@ class BASE_EXPORT Comparator { // printed line. bool IsGroupedBy(Selector selector) const; - // Using the tiebreakers as set above, we mostly get an ordering, which + // Using the tiebreakers as set above, we mostly get an ordering, with some // equivalent groups. If those groups are displayed (rather than just being // aggregated, then the following is used to order them (within the group). void SetSubgroupTiebreaker(Selector selector); @@ -462,7 +474,6 @@ class BASE_EXPORT Comparator { bool use_tiebreaker_for_sort_only_; }; - //------------------------------------------------------------------------------ // For each thread, we have a ThreadData that stores all tracking info generated // on this thread. This prevents the need for locking as data accumulates. @@ -472,15 +483,18 @@ class BASE_EXPORT ThreadData { typedef std::map<Location, Births*> BirthMap; typedef std::map<const Births*, DeathData> DeathMap; - ThreadData(); - ~ThreadData(); + // Initialize the current thread context with a new instance of ThreadData. + // This is used by all threads that have names, and can be explicitly + // set *before* any births are threads have taken place. It is generally + // only used by the message loop, which has a well defined name. + static void InitializeThreadContext(const std::string& suggested_name); // Using Thread Local Store, find the current instance for collecting data. // If an instance does not exist, construct one (and remember it for use on // this thread. // If shutdown has already started, and we don't yet have an instance, then // return null. - static ThreadData* current(); + static ThreadData* Get(); // For a given (unescaped) about:tracking query, develop resulting HTML, and // append to output. @@ -496,22 +510,33 @@ class BASE_EXPORT ThreadData { Births* TallyABirth(const Location& location); // Find a place to record a death on this thread. - void TallyADeath(const Births& lifetimes, const base::TimeDelta& duration); + void TallyADeath(const Births& the_birth, + const base::TimeDelta& queue_duration, + const base::TimeDelta& duration); // Helper methods to only tally if the current thread has tracking active. // // TallyABirthIfActive will returns NULL if the birth cannot be tallied. static Births* TallyABirthIfActive(const Location& location); - static void TallyADeathIfActive(const Births* lifetimes, - const base::TimeDelta& duration); + + // Record the end of a timed run of an object. The |the_birth| is the record + // for the instance, the |time_posted| and |start_of_run| are times of posting + // into a message loop queue, and of starting to perform the run of the task. + // Implied is that the run just (Now()) ended. The current_message_loop is + // optional, and only used in DEBUG mode (when supplied) to verify that the + // ThreadData has a thread name that does indeed match the given loop's + // associated thread name (in RELEASE mode, its use is compiled away). + static void TallyADeathIfActive(const Births* the_birth, + const base::TimeTicks& time_posted, + const base::TimeTicks& delayed_start_time, + const base::TimeTicks& start_of_run); // (Thread safe) Get start of list of instances. static ThreadData* first(); // Iterate through the null terminated list of instances. ThreadData* next() const { return next_; } - MessageLoop* message_loop() const { return message_loop_; } - const std::string ThreadName() const; + const std::string thread_name() const { return thread_name_; } // Using our lock, make a copy of the specified maps. These calls may arrive // from non-local threads, and are used to quickly scan data from all threads @@ -528,31 +553,17 @@ class BASE_EXPORT ThreadData { // Using our lock to protect the iteration, Clear all birth and death data. void Reset(); - // Using the "known list of threads" gathered during births and deaths, the - // following attempts to run the given function once all all such threads. - // Note that the function can only be run on threads which have a message - // loop! - static void RunOnAllThreads(void (*Func)()); - // Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN, // based on argument being true or false respectively. // IF tracking is not compiled in, this function will return false. static bool StartTracking(bool status); static bool IsActive(); -#ifdef OS_WIN - // WARNING: ONLY call this function when all MessageLoops are still intact for - // all registered threads. IF you call it later, you will crash. - // Note: You don't need to call it at all, and you can wait till you are - // single threaded (again) to do the cleanup via - // ShutdownSingleThreadedCleanup(). - // Start the teardown (shutdown) process in a multi-thread mode by disabling - // further additions to thread database on all threads. First it makes a - // local (locked) change to prevent any more threads from registering. Then - // it Posts a Task to all registered threads to be sure they are aware that no - // more accumulation can take place. - static void ShutdownMultiThreadTracking(); -#endif + // Provide a time function that does nothing (runs fast) when we don't have + // the profiler enabled. It will generally be optimized away when it is + // ifdef'ed to be small enough (allowing the profiler to be "compiled out" of + // the code). + static base::TimeTicks Now(); // WARNING: ONLY call this function when you are running single threaded // (again) and all message loops and threads have terminated. Until that @@ -562,6 +573,18 @@ class BASE_EXPORT ThreadData { static void ShutdownSingleThreadedCleanup(); private: + // Worker thread construction creates a name. + ThreadData(); + // Message loop based construction should provide a name. + explicit ThreadData(const std::string& suggested_name); + + ~ThreadData(); + + // Enter a new instance into Thread Local Store. + // Return the instance, or null if we can't register it (because we're + // shutting down). + static ThreadData* RegisterCurrentContext(ThreadData* unregistered); + // Current allowable states of the tracking system. The states always // proceed towards SHUTDOWN, and never go backwards. enum Status { @@ -570,17 +593,6 @@ class BASE_EXPORT ThreadData { SHUTDOWN, }; -#if defined(OS_WIN) - class ThreadSafeDownCounter; - class RunTheStatic; -#endif - - // Each registered thread is called to set status_ to SHUTDOWN. - // This is done redundantly on every registered thread because it is not - // protected by a mutex. Running on all threads guarantees we get the - // notification into the memory cache of all possible threads. - static void ShutdownDisablingFurtherTracking(); - // We use thread local store to identify which ThreadData to interact with. static base::ThreadLocalStorage::Slot tls_index_; @@ -589,10 +601,7 @@ class BASE_EXPORT ThreadData { // Protection for access to first_. static base::Lock list_lock_; - // We set status_ to SHUTDOWN when we shut down the tracking service. This - // setting is redundantly established by all participating threads so that we - // are *guaranteed* (without locking) that all threads can "see" the status - // and avoid additional calls into the service. + // We set status_ to SHUTDOWN when we shut down the tracking service. static Status status_; // Link to next instance (null terminated list). Used to globally track all @@ -600,10 +609,9 @@ class BASE_EXPORT ThreadData { // data). ThreadData* next_; - // The message loop where tasks needing to access this instance's private data - // should be directed. Since some threads have no message loop, some - // instances have data that can't be (safely) modified externally. - MessageLoop* message_loop_; + // The name of the thread that is being recorded. If this thread has no + // message_loop, then this is a worker thread, with a sequence number postfix. + std::string thread_name_; // A map used on each thread to keep track of Births on this thread. // This map should only be accessed on the thread it was constructed on. @@ -625,10 +633,13 @@ class BASE_EXPORT ThreadData { // writing is only done from this thread. mutable base::Lock lock_; + // The next available thread number. This should only be accessed when the + // list_lock_ is held. + static int thread_number_counter; + DISALLOW_COPY_AND_ASSIGN(ThreadData); }; - //------------------------------------------------------------------------------ // Provide simple way to to start global tracking, and to tear down tracking // when done. Note that construction and destruction of this object must be diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc index e10659d4..c9ab9682 100644 --- a/base/tracked_objects_unittest.cc +++ b/base/tracked_objects_unittest.cc @@ -23,11 +23,11 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { return; EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. - ThreadData* data = ThreadData::current(); + ThreadData* data = ThreadData::Get(); EXPECT_TRUE(ThreadData::first()); // Now class was constructed. EXPECT_TRUE(data); EXPECT_TRUE(!data->next()); - EXPECT_EQ(data, ThreadData::current()); + EXPECT_EQ(data, ThreadData::Get()); ThreadData::BirthMap birth_map; data->SnapshotBirthMap(&birth_map); EXPECT_EQ(0u, birth_map.size()); @@ -39,11 +39,11 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { // Do it again, just to be sure we reset state completely. ThreadData::StartTracking(true); EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. - data = ThreadData::current(); + data = ThreadData::Get(); EXPECT_TRUE(ThreadData::first()); // Now class was constructed. EXPECT_TRUE(data); EXPECT_TRUE(!data->next()); - EXPECT_EQ(data, ThreadData::current()); + EXPECT_EQ(data, ThreadData::Get()); birth_map.clear(); data->SnapshotBirthMap(&birth_map); EXPECT_EQ(0u, birth_map.size()); @@ -64,7 +64,7 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { const ThreadData* data = ThreadData::first(); ASSERT_TRUE(data); EXPECT_TRUE(!data->next()); - EXPECT_EQ(data, ThreadData::current()); + EXPECT_EQ(data, ThreadData::Get()); ThreadData::BirthMap birth_map; data->SnapshotBirthMap(&birth_map); EXPECT_EQ(1u, birth_map.size()); // 1 birth location. @@ -78,7 +78,9 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { const Births* second_birth = ThreadData::TallyABirthIfActive(location); ThreadData::TallyADeathIfActive( second_birth, - base::TimeDelta::FromSeconds(1) /* Bogus duration. */); + base::TimeTicks::TimeTicks(), /* Bogus post_time. */ + base::TimeTicks::TimeTicks(), /* Bogus delayed_start_time. */ + base::TimeTicks::TimeTicks() /* Bogus start_run_time. */); birth_map.clear(); data->SnapshotBirthMap(&birth_map); diff --git a/content/browser/browser_main.cc b/content/browser/browser_main.cc index 75e80c3..b0c9c19 100644 --- a/content/browser/browser_main.cc +++ b/content/browser/browser_main.cc @@ -12,6 +12,7 @@ #include "base/metrics/histogram.h" #include "base/system_monitor/system_monitor.h" #include "base/threading/thread_restrictions.h" +#include "base/tracked_objects.h" #include "content/browser/browser_thread.h" #include "content/browser/content_browser_client.h" #include "content/common/hi_res_timer_manager.h" @@ -241,12 +242,11 @@ void BrowserMainParts::MainMessageLoopStart() { main_message_loop_.reset(new MessageLoop(MessageLoop::TYPE_UI)); - // TODO(viettrungluu): should these really go before setting the thread name? + InitializeMainThread(); + system_monitor_.reset(new base::SystemMonitor); hi_res_timer_manager_.reset(new HighResolutionTimerManager); - InitializeMainThread(); - network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); PostMainMessageLoopStart(); @@ -274,6 +274,10 @@ void BrowserMainParts::InitializeMainThread() { base::PlatformThread::SetName(kThreadName); main_message_loop().set_thread_name(kThreadName); +#if defined(TRACK_ALL_TASK_OBJECTS) + tracked_objects::ThreadData::InitializeThreadContext(kThreadName); +#endif // TRACK_ALL_TASK_OBJECTS + // Register the main thread by instantiating it, but don't call any methods. main_thread_.reset(new BrowserThread(BrowserThread::UI, MessageLoop::current())); |