Fully enable about:tracking by default
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.
r=rtenneti
bug=101856
Review URL: http://codereview.chromium.org/8391019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107793 0039d316-1c4b-4281-b951-d872f2087c98
CrOS-Libchrome-Original-Commit: f15841fb802ccfd27e9fd914734e38f64e6b880d
diff --git a/base/location.h b/base/location.h
index 42b3a92..523bfaf 100644
--- a/base/location.h
+++ b/base/location.h
@@ -10,12 +10,6 @@
#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 553efb1..f4baa45 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -486,20 +486,16 @@
HistogramEvent(kTaskRunEvent);
-#if defined(TRACK_ALL_TASK_OBJECTS)
- TimeTicks start_of_run = tracked_objects::ThreadData::Now();
-#endif // defined(TRACK_ALL_TASK_OBJECTS)
+ tracked_objects::TrackedTime start_time = tracked_objects::ThreadData::Now();
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,
- pending_task.time_posted, pending_task.delayed_run_time, start_of_run,
- tracked_objects::ThreadData::Now());
-#endif // defined(TRACK_ALL_TASK_OBJECTS)
+
+ tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ start_time, tracked_objects::ThreadData::Now());
nestable_tasks_allowed_ = true;
}
@@ -772,22 +768,31 @@
}
//------------------------------------------------------------------------------
-// MessageLoop::PendingTask
+// MessageLoop::TrackingInfo
+MessageLoop::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) {
+}
+
+MessageLoop::TrackingInfo::~TrackingInfo() {}
+
+//------------------------------------------------------------------------------
+// MessageLoop::PendingTask
MessageLoop::PendingTask::PendingTask(
const base::Closure& task,
const tracked_objects::Location& posted_from,
TimeTicks delayed_run_time,
bool nestable)
- : task(task),
- time_posted(TimeTicks::Now()),
- delayed_run_time(delayed_run_time),
+ : TrackingInfo(posted_from, delayed_run_time),
+ task(task),
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 732de03..aa7449f 100644
--- a/base/message_loop.h
+++ b/base/message_loop.h
@@ -44,11 +44,9 @@
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.
@@ -377,6 +375,22 @@
}
#endif // OS_WIN
+ // This structure is copied around by value.
+ struct TrackingInfo {
+ TrackingInfo(const tracked_objects::Location& posted_from,
+ base::TimeTicks delayed_run_time);
+ ~TrackingInfo();
+
+ // Counter for location where the Closure was posted from.
+ tracked_objects::Births* birth_tally;
+
+ // Time this PendingTask was posted.
+ base::TimeTicks time_posted;
+
+ // The time when the task should be run.
+ base::TimeTicks delayed_run_time;
+ };
+
//----------------------------------------------------------------------------
protected:
struct RunState {
@@ -410,7 +424,7 @@
#endif
// This structure is copied around by value.
- struct PendingTask {
+ struct PendingTask : public TrackingInfo {
PendingTask(const base::Closure& task,
const tracked_objects::Location& posted_from,
base::TimeTicks delayed_run_time,
@@ -423,17 +437,6 @@
// 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 2a8d999..d49f247 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -152,9 +152,7 @@
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 41fa01a..917565d 100644
--- a/base/threading/worker_pool_posix.cc
+++ b/base/threading/worker_pool_posix.cc
@@ -88,15 +88,14 @@
"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)
+ tracked_objects::TrackedTime start_time =
+ tracked_objects::ThreadData::Now();
+
pending_task.task.Run();
-#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)
+
+ tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking(
+ pending_task.birth_tally, pending_task.time_posted,
+ start_time, tracked_objects::ThreadData::Now());
}
// The WorkerThread is non-joinable, so it deletes itself.
@@ -122,10 +121,8 @@
const base::Closure& task)
: posted_from(posted_from),
task(task) {
-#if defined(TRACK_ALL_TASK_OBJECTS)
- post_births = tracked_objects::ThreadData::TallyABirthIfActive(posted_from);
+ birth_tally = 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 c0a60cc..2cc1150 100644
--- a/base/threading/worker_pool_posix.h
+++ b/base/threading/worker_pool_posix.h
@@ -53,13 +53,11 @@
const base::Closure& task);
~PendingTask();
-#if defined(TRACK_ALL_TASK_OBJECTS)
// Counter for location where the Closure was posted from.
- tracked_objects::Births* post_births;
+ tracked_objects::Births* birth_tally;
// Time the task was posted.
- TimeTicks time_posted;
-#endif // defined(TRACK_ALL_TASK_OBJECTS)
+ tracked_objects::TrackedTime time_posted;
const tracked_objects::Location posted_from;
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc
index dfb88eb..bdc4cfb 100644
--- a/base/tracked_objects.cc
+++ b/base/tracked_objects.cc
@@ -16,52 +16,43 @@
namespace tracked_objects {
-
-#if defined(TRACK_ALL_TASK_OBJECTS)
+namespace {
+// Flag to compile out almost all of the task tracking code.
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;
-
-// 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;
+// 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.
//------------------------------------------------------------------------------
// Death data tallies durations when a death takes place.
-void DeathData::RecordDeath(const TimeDelta& queue_duration,
- const TimeDelta& run_duration) {
+void DeathData::RecordDeath(const Duration& queue_duration,
+ const Duration& run_duration) {
++count_;
queue_duration_ += queue_duration;
run_duration_ += run_duration;
}
int DeathData::AverageMsRunDuration() const {
- if (run_duration_ == base::TimeDelta())
+ if (run_duration_ == Duration() || !count_)
return 0;
- return static_cast<int>(run_duration_.InMilliseconds() / count_);
+ // Add half of denominator to achieve rounding.
+ return static_cast<int>(run_duration_.InMilliseconds() + count_ / 2) /
+ count_;
}
int DeathData::AverageMsQueueDuration() const {
- if (queue_duration_ == base::TimeDelta())
+ if (queue_duration_ == Duration() || !count_)
return 0;
- return static_cast<int>(queue_duration_.InMilliseconds() / count_);
+ // Add half of denominator to achieve rounding.
+ return (static_cast<int>(queue_duration_.InMilliseconds() + count_ / 2) /
+ count_);
}
void DeathData::AddDeathData(const DeathData& other) {
@@ -75,11 +66,14 @@
return;
base::StringAppendF(output, "%s:%d, ",
(count_ == 1) ? "Life" : "Lives", count_);
- base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ",
- run_duration_.InMilliseconds(),
+ // 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()),
AverageMsRunDuration());
- base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ",
- queue_duration_.InMilliseconds(),
+ base::StringAppendF(output, "Queue:%dms(%dms/life) ",
+ static_cast<int>(queue_duration_.InMilliseconds()),
AverageMsQueueDuration());
}
@@ -95,8 +89,8 @@
void DeathData::Clear() {
count_ = 0;
- queue_duration_ = TimeDelta();
- run_duration_ = TimeDelta();
+ queue_duration_ = Duration();
+ run_duration_ = Duration();
}
//------------------------------------------------------------------------------
@@ -113,6 +107,20 @@
//------------------------------------------------------------------------------
// 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;
@@ -120,7 +128,7 @@
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;
@@ -136,7 +144,7 @@
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);
@@ -147,15 +155,15 @@
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 (!tls_index_.initialized())
- return; // For unittests only.
+ if (!Initialize()) // Always initialize if needed.
+ return;
DCHECK_EQ(tls_index_.Get(), reinterpret_cast<void*>(NULL));
ThreadData* current_thread_data = new ThreadData(suggested_name);
tls_index_.Set(current_thread_data);
@@ -172,7 +180,7 @@
// 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());
@@ -203,13 +211,13 @@
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::IsActive())
+ if (!ThreadData::tracking_status())
return; // Not yet initialized.
DataCollector collected_data; // Gather data.
@@ -316,13 +324,12 @@
}
// static
-base::Value* ThreadData::ToValue(int process_type) {
+base::DictionaryValue* ThreadData::ToValue() {
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;
}
@@ -342,8 +349,8 @@
}
void ThreadData::TallyADeath(const Births& birth,
- const TimeDelta& queue_duration,
- const TimeDelta& run_duration) {
+ const Duration& queue_duration,
+ const Duration& run_duration) {
DeathMap::iterator it = death_map_.find(&birth);
DeathData* death_data;
if (it != death_map_.end()) {
@@ -360,7 +367,7 @@
if (!kTrackAllTaskObjects)
return NULL; // Not compiled in.
- if (!IsActive())
+ if (!tracking_status())
return NULL;
ThreadData* current_thread_data = Get();
if (!current_thread_data)
@@ -369,37 +376,74 @@
}
// static
-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) {
+void ThreadData::TallyRunOnNamedThreadIfTracking(
+ const MessageLoop::TrackingInfo& completed_task,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run) {
if (!kTrackAllTaskObjects)
return; // Not compiled in.
- if (!IsActive() || !birth)
+ // 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)
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
+ // were scheduled (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 = end_of_run - start_of_run;
+ 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;
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_;
}
@@ -439,52 +483,47 @@
it->second->Clear();
}
-// static
-bool ThreadData::StartTracking(bool status) {
+bool ThreadData::Initialize() {
if (!kTrackAllTaskObjects)
return false; // Not compiled in.
-
- // 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
+ 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
// process termination.
- if (!tls_index_.initialized())
+ if (!tls_index_.initialized()) // Testing may have initialized this.
tls_index_.Initialize(&ThreadData::OnThreadTermination);
DCHECK(tls_index_.initialized());
-
- 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;
+ 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::IsActive() {
+bool ThreadData::InitializeAndSetTrackingStatus(bool status) {
+ if (!Initialize()) // No-op if already initialized.
+ return false; // Not compiled in.
+
+ status_ = status ? ACTIVE : DEACTIVATED;
+ return true;
+}
+
+// static
+bool ThreadData::tracking_status() {
return status_ == ACTIVE;
}
// static
-base::TimeTicks ThreadData::Now() {
- if (kTrackAllTaskObjects && status_ == ACTIVE)
- return base::TimeTicks::Now();
- return base::TimeTicks(); // Super fast when disabled, or not compiled in.
+TrackedTime ThreadData::Now() {
+ if (!kTrackAllTaskObjects || status_ != ACTIVE)
+ return TrackedTime(); // Super fast when disabled, or not compiled.
+ return TrackedTime::Now();
}
// static
@@ -492,12 +531,12 @@
// 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 (!StartTracking(false))
+ if (!InitializeAndSetTrackingStatus(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_;
@@ -574,15 +613,13 @@
return dictionary;
}
-void Snapshot::Add(const Snapshot& other) {
- death_data_.AddDeathData(other.death_data_);
-}
-
//------------------------------------------------------------------------------
// DataCollector
DataCollector::DataCollector() {
- if (!ThreadData::IsActive())
+ if (!kTrackAllTaskObjects)
+ return; // Not compiled in.
+ if (!ThreadData::tracking_status())
return;
// Get an unchanging copy of a ThreadData list.
@@ -740,6 +777,35 @@
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_) {
@@ -956,28 +1022,6 @@
}
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);
@@ -987,13 +1031,14 @@
}
std::string keyword(key_phrase.substr(0, equal_offset));
keyword = StringToLowerASCII(keyword);
- KeyMap::iterator it = key_map.find(keyword);
- if (key_map.end() == it)
- return; // Unknown keyword.
- if (it->second == RESET_ALL_DATA)
+ Selector selector = FindSelector(keyword);
+ if (selector == UNKNOWN_KEYWORD)
+ return;
+ if (selector == RESET_ALL_DATA) {
ThreadData::ResetAllThreadData();
- else
- SetTiebreaker(key_map[keyword], required);
+ return;
+ }
+ SetTiebreaker(selector, required);
}
bool Comparator::ParseQuery(const std::string& query) {
diff --git a/base/tracked_objects.h b/base/tracked_objects.h
index 1593991..f80db58 100644
--- a/base/tracked_objects.h
+++ b/base/tracked_objects.h
@@ -13,11 +13,16 @@
#include "base/base_export.h"
#include "base/location.h"
+#include "base/message_loop.h"
#include "base/time.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_local_storage.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
@@ -69,7 +74,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 TallyADeathIfActive() as appropriate.
+// TallyABirthIfActive() and TallyRunOnNamedThreadIfTracking() 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.
@@ -167,6 +172,96 @@
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 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;
@@ -217,8 +312,8 @@
};
//------------------------------------------------------------------------------
-// Basic info summarizing multiple destructions of an object with a single
-// birthplace (fixed Location). Used both on specific threads, and also used
+// Basic info summarizing multiple destructions of a tracked object with a
+// single birthplace (fixed Location). Used both on specific threads, and also
// in snapshots when integrating assembled data.
class BASE_EXPORT DeathData {
@@ -233,14 +328,14 @@
// 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);
+ void RecordDeath(const Duration& queue_duration,
+ const Duration& run_duration);
// Metrics accessors.
int count() const { return count_; }
- base::TimeDelta run_duration() const { return run_duration_; }
+ Duration run_duration() const { return run_duration_; }
int AverageMsRunDuration() const;
- base::TimeDelta queue_duration() const { return queue_duration_; }
+ Duration queue_duration() const { return queue_duration_; }
int AverageMsQueueDuration() const;
// Accumulate metrics from other into this. This method is never used on
@@ -250,7 +345,7 @@
// Simple print of internal state for use in line of HTML.
void WriteHTML(std::string* output) const;
- // Constructe a DictionaryValue instance containing all our stats. The caller
+ // Construct a DictionaryValue instance containing all our stats. The caller
// assumes ownership of the returned instance.
base::DictionaryValue* ToValue() const;
@@ -259,8 +354,8 @@
private:
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.
+ Duration run_duration_; // Sum of all Run()time durations.
+ Duration queue_duration_; // Sum of all queue time durations.
};
//------------------------------------------------------------------------------
@@ -287,13 +382,11 @@
const std::string DeathThreadName() const;
int count() const { return death_data_.count(); }
- base::TimeDelta run_duration() const { return death_data_.run_duration(); }
+ Duration run_duration() const { return death_data_.run_duration(); }
+ Duration queue_duration() const { return death_data_.queue_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();
}
@@ -305,8 +398,6 @@
// 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_;
@@ -326,20 +417,23 @@
DataCollector();
~DataCollector();
- // 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).
+ // Adds all stats from the indicated thread into our arrays. This function is
+ // uses locks at the lowest level (when accessing the underlying maps which
+ // could change when not locked), and can be called from any threads.
void Append(const ThreadData& thread_data);
// After the accumulation phase, the following accessor is used to process the
- // data.
+ // data (i.e., sort it, filter it, etc.).
Collection* collection();
- // After collection of death data is complete, we can add entries for all the
- // remaining living objects.
+ // 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().
void AddListOfLivingObjects();
- // Generate a ListValue representation of the vector of snapshots. The caller
+ // Generates a ListValue representation of the vector of snapshots. The caller
// assumes ownership of the memory in the returned instance.
base::ListValue* ToValue() const;
@@ -350,7 +444,8 @@
Collection collection_;
// The total number of births recorded at each location for which we have not
- // seen a death count.
+ // seen a death count. This map changes as we do Append() calls, and is later
+ // used by AddListOfLivingObjects() to gather up unaccounted for births.
BirthCount global_birth_count_;
DISALLOW_COPY_AND_ASSIGN(DataCollector);
@@ -359,6 +454,9 @@
//------------------------------------------------------------------------------
// Aggregation contains summaries (totals and subtotals) of groups of Snapshot
// instances to provide printing of these collections on a single line.
+// We generally provide a 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:
@@ -414,6 +512,7 @@
// Imediate action keywords.
RESET_ALL_DATA = -1,
+ UNKNOWN_KEYWORD = -2,
};
explicit Comparator();
@@ -470,6 +569,10 @@
// 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_;
@@ -493,16 +596,27 @@
//------------------------------------------------------------------------------
// 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 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.
+ // 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.
static void InitializeThreadContext(const std::string& suggested_name);
// Using Thread Local Store, find the current instance for collecting data.
@@ -515,33 +629,47 @@
// 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);
- // Find (or create) a place to count births from the given location in this
+ // 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
// thread, and increment that tally.
// TallyABirthIfActive will returns NULL if the birth cannot be tallied.
static Births* TallyABirthIfActive(const Location& location);
- // Record the end of a timed run of an object. The |birth| is the record for
+ // Records the end of a timed run of an object. 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.
+ // 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 calculated remotely to help with testing.
+ static void TallyRunOnNamedThreadIfTracking(
+ const MessageLoop::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 |end_of_run| was just obtained by a call to Now() (just after the task
// finished).
- 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);
+ static void TallyRunOnWorkerThreadIfTracking(
+ const Births* birth,
+ const TrackedTime& time_posted,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run);
const std::string thread_name() const { return thread_name_; }
@@ -567,17 +695,21 @@
// bogus counts VERY rarely.
static void ResetAllThreadData();
- // Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN,
+ // 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,
// 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();
+ // If tracking is not compiled in, this function will return false.
+ static bool InitializeAndSetTrackingStatus(bool status);
+ static bool tracking_status();
// 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();
+ static TrackedTime Now();
// WARNING: ONLY call this function when you are running single threaded
// (again) and all message loops and threads have terminated. Until that
@@ -587,14 +719,6 @@
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.
@@ -614,8 +738,8 @@
// Find a place to record a death on this thread.
void TallyADeath(const Births& birth,
- const base::TimeDelta& queue_duration,
- const base::TimeDelta& duration);
+ const Duration& queue_duration,
+ const Duration& duration);
// Using our lock to protect the iteration, Clear all birth and death data.
void Reset();
@@ -640,8 +764,8 @@
// 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_.
- static base::Lock list_lock_;
+ // unregistered_thread_data_pool_. This lock is leaked at shutdown.
+ static base::Lock* list_lock_;
// We set status_ to SHUTDOWN when we shut down the tracking service.
static Status status_;
@@ -695,21 +819,17 @@
class BASE_EXPORT AutoTracking {
public:
AutoTracking() {
- if (state_ != kNeverBeenRun)
- return;
- ThreadData::StartTracking(true);
- state_ = kRunning;
+ ThreadData::Initialize();
}
~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 ef88183..15c88e8 100644
--- a/base/tracked_objects_unittest.cc
+++ b/base/tracked_objects_unittest.cc
@@ -15,15 +15,17 @@
class TrackedObjectsTest : public testing::Test {
public:
- ~TrackedObjectsTest() {
- ThreadData::ShutdownSingleThreadedCleanup();
- }
+ TrackedObjectsTest() {
+ }
+ ~TrackedObjectsTest() {
+ ThreadData::ShutdownSingleThreadedCleanup();
+ }
};
TEST_F(TrackedObjectsTest, MinimalStartupShutdown) {
// Minimal test doesn't even create any tasks.
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
EXPECT_FALSE(ThreadData::first()); // No activity even on this thread.
@@ -41,7 +43,7 @@
ThreadData::ShutdownSingleThreadedCleanup();
// Do it again, just to be sure we reset state completely.
- ThreadData::StartTracking(true);
+ ThreadData::InitializeAndSetTrackingStatus(true);
EXPECT_FALSE(ThreadData::first()); // No activity even on this thread.
data = ThreadData::Get();
EXPECT_TRUE(ThreadData::first()); // Now class was constructed.
@@ -57,7 +59,7 @@
}
TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
// Instigate tracking on a single tracked object, on our thread.
@@ -77,14 +79,14 @@
EXPECT_EQ(0u, death_map.size()); // No deaths.
- // 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 */ );
+ // Now instigate another birth, and a first death at the same location.
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TimeTicks kBogusStartTime;
+ MessageLoop::TrackingInfo pending_task(location, kBogusStartTime);
+ TrackedTime kBogusStartRunTime;
+ TrackedTime kBogusEndRunTime;
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, kBogusStartRunTime,
+ kBogusEndRunTime);
birth_map.clear();
data->SnapshotBirthMap(&birth_map);
@@ -100,13 +102,13 @@
}
TEST_F(TrackedObjectsTest, DeathDataTest) {
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
scoped_ptr<DeathData> data(new DeathData());
ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL));
- EXPECT_EQ(data->run_duration(), base::TimeDelta());
- EXPECT_EQ(data->queue_duration(), base::TimeDelta());
+ EXPECT_EQ(data->run_duration(), Duration());
+ EXPECT_EQ(data->queue_duration(), Duration());
EXPECT_EQ(data->AverageMsRunDuration(), 0);
EXPECT_EQ(data->AverageMsQueueDuration(), 0);
EXPECT_EQ(data->count(), 0);
@@ -114,8 +116,8 @@
int run_ms = 42;
int queue_ms = 8;
- base::TimeDelta run_duration = base::TimeDelta().FromMilliseconds(run_ms);
- base::TimeDelta queue_duration = base::TimeDelta().FromMilliseconds(queue_ms);
+ Duration run_duration = Duration().FromMilliseconds(run_ms);
+ Duration queue_duration = Duration().FromMilliseconds(queue_ms);
data->RecordDeath(queue_duration, run_duration);
EXPECT_EQ(data->run_duration(), run_duration);
EXPECT_EQ(data->queue_duration(), queue_duration);
@@ -152,19 +154,18 @@
}
TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) {
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
// We don't initialize system with a thread name, so we're viewed as a worker
// thread.
- int fake_line_number = 173;
+ const int kFakeLineNumber = 173;
const char* kFile = "FixedFileName";
const char* kFunction = "BirthOnlyToValueWorkerThread";
- Location location(kFunction, kFile, fake_line_number, NULL);
+ Location location(kFunction, kFile, kFakeLineNumber, NULL);
Births* birth = ThreadData::TallyABirthIfActive(location);
EXPECT_NE(birth, reinterpret_cast<Births*>(NULL));
- int process_type = 3;
- scoped_ptr<base::Value> value(ThreadData::ToValue(process_type));
+ scoped_ptr<base::Value> value(ThreadData::ToValue());
std::string json;
base::JSONWriter::Write(value.get(), false, &json);
std::string birth_only_result = "{"
@@ -183,28 +184,26 @@
"\"line_number\":173"
"}"
"}"
- "],"
- "\"process\":3"
+ "]"
"}";
EXPECT_EQ(json, birth_only_result);
}
TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) {
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
// Use a well named thread.
ThreadData::InitializeThreadContext("SomeMainThreadName");
- int fake_line_number = 173;
+ const int kFakeLineNumber = 173;
const char* kFile = "FixedFileName";
const char* kFunction = "BirthOnlyToValueMainThread";
- Location location(kFunction, kFile, fake_line_number, NULL);
+ 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));
- int process_type = 34;
- scoped_ptr<base::Value> value(ThreadData::ToValue(process_type));
+ scoped_ptr<base::Value> value(ThreadData::ToValue());
std::string json;
base::JSONWriter::Write(value.get(), false, &json);
std::string birth_only_result = "{"
@@ -223,40 +222,39 @@
"\"line_number\":173"
"}"
"}"
- "],"
- "\"process\":34"
+ "]"
"}";
EXPECT_EQ(json, birth_only_result);
}
TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) {
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
// Use a well named thread.
ThreadData::InitializeThreadContext("SomeMainThreadName");
- int fake_line_number = 236;
+ const int kFakeLineNumber = 236;
const char* kFile = "FixedFileName";
const char* kFunction = "LifeCycleToValueMainThread";
- Location location(kFunction, kFile, fake_line_number, NULL);
+ 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));
- // 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);
+ const base::TimeTicks kTimePosted = base::TimeTicks()
+ + base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ MessageLoop::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
- int process_type = 7;
- scoped_ptr<base::Value> value(ThreadData::ToValue(process_type));
+ 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 = "{"
@@ -275,44 +273,93 @@
"\"line_number\":236"
"}"
"}"
- "],"
- "\"process\":7"
+ "]"
+ "}";
+ EXPECT_EQ(json, one_line_result);
+}
+
+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(json, one_line_result);
}
TEST_F(TrackedObjectsTest, TwoLives) {
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
// Use a well named thread.
ThreadData::InitializeThreadContext("SomeFileThreadName");
- int fake_line_number = 222;
+ const int kFakeLineNumber = 222;
const char* kFile = "AnotherFileName";
const char* kFunction = "TwoLives";
- Location location(kFunction, kFile, fake_line_number, NULL);
+ 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));
- // 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);
+ const base::TimeTicks kTimePosted = base::TimeTicks()
+ + base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ MessageLoop::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
- int process_type = 7;
- scoped_ptr<base::Value> value(ThreadData::ToValue(process_type));
+ 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.
+ MessageLoop::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());
std::string json;
base::JSONWriter::Write(value.get(), false, &json);
std::string one_line_result = "{"
@@ -331,44 +378,43 @@
"\"line_number\":222"
"}"
"}"
- "],"
- "\"process\":7"
+ "]"
"}";
EXPECT_EQ(json, one_line_result);
}
TEST_F(TrackedObjectsTest, DifferentLives) {
- if (!ThreadData::StartTracking(true))
+ if (!ThreadData::InitializeAndSetTrackingStatus(true))
return;
// Use a well named thread.
ThreadData::InitializeThreadContext("SomeFileThreadName");
- int fake_line_number = 567;
+ const int kFakeLineNumber = 567;
const char* kFile = "AnotherFileName";
const char* kFunction = "DifferentLives";
- 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));
+ Location location(kFunction, kFile, kFakeLineNumber, NULL);
- // 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);
+ const base::TimeTicks kTimePosted = base::TimeTicks()
+ + base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ MessageLoop::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
- int second_fake_line_number = 999;
- Location second_location(kFunction, kFile, second_fake_line_number, NULL);
- birth = ThreadData::TallyABirthIfActive(second_location);
+ const TrackedTime kStartOfRun = TrackedTime() +
+ Duration::FromMilliseconds(5);
+ const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7);
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ kStartOfRun, kEndOfRun);
- int process_type = 2;
- scoped_ptr<base::Value> value(ThreadData::ToValue(process_type));
+ const int kSecondFakeLineNumber = 999;
+ Location second_location(kFunction, kFile, kSecondFakeLineNumber, NULL);
+
+ // TrackingInfo will call TallyABirth() during construction.
+ MessageLoop::TrackingInfo pending_task2(second_location, kDelayedStartTime);
+ pending_task2.time_posted = kTimePosted; // Overwrite implied Now().
+
+ scoped_ptr<base::Value> value(ThreadData::ToValue());
std::string json;
base::JSONWriter::Write(value.get(), false, &json);
std::string one_line_result = "{"
@@ -401,8 +447,7 @@
"\"line_number\":999"
"}"
"}"
- "],"
- "\"process\":2"
+ "]"
"}";
EXPECT_EQ(json, one_line_result);
}