Index: base/tracked_objects.cc |
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc |
index 1db578e38157c5f3a9896d14fb4067f2014f39da..38b9320eb5a653d6db254633f7483bd6d8d9c00a 100644 |
--- a/base/tracked_objects.cc |
+++ b/base/tracked_objects.cc |
@@ -8,11 +8,12 @@ |
#include <stdlib.h> |
#include "base/format_macros.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/process_util.h" |
#include "base/profiler/alternate_timer.h" |
#include "base/stringprintf.h" |
#include "base/third_party/valgrind/memcheck.h" |
#include "base/threading/thread_restrictions.h" |
-#include "build/build_config.h" |
#include "base/port.h" |
using base::TimeDelta; |
@@ -24,6 +25,8 @@ namespace { |
// Flag to compile out almost all of the task tracking code. |
const bool kTrackAllTaskObjects = true; |
+// TODO(jar): Evaluate the perf impact of enabling this. If the perf impact is |
+// negligible, enable by default. |
// Flag to compile out parent-child link recording. |
const bool kTrackParentChildLinks = false; |
@@ -44,6 +47,7 @@ const ThreadData::Status kInitialStartupState = |
// can be flipped to efficiently disable this path (if there is a performance |
// problem with its presence). |
static const bool kAllowAlternateTimeSourceHandling = true; |
+ |
} // namespace |
//------------------------------------------------------------------------------ |
@@ -113,25 +117,6 @@ int32 DeathData::queue_duration_sample() const { |
return queue_duration_sample_; |
} |
- |
-base::DictionaryValue* DeathData::ToValue() const { |
- base::DictionaryValue* dictionary = new base::DictionaryValue; |
- dictionary->Set("count", base::Value::CreateIntegerValue(count_)); |
- dictionary->Set("run_ms", |
- base::Value::CreateIntegerValue(run_duration_sum())); |
- dictionary->Set("run_ms_max", |
- base::Value::CreateIntegerValue(run_duration_max())); |
- dictionary->Set("run_ms_sample", |
- base::Value::CreateIntegerValue(run_duration_sample())); |
- dictionary->Set("queue_ms", |
- base::Value::CreateIntegerValue(queue_duration_sum())); |
- dictionary->Set("queue_ms_max", |
- base::Value::CreateIntegerValue(queue_duration_max())); |
- dictionary->Set("queue_ms_sample", |
- base::Value::CreateIntegerValue(queue_duration_sample())); |
- return dictionary; |
-} |
- |
void DeathData::ResetMax() { |
run_duration_max_ = 0; |
queue_duration_max_ = 0; |
@@ -148,20 +133,48 @@ void DeathData::Clear() { |
} |
//------------------------------------------------------------------------------ |
+DeathDataSnapshot::DeathDataSnapshot() |
+ : count(-1), |
+ run_duration_sum(-1), |
+ run_duration_max(-1), |
+ run_duration_sample(-1), |
+ queue_duration_sum(-1), |
+ queue_duration_max(-1), |
+ queue_duration_sample(-1) { |
+} |
+ |
+DeathDataSnapshot::DeathDataSnapshot( |
+ const tracked_objects::DeathData& death_data) |
+ : count(death_data.count()), |
+ run_duration_sum(death_data.run_duration_sum()), |
+ run_duration_max(death_data.run_duration_max()), |
+ run_duration_sample(death_data.run_duration_sample()), |
+ queue_duration_sum(death_data.queue_duration_sum()), |
+ queue_duration_max(death_data.queue_duration_max()), |
+ queue_duration_sample(death_data.queue_duration_sample()) { |
+} |
+ |
+DeathDataSnapshot::~DeathDataSnapshot() { |
+} |
+ |
+//------------------------------------------------------------------------------ |
BirthOnThread::BirthOnThread(const Location& location, |
const ThreadData& current) |
: location_(location), |
birth_thread_(¤t) { |
} |
-const Location BirthOnThread::location() const { return location_; } |
-const ThreadData* BirthOnThread::birth_thread() const { return birth_thread_; } |
+//------------------------------------------------------------------------------ |
+BirthOnThreadSnapshot::BirthOnThreadSnapshot() { |
+} |
+ |
+BirthOnThreadSnapshot::BirthOnThreadSnapshot( |
+ const tracked_objects::BirthOnThread& birth) |
+ : location(birth.location()), |
+ thread_name(birth.birth_thread()->thread_name()) { |
+} |
-void BirthOnThread::ToValue(const std::string& prefix, |
- base::DictionaryValue* dictionary) const { |
- dictionary->Set(prefix + "_location", location_.ToValue()); |
- dictionary->Set(prefix + "_thread", |
- base::Value::CreateStringValue(birth_thread_->thread_name())); |
+BirthOnThreadSnapshot::~BirthOnThreadSnapshot() { |
} |
//------------------------------------------------------------------------------ |
@@ -334,14 +347,23 @@ void ThreadData::OnThreadTerminationCleanup() { |
} |
// static |
-base::DictionaryValue* ThreadData::ToValue(bool reset_max) { |
- DataCollector collected_data; // Gather data. |
- // Request multiple calls to collected_data.Append() for all threads. |
- SendAllMaps(reset_max, &collected_data); |
- collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
- base::DictionaryValue* dictionary = new base::DictionaryValue(); |
- collected_data.ToValue(dictionary); |
- return dictionary; |
+void ThreadData::Snapshot(bool reset_max, ProcessDataSnapshot* process_data) { |
+ // Add births that have run to completion to |collected_data|. |
+ // |birth_counts| tracks the total number of births recorded at each location |
+ // for which we have not seen a death count. |
+ BirthCountMap birth_counts; |
+ ThreadData::SnapshotAllExecutedTasks(reset_max, process_data, &birth_counts); |
+ |
+ // Add births that are still active -- i.e. 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. |
+ for (BirthCountMap::const_iterator it = birth_counts.begin(); |
+ it != birth_counts.end(); ++it) { |
+ if (it->second > 0) { |
+ process_data->tasks.push_back( |
+ TaskSnapshot(*it->first, DeathData(it->second), "Still_Alive")); |
+ } |
+ } |
} |
Births* ThreadData::TallyABirth(const Location& location) { |
@@ -527,6 +549,60 @@ void ThreadData::TallyRunInAScopedRegionIfTracking( |
const std::string ThreadData::thread_name() const { return thread_name_; } |
+// static |
+void ThreadData::SnapshotAllExecutedTasks(bool reset_max, |
+ ProcessDataSnapshot* process_data, |
+ BirthCountMap* birth_counts) { |
+ if (!kTrackAllTaskObjects) |
+ return; // Not compiled in. |
+ |
+ // Get an unchanging copy of a ThreadData list. |
+ ThreadData* my_list = ThreadData::first(); |
+ |
+ // Gather data serially. |
+ // This hackish approach *can* get some slighly corrupt tallies, as we are |
+ // grabbing values without the protection of a lock, but it has the advantage |
+ // of working even with threads that don't have message loops. If a user |
+ // sees any strangeness, they can always just run their stats gathering a |
+ // second time. |
+ for (ThreadData* thread_data = my_list; |
+ thread_data; |
+ thread_data = thread_data->next()) { |
+ thread_data->SnapshotExecutedTasks(reset_max, process_data, birth_counts); |
+ } |
+} |
+ |
+void ThreadData::SnapshotExecutedTasks(bool reset_max, |
+ ProcessDataSnapshot* process_data, |
+ BirthCountMap* birth_counts) { |
+ // Get copy of data, so that the data will not change during the iterations |
+ // and processing. |
+ ThreadData::BirthMap birth_map; |
+ ThreadData::DeathMap death_map; |
+ ThreadData::ParentChildSet parent_child_set; |
+ SnapshotMaps(reset_max, &birth_map, &death_map, &parent_child_set); |
+ |
+ for (ThreadData::DeathMap::const_iterator it = death_map.begin(); |
+ it != death_map.end(); ++it) { |
+ process_data->tasks.push_back( |
+ TaskSnapshot(*it->first, it->second, thread_name())); |
+ (*birth_counts)[it->first] -= it->first->birth_count(); |
+ } |
+ |
+ for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); |
+ it != birth_map.end(); ++it) { |
+ (*birth_counts)[it->second] += it->second->birth_count(); |
+ } |
+ |
+ if (!kTrackParentChildLinks) |
+ return; |
+ |
+ for (ThreadData::ParentChildSet::const_iterator it = parent_child_set.begin(); |
+ it != parent_child_set.end(); ++it) { |
+ process_data->descendants.push_back(ParentChildPairSnapshot(*it)); |
+ } |
+} |
+ |
// This may be called from another thread. |
void ThreadData::SnapshotMaps(bool reset_max, |
BirthMap* birth_map, |
@@ -552,32 +628,6 @@ void ThreadData::SnapshotMaps(bool reset_max, |
} |
// static |
-void ThreadData::SendAllMaps(bool reset_max, class DataCollector* target) { |
- if (!kTrackAllTaskObjects) |
- return; // Not compiled in. |
- // Get an unchanging copy of a ThreadData list. |
- ThreadData* my_list = ThreadData::first(); |
- |
- // Gather data serially. |
- // This hackish approach *can* get some slighly corrupt tallies, as we are |
- // grabbing values without the protection of a lock, but it has the advantage |
- // of working even with threads that don't have message loops. If a user |
- // sees any strangeness, they can always just run their stats gathering a |
- // second time. |
- for (ThreadData* thread_data = my_list; |
- thread_data; |
- thread_data = thread_data->next()) { |
- // Get copy of data. |
- ThreadData::BirthMap birth_map; |
- ThreadData::DeathMap death_map; |
- ThreadData::ParentChildSet parent_child_set; |
- thread_data->SnapshotMaps(reset_max, &birth_map, &death_map, |
- &parent_child_set); |
- target->Append(*thread_data, birth_map, death_map, parent_child_set); |
- } |
-} |
- |
-// static |
void ThreadData::ResetAllThreadData() { |
ThreadData* my_list = first(); |
@@ -785,107 +835,43 @@ void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { |
} |
//------------------------------------------------------------------------------ |
-// Individual 3-tuple of birth (place and thread) along with death thread, and |
-// the accumulated stats for instances (DeathData). |
- |
-Snapshot::Snapshot(const BirthOnThread& birth_on_thread, |
- const ThreadData& death_thread, |
- const DeathData& death_data) |
- : birth_(&birth_on_thread), |
- death_thread_(&death_thread), |
- death_data_(death_data) { |
+TaskSnapshot::TaskSnapshot() { |
} |
-Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) |
- : birth_(&birth_on_thread), |
- death_thread_(NULL), |
- death_data_(DeathData(count)) { |
+TaskSnapshot::TaskSnapshot(const BirthOnThread& birth, |
+ const DeathData& death_data, |
+ const std::string& death_thread_name) |
+ : birth(birth), |
+ death_data(death_data), |
+ death_thread_name(death_thread_name) { |
} |
-const std::string Snapshot::DeathThreadName() const { |
- if (death_thread_) |
- return death_thread_->thread_name(); |
- return "Still_Alive"; |
-} |
- |
-base::DictionaryValue* Snapshot::ToValue() const { |
- base::DictionaryValue* dictionary = new base::DictionaryValue; |
- // TODO(jar): Switch the next two lines to: |
- // birth_->ToValue("birth", dictionary); |
- // ...but that will require fixing unit tests, and JS to take |
- // "birth_location" rather than "location" |
- dictionary->Set("birth_thread", |
- base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); |
- dictionary->Set("location", birth_->location().ToValue()); |
- |
- dictionary->Set("death_data", death_data_.ToValue()); |
- dictionary->Set("death_thread", |
- base::Value::CreateStringValue(DeathThreadName())); |
- return dictionary; |
+TaskSnapshot::~TaskSnapshot() { |
} |
//------------------------------------------------------------------------------ |
-// DataCollector |
+// ParentChildPairSnapshot |
-DataCollector::DataCollector() {} |
- |
-DataCollector::~DataCollector() { |
+ParentChildPairSnapshot::ParentChildPairSnapshot(){ |
} |
-void DataCollector::Append(const ThreadData& thread_data, |
- const ThreadData::BirthMap& birth_map, |
- const ThreadData::DeathMap& death_map, |
- const ThreadData::ParentChildSet& parent_child_set) { |
- for (ThreadData::DeathMap::const_iterator it = death_map.begin(); |
- it != death_map.end(); ++it) { |
- collection_.push_back(Snapshot(*it->first, thread_data, it->second)); |
- global_birth_count_[it->first] -= it->first->birth_count(); |
- } |
- |
- for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); |
- it != birth_map.end(); ++it) { |
- global_birth_count_[it->second] += it->second->birth_count(); |
- } |
- |
- if (!kTrackParentChildLinks) |
- return; |
- |
- for (ThreadData::ParentChildSet::const_iterator it = parent_child_set.begin(); |
- it != parent_child_set.end(); ++it) { |
- parent_child_set_.insert(*it); |
- } |
+ParentChildPairSnapshot::ParentChildPairSnapshot( |
+ const ThreadData::ParentChildPair& parent_child) |
+ : parent(*parent_child.first), |
+ child(*parent_child.second) { |
} |
-DataCollector::Collection* DataCollector::collection() { |
- return &collection_; |
+ParentChildPairSnapshot::~ParentChildPairSnapshot() { |
} |
-void DataCollector::AddListOfLivingObjects() { |
- for (BirthCount::iterator it = global_birth_count_.begin(); |
- it != global_birth_count_.end(); ++it) { |
- if (it->second > 0) |
- collection_.push_back(Snapshot(*it->first, it->second)); |
- } |
+//------------------------------------------------------------------------------ |
+// ProcessDataSnapshot |
+ |
+ProcessDataSnapshot::ProcessDataSnapshot() |
+ : process_id(base::GetCurrentProcId()) { |
} |
-void DataCollector::ToValue(base::DictionaryValue* dictionary) const { |
- base::ListValue* list = new base::ListValue; |
- for (size_t i = 0; i < collection_.size(); ++i) { |
- list->Append(collection_[i].ToValue()); |
- } |
- dictionary->Set("list", list); |
- |
- base::ListValue* descendants = new base::ListValue; |
- for (ThreadData::ParentChildSet::const_iterator it = |
- parent_child_set_.begin(); |
- it != parent_child_set_.end(); |
- ++it) { |
- base::DictionaryValue* parent_child = new base::DictionaryValue; |
- it->first->ToValue("parent", parent_child); |
- it->second->ToValue("child", parent_child); |
- descendants->Append(parent_child); |
- } |
- dictionary->Set("descendants", descendants); |
+ProcessDataSnapshot::~ProcessDataSnapshot() { |
} |
} // namespace tracked_objects |