summaryrefslogtreecommitdiffstats
path: root/base/tracked_objects.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/tracked_objects.cc')
-rw-r--r--base/tracked_objects.cc308
1 files changed, 131 insertions, 177 deletions
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) {