Index: base/tracked_objects.cc |
=================================================================== |
--- base/tracked_objects.cc (revision 107960) |
+++ base/tracked_objects.cc (working copy) |
@@ -16,43 +16,52 @@ |
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 |
-// 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. |
+// 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; |
+ |
//------------------------------------------------------------------------------ |
// 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 @@ |
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 @@ |
void DeathData::Clear() { |
count_ = 0; |
- queue_duration_ = Duration(); |
- run_duration_ = Duration(); |
+ queue_duration_ = TimeDelta(); |
+ run_duration_ = TimeDelta(); |
} |
//------------------------------------------------------------------------------ |
@@ -107,28 +113,14 @@ |
//------------------------------------------------------------------------------ |
// 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; |
// static |
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() : 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 @@ |
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 @@ |
// 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 @@ |
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 @@ |
} |
// 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 @@ |
} |
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 @@ |
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 @@ |
} |
// 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; |
+ 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 |
-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_; |
} |
@@ -486,47 +439,52 @@ |
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 @@ |
// 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 @@ |
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 @@ |
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::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 @@ |
} |
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) { |