Index: base/tracked_objects.cc |
=================================================================== |
--- base/tracked_objects.cc (revision 107521) |
+++ base/tracked_objects.cc (working copy) |
@@ -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; |
+// 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 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,14 +107,28 @@ |
//------------------------------------------------------------------------------ |
// 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; |
@@ -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,14 @@ |
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. |
+ Initialize(); // Always initialize if needed. |
ramant (doing other things)
2011/10/28 17:56:09
nit: if not initialized bail.
jar (doing other things)
2011/10/28 19:37:25
Done.
|
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 +179,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 +210,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 +323,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 +348,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 +366,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 +375,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 +482,47 @@ |
it->second->Clear(); |
} |
+void ThreadData::Initialize() { |
+ if (status_ != UNINITIALIZED) |
ramant (doing other things)
2011/10/28 17:56:09
nit: return false if not enabled.
jar (doing other things)
2011/10/28 19:37:25
Done.
|
+ return; |
+ // 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()) // Testing may have initialized this. |
+ 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; |
+} |
+ |
// static |
-bool ThreadData::StartTracking(bool status) { |
+bool ThreadData::InitializeAndSetTrackingStatus(bool status) { |
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. |
- } |
+ if (status_ == UNINITIALIZED) |
+ Initialize(); |
- // Perform the "real" initialization now, and leave it intact through |
- // process termination. |
- if (!tls_index_.initialized()) |
- 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; |
+ status_ = status ? ACTIVE : DEACTIVATED; |
return true; |
} |
// static |
-bool ThreadData::IsActive() { |
+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 +530,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 +612,11 @@ |
return dictionary; |
} |
-void Snapshot::Add(const Snapshot& other) { |
- death_data_.AddDeathData(other.death_data_); |
-} |
- |
//------------------------------------------------------------------------------ |
// DataCollector |
DataCollector::DataCollector() { |
- if (!ThreadData::IsActive()) |
+ if (!ThreadData::tracking_status()) |
return; |
// Get an unchanging copy of a ThreadData list. |
@@ -740,6 +774,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 +1019,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 +1028,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) { |