diff options
author | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-31 15:32:08 +0000 |
---|---|---|
committer | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-31 15:32:08 +0000 |
commit | 3f095c0a8f6a5ee43e205be6add05276c42bd240 (patch) | |
tree | 3b3e29d1814e7aad93304d09be237e9cd936b7e3 /base | |
parent | a0645703e4069c2def1dff4cca774ec47763d052 (diff) | |
download | chromium_src-3f095c0a8f6a5ee43e205be6add05276c42bd240.zip chromium_src-3f095c0a8f6a5ee43e205be6add05276c42bd240.tar.gz chromium_src-3f095c0a8f6a5ee43e205be6add05276c42bd240.tar.bz2 |
Revert 107895 - Fully enable about:tracking by default
This is a re-land of:
http://codereview.chromium.org/8391019/
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=107793
Original landing had trouble with message_loop_x.h, due to header
include ordering. I pulled out the structure that was really needed by
tracked_objects.h into a new file tracked_info.*. This allows tracked_objects
to inlude this tracked_info, but not have to include the message_loop.h
totality. I also removed a DCHECK that that was triggering on a test,
and added yet one more file (browser_main.cc) where I removed a #ifdef
for TRACKING_ALL_OBJECTS. The changes were minor, and I'm hoping to get
clear perf runs with tihs landing, so I'm going to TBR it and reland
early in the morning.
Comments from original landing:
Support is now controlled by the flag:
--enable-tracking
and the default is always on. To turn it off, use:
--enable-tracking=0
All profiler code is compiled now in release and official
builds (in addition to debug, where it was already active),
but most entry points can be disabled (turned into no-ops)
by a single const bool setting atop tracked_objects.cc (in
case folks want to revert the perf-impact of this change).
Transition to faster Now() service on Windows for the
profiler use only.
The TimeTicks::Now() function on Window uses locking
to get a 64 bit time value. This CL transitions
times used for profiling to more directly use a
32 bit Time interface, which is actually what drives the
64 bit TimeTicks. By using the smaller value, we avoid
the need for locks, or even atomic operations for the most
part in the tracking system. On linux, we just down-sample
the standard TimeTicks to 32 bits for consistency (clean
ability to snapshot asyncronously without atomics...
but I should verify that such is helpful to performance).
I've also put in yet more cleanup and refactoring.
tbr=rtenneti
bug=101856
Review URL: http://codereview.chromium.org/8414036
TBR=jar@chromium.org
Review URL: http://codereview.chromium.org/8430004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107961 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gypi | 2 | ||||
-rw-r--r-- | base/location.h | 6 | ||||
-rw-r--r-- | base/message_loop.cc | 20 | ||||
-rw-r--r-- | base/message_loop.h | 16 | ||||
-rw-r--r-- | base/threading/thread.cc | 2 | ||||
-rw-r--r-- | base/threading/worker_pool_posix.cc | 19 | ||||
-rw-r--r-- | base/threading/worker_pool_posix.h | 6 | ||||
-rw-r--r-- | base/threading/worker_pool_win.cc | 24 | ||||
-rw-r--r-- | base/tracked_objects.cc | 308 | ||||
-rw-r--r-- | base/tracked_objects.h | 254 | ||||
-rw-r--r-- | base/tracked_objects_unittest.cc | 401 | ||||
-rw-r--r-- | base/tracking_info.cc | 23 | ||||
-rw-r--r-- | base/tracking_info.h | 41 |
13 files changed, 368 insertions, 754 deletions
diff --git a/base/base.gypi b/base/base.gypi index dd99be3..8fa20dc 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -336,8 +336,6 @@ 'timer.h', 'tracked_objects.cc', 'tracked_objects.h', - 'tracking_info.cc', - 'tracking_info.h', 'tuple.h', 'utf_offset_string_conversions.cc', 'utf_offset_string_conversions.h', diff --git a/base/location.h b/base/location.h index 523bfaf..42b3a92 100644 --- a/base/location.h +++ b/base/location.h @@ -10,6 +10,12 @@ #include "base/base_export.h" #include "base/values.h" +#ifndef NDEBUG +#ifndef TRACK_ALL_TASK_OBJECTS +#define TRACK_ALL_TASK_OBJECTS +#endif // TRACK_ALL_TASK_OBJECTS +#endif // NDEBUG + namespace tracked_objects { // Location provides basic info where of an object was constructed, or was diff --git a/base/message_loop.cc b/base/message_loop.cc index c1d4abb..553efb1 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -486,16 +486,20 @@ void MessageLoop::RunTask(const PendingTask& pending_task) { HistogramEvent(kTaskRunEvent); - tracked_objects::TrackedTime start_time = tracked_objects::ThreadData::Now(); +#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)); - - tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, - start_time, tracked_objects::ThreadData::Now()); +#if defined(TRACK_ALL_TASK_OBJECTS) + tracked_objects::ThreadData::TallyADeathIfActive(pending_task.post_births, + pending_task.time_posted, pending_task.delayed_run_time, start_of_run, + tracked_objects::ThreadData::Now()); +#endif // defined(TRACK_ALL_TASK_OBJECTS) nestable_tasks_allowed_ = true; } @@ -775,11 +779,15 @@ MessageLoop::PendingTask::PendingTask( const tracked_objects::Location& posted_from, TimeTicks delayed_run_time, bool nestable) - : base::TrackingInfo(posted_from, delayed_run_time), - task(task), + : task(task), + time_posted(TimeTicks::Now()), + delayed_run_time(delayed_run_time), posted_from(posted_from), sequence_num(0), nestable(nestable) { +#if defined(TRACK_ALL_TASK_OBJECTS) + post_births = tracked_objects::ThreadData::TallyABirthIfActive(posted_from); +#endif // defined(TRACK_ALL_TASK_OBJECTS) } MessageLoop::PendingTask::~PendingTask() { diff --git a/base/message_loop.h b/base/message_loop.h index d94dc2f..732de03 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -19,7 +19,6 @@ #include "base/observer_list.h" #include "base/synchronization/lock.h" #include "base/task.h" -#include "base/tracking_info.h" #include "base/time.h" #if defined(OS_WIN) @@ -45,9 +44,11 @@ namespace base { class Histogram; } +#if defined(TRACK_ALL_TASK_OBJECTS) namespace tracked_objects { class Births; } +#endif // defined(TRACK_ALL_TASK_OBJECTS) // A MessageLoop is used to process events for a particular thread. There is // at most one MessageLoop instance per thread. @@ -409,7 +410,7 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { #endif // This structure is copied around by value. - struct PendingTask : public base::TrackingInfo { + struct PendingTask { PendingTask(const base::Closure& task, const tracked_objects::Location& posted_from, base::TimeTicks delayed_run_time, @@ -422,6 +423,17 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { // The task to run. base::Closure task; +#if defined(TRACK_ALL_TASK_OBJECTS) + // Counter for location where the Closure was posted from. + tracked_objects::Births* post_births; +#endif // defined(TRACK_ALL_TASK_OBJECTS) + + // Time this PendingTask was posted. + base::TimeTicks time_posted; + + // The time when the task should be run. + base::TimeTicks delayed_run_time; + // The site this PendingTask was posted from. tracked_objects::Location posted_from; diff --git a/base/threading/thread.cc b/base/threading/thread.cc index d49f247..2a8d999 100644 --- a/base/threading/thread.cc +++ b/base/threading/thread.cc @@ -152,7 +152,9 @@ void Thread::ThreadMain() { ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. message_loop.set_thread_name(name_); message_loop_ = &message_loop; +#if defined(TRACK_ALL_TASK_OBJECTS) tracked_objects::ThreadData::InitializeThreadContext(name_); +#endif // TRACK_ALL_TASK_OBJECTS // Let the thread do extra initialization. // Let's do this before signaling we are started. diff --git a/base/threading/worker_pool_posix.cc b/base/threading/worker_pool_posix.cc index 917565d..41fa01a 100644 --- a/base/threading/worker_pool_posix.cc +++ b/base/threading/worker_pool_posix.cc @@ -88,14 +88,15 @@ void WorkerThread::ThreadMain() { "src_file", pending_task.posted_from.file_name(), "src_func", pending_task.posted_from.function_name()); - tracked_objects::TrackedTime start_time = - tracked_objects::ThreadData::Now(); - +#if defined(TRACK_ALL_TASK_OBJECTS) + TimeTicks start_of_run = tracked_objects::ThreadData::Now(); +#endif // defined(TRACK_ALL_TASK_OBJECTS) pending_task.task.Run(); - - tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking( - pending_task.birth_tally, pending_task.time_posted, - start_time, tracked_objects::ThreadData::Now()); +#if defined(TRACK_ALL_TASK_OBJECTS) + tracked_objects::ThreadData::TallyADeathIfActive(pending_task.post_births, + pending_task.time_posted, TimeTicks(), start_of_run, + tracked_objects::ThreadData::Now()); +#endif // defined(TRACK_ALL_TASK_OBJECTS) } // The WorkerThread is non-joinable, so it deletes itself. @@ -121,8 +122,10 @@ PosixDynamicThreadPool::PendingTask::PendingTask( const base::Closure& task) : posted_from(posted_from), task(task) { - birth_tally = tracked_objects::ThreadData::TallyABirthIfActive(posted_from); +#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 2cc1150..c0a60cc 100644 --- a/base/threading/worker_pool_posix.h +++ b/base/threading/worker_pool_posix.h @@ -53,11 +53,13 @@ class BASE_EXPORT PosixDynamicThreadPool const base::Closure& task); ~PendingTask(); +#if defined(TRACK_ALL_TASK_OBJECTS) // Counter for location where the Closure was posted from. - tracked_objects::Births* birth_tally; + tracked_objects::Births* post_births; // Time the task was posted. - tracked_objects::TrackedTime time_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 b73cabd..0cd3d96 100644 --- a/base/threading/worker_pool_win.cc +++ b/base/threading/worker_pool_win.cc @@ -20,15 +20,19 @@ struct PendingTask { const base::Closure& task) : posted_from(posted_from), task(task) { - birth_tally = tracked_objects::ThreadData::TallyABirthIfActive(posted_from); +#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) } +#if defined(TRACK_ALL_TASK_OBJECTS) // Counter for location where the Closure was posted from. - tracked_objects::Births* birth_tally; + tracked_objects::Births* post_births; // Time the task was posted. - tracked_objects::TrackedTime time_posted; + TimeTicks time_posted; +#endif // defined(TRACK_ALL_TASK_OBJECTS) // The site this PendingTask was posted from. tracked_objects::Location posted_from; @@ -43,13 +47,15 @@ DWORD CALLBACK WorkItemCallback(void* param) { "src_file", pending_task->posted_from.file_name(), "src_func", pending_task->posted_from.function_name()); - tracked_objects::TrackedTime start_time = tracked_objects::ThreadData::Now(); - +#if defined(TRACK_ALL_TASK_OBJECTS) + TimeTicks start_of_run = tracked_objects::ThreadData::Now(); +#endif // defined(TRACK_ALL_TASK_OBJECTS) pending_task->task.Run(); - - tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking( - pending_task->birth_tally, pending_task->time_posted, - start_time, tracked_objects::ThreadData::Now()); +#if defined(TRACK_ALL_TASK_OBJECTS) + tracked_objects::ThreadData::TallyADeathIfActive(pending_task->post_births, + pending_task->time_posted, TimeTicks::TimeTicks(), start_of_run, + tracked_objects::ThreadData::Now()); +#endif // defined(TRACK_ALL_TASK_OBJECTS) delete pending_task; return 0; diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index 470d157..dfb88eb 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc @@ -16,43 +16,52 @@ using base::TimeDelta; namespace tracked_objects { -namespace { -// Flag to compile out almost all of the task tracking code. + +#if defined(TRACK_ALL_TASK_OBJECTS) static const bool kTrackAllTaskObjects = true; +#else +static const bool kTrackAllTaskObjects = false; +#endif + +// Can we count on thread termination to call for thread cleanup? If not, then +// we can't risk putting references to ThreadData in TLS, as it will leak on +// worker thread termination. +static const bool kWorkerThreadCleanupSupported = true; -// When ThreadData is first initialized, should we start in an ACTIVE state to -// record all of the startup-time tasks, or should we start up DEACTIVATED, so -// that we only record after parsing the command line flag --enable-tracking. -// Note that the flag may force either state, so this really controls only the -// period of time up until that flag is parsed. If there is no flag seen, then -// this state may prevail for much or all of the process lifetime. -static const ThreadData::Status kInitialStartupState = ThreadData::ACTIVE; -} // anonymous namespace. +// A TLS slot which points to the ThreadData instance for the current thread. We +// do a fake initialization here (zeroing out data), and then the real in-place +// construction happens when we call tls_index_.Initialize(). +// static +base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); + +// A global state variable to prevent repeated initialization during tests. +// 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 Duration& queue_duration, - const Duration& run_duration) { +void DeathData::RecordDeath(const TimeDelta& queue_duration, + const TimeDelta& run_duration) { ++count_; queue_duration_ += queue_duration; run_duration_ += run_duration; } int DeathData::AverageMsRunDuration() const { - if (run_duration_ == Duration() || !count_) + if (run_duration_ == base::TimeDelta()) return 0; - // Add half of denominator to achieve rounding. - return static_cast<int>(run_duration_.InMilliseconds() + count_ / 2) / - count_; + return static_cast<int>(run_duration_.InMilliseconds() / count_); } int DeathData::AverageMsQueueDuration() const { - if (queue_duration_ == Duration() || !count_) + if (queue_duration_ == base::TimeDelta()) return 0; - // Add half of denominator to achieve rounding. - return (static_cast<int>(queue_duration_.InMilliseconds() + count_ / 2) / - count_); + return static_cast<int>(queue_duration_.InMilliseconds() / count_); } void DeathData::AddDeathData(const DeathData& other) { @@ -66,14 +75,11 @@ void DeathData::WriteHTML(std::string* output) const { return; base::StringAppendF(output, "%s:%d, ", (count_ == 1) ? "Life" : "Lives", count_); - // Be careful to leave static_casts intact, as the type returned by - // InMilliseconds() may not always be an int, even if it can generally fit - // into an int. - base::StringAppendF(output, "Run:%dms(%dms/life) ", - static_cast<int>(run_duration_.InMilliseconds()), + base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ", + run_duration_.InMilliseconds(), AverageMsRunDuration()); - base::StringAppendF(output, "Queue:%dms(%dms/life) ", - static_cast<int>(queue_duration_.InMilliseconds()), + base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ", + queue_duration_.InMilliseconds(), AverageMsQueueDuration()); } @@ -89,8 +95,8 @@ base::DictionaryValue* DeathData::ToValue() const { void DeathData::Clear() { count_ = 0; - queue_duration_ = Duration(); - run_duration_ = Duration(); + queue_duration_ = TimeDelta(); + run_duration_ = TimeDelta(); } //------------------------------------------------------------------------------ @@ -107,20 +113,6 @@ Births::Births(const Location& location, const ThreadData& current) //------------------------------------------------------------------------------ // ThreadData maintains the central data for all births and deaths. -// TODO(jar): We should pull all these static vars together, into a struct, and -// optimize layout so that we benefit from locality of reference during accesses -// to them. - -// A TLS slot which points to the ThreadData instance for the current thread. We -// do a fake initialization here (zeroing out data), and then the real in-place -// construction happens when we call tls_index_.Initialize(). -// static -base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); - -// A lock-protected counter to assign sequence number to threads. -// static -int ThreadData::thread_number_counter_ = 0; - // static ThreadData* ThreadData::all_thread_data_list_head_ = NULL; @@ -128,7 +120,7 @@ ThreadData* ThreadData::all_thread_data_list_head_ = NULL; ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL; // static -base::Lock* ThreadData::list_lock_; +base::Lock ThreadData::list_lock_; // static ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; @@ -144,7 +136,7 @@ ThreadData::ThreadData(const std::string& suggested_name) ThreadData::ThreadData() : next_(NULL), is_a_worker_thread_(true) { int thread_number; { - base::AutoLock lock(*list_lock_); + base::AutoLock lock(list_lock_); thread_number = ++thread_number_counter_; } base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); @@ -155,20 +147,17 @@ ThreadData::~ThreadData() {} void ThreadData::PushToHeadOfList() { DCHECK(!next_); - base::AutoLock lock(*list_lock_); + base::AutoLock lock(list_lock_); next_ = all_thread_data_list_head_; all_thread_data_list_head_ = this; } // static void ThreadData::InitializeThreadContext(const std::string& suggested_name) { - if (!Initialize()) // Always initialize if needed. - return; - ThreadData* current_thread_data = - reinterpret_cast<ThreadData*>(tls_index_.Get()); - if (current_thread_data) - return; // Browser tests instigate this. - current_thread_data = new ThreadData(suggested_name); + if (!tls_index_.initialized()) + return; // For unittests only. + DCHECK_EQ(tls_index_.Get(), reinterpret_cast<void*>(NULL)); + ThreadData* current_thread_data = new ThreadData(suggested_name); tls_index_.Set(current_thread_data); } @@ -183,7 +172,7 @@ ThreadData* ThreadData::Get() { // We must be a worker thread, since we didn't pre-register. ThreadData* worker_thread_data = NULL; { - base::AutoLock lock(*list_lock_); + base::AutoLock lock(list_lock_); if (!unregistered_thread_data_pool_->empty()) { worker_thread_data = const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); @@ -214,13 +203,13 @@ void ThreadData::OnThreadTerminationCleanup() const { tls_index_.Set(NULL); if (!is_a_worker_thread_) return; - base::AutoLock lock(*list_lock_); + base::AutoLock lock(list_lock_); unregistered_thread_data_pool_->push(this); } // static void ThreadData::WriteHTML(const std::string& query, std::string* output) { - if (!ThreadData::tracking_status()) + if (!ThreadData::IsActive()) return; // Not yet initialized. DataCollector collected_data; // Gather data. @@ -327,12 +316,13 @@ void ThreadData::WriteHTMLTotalAndSubtotals( } // static -base::DictionaryValue* ThreadData::ToValue() { +base::Value* ThreadData::ToValue(int process_type) { DataCollector collected_data; // Gather data. collected_data.AddListOfLivingObjects(); // Add births that are still alive. base::ListValue* list = collected_data.ToValue(); base::DictionaryValue* dictionary = new base::DictionaryValue(); dictionary->Set("list", list); + dictionary->SetInteger("process", process_type); return dictionary; } @@ -352,8 +342,8 @@ Births* ThreadData::TallyABirth(const Location& location) { } void ThreadData::TallyADeath(const Births& birth, - const Duration& queue_duration, - const Duration& run_duration) { + const TimeDelta& queue_duration, + const TimeDelta& run_duration) { DeathMap::iterator it = death_map_.find(&birth); DeathData* death_data; if (it != death_map_.end()) { @@ -370,7 +360,7 @@ Births* ThreadData::TallyABirthIfActive(const Location& location) { if (!kTrackAllTaskObjects) return NULL; // Not compiled in. - if (!tracking_status()) + if (!IsActive()) return NULL; ThreadData* current_thread_data = Get(); if (!current_thread_data) @@ -379,74 +369,37 @@ Births* ThreadData::TallyABirthIfActive(const Location& location) { } // static -void ThreadData::TallyRunOnNamedThreadIfTracking( - const base::TrackingInfo& completed_task, - const TrackedTime& start_of_run, - const TrackedTime& end_of_run) { +void ThreadData::TallyADeathIfActive(const Births* birth, + const base::TimeTicks& time_posted, + const base::TimeTicks& delayed_start_time, + const base::TimeTicks& start_of_run, + const base::TimeTicks& end_of_run) { if (!kTrackAllTaskObjects) return; // Not compiled in. - // Even if we have been DEACTIVATED, we will process any pending births so - // that our data structures (which counted the outstanding births) remain - // consistent. - const Births* birth = completed_task.birth_tally; - if (!birth) + if (!IsActive() || !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 scheduled (requested?) to emerge from the delayed task queue. This + // 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. - TrackedTime effective_post_time = completed_task.delayed_run_time.is_null() - ? tracked_objects::TrackedTime(completed_task.time_posted) - : tracked_objects::TrackedTime(completed_task.delayed_run_time); - - Duration queue_duration = start_of_run - effective_post_time; - Duration run_duration = end_of_run - start_of_run; - current_thread_data->TallyADeath(*birth, queue_duration, run_duration); -} - -// static -void ThreadData::TallyRunOnWorkerThreadIfTracking( - const Births* birth, - const TrackedTime& time_posted, - const TrackedTime& start_of_run, - const TrackedTime& end_of_run) { - if (!kTrackAllTaskObjects) - return; // Not compiled in. - - // Even if we have been DEACTIVATED, we will process any pending births so - // that our data structures (which counted the outstanding births) remain - // consistent. - if (!birth) - return; - - // TODO(jar): Support the option to coalesce all worker-thread activity under - // one ThreadData instance that uses locks to protect *all* access. This will - // reduce memory (making it provably bounded), but run incrementally slower - // (since we'll use locks on TallyBirth and TallyDeath). The good news is - // that the locks on TallyDeath will be *after* the worker thread has run, and - // hence nothing will be waiting for the completion (... besides some other - // thread that might like to run). Also, the worker threads tasks are - // generally longer, and hence the cost of the lock may perchance be amortized - // over the long task's lifetime. - ThreadData* current_thread_data = Get(); - if (!current_thread_data) - return; - - Duration queue_duration = start_of_run - time_posted; - Duration run_duration = end_of_run - start_of_run; + 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 = end_of_run - start_of_run; current_thread_data->TallyADeath(*birth, queue_duration, run_duration); } // static ThreadData* ThreadData::first() { - base::AutoLock lock(*list_lock_); + base::AutoLock lock(list_lock_); return all_thread_data_list_head_; } @@ -486,47 +439,52 @@ void ThreadData::Reset() { it->second->Clear(); } -bool ThreadData::Initialize() { +// static +bool ThreadData::StartTracking(bool status) { if (!kTrackAllTaskObjects) return false; // Not compiled in. - if (status_ != UNINITIALIZED) - return true; - // Initialize all leaking constants that are difficult to toggle in and out - // of existance. - // First call must be made when single threaded at startup. - // Perform the "real" TLS initialization now, and leave it intact through + + // Do a bit of class initialization. + if (!unregistered_thread_data_pool_) { + ThreadDataPool* initial_pool = new ThreadDataPool; + { + base::AutoLock lock(list_lock_); + if (!unregistered_thread_data_pool_) { + unregistered_thread_data_pool_ = initial_pool; + initial_pool = NULL; + } + } + delete initial_pool; // In case it was not used. + } + + // Perform the "real" initialization now, and leave it intact through // process termination. - if (!tls_index_.initialized()) // Testing may have initialized this. + if (!tls_index_.initialized()) tls_index_.Initialize(&ThreadData::OnThreadTermination); DCHECK(tls_index_.initialized()); - unregistered_thread_data_pool_ = new ThreadDataPool; - // TODO(jar): A linker initialized spin lock would be much safer than this - // allocation, which relies on being called while single threaded. - if (!list_lock_) // In case testing deleted this. - list_lock_ = new base::Lock; - status_ = kInitialStartupState; - return true; -} -// static -bool ThreadData::InitializeAndSetTrackingStatus(bool status) { - if (!Initialize()) // No-op if already initialized. - return false; // Not compiled in. - - status_ = status ? ACTIVE : DEACTIVATED; + if (!status) { + base::AutoLock lock(list_lock_); + DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); + status_ = SHUTDOWN; + return true; + } + base::AutoLock lock(list_lock_); + DCHECK_EQ(UNINITIALIZED, status_); + status_ = ACTIVE; return true; } // static -bool ThreadData::tracking_status() { +bool ThreadData::IsActive() { return status_ == ACTIVE; } // static -TrackedTime ThreadData::Now() { - if (!kTrackAllTaskObjects || status_ != ACTIVE) - return TrackedTime(); // Super fast when disabled, or not compiled. - return TrackedTime::Now(); +base::TimeTicks ThreadData::Now() { + if (kTrackAllTaskObjects && status_ == ACTIVE) + return base::TimeTicks::Now(); + return base::TimeTicks(); // Super fast when disabled, or not compiled in. } // static @@ -534,12 +492,12 @@ void ThreadData::ShutdownSingleThreadedCleanup() { // This is only called from test code, where we need to cleanup so that // additional tests can be run. // We must be single threaded... but be careful anyway. - if (!InitializeAndSetTrackingStatus(false)) + if (!StartTracking(false)) return; ThreadData* thread_data_list; ThreadDataPool* final_pool; { - base::AutoLock lock(*list_lock_); + base::AutoLock lock(list_lock_); thread_data_list = all_thread_data_list_head_; all_thread_data_list_head_ = NULL; final_pool = unregistered_thread_data_pool_; @@ -616,12 +574,16 @@ base::DictionaryValue* Snapshot::ToValue() const { return dictionary; } +void Snapshot::Add(const Snapshot& other) { + death_data_.AddDeathData(other.death_data_); +} + //------------------------------------------------------------------------------ // DataCollector DataCollector::DataCollector() { - if (!kTrackAllTaskObjects) - return; // Not compiled in. + if (!ThreadData::IsActive()) + return; // Get an unchanging copy of a ThreadData list. ThreadData* my_list = ThreadData::first(); @@ -778,35 +740,6 @@ void Comparator::Clear() { selector_ = NIL; } -// static -Comparator::Selector Comparator::FindSelector(const std::string& keyword) { - // Sorting and aggretation keywords, which specify how to sort the data, or - // can specify a required match from the specified field in the record. - if (0 == keyword.compare("count")) - return COUNT; - if (0 == keyword.compare("totalduration")) - return TOTAL_RUN_DURATION; - if (0 == keyword.compare("duration")) - return AVERAGE_RUN_DURATION; - if (0 == keyword.compare("totalqueueduration")) - return TOTAL_QUEUE_DURATION; - if (0 == keyword.compare("averagequeueduration")) - return AVERAGE_QUEUE_DURATION; - if (0 == keyword.compare("birth")) - return BIRTH_THREAD; - if (0 == keyword.compare("death")) - return DEATH_THREAD; - if (0 == keyword.compare("file")) - return BIRTH_FILE; - if (0 == keyword.compare("function")) - return BIRTH_FUNCTION; - if (0 == keyword.compare("line")) - return BIRTH_LINE; - if (0 == keyword.compare("reset")) - return RESET_ALL_DATA; - return UNKNOWN_KEYWORD; -} - bool Comparator::operator()(const Snapshot& left, const Snapshot& right) const { switch (selector_) { @@ -1023,6 +956,28 @@ void Comparator::SetSubgroupTiebreaker(Selector selector) { } void Comparator::ParseKeyphrase(const std::string& key_phrase) { + typedef std::map<const std::string, Selector> KeyMap; + static KeyMap key_map; + static bool initialized = false; + if (!initialized) { + 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["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; + } + std::string required; // Watch for: "sort_key=value" as we parse. size_t equal_offset = key_phrase.find('=', 0); @@ -1032,14 +987,13 @@ void Comparator::ParseKeyphrase(const std::string& key_phrase) { } std::string keyword(key_phrase.substr(0, equal_offset)); keyword = StringToLowerASCII(keyword); - Selector selector = FindSelector(keyword); - if (selector == UNKNOWN_KEYWORD) - return; - if (selector == RESET_ALL_DATA) { + KeyMap::iterator it = key_map.find(keyword); + if (key_map.end() == it) + return; // Unknown keyword. + if (it->second == RESET_ALL_DATA) ThreadData::ResetAllThreadData(); - return; - } - SetTiebreaker(selector, required); + else + SetTiebreaker(key_map[keyword], required); } bool Comparator::ParseQuery(const std::string& query) { diff --git a/base/tracked_objects.h b/base/tracked_objects.h index b243da9..1593991 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h @@ -16,13 +16,8 @@ #include "base/time.h" #include "base/synchronization/lock.h" #include "base/threading/thread_local_storage.h" -#include "base/tracking_info.h" #include "base/values.h" -#if defined(OS_WIN) -#include <mmsystem.h> // Declare timeGetTime(); -#endif - // TrackedObjects provides a database of stats about objects (generally Tasks) // that are tracked. Tracking means their birth, death, duration, birth thread, // death thread, and birth place are recorded. This data is carefully spread @@ -74,7 +69,7 @@ // any locks, as all that data is constant across the life of the process. // // The above work *could* also be done for any other object as well by calling -// TallyABirthIfActive() and TallyRunOnNamedThreadIfTracking() as appropriate. +// TallyABirthIfActive() and TallyADeathIfActive() as appropriate. // // The amount of memory used in the above data structures depends on how many // threads there are, and how many Locations of construction there are. @@ -172,96 +167,6 @@ class MessageLoop; namespace tracked_objects { //------------------------------------------------------------------------------ - -#define USE_FAST_TIME_CLASS_FOR_DURATION_CALCULATIONS - -#if defined(USE_FAST_TIME_CLASS_FOR_DURATION_CALCULATIONS) - -// TimeTicks maintains a wasteful 64 bits of data (we need less than 32), and on -// windows, a 64 bit timer is expensive to even obtain. We use a simple -// millisecond counter for most of our time values, as well as millisecond units -// of duration between those values. This means we can only handle durations -// up to 49 days (range), or 24 days (non-negative time durations). -// We only define enough methods to service the needs of the tracking classes, -// and our interfaces are modeled after what TimeTicks and TimeDelta use (so we -// can swap them into place if we want to use the "real" classes). - -class BASE_EXPORT Duration { // Similar to base::TimeDelta. - public: - Duration() : ms_(0) {} - - Duration& operator+=(const Duration& other) { - ms_ += other.ms_; - return *this; - } - - Duration operator+(const Duration& other) const { - return Duration(ms_ + other.ms_); - } - - bool operator==(const Duration& other) const { return ms_ == other.ms_; } - bool operator!=(const Duration& other) const { return ms_ != other.ms_; } - bool operator>(const Duration& other) const { return ms_ > other.ms_; } - - static Duration FromMilliseconds(int ms) { return Duration(ms); } - - int32 InMilliseconds() const { return ms_; } - - private: - friend class TrackedTime; - explicit Duration(int32 duration) : ms_(duration) {} - - // Internal time is stored directly in milliseconds. - int32 ms_; -}; - -class BASE_EXPORT TrackedTime { // Similar to base::TimeTicks. - public: - TrackedTime() : ms_(0) {} - explicit TrackedTime(const base::TimeTicks& time) - : ms_((time - base::TimeTicks()).InMilliseconds()) { - } - - static TrackedTime Now() { -#if defined(OS_WIN) - // Use lock-free accessor to 32 bit time. - // Note that TimeTicks::Now() is built on this, so we have "compatible" - // times when we down-convert a TimeTicks sample. - // TODO(jar): Surface this interface via something in base/time.h. - return TrackedTime(static_cast<int32>(::timeGetTime())); -#else - // Posix has nice cheap 64 bit times, so we just down-convert it. - return TrackedTime(base::TimeTicks::Now()); -#endif // OS_WIN - } - - Duration operator-(const TrackedTime& other) const { - return Duration(ms_ - other.ms_); - } - - TrackedTime operator+(const Duration& other) const { - return TrackedTime(ms_ + other.ms_); - } - - bool is_null() const { return ms_ == 0; } - - private: - friend class Duration; - explicit TrackedTime(int32 ms) : ms_(ms) {} - - // Internal duration is stored directly in milliseconds. - uint32 ms_; -}; - -#else - -// Just use full 64 bit time calculations, and the slower TimeTicks::Now(). -typedef base::TimeTicks TrackedTime; -typedef base::TimeDelta Duration; - -#endif // USE_FAST_TIME_CLASS_FOR_DURATION_CALCULATIONS - -//------------------------------------------------------------------------------ // For a specific thread, and a specific birth place, the collection of all // death info (with tallies for each death thread, to prevent access conflicts). class ThreadData; @@ -312,8 +217,8 @@ class BASE_EXPORT Births: public BirthOnThread { }; //------------------------------------------------------------------------------ -// Basic info summarizing multiple destructions of a tracked object with a -// single birthplace (fixed Location). Used both on specific threads, and also +// Basic info summarizing multiple destructions of an object with a single +// birthplace (fixed Location). Used both on specific threads, and also used // in snapshots when integrating assembled data. class BASE_EXPORT DeathData { @@ -328,14 +233,14 @@ class BASE_EXPORT DeathData { // 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 Duration& queue_duration, - const Duration& run_duration); + void RecordDeath(const base::TimeDelta& queue_duration, + const base::TimeDelta& run_duration); // Metrics accessors. int count() const { return count_; } - Duration run_duration() const { return run_duration_; } + base::TimeDelta run_duration() const { return run_duration_; } int AverageMsRunDuration() const; - Duration queue_duration() const { return queue_duration_; } + base::TimeDelta queue_duration() const { return queue_duration_; } int AverageMsQueueDuration() const; // Accumulate metrics from other into this. This method is never used on @@ -345,7 +250,7 @@ class BASE_EXPORT DeathData { // Simple print of internal state for use in line of HTML. void WriteHTML(std::string* output) const; - // Construct a DictionaryValue instance containing all our stats. The caller + // Constructe a DictionaryValue instance containing all our stats. The caller // assumes ownership of the returned instance. base::DictionaryValue* ToValue() const; @@ -354,8 +259,8 @@ class BASE_EXPORT DeathData { private: int count_; // Number of destructions. - Duration run_duration_; // Sum of all Run()time durations. - Duration queue_duration_; // Sum of all queue time durations. + base::TimeDelta run_duration_; // Sum of all Run()time durations. + base::TimeDelta queue_duration_; // Sum of all queue time durations. }; //------------------------------------------------------------------------------ @@ -382,11 +287,13 @@ class BASE_EXPORT Snapshot { const std::string DeathThreadName() const; int count() const { return death_data_.count(); } - Duration run_duration() const { return death_data_.run_duration(); } - Duration queue_duration() const { return death_data_.queue_duration(); } + 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(); } @@ -398,6 +305,8 @@ class BASE_EXPORT Snapshot { // The caller assumes ownership of the memory in the returned instance. base::DictionaryValue* ToValue() const; + void Add(const Snapshot& other); + private: const BirthOnThread* birth_; // Includes Location and birth_thread. const ThreadData* death_thread_; @@ -417,23 +326,20 @@ class BASE_EXPORT DataCollector { DataCollector(); ~DataCollector(); - // Adds all stats from the indicated thread into our arrays. This function - // uses locks at the lowest level (when accessing the underlying maps which - // could change when not locked), and can be called from any threads. + // Add all stats from the indicated thread into our arrays. This function is + // mutex protected, and *could* be called from any threads (although current + // implementation serialized calls to Append). void Append(const ThreadData& thread_data); // After the accumulation phase, the following accessor is used to process the - // data (i.e., sort it, filter it, etc.). + // data. Collection* collection(); - // Adds entries for all the remaining living objects (objects that have - // tallied a birth, but have not yet tallied a matching death, and hence must - // be either running, queued up, or being held in limbo for future posting). - // This should be called after all known ThreadData instances have been - // processed using Append(). + // After collection of death data is complete, we can add entries for all the + // remaining living objects. void AddListOfLivingObjects(); - // Generates a ListValue representation of the vector of snapshots. The caller + // Generate a ListValue representation of the vector of snapshots. The caller // assumes ownership of the memory in the returned instance. base::ListValue* ToValue() const; @@ -444,8 +350,7 @@ class BASE_EXPORT DataCollector { Collection collection_; // The total number of births recorded at each location for which we have not - // seen a death count. This map changes as we do Append() calls, and is later - // used by AddListOfLivingObjects() to gather up unaccounted for births. + // seen a death count. BirthCount global_birth_count_; DISALLOW_COPY_AND_ASSIGN(DataCollector); @@ -454,9 +359,6 @@ class BASE_EXPORT DataCollector { //------------------------------------------------------------------------------ // Aggregation contains summaries (totals and subtotals) of groups of Snapshot // instances to provide printing of these collections on a single line. -// We generally provide an aggregate total for the entire list, as well as -// aggregate subtotals for groups of stats (example: group of all lives that -// died on the specific thread). class BASE_EXPORT Aggregation: public DeathData { public: @@ -512,7 +414,6 @@ class BASE_EXPORT Comparator { // Imediate action keywords. RESET_ALL_DATA = -1, - UNKNOWN_KEYWORD = -2, }; explicit Comparator(); @@ -569,10 +470,6 @@ class BASE_EXPORT Comparator { // members of the tested elements. enum Selector selector_; - // Translate a path keyword into a selector. This is a slow implementation, - // but this is rarely done, and only for HTML presentations. - static Selector FindSelector(const std::string& keyword); - // For filtering into acceptable and unacceptable snapshot instance, the // following is required to be a substring of the selector_ field. std::string required_; @@ -596,27 +493,16 @@ class BASE_EXPORT Comparator { //------------------------------------------------------------------------------ // 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. -// We use ThreadLocalStorage to quickly identfy the current ThreadData context. -// We also have a linked list of ThreadData instances, and that list is used to -// harvest data from all existing instances. class BASE_EXPORT ThreadData { public: - // Current allowable states of the tracking system. The states can vary - // between ACTIVE and DEACTIVATED, but can never go back to UNINITIALIZED. - enum Status { - UNINITIALIZED, - ACTIVE, - DEACTIVATED, - }; - typedef std::map<Location, Births*> BirthMap; typedef std::map<const Births*, DeathData> DeathMap; // Initialize the current thread context with a new instance of ThreadData. - // This is used by all threads that have names, and should be explicitly - // set *before* any births on the threads have taken place. It is generally - // only used by the message loop, which has a well defined thread name. + // 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. @@ -629,47 +515,33 @@ class BASE_EXPORT ThreadData { // append to output. static void WriteHTML(const std::string& query, std::string* output); + // Constructe a ListValue instance containing all recursive results in our + // process. The caller assumes ownership of the memory in the returned + // instance. The |process_type| should become an enum, which corresponds + // to all possible process types. I'm using an int as a placeholder. + static base::Value* ToValue(int process_type); + // For a given accumulated array of results, use the comparator to sort and // subtotal, writing the results to the output. static void WriteHTMLTotalAndSubtotals( const DataCollector::Collection& match_array, const Comparator& comparator, std::string* output); - // Constructs a DictionaryValue instance containing all recursive results in - // our process. The caller assumes ownership of the memory in the returned - // instance. - static base::DictionaryValue* ToValue(); - - // Finds (or creates) a place to count births from the given location in this + // Find (or create) a place to count births from the given location in this // thread, and increment that tally. // TallyABirthIfActive will returns NULL if the birth cannot be tallied. static Births* TallyABirthIfActive(const Location& location); - // Records the end of a timed run of an object. The |completed_task| contains - // a pointer to a Births, the time_posted, and a delayed_start_time if any. - // The |start_of_run| indicates when we started to perform the run of the - // task. The delayed_start_time is non-null for tasks that were posted as - // delayed tasks, and it indicates when the task should have run (i.e., when - // it should have posted out of the timer queue, and into the work queue. - // The |end_of_run| was just obtained by a call to Now() (just after the task - // finished). It is provided as an argument to help with testing. - static void TallyRunOnNamedThreadIfTracking( - const base::TrackingInfo& completed_task, - const TrackedTime& start_of_run, - const TrackedTime& end_of_run); - // Record the end of a timed run of an object. The |birth| is the record for - // the instance, the |time_posted| records that instant, which is presumed to - // be when the task was posted into a queue to run on a worker thread. - // The |start_of_run| is when the worker thread started to perform the run of - // the task. + // 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. // The |end_of_run| was just obtained by a call to Now() (just after the task // finished). - static void TallyRunOnWorkerThreadIfTracking( - const Births* birth, - const TrackedTime& time_posted, - const TrackedTime& start_of_run, - const TrackedTime& end_of_run); + static void TallyADeathIfActive(const Births* birth, + const base::TimeTicks& time_posted, + const base::TimeTicks& delayed_start_time, + const base::TimeTicks& start_of_run, + const base::TimeTicks& end_of_run); const std::string thread_name() const { return thread_name_; } @@ -695,21 +567,17 @@ class BASE_EXPORT ThreadData { // bogus counts VERY rarely. static void ResetAllThreadData(); - // Initializes all statics if needed (this initialization call should be made - // while we are single threaded). Returns false if unable to initialize. - static bool Initialize(); - - // Sets internal status_ to either become ACTIVE, or DEACTIVATED, + // 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 InitializeAndSetTrackingStatus(bool status); - static bool tracking_status(); + // IF tracking is not compiled in, this function will return false. + static bool StartTracking(bool status); + static bool IsActive(); // 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 TrackedTime Now(); + 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 @@ -719,6 +587,14 @@ class BASE_EXPORT ThreadData { static void ShutdownSingleThreadedCleanup(); private: + // Current allowable states of the tracking system. The states always + // proceed towards SHUTDOWN, and never go backwards. + enum Status { + UNINITIALIZED, + ACTIVE, + SHUTDOWN, + }; + typedef std::stack<const ThreadData*> ThreadDataPool; // Worker thread construction creates a name since there is none. @@ -738,8 +614,8 @@ class BASE_EXPORT ThreadData { // Find a place to record a death on this thread. void TallyADeath(const Births& birth, - const Duration& queue_duration, - const Duration& duration); + const base::TimeDelta& queue_duration, + const base::TimeDelta& duration); // Using our lock to protect the iteration, Clear all birth and death data. void Reset(); @@ -764,8 +640,8 @@ class BASE_EXPORT ThreadData { // pool, and if none are available, construct a new one. static ThreadDataPool* unregistered_thread_data_pool_; // Protection for access to all_thread_data_list_head_, and to - // unregistered_thread_data_pool_. This lock is leaked at shutdown. - static base::Lock* list_lock_; + // unregistered_thread_data_pool_. + static base::Lock list_lock_; // We set status_ to SHUTDOWN when we shut down the tracking service. static Status status_; @@ -819,17 +695,21 @@ class BASE_EXPORT ThreadData { class BASE_EXPORT AutoTracking { public: AutoTracking() { - ThreadData::Initialize(); + if (state_ != kNeverBeenRun) + return; + ThreadData::StartTracking(true); + state_ = kRunning; } ~AutoTracking() { - // TODO(jar): Consider emitting a CSV dump of the data at this point. This - // should be called after the message loops have all terminated (or at least - // the main message loop is gone), so there is little chance for additional - // tasks to be Run. } private: + enum State { + kNeverBeenRun, + kRunning, + }; + static State state_; DISALLOW_COPY_AND_ASSIGN(AutoTracking); }; diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc index b457cb1..ef88183 100644 --- a/base/tracked_objects_unittest.cc +++ b/base/tracked_objects_unittest.cc @@ -15,18 +15,15 @@ namespace tracked_objects { class TrackedObjectsTest : public testing::Test { public: - TrackedObjectsTest() { - ThreadData::ShutdownSingleThreadedCleanup(); - } + ~TrackedObjectsTest() { + ThreadData::ShutdownSingleThreadedCleanup(); + } - ~TrackedObjectsTest() { - ThreadData::ShutdownSingleThreadedCleanup(); - } }; TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { // Minimal test doesn't even create any tasks. - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. @@ -44,7 +41,7 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { ThreadData::ShutdownSingleThreadedCleanup(); // Do it again, just to be sure we reset state completely. - ThreadData::InitializeAndSetTrackingStatus(true); + ThreadData::StartTracking(true); EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. data = ThreadData::Get(); EXPECT_TRUE(ThreadData::first()); // Now class was constructed. @@ -60,7 +57,7 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { } TEST_F(TrackedObjectsTest, TinyStartupShutdown) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; // Instigate tracking on a single tracked object, on our thread. @@ -80,14 +77,14 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { EXPECT_EQ(0u, death_map.size()); // No deaths. - // Now instigate another birth, and a first death at the same location. - // TrackingInfo will call TallyABirth() during construction. - base::TimeTicks kBogusStartTime; - base::TrackingInfo pending_task(location, kBogusStartTime); - TrackedTime kBogusStartRunTime; - TrackedTime kBogusEndRunTime; - ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, kBogusStartRunTime, - kBogusEndRunTime); + // Now instigate a birth, and a death. + const Births* second_birth = ThreadData::TallyABirthIfActive(location); + ThreadData::TallyADeathIfActive( + second_birth, + base::TimeTicks(), /* Bogus post_time. */ + base::TimeTicks(), /* Bogus delayed_start_time. */ + base::TimeTicks(), /* Bogus start_run_time. */ + base::TimeTicks() /* Bogus end_run_time */ ); birth_map.clear(); data->SnapshotBirthMap(&birth_map); @@ -103,13 +100,13 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { } TEST_F(TrackedObjectsTest, DeathDataTest) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; scoped_ptr<DeathData> data(new DeathData()); ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL)); - EXPECT_EQ(data->run_duration(), Duration()); - EXPECT_EQ(data->queue_duration(), Duration()); + EXPECT_EQ(data->run_duration(), base::TimeDelta()); + EXPECT_EQ(data->queue_duration(), base::TimeDelta()); EXPECT_EQ(data->AverageMsRunDuration(), 0); EXPECT_EQ(data->AverageMsQueueDuration(), 0); EXPECT_EQ(data->count(), 0); @@ -117,8 +114,8 @@ TEST_F(TrackedObjectsTest, DeathDataTest) { int run_ms = 42; int queue_ms = 8; - Duration run_duration = Duration().FromMilliseconds(run_ms); - Duration queue_duration = Duration().FromMilliseconds(queue_ms); + base::TimeDelta run_duration = base::TimeDelta().FromMilliseconds(run_ms); + base::TimeDelta queue_duration = base::TimeDelta().FromMilliseconds(queue_ms); data->RecordDeath(queue_duration, run_duration); EXPECT_EQ(data->run_duration(), run_duration); EXPECT_EQ(data->queue_duration(), queue_duration); @@ -154,69 +151,20 @@ TEST_F(TrackedObjectsTest, DeathDataTest) { EXPECT_EQ(json, birth_only_result); } -TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueWorkerThread) { - // Transition to Deactivated state before doing anything. - if (!ThreadData::InitializeAndSetTrackingStatus(false)) - return; - // We don't initialize system with a thread name, so we're viewed as a worker - // thread. - const int kFakeLineNumber = 173; - const char* kFile = "FixedFileName"; - const char* kFunction = "BirthOnlyToValueWorkerThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - Births* birth = ThreadData::TallyABirthIfActive(location); - // We should now see a NULL birth record. - EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); - - scoped_ptr<base::Value> value(ThreadData::ToValue()); - std::string json; - base::JSONWriter::Write(value.get(), false, &json); - std::string birth_only_result = "{" - "\"list\":[" - "]" - "}"; - EXPECT_EQ(json, birth_only_result); -} - -TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueMainThread) { - // Start in the deactivated state. - if (!ThreadData::InitializeAndSetTrackingStatus(false)) - return; - - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 173; - const char* kFile = "FixedFileName"; - const char* kFunction = "BirthOnlyToValueMainThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - // Do not delete birth. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - // We expect to not get a birth record. - EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); - - scoped_ptr<base::Value> value(ThreadData::ToValue()); - std::string json; - base::JSONWriter::Write(value.get(), false, &json); - std::string birth_only_result = "{" - "\"list\":[" - "]" - "}"; - EXPECT_EQ(json, birth_only_result); -} - TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; // We don't initialize system with a thread name, so we're viewed as a worker // thread. - const int kFakeLineNumber = 173; + int fake_line_number = 173; const char* kFile = "FixedFileName"; const char* kFunction = "BirthOnlyToValueWorkerThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); + Location location(kFunction, kFile, fake_line_number, NULL); Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - scoped_ptr<base::Value> value(ThreadData::ToValue()); + int process_type = 3; + scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string birth_only_result = "{" @@ -235,26 +183,28 @@ TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { "\"line_number\":173" "}" "}" - "]" + "]," + "\"process\":3" "}"; EXPECT_EQ(json, birth_only_result); } TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 173; + int fake_line_number = 173; const char* kFile = "FixedFileName"; const char* kFunction = "BirthOnlyToValueMainThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); + Location location(kFunction, kFile, fake_line_number, NULL); // Do not delete birth. We don't own it. Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - scoped_ptr<base::Value> value(ThreadData::ToValue()); + int process_type = 34; + scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string birth_only_result = "{" @@ -273,39 +223,40 @@ TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { "\"line_number\":173" "}" "}" - "]" + "]," + "\"process\":34" "}"; EXPECT_EQ(json, birth_only_result); } TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 236; + int fake_line_number = 236; const char* kFile = "FixedFileName"; const char* kFunction = "LifeCycleToValueMainThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); + Location location(kFunction, kFile, fake_line_number, NULL); // Do not delete birth. We don't own it. Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks kDelayedStartTime = base::TimeTicks(); - // TrackingInfo will call TallyABirth() during construction. - base::TrackingInfo pending_task(location, kDelayedStartTime); - pending_task.time_posted = kTimePosted; // Overwrite implied Now(). - - const TrackedTime kStartOfRun = TrackedTime() + - Duration::FromMilliseconds(5); - const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); - ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, - kStartOfRun, kEndOfRun); - - scoped_ptr<base::Value> value(ThreadData::ToValue()); + // TimeTicks initializers ar ein microseconds. Durations are calculated in + // milliseconds, so we need to use 1000x. + const base::TimeTicks time_posted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks delayed_start_time = base::TimeTicks(); + const base::TimeTicks start_of_run = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(5); + const base::TimeTicks end_of_run = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(7); + ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, + start_of_run, end_of_run); + + int process_type = 7; + scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string one_line_result = "{" @@ -324,190 +275,44 @@ TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { "\"line_number\":236" "}" "}" - "]" + "]," + "\"process\":7" "}"; - EXPECT_EQ(one_line_result, json); -} - -// We will deactivate tracking after the birth, and before the death, and -// demonstrate that the lifecycle is completely tallied. This ensures that -// our tallied births are matched by tallied deaths (except for when the -// task is still running, or is queued). -TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToValueMainThread) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) - return; - - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 236; - const char* kFile = "FixedFileName"; - const char* kFunction = "LifeCycleToValueMainThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - // Do not delete birth. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks kDelayedStartTime = base::TimeTicks(); - // TrackingInfo will call TallyABirth() during construction. - base::TrackingInfo pending_task(location, kDelayedStartTime); - pending_task.time_posted = kTimePosted; // Overwrite implied Now(). - - // Turn off tracking now that we have births. - EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus(false)); - - const TrackedTime kStartOfRun = TrackedTime() + - Duration::FromMilliseconds(5); - const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); - ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, - kStartOfRun, kEndOfRun); - - scoped_ptr<base::Value> value(ThreadData::ToValue()); - std::string json; - base::JSONWriter::Write(value.get(), false, &json); - std::string one_line_result = "{" - "\"list\":[" - "{" - "\"birth_thread\":\"SomeMainThreadName\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":4," - "\"run_ms\":2" - "}," - "\"death_thread\":\"SomeMainThreadName\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"LifeCycleToValueMainThread\"," - "\"line_number\":236" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result, json); -} - -// We will deactivate tracking before starting a life cycle, and neither -// the birth nor the death will be recorded. -TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToValueMainThread) { - if (!ThreadData::InitializeAndSetTrackingStatus(false)) - return; - - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 236; - const char* kFile = "FixedFileName"; - const char* kFunction = "LifeCycleToValueMainThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - // Do not delete birth. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); - - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks kDelayedStartTime = base::TimeTicks(); - // TrackingInfo will call TallyABirth() during construction. - base::TrackingInfo pending_task(location, kDelayedStartTime); - pending_task.time_posted = kTimePosted; // Overwrite implied Now(). - - const TrackedTime kStartOfRun = TrackedTime() + - Duration::FromMilliseconds(5); - const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); - ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, - kStartOfRun, kEndOfRun); - - scoped_ptr<base::Value> value(ThreadData::ToValue()); - std::string json; - base::JSONWriter::Write(value.get(), false, &json); - std::string one_line_result = "{" - "\"list\":[" - "]" - "}"; - EXPECT_EQ(one_line_result, json); -} - -TEST_F(TrackedObjectsTest, LifeCycleToValueWorkerThread) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) - return; - - // Don't initialize thread, so that we appear as a worker thread. - // ThreadData::InitializeThreadContext("SomeMainThreadName"); - - const int kFakeLineNumber = 236; - const char* kFile = "FixedFileName"; - const char* kFunction = "LifeCycleToValueWorkerThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - // Do not delete birth. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - - const TrackedTime kTimePosted = TrackedTime() + Duration::FromMilliseconds(1); - const TrackedTime kStartOfRun = TrackedTime() + - Duration::FromMilliseconds(5); - const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); - ThreadData::TallyRunOnWorkerThreadIfTracking(birth, kTimePosted, - kStartOfRun, kEndOfRun); - - scoped_ptr<base::Value> value(ThreadData::ToValue()); - std::string json; - base::JSONWriter::Write(value.get(), false, &json); - std::string one_line_result = "{" - "\"list\":[" - "{" - "\"birth_thread\":\"WorkerThread-1\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":4," - "\"run_ms\":2" - "}," - "\"death_thread\":\"WorkerThread-1\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"LifeCycleToValueWorkerThread\"," - "\"line_number\":236" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result, json); + EXPECT_EQ(json, one_line_result); } TEST_F(TrackedObjectsTest, TwoLives) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeFileThreadName"); - const int kFakeLineNumber = 222; + int fake_line_number = 222; const char* kFile = "AnotherFileName"; const char* kFunction = "TwoLives"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); + Location location(kFunction, kFile, fake_line_number, NULL); // Do not delete birth. We don't own it. Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks kDelayedStartTime = base::TimeTicks(); - // TrackingInfo will call TallyABirth() during construction. - base::TrackingInfo pending_task(location, kDelayedStartTime); - pending_task.time_posted = kTimePosted; // Overwrite implied Now(). - - const TrackedTime kStartOfRun = TrackedTime() + - Duration::FromMilliseconds(5); - const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); - ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, - kStartOfRun, kEndOfRun); - - // TrackingInfo will call TallyABirth() during construction. - base::TrackingInfo pending_task2(location, kDelayedStartTime); - pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). - - ThreadData::TallyRunOnNamedThreadIfTracking(pending_task2, - kStartOfRun, kEndOfRun); - - scoped_ptr<base::Value> value(ThreadData::ToValue()); + // TimeTicks initializers ar ein microseconds. Durations are calculated in + // milliseconds, so we need to use 1000x. + const base::TimeTicks time_posted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks delayed_start_time = base::TimeTicks(); + const base::TimeTicks start_of_run = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(5); + const base::TimeTicks end_of_run = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(7); + ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, + start_of_run, end_of_run); + + birth = ThreadData::TallyABirthIfActive(location); + ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, + start_of_run, end_of_run); + + int process_type = 7; + scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string one_line_result = "{" @@ -526,43 +331,44 @@ TEST_F(TrackedObjectsTest, TwoLives) { "\"line_number\":222" "}" "}" - "]" + "]," + "\"process\":7" "}"; - EXPECT_EQ(one_line_result, json); + EXPECT_EQ(json, one_line_result); } TEST_F(TrackedObjectsTest, DifferentLives) { - if (!ThreadData::InitializeAndSetTrackingStatus(true)) + if (!ThreadData::StartTracking(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeFileThreadName"); - const int kFakeLineNumber = 567; + int fake_line_number = 567; const char* kFile = "AnotherFileName"; const char* kFunction = "DifferentLives"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks kDelayedStartTime = base::TimeTicks(); - // TrackingInfo will call TallyABirth() during construction. - base::TrackingInfo pending_task(location, kDelayedStartTime); - pending_task.time_posted = kTimePosted; // Overwrite implied Now(). - - const TrackedTime kStartOfRun = TrackedTime() + - Duration::FromMilliseconds(5); - const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); - ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, - kStartOfRun, kEndOfRun); - - const int kSecondFakeLineNumber = 999; - Location second_location(kFunction, kFile, kSecondFakeLineNumber, NULL); - - // TrackingInfo will call TallyABirth() during construction. - base::TrackingInfo pending_task2(second_location, kDelayedStartTime); - pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). + Location location(kFunction, kFile, fake_line_number, NULL); + // Do not delete birth. We don't own it. + Births* birth = ThreadData::TallyABirthIfActive(location); + EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - scoped_ptr<base::Value> value(ThreadData::ToValue()); + // TimeTicks initializers ar ein microseconds. Durations are calculated in + // milliseconds, so we need to use 1000x. + const base::TimeTicks time_posted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks delayed_start_time = base::TimeTicks(); + const base::TimeTicks start_of_run = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(5); + const base::TimeTicks end_of_run = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(7); + ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, + start_of_run, end_of_run); + + int second_fake_line_number = 999; + Location second_location(kFunction, kFile, second_fake_line_number, NULL); + birth = ThreadData::TallyABirthIfActive(second_location); + + int process_type = 2; + scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string one_line_result = "{" @@ -595,9 +401,10 @@ TEST_F(TrackedObjectsTest, DifferentLives) { "\"line_number\":999" "}" "}" - "]" + "]," + "\"process\":2" "}"; - EXPECT_EQ(one_line_result, json); + EXPECT_EQ(json, one_line_result); } } // namespace tracked_objects diff --git a/base/tracking_info.cc b/base/tracking_info.cc deleted file mode 100644 index 7ac4221..0000000 --- a/base/tracking_info.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/tracking_info.h" - -#include "base/tracked_objects.h" - -namespace base { - -TrackingInfo::TrackingInfo( - const tracked_objects::Location& posted_from, - base::TimeTicks delayed_run_time) - : birth_tally( - tracked_objects::ThreadData::TallyABirthIfActive(posted_from)), - time_posted(TimeTicks::Now()), - delayed_run_time(delayed_run_time) { -} - -TrackingInfo::~TrackingInfo() {} - -} // namespace base - diff --git a/base/tracking_info.h b/base/tracking_info.h deleted file mode 100644 index 0886fdf..0000000 --- a/base/tracking_info.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a simple struct with tracking information that is stored -// with a PendingTask (when message_loop is handling the task). -// Only the information that is shared with the profiler in tracked_objects -// are included in this structure. - - -#ifndef BASE_TRACKING_INFO_H_ -#define BASE_TRACKING_INFO_H_ - -#include "base/time.h" - -namespace tracked_objects { -class Location; -class Births; -} - -namespace base { - -// This structure is copied around by value. -struct BASE_EXPORT TrackingInfo { - TrackingInfo(const tracked_objects::Location& posted_from, - base::TimeTicks delayed_run_time); - ~TrackingInfo(); - - // Record of location and thread that the task came from. - tracked_objects::Births* birth_tally; - - // Time when the related task was posted. - base::TimeTicks time_posted; - - // The time when the task should be run. - base::TimeTicks delayed_run_time; -}; - -} // namespace base - -#endif // BASE_TRACKING_INFO_H_ |