diff options
34 files changed, 1150 insertions, 1054 deletions
diff --git a/base/location.cc b/base/location.cc index 275efa5..3422138 100644 --- a/base/location.cc +++ b/base/location.cc @@ -14,7 +14,6 @@ extern "C" { #include "base/location.h" #include "base/string_number_conversions.h" #include "base/stringprintf.h" -#include "base/values.h" namespace tracked_objects { @@ -72,18 +71,21 @@ void Location::WriteFunctionName(std::string* output) const { } } -base::DictionaryValue* Location::ToValue() const { - base::DictionaryValue* dictionary = new base::DictionaryValue; - dictionary->Set("file_name", base::Value::CreateStringValue(file_name_)); - // Note: This function name is not escaped, and templates have less than - // characters, which means this is not suitable for display as HTML unless - // properly escaped. - dictionary->Set("function_name", - base::Value::CreateStringValue(function_name_)); - dictionary->Set("line_number", base::Value::CreateIntegerValue(line_number_)); - return dictionary; +//------------------------------------------------------------------------------ +LocationSnapshot::LocationSnapshot() : line_number(-1) { } +LocationSnapshot::LocationSnapshot( + const tracked_objects::Location& location) + : file_name(location.file_name()), + function_name(location.function_name()), + line_number(location.line_number()) { +} + +LocationSnapshot::~LocationSnapshot() { +} + +//------------------------------------------------------------------------------ #if defined(COMPILER_MSVC) __declspec(noinline) #endif diff --git a/base/location.h b/base/location.h index fc9d360..05a4f66 100644 --- a/base/location.h +++ b/base/location.h @@ -8,10 +8,7 @@ #include <string> #include "base/base_export.h" - -namespace base { -class DictionaryValue; -} +#include "base/basictypes.h" namespace tracked_objects { @@ -61,10 +58,6 @@ class BASE_EXPORT Location { // Write function_name_ in HTML with '<' and '>' properly encoded. void WriteFunctionName(std::string* output) const; - // Construct a Value* representation. The caller assumes ownership of the - // memory in the returned instance. - base::DictionaryValue* ToValue() const; - private: const char* function_name_; const char* file_name_; @@ -72,6 +65,19 @@ class BASE_EXPORT Location { const void* program_counter_; }; +// A "snapshotted" representation of the Location class that can safely be +// passed across process boundaries. +struct BASE_EXPORT LocationSnapshot { + // The default constructor is exposed to support the IPC serialization macros. + LocationSnapshot(); + explicit LocationSnapshot(const tracked_objects::Location& location); + ~LocationSnapshot(); + + std::string file_name; + std::string function_name; + int line_number; +}; + BASE_EXPORT const void* GetProgramCounter(); // Define a macro to record the current source location. diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index 1db578e..38b9320 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 diff --git a/base/tracked_objects.h b/base/tracked_objects.h index e09b70b30..25b8753 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h @@ -23,7 +23,6 @@ #include "base/synchronization/lock.h" #include "base/threading/thread_local_storage.h" #include "base/tracking_info.h" -#include "base/values.h" // TrackedObjects provides a database of stats about objects (generally Tasks) // that are tracked. Tracking means their birth, death, duration, birth thread, @@ -129,33 +128,36 @@ // be able to run concurrently with ongoing augmentation of the birth and death // data. // -// For a given birth location, information about births is spread across data -// structures that are asynchronously changing on various threads. For display -// purposes, we need to construct Snapshot instances for each combination of -// birth thread, death thread, and location, along with the count of such -// lifetimes. We gather such data into a Snapshot instances, so that such -// instances can be sorted and aggregated (and remain frozen during our -// processing). Snapshot instances use pointers to constant portions of the -// birth and death datastructures, but have local (frozen) copies of the actual -// statistics (birth count, durations, etc. etc.). +// This header also exports collection of classes that provide "snapshotted" +// representations of the core tracked_objects:: classes. These snapshotted +// representations are designed for safe transmission of the tracked_objects:: +// data across process boundaries. Each consists of: +// (1) a default constructor, to support the IPC serialization macros, +// (2) a constructor that extracts data from the type being snapshotted, and +// (3) the snapshotted data. // -// A DataCollector is a container object that holds a set of Snapshots. The -// statistics in a snapshot are gathered asynhcronously relative to their -// ongoing updates. It is possible, though highly unlikely, that stats could be -// incorrectly recorded by this process (all data is held in 32 bit ints, but we -// are not atomically collecting all data, so we could have count that does not, -// for example, match with the number of durations we accumulated). The -// advantage to having fast (non-atomic) updates of the data outweighs the -// minimal risk of a singular corrupt statistic snapshot (only the snapshot -// could be corrupt, not the underlying and ongoing statistic). In constrast, -// pointer data that is accessed during snapshotting is completely invariant, -// and hence is perfectly acquired (i.e., no potential corruption, and no risk -// of a bad memory reference). +// For a given birth location, information about births is spread across data +// structures that are asynchronously changing on various threads. For +// serialization and display purposes, we need to construct TaskSnapshot +// instances for each combination of birth thread, death thread, and location, +// along with the count of such lifetimes. We gather such data into a +// TaskSnapshot instances, so that such instances can be sorted and +// aggregated (and remain frozen during our processing). // -// After an array of Snapshots instances are collected into a DataCollector, -// they need to be prepared for displaying our output. We currently implement a -// serialization into a Value hierarchy, which is automatically translated to -// JSON when supplied to rendering Java Scirpt. +// The ProcessDataSnapshot struct is a serialized representation of the list +// of ThreadData objects for a process. It holds a set of TaskSnapshots +// and tracks parent/child relationships for the executed tasks. The statistics +// in a snapshot are gathered asynhcronously relative to their ongoing updates. +// It is possible, though highly unlikely, that stats could be incorrectly +// recorded by this process (all data is held in 32 bit ints, but we are not +// atomically collecting all data, so we could have count that does not, for +// example, match with the number of durations we accumulated). The advantage +// to having fast (non-atomic) updates of the data outweighs the minimal risk of +// a singular corrupt statistic snapshot (only the snapshot could be corrupt, +// not the underlying and ongoing statistic). In constrast, pointer data that +// is accessed during snapshotting is completely invariant, and hence is +// perfectly acquired (i.e., no potential corruption, and no risk of a bad +// memory reference). // // TODO(jar): We can implement a Snapshot system that *tries* to grab the // snapshots on the source threads *when* they have MessageLoops available @@ -196,14 +198,8 @@ class BASE_EXPORT BirthOnThread { public: BirthOnThread(const Location& location, const ThreadData& current); - const Location location() const; - const ThreadData* birth_thread() const; - - // Insert our state (location, and thread name) into the dictionary. - // Use the supplied |prefix| in front of "thread_name" and "location" - // respectively when defining keys. - void ToValue(const std::string& prefix, - base::DictionaryValue* dictionary) const; + const Location location() const { return location_; } + const ThreadData* birth_thread() const { return birth_thread_; } private: // File/lineno of birth. This defines the essence of the task, as the context @@ -219,6 +215,18 @@ class BASE_EXPORT BirthOnThread { }; //------------------------------------------------------------------------------ +// A "snapshotted" representation of the BirthOnThread class. + +struct BASE_EXPORT BirthOnThreadSnapshot { + BirthOnThreadSnapshot(); + explicit BirthOnThreadSnapshot(const BirthOnThread& birth); + ~BirthOnThreadSnapshot(); + + LocationSnapshot location; + std::string thread_name; +}; + +//------------------------------------------------------------------------------ // A class for accumulating counts of births (without bothering with a map<>). class BASE_EXPORT Births: public BirthOnThread { @@ -227,7 +235,7 @@ class BASE_EXPORT Births: public BirthOnThread { int birth_count() const; - // When we have a birth we update the count for this BirhPLace. + // When we have a birth we update the count for this birthplace. void RecordBirth(); // When a birthplace is changed (updated), we need to decrement the counter @@ -265,7 +273,7 @@ class BASE_EXPORT DeathData { const int32 run_duration, int random_number); - // Metrics accessors, used only in tests. + // Metrics accessors, used only for serialization and in tests. int count() const; int32 run_duration_sum() const; int32 run_duration_max() const; @@ -274,10 +282,6 @@ class BASE_EXPORT DeathData { int32 queue_duration_max() const; int32 queue_duration_sample() const; - // Construct a DictionaryValue instance containing all our stats. The caller - // assumes ownership of the returned instance. - base::DictionaryValue* ToValue() const; - // Reset the max values to zero. void ResetMax(); @@ -296,40 +300,46 @@ class BASE_EXPORT DeathData { // but rarely updated. int32 run_duration_max_; int32 queue_duration_max_; - // Samples, used by by crowd sourcing gatherers. These are almost never read, + // Samples, used by crowd sourcing gatherers. These are almost never read, // and rarely updated. int32 run_duration_sample_; int32 queue_duration_sample_; }; //------------------------------------------------------------------------------ +// A "snapshotted" representation of the DeathData class. + +struct BASE_EXPORT DeathDataSnapshot { + DeathDataSnapshot(); + explicit DeathDataSnapshot(const DeathData& death_data); + ~DeathDataSnapshot(); + + int count; + int32 run_duration_sum; + int32 run_duration_max; + int32 run_duration_sample; + int32 queue_duration_sum; + int32 queue_duration_max; + int32 queue_duration_sample; +}; + +//------------------------------------------------------------------------------ // A temporary collection of data that can be sorted and summarized. It is // gathered (carefully) from many threads. Instances are held in arrays and // processed, filtered, and rendered. // The source of this data was collected on many threads, and is asynchronously // changing. The data in this instance is not asynchronously changing. -class BASE_EXPORT Snapshot { - public: - // When snapshotting a full life cycle set (birth-to-death), use this: - Snapshot(const BirthOnThread& birth_on_thread, - const ThreadData& death_thread, - const DeathData& death_data); +struct BASE_EXPORT TaskSnapshot { + TaskSnapshot(); + TaskSnapshot(const BirthOnThread& birth, + const DeathData& death_data, + const std::string& death_thread_name); + ~TaskSnapshot(); - // When snapshotting a birth, with no death yet, use this: - Snapshot(const BirthOnThread& birth_on_thread, int count); - - // Accessor, that provides default value when there is no death thread. - const std::string DeathThreadName() const; - - // Construct a DictionaryValue instance containing all our data recursively. - // The caller assumes ownership of the memory in the returned instance. - base::DictionaryValue* ToValue() const; - - private: - const BirthOnThread* birth_; // Includes Location and birth_thread. - const ThreadData* death_thread_; - DeathData death_data_; + BirthOnThreadSnapshot birth; + DeathDataSnapshot death_data; + std::string death_thread_name; }; //------------------------------------------------------------------------------ @@ -339,6 +349,7 @@ class BASE_EXPORT Snapshot { // We also have a linked list of ThreadData instances, and that list is used to // harvest data from all existing instances. +struct ProcessDataSnapshot; class BASE_EXPORT ThreadData { public: // Current allowable states of the tracking system. The states can vary @@ -369,11 +380,10 @@ class BASE_EXPORT ThreadData { // This may return NULL if the system is disabled for any reason. static ThreadData* Get(); - // Constructs a DictionaryValue instance containing all recursive results in - // our process. The caller assumes ownership of the memory in the returned - // instance. During the scavenging, if |reset_max| is true, then the - // DeathData instances max-values are reset to zero during this scan. - static base::DictionaryValue* ToValue(bool reset_max); + // Fills |process_data| with all the recursive results in our process. + // During the scavenging, if |reset_max| is true, then the DeathData instances + // max-values are reset to zero during this scan. + static void Snapshot(bool reset_max, ProcessDataSnapshot* process_data); // Finds (or creates) a place to count births from the given location in this // thread, and increment that tally. @@ -415,12 +425,6 @@ class BASE_EXPORT ThreadData { const std::string thread_name() const; - // Snapshot (under a lock) copies of the maps in each ThreadData instance. For - // each set of maps (BirthMap, DeathMap, and ParentChildSet) call the Append() - // method of the |target| DataCollector. If |reset_max| is true, then the max - // values in each DeathData instance should be reset during the scan. - static void SendAllMaps(bool reset_max, class DataCollector* target); - // Hack: asynchronously clear all birth counts and death tallies data values // in all ThreadData instances. The numerical (zeroing) part is done without // use of a locks or atomics exchanges, and may (for int64 values) produce @@ -490,6 +494,8 @@ class BASE_EXPORT ThreadData { FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, TinyStartupShutdown); FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, ParentChildTest); + typedef std::map<const BirthOnThread*, int> BirthCountMap; + // Worker thread construction creates a name since there is none. explicit ThreadData(int thread_number); @@ -516,6 +522,27 @@ class BASE_EXPORT ThreadData { // Find a place to record a death on this thread. void TallyADeath(const Births& birth, int32 queue_duration, int32 duration); + // Snapshot (under a lock) the profiled data for the tasks in each ThreadData + // instance. Also updates the |birth_counts| tally for each task to keep + // track of the number of living instances of the task. If |reset_max| is + // true, then the max values in each DeathData instance are reset during the + // scan. + static void SnapshotAllExecutedTasks(bool reset_max, + ProcessDataSnapshot* process_data, + BirthCountMap* birth_counts); + + // Snapshots (under a lock) the profiled data for the tasks for this thread + // and writes all of the executed tasks' data -- i.e. the data for the tasks + // with with entries in the death_map_ -- into |process_data|. Also updates + // the |birth_counts| tally for each task to keep track of the number of + // living instances of the task -- that is, each task maps to the number of + // births for the task that have not yet been balanced by a death. If + // |reset_max| is true, then the max values in each DeathData instance are + // reset during the scan. + void SnapshotExecutedTasks(bool reset_max, + ProcessDataSnapshot* process_data, + BirthCountMap* birth_counts); + // Using our lock, make a copy of the specified maps. This call may be made // on non-local threads, which necessitate the use of the lock to prevent // the map(s) from being reallocaed while they are copied. If |reset_max| is @@ -660,56 +687,31 @@ class BASE_EXPORT ThreadData { }; //------------------------------------------------------------------------------ -// DataCollector is a container class for Snapshot and BirthOnThread count -// items. +// A snapshotted representation of a (parent, child) task pair, for tracking +// hierarchical profiles. -class BASE_EXPORT DataCollector { +struct BASE_EXPORT ParentChildPairSnapshot { public: - typedef std::vector<Snapshot> Collection; - - // Construct with a list of how many threads should contribute. This helps us - // determine (in the async case) when we are done with all contributions. - DataCollector(); - ~DataCollector(); - - // Adds all stats from the indicated thread into our arrays. Accepts copies - // of the birth_map and death_map, so that the data will not change during the - // iterations and processing. - void Append(const ThreadData &thread_data, - const ThreadData::BirthMap& birth_map, - const ThreadData::DeathMap& death_map, - const ThreadData::ParentChildSet& parent_child_set); - - // After the accumulation phase, the following accessor is used to process the - // data (i.e., sort it, filter it, etc.). - Collection* collection(); - - // 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(); - - // Generates a ListValue representation of the vector of snapshots, and - // inserts the results into |dictionary|. - void ToValue(base::DictionaryValue* dictionary) const; - - private: - typedef std::map<const BirthOnThread*, int> BirthCount; + ParentChildPairSnapshot(); + explicit ParentChildPairSnapshot( + const ThreadData::ParentChildPair& parent_child); + ~ParentChildPairSnapshot(); - // The array that we collect data into. - Collection collection_; + BirthOnThreadSnapshot parent; + BirthOnThreadSnapshot child; +}; - // The total number of births recorded at each location for which we have not - // 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_; +//------------------------------------------------------------------------------ +// A snapshotted representation of the list of ThreadData objects for a process. - // The complete list of parent-child relationships among tasks. - ThreadData::ParentChildSet parent_child_set_; +struct BASE_EXPORT ProcessDataSnapshot { + public: + ProcessDataSnapshot(); + ~ProcessDataSnapshot(); - DISALLOW_COPY_AND_ASSIGN(DataCollector); + std::vector<TaskSnapshot> tasks; + std::vector<ParentChildPairSnapshot> descendants; + int process_id; }; } // namespace tracked_objects diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc index 386dc05..f364c2b 100644 --- a/base/tracked_objects_unittest.cc +++ b/base/tracked_objects_unittest.cc @@ -6,11 +6,17 @@ #include "base/tracked_objects.h" -#include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" +#include "base/process_util.h" #include "base/time.h" #include "testing/gtest/include/gtest/gtest.h" +const int kLineNumber = 1776; +const char kFile[] = "FixedUnitTestFileName"; +const char kWorkerThreadName[] = "WorkerThread-1"; +const char kMainThreadName[] = "SomeMainThreadName"; +const char kStillAlive[] = "Still_Alive"; + namespace tracked_objects { class TrackedObjectsTest : public testing::Test { @@ -27,23 +33,74 @@ class TrackedObjectsTest : public testing::Test { ThreadData::ShutdownSingleThreadedCleanup(false); } - // Provide access, since this class is a friend of ThreadData. - void ShutdownSingleThreadedCleanup(bool leak) { - ThreadData::ShutdownSingleThreadedCleanup(leak); + // Reset the profiler state. + void Reset() { + ThreadData::ShutdownSingleThreadedCleanup(false); + } + + // Simulate a birth on the thread named |thread_name|, at the given + // |location|. + void TallyABirth(const Location& location, const std::string& thread_name) { + // If the |thread_name| is empty, we don't initialize system with a thread + // name, so we're viewed as a worker thread. + if (!thread_name.empty()) + ThreadData::InitializeThreadContext(kMainThreadName); + + // Do not delete |birth|. We don't own it. + Births* birth = ThreadData::TallyABirthIfActive(location); + + if (ThreadData::status() == ThreadData::DEACTIVATED) + EXPECT_EQ(reinterpret_cast<Births*>(NULL), birth); + else + EXPECT_NE(reinterpret_cast<Births*>(NULL), birth); + } + + // Helper function to verify the most common test expectations. + void ExpectSimpleProcessData(const ProcessDataSnapshot& process_data, + const std::string& function_name, + const std::string& birth_thread, + const std::string& death_thread, + int count, + int run_ms, + int queue_ms) { + ASSERT_EQ(1u, process_data.tasks.size()); + + EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name); + EXPECT_EQ(function_name, + process_data.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number); + + EXPECT_EQ(birth_thread, process_data.tasks[0].birth.thread_name); + + EXPECT_EQ(count, process_data.tasks[0].death_data.count); + EXPECT_EQ(count * run_ms, + process_data.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(run_ms, process_data.tasks[0].death_data.run_duration_max); + EXPECT_EQ(run_ms, process_data.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(count * queue_ms, + process_data.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(queue_ms, process_data.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(queue_ms, process_data.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(death_thread, process_data.tasks[0].death_thread_name); + + EXPECT_EQ(0u, process_data.descendants.size()); + + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } }; TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { // Minimal test doesn't even create any tasks. if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. ThreadData* data = ThreadData::Get(); EXPECT_TRUE(ThreadData::first()); // Now class was constructed. - EXPECT_TRUE(data); - EXPECT_TRUE(!data->next()); + ASSERT_TRUE(data); + EXPECT_FALSE(data->next()); EXPECT_EQ(data, ThreadData::Get()); ThreadData::BirthMap birth_map; ThreadData::DeathMap death_map; @@ -52,20 +109,22 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { EXPECT_EQ(0u, birth_map.size()); EXPECT_EQ(0u, death_map.size()); EXPECT_EQ(0u, parent_child_set.size()); - // Cleanup with no leaking. - ShutdownSingleThreadedCleanup(false); + + // Clean up with no leaking. + Reset(); // Do it again, just to be sure we reset state completely. - ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE); + EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus( + ThreadData::PROFILING_CHILDREN_ACTIVE)); EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. data = ThreadData::Get(); EXPECT_TRUE(ThreadData::first()); // Now class was constructed. - EXPECT_TRUE(data); - EXPECT_TRUE(!data->next()); + ASSERT_TRUE(data); + EXPECT_FALSE(data->next()); EXPECT_EQ(data, ThreadData::Get()); birth_map.clear(); death_map.clear(); + parent_child_set.clear(); data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); EXPECT_EQ(0u, birth_map.size()); EXPECT_EQ(0u, death_map.size()); @@ -74,16 +133,17 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { TEST_F(TrackedObjectsTest, TinyStartupShutdown) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; // Instigate tracking on a single tracked object, on our thread. - const Location& location = FROM_HERE; + const char kFunction[] = "TinyStartupShutdown"; + Location location(kFunction, kFile, kLineNumber, NULL); Births* first_birth = ThreadData::TallyABirthIfActive(location); ThreadData* data = ThreadData::first(); ASSERT_TRUE(data); - EXPECT_TRUE(!data->next()); + EXPECT_FALSE(data->next()); EXPECT_EQ(data, ThreadData::Get()); ThreadData::BirthMap birth_map; ThreadData::DeathMap death_map; @@ -97,8 +157,7 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { // Now instigate another birth, while we are timing the run of the first // execution. - TrackedTime start_time = - ThreadData::NowForStartOfRun(first_birth); + TrackedTime start_time = ThreadData::NowForStartOfRun(first_birth); // Create a child (using the same birth location). // TrackingInfo will call TallyABirth() during construction. base::TimeTicks kBogusBirthTime; @@ -126,97 +185,48 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { // The births were at the same location as the one known death. EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first); -} - -TEST_F(TrackedObjectsTest, ParentChildTest) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) - return; - if (!ThreadData::TrackingParentChildStatus()) - return; // Feature not compiled in. - // Instigate tracking on a single tracked object, on our thread. - const int kFakeLineNumber = 1776; - const char* kFile = "FixedUnitTestFileName"; - const char* kFunction = "ParentChildTest"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + + const int32 time_elapsed = (end_time - start_time).InMilliseconds(); + ASSERT_EQ(1u, process_data.tasks.size()); + EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, process_data.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number); + EXPECT_EQ(kWorkerThreadName, process_data.tasks[0].birth.thread_name); + EXPECT_EQ(1, process_data.tasks[0].death_data.count); + EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_max); + EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_sample); + EXPECT_EQ(kWorkerThreadName, process_data.tasks[0].death_thread_name); - // Now instigate another birth, while we are timing the run of the first - // execution. - - // Create a child (using the same birth location). - // TrackingInfo will call TallyABirth() during construction. - base::TimeTicks kBogusBirthTime; - base::TrackingInfo pending_task(location, kBogusBirthTime); - - // Don't conclude the run, so that we don't use the actual timer that we - // started for the outer profile. This way the JSON will not include some - // random time. - ThreadData* data = ThreadData::first(); - ASSERT_TRUE(data); - EXPECT_TRUE(!data->next()); - EXPECT_EQ(data, ThreadData::Get()); - - ThreadData::BirthMap birth_map; - ThreadData::DeathMap death_map; - ThreadData::ParentChildSet parent_child_set; - - data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); - EXPECT_EQ(1u, birth_map.size()); // 1 birth location. - EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births. - EXPECT_EQ(0u, death_map.size()); // No status yet. - // Just like TinyStartupShutdown test. - EXPECT_EQ(1u, parent_child_set.size()); // 1 child. - EXPECT_EQ(parent_child_set.begin()->first, - parent_child_set.begin()->second); - - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string birth_only_result = "{" - "\"descendants\":[" - "{" - "\"child_location\":{" - "\"file_name\":\"FixedUnitTestFileName\"," - "\"function_name\":\"ParentChildTest\"," - "\"line_number\":1776" - "}," - "\"child_thread\":\"WorkerThread-1\"," - "\"parent_location\":{" - "\"file_name\":\"FixedUnitTestFileName\"," - "\"function_name\":\"ParentChildTest\"," - "\"line_number\":1776" - "}," - "\"parent_thread\":\"WorkerThread-1\"" - "}" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"WorkerThread-1\"," - "\"death_data\":{" - "\"count\":2," - "\"queue_ms\":0," - "\"queue_ms_max\":0," - "\"queue_ms_sample\":0," - "\"run_ms\":0," - "\"run_ms_max\":0," - "\"run_ms_sample\":0" - "}," - "\"death_thread\":\"Still_Alive\"," - "\"location\":{" - "\"file_name\":\"FixedUnitTestFileName\"," - "\"function_name\":\"ParentChildTest\"," - "\"line_number\":1776" - "}" - "}" - "]" - "}"; - EXPECT_EQ(json, birth_only_result); + if (ThreadData::TrackingParentChildStatus()) { + ASSERT_EQ(1u, process_data.descendants.size()); + EXPECT_EQ(kFile, process_data.descendants[0].parent.location.file_name); + EXPECT_EQ(kFunction, + process_data.descendants[0].parent.location.function_name); + EXPECT_EQ(kLineNumber, + process_data.descendants[0].parent.location.line_number); + EXPECT_EQ(kWorkerThreadName, + process_data.descendants[0].parent.thread_name); + EXPECT_EQ(kFile, process_data.descendants[0].child.location.file_name); + EXPECT_EQ(kFunction, + process_data.descendants[0].child.location.function_name); + EXPECT_EQ(kLineNumber, + process_data.descendants[0].child.location.line_number); + EXPECT_EQ(kWorkerThreadName, process_data.descendants[0].child.thread_name); + } else { + EXPECT_EQ(0u, process_data.descendants.size()); + } } TEST_F(TrackedObjectsTest, DeathDataTest) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; scoped_ptr<DeathData> data(new DeathData()); @@ -245,193 +255,89 @@ TEST_F(TrackedObjectsTest, DeathDataTest) { EXPECT_EQ(data->queue_duration_sample(), queue_ms); EXPECT_EQ(data->count(), 2); - scoped_ptr<base::DictionaryValue> dictionary(data->ToValue()); - int integer; - EXPECT_TRUE(dictionary->GetInteger("run_ms", &integer)); - EXPECT_EQ(integer, 2 * run_ms); - EXPECT_TRUE(dictionary->GetInteger("run_ms_sample", &integer)); - EXPECT_EQ(integer, run_ms); - EXPECT_TRUE(dictionary->GetInteger("queue_ms", &integer)); - EXPECT_EQ(integer, 2 * queue_ms); - EXPECT_TRUE(dictionary->GetInteger("queue_ms_sample", &integer)); - EXPECT_EQ(integer, queue_ms); - EXPECT_TRUE(dictionary->GetInteger("count", &integer)); - EXPECT_EQ(integer, 2); - - scoped_ptr<base::Value> value(data->ToValue()); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string birth_only_result = "{" - "\"count\":2," - "\"queue_ms\":16," - "\"queue_ms_max\":8," - "\"queue_ms_sample\":8," - "\"run_ms\":84," - "\"run_ms_max\":42," - "\"run_ms_sample\":42" - "}"; - EXPECT_EQ(birth_only_result, json); + DeathDataSnapshot snapshot(*data); + EXPECT_EQ(2, snapshot.count); + EXPECT_EQ(2 * run_ms, snapshot.run_duration_sum); + EXPECT_EQ(run_ms, snapshot.run_duration_max); + EXPECT_EQ(run_ms, snapshot.run_duration_sample); + EXPECT_EQ(2 * queue_ms, snapshot.queue_duration_sum); + EXPECT_EQ(queue_ms, snapshot.queue_duration_max); + EXPECT_EQ(queue_ms, snapshot.queue_duration_sample); } -TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueWorkerThread) { - // Transition to Deactivated state before doing anything. +TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToSnapshotWorkerThread) { + // Start in the deactivated state. if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED)) return; - // We don't initialize system with a thread name, so we're viewed as a worker - // thread. - const int kFakeLineNumber = 173; - const char* kFile = "FixedFileName"; - const char* kFunction = "BirthOnlyToValueWorkerThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - Births* birth = ThreadData::TallyABirthIfActive(location); - // We should now see a NULL birth record. - EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); - - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string birth_only_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "]" - "}"; - EXPECT_EQ(json, birth_only_result); + + const char kFunction[] = "DeactivatedBirthOnlyToSnapshotWorkerThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, std::string()); + + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + EXPECT_EQ(0u, process_data.tasks.size()); + EXPECT_EQ(0u, process_data.descendants.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } -TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueMainThread) { +TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToSnapshotMainThread) { // Start in the deactivated state. if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED)) return; - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 173; - const char* kFile = "FixedFileName"; - const char* kFunction = "BirthOnlyToValueMainThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - // Do not delete birth. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - // We expect to not get a birth record. - EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); - - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string birth_only_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "]" - "}"; - EXPECT_EQ(json, birth_only_result); + const char kFunction[] = "DeactivatedBirthOnlyToSnapshotMainThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); + + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + EXPECT_EQ(0u, process_data.tasks.size()); + EXPECT_EQ(0u, process_data.descendants.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } -TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { +TEST_F(TrackedObjectsTest, BirthOnlyToSnapshotWorkerThread) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; - // We don't initialize system with a thread name, so we're viewed as a worker - // thread. - const int kFakeLineNumber = 173; - const char* kFile = "FixedFileName"; - const char* kFunction = "BirthOnlyToValueWorkerThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string birth_only_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"WorkerThread-1\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":0," - "\"queue_ms_max\":0," - "\"queue_ms_sample\":0," - "\"run_ms\":0," - "\"run_ms_max\":0," - "\"run_ms_sample\":0" - "}," - "\"death_thread\":\"Still_Alive\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"BirthOnlyToValueWorkerThread\"," - "\"line_number\":173" - "}" - "}" - "]" - "}"; - EXPECT_EQ(json, birth_only_result); + + const char kFunction[] = "BirthOnlyToSnapshotWorkerThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, std::string()); + + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName, + kStillAlive, 1, 0, 0); } -TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { +TEST_F(TrackedObjectsTest, BirthOnlyToSnapshotMainThread) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 173; - const char* kFile = "FixedFileName"; - const char* kFunction = "BirthOnlyToValueMainThread"; - 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)); - - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string birth_only_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"SomeMainThreadName\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":0," - "\"queue_ms_max\":0," - "\"queue_ms_sample\":0," - "\"run_ms\":0," - "\"run_ms_max\":0," - "\"run_ms_sample\":0" - "}," - "\"death_thread\":\"Still_Alive\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"BirthOnlyToValueMainThread\"," - "\"line_number\":173" - "}" - "}" - "]" - "}"; - EXPECT_EQ(json, birth_only_result); + const char kFunction[] = "BirthOnlyToSnapshotMainThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); + + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, kStillAlive, + 1, 0, 0); } -TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { +TEST_F(TrackedObjectsTest, LifeCycleToSnapshotMainThread) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 236; - const char* kFile = "FixedFileName"; - const char* kFunction = "LifeCycleToValueMainThread"; - 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 char kFunction[] = "LifeCycleToSnapshotMainThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -443,57 +349,27 @@ TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, kStartOfRun, kEndOfRun); - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string one_line_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"SomeMainThreadName\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":4," - "\"queue_ms_max\":4," - "\"queue_ms_sample\":4," - "\"run_ms\":2," - "\"run_ms_max\":2," - "\"run_ms_sample\":2" - "}," - "\"death_thread\":\"SomeMainThreadName\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"LifeCycleToValueMainThread\"," - "\"line_number\":236" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result, json); + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, + kMainThreadName, 1, 2, 4); } // We will deactivate tracking after the birth, and before the death, and // demonstrate that the lifecycle is completely tallied. This ensures that // our tallied births are matched by tallied deaths (except for when the // task is still running, or is queued). -TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToValueMainThread) { +TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToSnapshotMainThread) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 236; - const char* kFile = "FixedFileName"; - const char* kFunction = "LifeCycleToValueMainThread"; - 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 char kFunction[] = "LifeCycleMidDeactivatedToSnapshotMainThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -509,54 +385,25 @@ TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToValueMainThread) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, kStartOfRun, kEndOfRun); - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string one_line_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"SomeMainThreadName\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":4," - "\"queue_ms_max\":4," - "\"queue_ms_sample\":4," - "\"run_ms\":2," - "\"run_ms_max\":2," - "\"run_ms_sample\":2" - "}," - "\"death_thread\":\"SomeMainThreadName\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"LifeCycleToValueMainThread\"," - "\"line_number\":236" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result, json); + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, + kMainThreadName, 1, 2, 4); } // We will deactivate tracking before starting a life cycle, and neither // the birth nor the death will be recorded. -TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToValueMainThread) { +TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToSnapshotMainThread) { + // Start in the deactivated state. if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED)) return; - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeMainThreadName"); - const int kFakeLineNumber = 236; - const char* kFile = "FixedFileName"; - const char* kFunction = "LifeCycleToValueMainThread"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - // Do not delete birth. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); + const char kFunction[] = "LifeCyclePreDeactivatedToSnapshotMainThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -568,33 +415,23 @@ TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToValueMainThread) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, kStartOfRun, kEndOfRun); - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string one_line_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "]" - "}"; - EXPECT_EQ(one_line_result, json); + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + EXPECT_EQ(0u, process_data.tasks.size()); + EXPECT_EQ(0u, process_data.descendants.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } -TEST_F(TrackedObjectsTest, LifeCycleToValueWorkerThread) { +TEST_F(TrackedObjectsTest, LifeCycleToSnapshotWorkerThread) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) 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. + const char kFunction[] = "LifeCycleToSnapshotWorkerThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + // Do not delete |birth|. We don't own it. Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); + EXPECT_NE(reinterpret_cast<Births*>(NULL), birth); const TrackedTime kTimePosted = TrackedTime() + Duration::FromMilliseconds(1); const TrackedTime kStartOfRun = TrackedTime() + @@ -603,93 +440,57 @@ TEST_F(TrackedObjectsTest, LifeCycleToValueWorkerThread) { ThreadData::TallyRunOnWorkerThreadIfTracking(birth, kTimePosted, kStartOfRun, kEndOfRun); - // Call for the ToValue, but tell it to not the maxes after scanning. - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string one_line_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"WorkerThread-1\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":4," - "\"queue_ms_max\":4," - "\"queue_ms_sample\":4," - "\"run_ms\":2," - "\"run_ms_max\":2," - "\"run_ms_sample\":2" - "}," - "\"death_thread\":\"WorkerThread-1\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"LifeCycleToValueWorkerThread\"," - "\"line_number\":236" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result, json); - - // Call for the ToValue, but tell it to reset the maxes after scanning. + // Call for the ToSnapshot, but tell it to not reset the maxes after scanning. + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName, + kWorkerThreadName, 1, 2, 4); + + // Call for the ToSnapshot, but tell it to reset the maxes after scanning. // We'll still get the same values, but the data will be reset (which we'll // see in a moment). - value.reset(ThreadData::ToValue(true)); - base::JSONWriter::Write(value.get(), &json); - // Result should be unchanged. - EXPECT_EQ(one_line_result, json); - - // Call for the ToValue, and now we'll see the result of the last translation, - // as the max will have been pushed back to zero. - value.reset(ThreadData::ToValue(false)); - base::JSONWriter::Write(value.get(), &json); - std::string one_line_result_with_zeros = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"WorkerThread-1\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":4," - "\"queue_ms_max\":0," // Note zero here. - "\"queue_ms_sample\":4," - "\"run_ms\":2," - "\"run_ms_max\":0," // Note zero here. - "\"run_ms_sample\":2" - "}," - "\"death_thread\":\"WorkerThread-1\"," - "\"location\":{" - "\"file_name\":\"FixedFileName\"," - "\"function_name\":\"LifeCycleToValueWorkerThread\"," - "\"line_number\":236" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result_with_zeros, json); + ProcessDataSnapshot process_data_pre_reset; + ThreadData::Snapshot(true, &process_data_pre_reset); + ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName, + kWorkerThreadName, 1, 2, 4); + + // Call for the ToSnapshot, and now we'll see the result of the last + // translation, as the max will have been pushed back to zero. + ProcessDataSnapshot process_data_post_reset; + ThreadData::Snapshot(true, &process_data_post_reset); + ASSERT_EQ(1u, process_data_post_reset.tasks.size()); + EXPECT_EQ(kFile, process_data_post_reset.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_post_reset.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_post_reset.tasks[0].birth.location.line_number); + EXPECT_EQ(kWorkerThreadName, + process_data_post_reset.tasks[0].birth.thread_name); + EXPECT_EQ(1, process_data_post_reset.tasks[0].death_data.count); + EXPECT_EQ(2, process_data_post_reset.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(0, process_data_post_reset.tasks[0].death_data.run_duration_max); + EXPECT_EQ(2, process_data_post_reset.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(4, process_data_post_reset.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(0, process_data_post_reset.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(4, + process_data_post_reset.tasks[0].death_data.queue_duration_sample); + EXPECT_EQ(kWorkerThreadName, + process_data_post_reset.tasks[0].death_thread_name); + EXPECT_EQ(0u, process_data_post_reset.descendants.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data_post_reset.process_id); } TEST_F(TrackedObjectsTest, TwoLives) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; - // Use a well named thread. - ThreadData::InitializeThreadContext("SomeFileThreadName"); - const int kFakeLineNumber = 222; - const char* kFile = "AnotherFileName"; - const char* kFunction = "TwoLives"; - 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 char kFunction[] = "TwoLives"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -708,50 +509,24 @@ TEST_F(TrackedObjectsTest, TwoLives) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task2, kStartOfRun, kEndOfRun); - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string one_line_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"SomeFileThreadName\"," - "\"death_data\":{" - "\"count\":2," - "\"queue_ms\":8," - "\"queue_ms_max\":4," - "\"queue_ms_sample\":4," - "\"run_ms\":4," - "\"run_ms_max\":2," - "\"run_ms_sample\":2" - "}," - "\"death_thread\":\"SomeFileThreadName\"," - "\"location\":{" - "\"file_name\":\"AnotherFileName\"," - "\"function_name\":\"TwoLives\"," - "\"line_number\":222" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result, json); + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, + kMainThreadName, 2, 2, 4); } TEST_F(TrackedObjectsTest, DifferentLives) { if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) + ThreadData::PROFILING_CHILDREN_ACTIVE)) return; // Use a well named thread. - ThreadData::InitializeThreadContext("SomeFileThreadName"); - const int kFakeLineNumber = 567; - const char* kFile = "AnotherFileName"; - const char* kFunction = "DifferentLives"; - Location location(kFunction, kFile, kFakeLineNumber, NULL); - - const base::TimeTicks kTimePosted = base::TimeTicks() - + base::TimeDelta::FromMilliseconds(1); + ThreadData::InitializeThreadContext(kMainThreadName); + const char kFunction[] = "DifferentLives"; + Location location(kFunction, kFile, kLineNumber, NULL); + + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -770,53 +545,36 @@ TEST_F(TrackedObjectsTest, DifferentLives) { base::TrackingInfo pending_task2(second_location, kDelayedStartTime); pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). - scoped_ptr<base::Value> value(ThreadData::ToValue(false)); - std::string json; - base::JSONWriter::Write(value.get(), &json); - std::string one_line_result = "{" - "\"descendants\":[" - "]," - "\"list\":[" - "{" - "\"birth_thread\":\"SomeFileThreadName\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":4," - "\"queue_ms_max\":4," - "\"queue_ms_sample\":4," - "\"run_ms\":2," - "\"run_ms_max\":2," - "\"run_ms_sample\":2" - "}," - "\"death_thread\":\"SomeFileThreadName\"," - "\"location\":{" - "\"file_name\":\"AnotherFileName\"," - "\"function_name\":\"DifferentLives\"," - "\"line_number\":567" - "}" - "}," - "{" - "\"birth_thread\":\"SomeFileThreadName\"," - "\"death_data\":{" - "\"count\":1," - "\"queue_ms\":0," - "\"queue_ms_max\":0," - "\"queue_ms_sample\":0," - "\"run_ms\":0," - "\"run_ms_max\":0," - "\"run_ms_sample\":0" - "}," - "\"death_thread\":\"Still_Alive\"," - "\"location\":{" - "\"file_name\":\"AnotherFileName\"," - "\"function_name\":\"DifferentLives\"," - "\"line_number\":999" - "}" - "}" - "]" - "}"; - EXPECT_EQ(one_line_result, json); + ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); + ASSERT_EQ(2u, process_data.tasks.size()); + EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, process_data.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number); + EXPECT_EQ(kMainThreadName, process_data.tasks[0].birth.thread_name); + EXPECT_EQ(1, process_data.tasks[0].death_data.count); + EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_max); + EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_sample); + EXPECT_EQ(kMainThreadName, process_data.tasks[0].death_thread_name); + EXPECT_EQ(kFile, process_data.tasks[1].birth.location.file_name); + EXPECT_EQ(kFunction, process_data.tasks[1].birth.location.function_name); + EXPECT_EQ(kSecondFakeLineNumber, + process_data.tasks[1].birth.location.line_number); + EXPECT_EQ(kMainThreadName, process_data.tasks[1].birth.thread_name); + EXPECT_EQ(1, process_data.tasks[1].death_data.count); + EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_sum); + EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_max); + EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_sample); + EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_sum); + EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_max); + EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_sample); + EXPECT_EQ(kStillAlive, process_data.tasks[1].death_thread_name); + EXPECT_EQ(0u, process_data.descendants.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } - } // namespace tracked_objects diff --git a/chrome/browser/metrics/tracking_synchronizer.cc b/chrome/browser/metrics/tracking_synchronizer.cc index e64b924..635e0fd 100644 --- a/chrome/browser/metrics/tracking_synchronizer.cc +++ b/chrome/browser/metrics/tracking_synchronizer.cc @@ -9,31 +9,50 @@ #include "base/process_util.h" #include "base/threading/thread.h" #include "base/tracked_objects.h" +#include "chrome/browser/metrics/tracking_synchronizer_observer.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/profiler_controller.h" -#include "content/public/common/process_type.h" using base::TimeTicks; using content::BrowserThread; +namespace { + +// Negative numbers are never used as sequence numbers. We explicitly pick a +// negative number that is "so negative" that even when we add one (as is done +// when we generated the next sequence number) that it will still be negative. +// We have code that handles wrapping around on an overflow into negative +// territory. +const int kNeverUsableSequenceNumber = -2; + +// This singleton instance should be started during the single threaded +// portion of main(). It initializes globals to provide support for all future +// calls. This object is created on the UI thread, and it is destroyed after +// all the other threads have gone away. As a result, it is ok to call it +// from the UI thread, or for about:profiler. +static chrome_browser_metrics::TrackingSynchronizer* g_tracking_synchronizer = + NULL; + +} // anonymous namespace + namespace chrome_browser_metrics { // The "RequestContext" structure describes an individual request received // from the UI. All methods are accessible on UI thread. -class RequestContext { +class TrackingSynchronizer::RequestContext { public: // A map from sequence_number_ to the actual RequestContexts. typedef std::map<int, RequestContext*> RequestContextMap; - ~RequestContext() {} - - RequestContext(const base::WeakPtr<ProfilerUI>& callback_object, - int sequence_number) + RequestContext( + const base::WeakPtr<TrackingSynchronizerObserver>& callback_object, + int sequence_number) : callback_object_(callback_object), sequence_number_(sequence_number), received_process_group_count_(0), processes_pending_(0) { } + ~RequestContext() {} void SetReceivedProcessGroupCount(bool done) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -63,9 +82,8 @@ class RequestContext { void DeleteIfAllDone() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (processes_pending_ <= 0 && received_process_group_count_) { + if (processes_pending_ <= 0 && received_process_group_count_) RequestContext::Unregister(sequence_number_); - } } @@ -73,7 +91,7 @@ class RequestContext { // |sequence_number|. static RequestContext* Register( int sequence_number, - const base::WeakPtr<ProfilerUI>& callback_object) { + const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); RequestContext* request = new RequestContext( @@ -93,9 +111,8 @@ class RequestContext { if (it == outstanding_requests_.Get().end()) return NULL; - RequestContext* request = NULL; - request = it->second; - DCHECK(sequence_number == request->sequence_number_); + RequestContext* request = it->second; + DCHECK_EQ(sequence_number, request->sequence_number_); return request; } @@ -111,11 +128,13 @@ class RequestContext { return; RequestContext* request = it->second; - DCHECK(sequence_number == request->sequence_number_); + DCHECK_EQ(sequence_number, request->sequence_number_); bool received_process_group_count = request->received_process_group_count_; int unresponsive_processes = request->processes_pending_; - delete it->second; + request->callback_object_->FinishedReceivingProfilerData(); + + delete request; outstanding_requests_.Get().erase(it); UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount", @@ -136,7 +155,7 @@ class RequestContext { } // Requests are made to asynchronously send data to the |callback_object_|. - base::WeakPtr<ProfilerUI> callback_object_; + base::WeakPtr<TrackingSynchronizerObserver> callback_object_; // The sequence number used by the most recent update request to contact all // processes. @@ -154,22 +173,17 @@ class RequestContext { static base::LazyInstance<RequestContextMap> outstanding_requests_; }; -// Negative numbers are never used as sequence numbers. We explicitly pick a -// negative number that is "so negative" that even when we add one (as is done -// when we generated the next sequence number) that it will still be negative. -// We have code that handles wrapping around on an overflow into negative -// territory. -static const int kNeverUsableSequenceNumber = -2; +// static +base::LazyInstance<TrackingSynchronizer::RequestContext::RequestContextMap> + TrackingSynchronizer::RequestContext::outstanding_requests_ = + LAZY_INSTANCE_INITIALIZER; // TrackingSynchronizer methods and members. -// -// static -TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; TrackingSynchronizer::TrackingSynchronizer() : last_used_sequence_number_(kNeverUsableSequenceNumber) { - DCHECK(tracking_synchronizer_ == NULL); - tracking_synchronizer_ = this; + DCHECK(!g_tracking_synchronizer); + g_tracking_synchronizer = this; content::ProfilerController::GetInstance()->Register(this); } @@ -179,21 +193,20 @@ TrackingSynchronizer::~TrackingSynchronizer() { // Just in case we have any pending tasks, clear them out. RequestContext::OnShutdown(); - tracking_synchronizer_ = NULL; + g_tracking_synchronizer = NULL; } // static void TrackingSynchronizer::FetchProfilerDataAsynchronously( - const base::WeakPtr<ProfilerUI>& callback_object) { + const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); - if (current_synchronizer == NULL) { + if (!g_tracking_synchronizer) { // System teardown is happening. return; } - int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses( + int sequence_number = g_tracking_synchronizer->RegisterAndNotifyAllProcesses( callback_object); // Post a task that would be called after waiting for wait_time. This acts @@ -219,24 +232,21 @@ void TrackingSynchronizer::OnPendingProcesses(int sequence_number, void TrackingSynchronizer::OnProfilerDataCollected( int sequence_number, - base::DictionaryValue* profiler_data) { + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - RequestContext* request = RequestContext::GetRequestContext(sequence_number); - if (!request) - return; - - DecrementPendingProcessesAndSendData(sequence_number, profiler_data); + DecrementPendingProcessesAndSendData(sequence_number, profiler_data, + process_type); } int TrackingSynchronizer::RegisterAndNotifyAllProcesses( - const base::WeakPtr<ProfilerUI>& callback_object) { + const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); int sequence_number = GetNextAvailableSequenceNumber(); RequestContext* request = - RequestContext::Register(sequence_number, callback_object); + RequestContext::Register(sequence_number, callback_object); // Increment pending process count for sending browser's profiler data. request->IncrementProcessesPending(); @@ -245,32 +255,27 @@ int TrackingSynchronizer::RegisterAndNotifyAllProcesses( content::ProfilerController::GetInstance()->GetProfilerData(sequence_number); // Send profiler_data from browser process. - base::DictionaryValue* value = tracked_objects::ThreadData::ToValue(false); - const std::string process_type = - content::GetProcessTypeNameInEnglish(content::PROCESS_TYPE_BROWSER); - value->SetString("process_type", process_type); - value->SetInteger("process_id", base::GetCurrentProcId()); - DecrementPendingProcessesAndSendData(sequence_number, value); + tracked_objects::ProcessDataSnapshot process_data; + tracked_objects::ThreadData::Snapshot(false, &process_data); + DecrementPendingProcessesAndSendData(sequence_number, process_data, + content::PROCESS_TYPE_BROWSER); return sequence_number; } void TrackingSynchronizer::DecrementPendingProcessesAndSendData( int sequence_number, - base::DictionaryValue* value) { + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); RequestContext* request = RequestContext::GetRequestContext(sequence_number); - if (!request) { - delete value; + if (!request) return; - } - if (value && request->callback_object_) { - // Transfers ownership of |value| to |callback_object_|. - request->callback_object_->ReceivedData(value); - } else { - delete value; + if (request->callback_object_) { + request->callback_object_->ReceivedProfilerData(profiler_data, + process_type); } // Delete request if we have heard back from all child processes. @@ -289,15 +294,4 @@ int TrackingSynchronizer::GetNextAvailableSequenceNumber() { return last_used_sequence_number_; } -// static -TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(tracking_synchronizer_ != NULL); - return tracking_synchronizer_; -} - -// static -base::LazyInstance<RequestContext::RequestContextMap> - RequestContext::outstanding_requests_ = LAZY_INSTANCE_INITIALIZER; - } // namespace chrome_browser_metrics diff --git a/chrome/browser/metrics/tracking_synchronizer.h b/chrome/browser/metrics/tracking_synchronizer.h index 75fe3c9d..e30f823 100644 --- a/chrome/browser/metrics/tracking_synchronizer.h +++ b/chrome/browser/metrics/tracking_synchronizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,8 +14,6 @@ #include "base/lazy_instance.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/values.h" -#include "chrome/browser/ui/webui/profiler_ui.h" #include "content/public/browser/profiler_subscriber.h" // This class maintains state that is used to upload profiler data from the @@ -35,7 +33,7 @@ namespace chrome_browser_metrics { -class RequestContext; +class TrackingSynchronizerObserver; class TrackingSynchronizer : public content::ProfilerSubscriber, @@ -52,7 +50,7 @@ class TrackingSynchronizer // the data received from each sub-process. // This method is accessible on UI thread. static void FetchProfilerDataAsynchronously( - const base::WeakPtr<ProfilerUI>& callback_object); + const base::WeakPtr<TrackingSynchronizerObserver>& callback_object); // ------------------------------------------------------ // ProfilerSubscriber methods for browser child processes @@ -64,32 +62,28 @@ class TrackingSynchronizer int pending_processes, bool end) OVERRIDE; - // Send profiler_data back to callback_object_ by calling - // DecrementPendingProcessesAndSendData which records that we are waiting - // for one less profiler data from renderer or browser child process for the - // given sequence number. This method is accessible on UI thread. - virtual void OnProfilerDataCollected( - int sequence_number, - base::DictionaryValue* profiler_data) OVERRIDE; - private: friend class base::RefCountedThreadSafe<TrackingSynchronizer>; - friend class RequestContext; + + class RequestContext; virtual ~TrackingSynchronizer(); - // Send profiler_data back to callback_object_. It records that we are waiting + // Send profiler_data back to callback_object_ by calling + // DecrementPendingProcessesAndSendData which records that we are waiting // for one less profiler data from renderer or browser child process for the // given sequence number. This method is accessible on UI thread. - void OnProfilerDataCollectedOnUI(int sequence_number, - base::DictionaryValue* profiler_data); + virtual void OnProfilerDataCollected( + int sequence_number, + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type) OVERRIDE; // Establish a new sequence_number_, and use it to notify all the processes of // the need to supply, to the browser, their tracking data. It also registers // |callback_object| in |outstanding_requests_| map. Return the // sequence_number_ that was used. This method is accessible on UI thread. int RegisterAndNotifyAllProcesses( - const base::WeakPtr<ProfilerUI>& callback_object); + const base::WeakPtr<TrackingSynchronizerObserver>& callback_object); // It finds the RequestContext for the given |sequence_number| and notifies // the RequestContext's |callback_object_| about the |value|. This is called @@ -98,18 +92,15 @@ class TrackingSynchronizer // sequence number. If we have received a response from all renderers and // browser processes, then it calls RequestContext's DeleteIfAllDone to delete // the entry for sequence_number. This method is accessible on UI thread. - void DecrementPendingProcessesAndSendData(int sequence_number, - base::DictionaryValue* value); + void DecrementPendingProcessesAndSendData( + int sequence_number, + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type); // Get a new sequence number to be sent to processes from browser process. // This method is accessible on UI thread. int GetNextAvailableSequenceNumber(); - // Return pointer to the singleton instance, which is allocated and - // deallocated on the main UI thread (during system startup and teardown). - // This method is accessible on UI thread. - static TrackingSynchronizer* CurrentSynchronizer(); - // We don't track the actual processes that are contacted for an update, only // the count of the number of processes, and we can sometimes time-out and // give up on a "slow to respond" process. We use a sequence_number to be @@ -119,13 +110,6 @@ class TrackingSynchronizer // reuse for a long time). int last_used_sequence_number_; - // This singleton instance should be started during the single threaded - // portion of main(). It initializes globals to provide support for all future - // calls. This object is created on the UI thread, and it is destroyed after - // all the other threads have gone away. As a result, it is ok to call it - // from the UI thread, or for about:profiler. - static TrackingSynchronizer* tracking_synchronizer_; - DISALLOW_COPY_AND_ASSIGN(TrackingSynchronizer); }; diff --git a/chrome/browser/metrics/tracking_synchronizer_observer.h b/chrome/browser/metrics/tracking_synchronizer_observer.h new file mode 100644 index 0000000..50bfe15 --- /dev/null +++ b/chrome/browser/metrics/tracking_synchronizer_observer.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_METRICS_TRACKING_SYNCHRONIZER_OBSERVER_H_ +#define CHROME_BROWSER_METRICS_TRACKING_SYNCHRONIZER_OBSERVER_H_ +#pragma once + +#include "content/public/common/process_type.h" + +namespace tracked_objects { +struct ProcessDataSnapshot; +} + +namespace chrome_browser_metrics { + +// Observer for notifications from the TrackingSynchronizer class. +class TrackingSynchronizerObserver { + public: + // Received |profiler_data| from a single process of |process_type|. + // The observer should assume there might be more data coming until + // |FinishedReceivingData()| is called. + virtual void ReceivedProfilerData( + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type) = 0; + + // The observer should not expect any more calls to |ReceivedProfilerData()| + // (without re-registering). This is sent either when data from all processes + // has been gathered, or when the request times out. + virtual void FinishedReceivingProfilerData() {} + + protected: + TrackingSynchronizerObserver() {} + virtual ~TrackingSynchronizerObserver() {} + + private: + DISALLOW_COPY_AND_ASSIGN(TrackingSynchronizerObserver); +}; + +} // namespace chrome_browser_metrics + +#endif // CHROME_BROWSER_METRICS_TRACKING_SYNCHRONIZER_OBSERVER_H_ diff --git a/chrome/browser/resources/profiler/profiler.js b/chrome/browser/resources/profiler/profiler.js index 09294de..1c15b13 100644 --- a/chrome/browser/resources/profiler/profiler.js +++ b/chrome/browser/resources/profiler/profiler.js @@ -367,20 +367,20 @@ var MainView = (function() { KEY_PROPERTIES[KEY_FUNCTION_NAME] = { name: 'Function name', - inputJsonKey: 'location.function_name', + inputJsonKey: 'birth_location.function_name', aggregator: UniquifyAggregator, }; KEY_PROPERTIES[KEY_FILE_NAME] = { name: 'File name', - inputJsonKey: 'location.file_name', + inputJsonKey: 'birth_location.file_name', aggregator: UniquifyAggregator, }; KEY_PROPERTIES[KEY_LINE_NUMBER] = { name: 'Line number', cellAlignment: 'right', - inputJsonKey: 'location.line_number', + inputJsonKey: 'birth_location.line_number', aggregator: UniquifyAggregator, }; diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer.cc b/chrome/browser/task_profiler/task_profiler_data_serializer.cc index fd76741..6a2f90b 100644 --- a/chrome/browser/task_profiler/task_profiler_data_serializer.cc +++ b/chrome/browser/task_profiler/task_profiler_data_serializer.cc @@ -10,11 +10,120 @@ #include "base/time.h" #include "base/tracked_objects.h" #include "content/public/common/content_client.h" +#include "content/public/common/process_type.h" #include "googleurl/src/gurl.h" +using base::DictionaryValue; +using base::ListValue; +using base::Value; +using tracked_objects::BirthOnThreadSnapshot; +using tracked_objects::DeathDataSnapshot; +using tracked_objects::LocationSnapshot; +using tracked_objects::ParentChildPairSnapshot; +using tracked_objects::TaskSnapshot; +using tracked_objects::ProcessDataSnapshot; + +namespace { + +// Re-serializes the |location| into |dictionary|. +void LocationSnapshotToValue(const LocationSnapshot& location, + DictionaryValue* dictionary) { + dictionary->Set("file_name", Value::CreateStringValue(location.file_name)); + // Note: This function name is not escaped, and templates have less-than + // characters, which means this is not suitable for display as HTML unless + // properly escaped. + dictionary->Set("function_name", + Value::CreateStringValue(location.function_name)); + dictionary->Set("line_number", + Value::CreateIntegerValue(location.line_number)); +} + +// Re-serializes the |birth| into |dictionary|. Prepends the |prefix| to the +// "thread" and "location" key names in the dictionary. +void BirthOnThreadSnapshotToValue(const BirthOnThreadSnapshot& birth, + const std::string& prefix, + DictionaryValue* dictionary) { + DCHECK(!prefix.empty()); + + scoped_ptr<DictionaryValue> location_value(new DictionaryValue); + LocationSnapshotToValue(birth.location, location_value.get()); + dictionary->Set(prefix + "_location", location_value.release()); + + dictionary->Set(prefix + "_thread", + Value::CreateStringValue(birth.thread_name)); +} + +// Re-serializes the |death_data| into |dictionary|. +void DeathDataSnapshotToValue(const DeathDataSnapshot& death_data, + base::DictionaryValue* dictionary) { + dictionary->Set("count", + Value::CreateIntegerValue(death_data.count)); + dictionary->Set("run_ms", + Value::CreateIntegerValue(death_data.run_duration_sum)); + dictionary->Set("run_ms_max", + Value::CreateIntegerValue(death_data.run_duration_max)); + dictionary->Set("run_ms_sample", + Value::CreateIntegerValue(death_data.run_duration_sample)); + dictionary->Set("queue_ms", + Value::CreateIntegerValue(death_data.queue_duration_sum)); + dictionary->Set("queue_ms_max", + Value::CreateIntegerValue(death_data.queue_duration_max)); + dictionary->Set("queue_ms_sample", + Value::CreateIntegerValue(death_data.queue_duration_sample)); + +} + +// Re-serializes the |snapshot| into |dictionary|. +void TaskSnapshotToValue(const TaskSnapshot& snapshot, + base::DictionaryValue* dictionary) { + BirthOnThreadSnapshotToValue(snapshot.birth, "birth", dictionary); + + scoped_ptr<DictionaryValue> death_data(new DictionaryValue); + DeathDataSnapshotToValue(snapshot.death_data, death_data.get()); + dictionary->Set("death_data", death_data.release()); + + dictionary->Set("death_thread", + Value::CreateStringValue(snapshot.death_thread_name)); + +} + +} // anonymous namespace + namespace task_profiler { -bool TaskProfilerDataSerializer::WriteToFile(const FilePath &path) { +// static +void TaskProfilerDataSerializer::ToValue( + const ProcessDataSnapshot& process_data, + content::ProcessType process_type, + base::DictionaryValue* dictionary) { + scoped_ptr<base::ListValue> tasks_list(new base::ListValue); + for (std::vector<TaskSnapshot>::const_iterator it = + process_data.tasks.begin(); + it != process_data.tasks.end(); ++it) { + scoped_ptr<DictionaryValue> snapshot(new DictionaryValue); + TaskSnapshotToValue(*it, snapshot.get()); + tasks_list->Append(snapshot.release()); + } + dictionary->Set("list", tasks_list.release()); + + dictionary->SetInteger("process_id", process_data.process_id); + dictionary->SetString("process_type", + content::GetProcessTypeNameInEnglish(process_type)); + + scoped_ptr<base::ListValue> descendants_list(new base::ListValue); + for (std::vector<ParentChildPairSnapshot>::const_iterator it = + process_data.descendants.begin(); + it != process_data.descendants.end(); ++it) { + scoped_ptr<base::DictionaryValue> parent_child(new base::DictionaryValue); + BirthOnThreadSnapshotToValue(it->parent, "parent", parent_child.get()); + BirthOnThreadSnapshotToValue(it->child, "child", parent_child.get()); + descendants_list->Append(parent_child.release()); + } + dictionary->Set("descendants", descendants_list.release()); +} + + +bool TaskProfilerDataSerializer::WriteToFile(const FilePath& path) { std::string output; JSONStringValueSerializer serializer(&output); serializer.set_pretty_print(true); @@ -29,10 +138,16 @@ bool TaskProfilerDataSerializer::WriteToFile(const FilePath &path) { root->SetString("userAgent", content::GetUserAgent(GURL())); // TODO(ramant): Collect data from other processes, then add that data to the - // 'per_process_data' array here. - base::DictionaryValue* this_process_data = - tracked_objects::ThreadData::ToValue(false); - per_process_data->Append(this_process_data); + // 'per_process_data' array here. Should leverage the TrackingSynchronizer + // class to implement this. + ProcessDataSnapshot this_process_data; + tracked_objects::ThreadData::Snapshot(false, &this_process_data); + scoped_ptr<base::DictionaryValue> this_process_data_json( + new base::DictionaryValue); + TaskProfilerDataSerializer::ToValue(this_process_data, + content::PROCESS_TYPE_BROWSER, + this_process_data_json.get()); + per_process_data->Append(this_process_data_json.release()); shutdown_snapshot->SetInteger( "timestamp", diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer.h b/chrome/browser/task_profiler/task_profiler_data_serializer.h index 8ada670..c76cb00 100644 --- a/chrome/browser/task_profiler/task_profiler_data_serializer.h +++ b/chrome/browser/task_profiler/task_profiler_data_serializer.h @@ -6,15 +6,36 @@ #define CHROME_BROWSER_TASK_PROFILER_TASK_PROFILER_DATA_SERIALIZER_H_ #pragma once +#include "base/basictypes.h" +#include "content/public/common/process_type.h" + class FilePath; +namespace base { +class DictionaryValue; +} + +namespace tracked_objects { +struct ProcessDataSnapshot; +} + namespace task_profiler { // This class collects task profiler data and serializes it to a file. The file // format is compatible with the about:profiler UI. class TaskProfilerDataSerializer { public: - bool WriteToFile(const FilePath &path); + TaskProfilerDataSerializer() {} + + // Writes the contents of |process_data| and |process_type| into |dictionary|. + static void ToValue(const tracked_objects::ProcessDataSnapshot& process_data, + content::ProcessType process_type, + base::DictionaryValue* dictionary); + + bool WriteToFile(const FilePath& path); + + private: + DISALLOW_COPY_AND_ASSIGN(TaskProfilerDataSerializer); }; } // namespace task_profiler diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc b/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc new file mode 100644 index 0000000..5b8e56f --- /dev/null +++ b/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/basictypes.h" +#include "base/json/json_writer.h" +#include "base/process_util.h" +#include "base/string_number_conversions.h" +#include "base/tracked_objects.h" +#include "base/values.h" +#include "chrome/browser/task_profiler/task_profiler_data_serializer.h" +#include "content/public/common/process_type.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +std::string GetProcessIdString() { + return base::IntToString(base::GetCurrentProcId()); +} + +void ExpectSerialization( + const tracked_objects::ProcessDataSnapshot& process_data, + content::ProcessType process_type, + const std::string& expected_json) { + base::DictionaryValue serialized_value; + task_profiler::TaskProfilerDataSerializer::ToValue( + process_data, process_type, &serialized_value); + + std::string serialized_json; + base::JSONWriter::Write(&serialized_value, &serialized_json); + + EXPECT_EQ(expected_json, serialized_json); +} + +} // anonymous namespace + +// Tests the JSON serialization format for profiled process data. +TEST(TaskProfilerDataSerializerTest, SerializeProcessDataToJson) { + { + // Empty data. + tracked_objects::ProcessDataSnapshot process_data; + content::ProcessType process_type = content::PROCESS_TYPE_BROWSER; + ExpectSerialization(process_data, process_type, + "{" + "\"descendants\":[" + "]," + "\"list\":[" + "]," + "\"process_id\":" + GetProcessIdString() + "," + "\"process_type\":\"Browser\"" + "}"); + } + + { + // Non-empty data. + tracked_objects::ProcessDataSnapshot process_data; + + tracked_objects::BirthOnThreadSnapshot parent; + parent.location.file_name = "path/to/foo.cc"; + parent.location.function_name = "WhizBang"; + parent.location.line_number = 101; + parent.thread_name = "CrBrowserMain"; + + tracked_objects::BirthOnThreadSnapshot child; + child.location.file_name = "path/to/bar.cc"; + child.location.function_name = "FizzBoom"; + child.location.line_number = 433; + child.thread_name = "Chrome_IOThread"; + + + // Add a snapshot. + process_data.tasks.push_back(tracked_objects::TaskSnapshot()); + process_data.tasks.back().birth = parent; + process_data.tasks.back().death_data.count = 37; + process_data.tasks.back().death_data.run_duration_max = 5; + process_data.tasks.back().death_data.run_duration_sample = 3; + process_data.tasks.back().death_data.run_duration_sum = 17; + process_data.tasks.back().death_data.queue_duration_max = 53; + process_data.tasks.back().death_data.queue_duration_sample = 13; + process_data.tasks.back().death_data.queue_duration_sum = 79; + process_data.tasks.back().death_thread_name = "WorkerPool/-1340960768"; + + // Add a second snapshot. + process_data.tasks.push_back(tracked_objects::TaskSnapshot()); + process_data.tasks.back().birth = child; + process_data.tasks.back().death_data.count = 41; + process_data.tasks.back().death_data.run_duration_max = 205; + process_data.tasks.back().death_data.run_duration_sample = 203; + process_data.tasks.back().death_data.run_duration_sum = 2017; + process_data.tasks.back().death_data.queue_duration_max = 2053; + process_data.tasks.back().death_data.queue_duration_sample = 2013; + process_data.tasks.back().death_data.queue_duration_sum = 2079; + process_data.tasks.back().death_thread_name = "PAC thread #3"; + + // Add a parent-child pair. + process_data.descendants.push_back( + tracked_objects::ParentChildPairSnapshot()); + process_data.descendants.back().parent = parent; + process_data.descendants.back().child = child; + + content::ProcessType process_type = content::PROCESS_TYPE_RENDERER; + ExpectSerialization(process_data, process_type, + "{" + "\"descendants\":[" + "{" + "\"child_location\":{" + "\"file_name\":\"path/to/bar.cc\"," + "\"function_name\":\"FizzBoom\"," + "\"line_number\":433" + "}," + "\"child_thread\":\"Chrome_IOThread\"," + "\"parent_location\":{" + "\"file_name\":\"path/to/foo.cc\"," + "\"function_name\":\"WhizBang\"," + "\"line_number\":101" + "}," + "\"parent_thread\":\"CrBrowserMain\"" + "}" + "]," + "\"list\":[{" + "\"birth_location\":{" + "\"file_name\":\"path/to/foo.cc\"," + "\"function_name\":\"WhizBang\"," + "\"line_number\":101" + "}," + "\"birth_thread\":\"CrBrowserMain\"," + "\"death_data\":{" + "\"count\":37," + "\"queue_ms\":79," + "\"queue_ms_max\":53," + "\"queue_ms_sample\":13," + "\"run_ms\":17," + "\"run_ms_max\":5," + "\"run_ms_sample\":3" + "}," + "\"death_thread\":\"WorkerPool/-1340960768\"" + "},{" + "\"birth_location\":{" + "\"file_name\":\"path/to/bar.cc\"," + "\"function_name\":\"FizzBoom\"," + "\"line_number\":433" + "}," + "\"birth_thread\":\"Chrome_IOThread\"," + "\"death_data\":{" + "\"count\":41," + "\"queue_ms\":2079," + "\"queue_ms_max\":2053," + "\"queue_ms_sample\":2013," + "\"run_ms\":2017," + "\"run_ms_max\":205," + "\"run_ms_sample\":203" + "}," + "\"death_thread\":\"PAC thread #3\"" + "}]," + "\"process_id\":" + GetProcessIdString() + "," + "\"process_type\":\"Tab\"" + "}"); + } +} diff --git a/chrome/browser/ui/webui/profiler_ui.cc b/chrome/browser/ui/webui/profiler_ui.cc index a995160..89e8a4a 100644 --- a/chrome/browser/ui/webui/profiler_ui.cc +++ b/chrome/browser/ui/webui/profiler_ui.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,9 +14,12 @@ //#define USE_SOURCE_FILES_DIRECTLY #include "base/bind.h" +#include "base/memory/scoped_ptr.h" #include "base/tracked_objects.h" +#include "base/values.h" #include "chrome/browser/metrics/tracking_synchronizer.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/task_profiler/task_profiler_data_serializer.h" #include "chrome/browser/ui/webui/chrome_web_ui_data_source.h" #include "chrome/common/url_constants.h" #include "content/public/browser/browser_thread.h" @@ -127,7 +130,7 @@ void ProfilerMessageHandler::RegisterMessages() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); web_ui()->RegisterMessageCallback("getData", - base::Bind(&ProfilerMessageHandler::OnGetData,base::Unretained(this))); + base::Bind(&ProfilerMessageHandler::OnGetData, base::Unretained(this))); web_ui()->RegisterMessageCallback("resetData", base::Bind(&ProfilerMessageHandler::OnResetData, base::Unretained(this))); @@ -144,10 +147,9 @@ void ProfilerMessageHandler::OnResetData(const ListValue* list) { } // namespace -ProfilerUI::ProfilerUI(content::WebUI* web_ui) : WebUIController(web_ui) { - ui_weak_ptr_factory_.reset(new base::WeakPtrFactory<ProfilerUI>(this)); - ui_weak_ptr_ = ui_weak_ptr_factory_->GetWeakPtr(); - +ProfilerUI::ProfilerUI(content::WebUI* web_ui) + : WebUIController(web_ui), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { web_ui->AddMessageHandler(new ProfilerMessageHandler()); // Set up the chrome://profiler/ source. @@ -159,13 +161,19 @@ ProfilerUI::~ProfilerUI() { } void ProfilerUI::GetData() { - TrackingSynchronizer::FetchProfilerDataAsynchronously(ui_weak_ptr_); + TrackingSynchronizer::FetchProfilerDataAsynchronously( + weak_ptr_factory_.GetWeakPtr()); } -void ProfilerUI::ReceivedData(base::Value* value) { +void ProfilerUI::ReceivedProfilerData( + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type) { + // Serialize the data to JSON. + DictionaryValue json_data; + task_profiler::TaskProfilerDataSerializer::ToValue(profiler_data, + process_type, + &json_data); + // Send the data to the renderer. - scoped_ptr<Value> data_values(value); - web_ui()->CallJavascriptFunction( - "g_browserBridge.receivedData", *data_values.get()); + web_ui()->CallJavascriptFunction("g_browserBridge.receivedData", json_data); } - diff --git a/chrome/browser/ui/webui/profiler_ui.h b/chrome/browser/ui/webui/profiler_ui.h index 090fbd6..730f58a 100644 --- a/chrome/browser/ui/webui/profiler_ui.h +++ b/chrome/browser/ui/webui/profiler_ui.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,13 +6,13 @@ #define CHROME_BROWSER_UI_WEBUI_PROFILER_UI_H_ #pragma once -#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/values.h" +#include "chrome/browser/metrics/tracking_synchronizer_observer.h" #include "content/public/browser/web_ui_controller.h" // The C++ back-end for the chrome://profiler webui page. -class ProfilerUI : public content::WebUIController { +class ProfilerUI : public content::WebUIController, + public chrome_browser_metrics::TrackingSynchronizerObserver { public: explicit ProfilerUI(content::WebUI* web_ui); virtual ~ProfilerUI(); @@ -20,13 +20,14 @@ class ProfilerUI : public content::WebUIController { // Get the tracking data from TrackingSynchronizer. void GetData(); - // Send the data to the renderer. - void ReceivedData(base::Value* value); - private: + // TrackingSynchronizerObserver: + virtual void ReceivedProfilerData( + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type) OVERRIDE; + // Used to get |weak_ptr_| to self on the UI thread. - scoped_ptr<base::WeakPtrFactory<ProfilerUI> > ui_weak_ptr_factory_; - base::WeakPtr<ProfilerUI> ui_weak_ptr_; + base::WeakPtrFactory<ProfilerUI> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ProfilerUI); }; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index dffd9de..91dd8c0 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1321,8 +1321,6 @@ 'browser/metrics/field_trial_synchronizer.h', 'browser/metrics/histogram_synchronizer.cc', 'browser/metrics/histogram_synchronizer.h', - 'browser/metrics/tracking_synchronizer.cc', - 'browser/metrics/tracking_synchronizer.h', 'browser/metrics/metric_event_duration_details.h', 'browser/metrics/metrics_log.cc', 'browser/metrics/metrics_log.h', @@ -1336,6 +1334,9 @@ 'browser/metrics/metrics_service.h', 'browser/metrics/thread_watcher.cc', 'browser/metrics/thread_watcher.h', + 'browser/metrics/tracking_synchronizer.cc', + 'browser/metrics/tracking_synchronizer.h', + 'browser/metrics/tracking_synchronizer_observer.h', 'browser/native_window_notification_source.h', 'browser/net/browser_url_util.cc', 'browser/net/browser_url_util.h', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 53eb666..8a3b344 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1751,6 +1751,7 @@ 'browser/tabs/test_tab_strip_model_delegate.cc', 'browser/tabs/test_tab_strip_model_delegate.h', 'browser/task_manager/task_manager_unittest.cc', + 'browser/task_profiler/task_profiler_data_serializer_unittest.cc', 'browser/themes/browser_theme_pack_unittest.cc', 'browser/themes/theme_service_unittest.cc', 'browser/ui/browser_list_unittest.cc', diff --git a/chromeos/dbus/flimflam_client_unittest_base.cc b/chromeos/dbus/flimflam_client_unittest_base.cc index 57549bc..0dc149a 100644 --- a/chromeos/dbus/flimflam_client_unittest_base.cc +++ b/chromeos/dbus/flimflam_client_unittest_base.cc @@ -5,6 +5,7 @@ #include "chromeos/dbus/flimflam_client_unittest_base.h" #include "base/bind.h" +#include "base/values.h" #include "dbus/message.h" #include "dbus/object_path.h" #include "dbus/values_util.h" diff --git a/chromeos/dbus/flimflam_ipconfig_client.cc b/chromeos/dbus/flimflam_ipconfig_client.cc index 2ad5b2a..8eb88e4 100644 --- a/chromeos/dbus/flimflam_ipconfig_client.cc +++ b/chromeos/dbus/flimflam_ipconfig_client.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/message_loop.h" +#include "base/values.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_path.h" diff --git a/chromeos/dbus/flimflam_manager_client.cc b/chromeos/dbus/flimflam_manager_client.cc index 29a0ed1..8b34273 100644 --- a/chromeos/dbus/flimflam_manager_client.cc +++ b/chromeos/dbus/flimflam_manager_client.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/chromeos/chromeos_version.h" #include "base/message_loop.h" +#include "base/values.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_path.h" diff --git a/chromeos/dbus/flimflam_manager_client_unittest.cc b/chromeos/dbus/flimflam_manager_client_unittest.cc index 834d714..453b644 100644 --- a/chromeos/dbus/flimflam_manager_client_unittest.cc +++ b/chromeos/dbus/flimflam_manager_client_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/bind.h" +#include "base/values.h" #include "chromeos/dbus/flimflam_client_unittest_base.h" #include "chromeos/dbus/flimflam_manager_client.h" #include "dbus/message.h" diff --git a/chromeos/dbus/flimflam_network_client_unittest.cc b/chromeos/dbus/flimflam_network_client_unittest.cc index f17bdbf..aa89918 100644 --- a/chromeos/dbus/flimflam_network_client_unittest.cc +++ b/chromeos/dbus/flimflam_network_client_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/bind.h" +#include "base/values.h" #include "chromeos/dbus/flimflam_client_unittest_base.h" #include "chromeos/dbus/flimflam_network_client.h" #include "dbus/message.h" diff --git a/chromeos/dbus/flimflam_profile_client.cc b/chromeos/dbus/flimflam_profile_client.cc index 81aedb6b..9079109 100644 --- a/chromeos/dbus/flimflam_profile_client.cc +++ b/chromeos/dbus/flimflam_profile_client.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/message_loop.h" +#include "base/values.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_path.h" diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc index 21b2139..23a0079 100644 --- a/content/browser/browser_child_process_host_impl.cc +++ b/content/browser/browser_child_process_host_impl.cc @@ -90,7 +90,7 @@ BrowserChildProcessHostImpl::BrowserChildProcessHostImpl( child_process_host_.reset(ChildProcessHost::Create(this)); child_process_host_->AddFilter(new TraceMessageFilter); - child_process_host_->AddFilter(new ProfilerMessageFilter); + child_process_host_->AddFilter(new content::ProfilerMessageFilter(type)); g_child_process_list.Get().push_back(this); content::GetContentClient()->browser()->BrowserChildProcessHostCreated(this); diff --git a/content/browser/profiler_controller_impl.cc b/content/browser/profiler_controller_impl.cc index b3e40e8..a850cfd 100644 --- a/content/browser/profiler_controller_impl.cc +++ b/content/browser/profiler_controller_impl.cc @@ -5,14 +5,13 @@ #include "content/browser/profiler_controller_impl.h" #include "base/bind.h" -#include "base/values.h" +#include "base/tracked_objects.h" #include "content/common/child_process_messages.h" #include "content/public/browser/browser_child_process_host_iterator.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_data.h" #include "content/public/browser/profiler_subscriber.h" #include "content/public/browser/render_process_host.h" -#include "content/public/common/process_type.h" using content::BrowserChildProcessHostIterator; using content::BrowserThread; @@ -43,20 +42,24 @@ void ProfilerControllerImpl::OnPendingProcesses(int sequence_number, void ProfilerControllerImpl::OnProfilerDataCollected( int sequence_number, - base::DictionaryValue* profiler_data) { + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ProfilerControllerImpl::OnProfilerDataCollected, base::Unretained(this), sequence_number, - profiler_data)); + profiler_data, + process_type)); return; } DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (subscriber_) - subscriber_->OnProfilerDataCollected(sequence_number, profiler_data); + if (subscriber_) { + subscriber_->OnProfilerDataCollected(sequence_number, profiler_data, + process_type); + } } void ProfilerControllerImpl::Register(ProfilerSubscriber* subscriber) { @@ -65,9 +68,9 @@ void ProfilerControllerImpl::Register(ProfilerSubscriber* subscriber) { subscriber_ = subscriber; } -void ProfilerControllerImpl::Unregister(ProfilerSubscriber* subscriber) { - if (subscriber == subscriber_) - subscriber_ = NULL; +void ProfilerControllerImpl::Unregister(const ProfilerSubscriber* subscriber) { + DCHECK_EQ(subscriber_, subscriber); + subscriber_ = NULL; } void ProfilerControllerImpl::GetProfilerDataFromChildProcesses( @@ -76,13 +79,9 @@ void ProfilerControllerImpl::GetProfilerDataFromChildProcesses( int pending_processes = 0; for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { - const std::string process_type = - content::GetProcessTypeNameInEnglish(iter.GetData().type); ++pending_processes; - if (!iter.Send(new ChildProcessMsg_GetChildProfilerData( - sequence_number, process_type))) { + if (!iter.Send(new ChildProcessMsg_GetChildProfilerData(sequence_number))) --pending_processes; - } } BrowserThread::PostTask( @@ -100,14 +99,11 @@ void ProfilerControllerImpl::GetProfilerData(int sequence_number) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); int pending_processes = 0; - const std::string render_process_type = - content::GetProcessTypeNameInEnglish(content::PROCESS_TYPE_RENDERER); - for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); !it.IsAtEnd(); it.Advance()) { ++pending_processes; - if (!it.GetCurrentValue()->Send(new ChildProcessMsg_GetChildProfilerData( - sequence_number, render_process_type))) { + if (!it.GetCurrentValue()->Send( + new ChildProcessMsg_GetChildProfilerData(sequence_number))) { --pending_processes; } } @@ -121,29 +117,4 @@ void ProfilerControllerImpl::GetProfilerData(int sequence_number) { sequence_number)); } -void ProfilerControllerImpl::SetProfilerStatusInChildProcesses( - tracked_objects::ThreadData::Status status) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) - iter.Send(new ChildProcessMsg_SetProfilerStatus(status)); -} - -void ProfilerControllerImpl::SetProfilerStatus( - tracked_objects::ThreadData::Status status) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&ProfilerControllerImpl::SetProfilerStatusInChildProcesses, - base::Unretained(this), - status)); - - for (content::RenderProcessHost::iterator it( - content::RenderProcessHost::AllHostsIterator()); - !it.IsAtEnd(); it.Advance()) { - it.GetCurrentValue()->Send(new ChildProcessMsg_SetProfilerStatus(status)); - } -} } // namespace content diff --git a/content/browser/profiler_controller_impl.h b/content/browser/profiler_controller_impl.h index ff361ea..13f9c33 100644 --- a/content/browser/profiler_controller_impl.h +++ b/content/browser/profiler_controller_impl.h @@ -6,14 +6,18 @@ #define CONTENT_BROWSER_PROFILER_CONTROLLER_IMPL_H_ #include "base/memory/singleton.h" -#include "base/tracked_objects.h" #include "content/common/content_export.h" #include "content/public/browser/profiler_controller.h" +#include "content/public/common/process_type.h" + +namespace tracked_objects { +struct ProcessDataSnapshot; +} namespace content { // ProfilerController's implementation. -class CONTENT_EXPORT ProfilerControllerImpl : public ProfilerController { +class ProfilerControllerImpl : public ProfilerController { public: static ProfilerControllerImpl* GetInstance(); @@ -22,21 +26,24 @@ class CONTENT_EXPORT ProfilerControllerImpl : public ProfilerController { ProfilerControllerImpl(); virtual ~ProfilerControllerImpl(); - // Send number of pending processes to subscriber_. |end| is set to true if it - // is the last time. This is called on UI thread. + // Notify the |subscriber_| that it should expect at least |pending_processes| + // additional calls to OnProfilerDataCollected(). OnPendingProcess() may be + // called repeatedly; the last call will have |end| set to true, indicating + // that there is no longer a possibility for the count of pending processes to + // increase. This is called on the UI thread. void OnPendingProcesses(int sequence_number, int pending_processes, bool end); - // Send profiler_data back to subscriber_. subscriber_ assumes the ownership - // of profiler_data. This is called on UI thread. - void OnProfilerDataCollected(int sequence_number, - base::DictionaryValue* profiler_data); + // Send the |profiler_data| back to the |subscriber_|. + // This is called on the UI thread. + void OnProfilerDataCollected( + int sequence_number, + const tracked_objects::ProcessDataSnapshot& profiler_data, + content::ProcessType process_type); // ProfilerController implementation: virtual void Register(ProfilerSubscriber* subscriber) OVERRIDE; - virtual void Unregister(ProfilerSubscriber* subscriber) OVERRIDE; + virtual void Unregister(const ProfilerSubscriber* subscriber) OVERRIDE; virtual void GetProfilerData(int sequence_number) OVERRIDE; - virtual void SetProfilerStatus( - tracked_objects::ThreadData::Status status) OVERRIDE; private: friend struct DefaultSingletonTraits<ProfilerControllerImpl>; @@ -44,10 +51,6 @@ class CONTENT_EXPORT ProfilerControllerImpl : public ProfilerController { // Contact child processes and get their profiler data. void GetProfilerDataFromChildProcesses(int sequence_number); - // Contact child processes and set profiler status to |enable|. - void SetProfilerStatusInChildProcesses( - tracked_objects::ThreadData::Status status); - ProfilerSubscriber* subscriber_; DISALLOW_COPY_AND_ASSIGN(ProfilerControllerImpl); @@ -56,4 +59,3 @@ class CONTENT_EXPORT ProfilerControllerImpl : public ProfilerController { } // namespace content #endif // CONTENT_BROWSER_PROFILER_CONTROLLER_IMPL_H_ - diff --git a/content/browser/profiler_message_filter.cc b/content/browser/profiler_message_filter.cc index e0f5979..d94c7d1 100644 --- a/content/browser/profiler_message_filter.cc +++ b/content/browser/profiler_message_filter.cc @@ -5,14 +5,13 @@ #include "content/browser/profiler_message_filter.h" #include "base/tracked_objects.h" -#include "base/values.h" #include "content/browser/profiler_controller_impl.h" #include "content/common/child_process_messages.h" -using content::BrowserMessageFilter; -using content::BrowserThread; +namespace content { -ProfilerMessageFilter::ProfilerMessageFilter() { +ProfilerMessageFilter::ProfilerMessageFilter(ProcessType process_type) + : process_type_(process_type) { } ProfilerMessageFilter::~ProfilerMessageFilter() { @@ -39,10 +38,9 @@ bool ProfilerMessageFilter::OnMessageReceived(const IPC::Message& message, void ProfilerMessageFilter::OnChildProfilerData( int sequence_number, - const base::DictionaryValue& profiler_data) { - base::DictionaryValue* dictionary_value = new base::DictionaryValue; - dictionary_value->MergeDictionary(&profiler_data); - // OnProfilerDataCollected assumes the ownership of profiler_data. - content::ProfilerControllerImpl::GetInstance()->OnProfilerDataCollected( - sequence_number, dictionary_value); + const tracked_objects::ProcessDataSnapshot& profiler_data) { + ProfilerControllerImpl::GetInstance()->OnProfilerDataCollected( + sequence_number, profiler_data, process_type_); +} + } diff --git a/content/browser/profiler_message_filter.h b/content/browser/profiler_message_filter.h index 79e6e94..d403765 100644 --- a/content/browser/profiler_message_filter.h +++ b/content/browser/profiler_message_filter.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,15 +8,18 @@ #include <string> #include "content/public/browser/browser_message_filter.h" +#include "content/public/common/process_type.h" -namespace base { -class DictionaryValue; +namespace tracked_objects { +struct ProcessDataSnapshot; } +namespace content { + // This class sends and receives profiler messages in the browser process. -class ProfilerMessageFilter : public content::BrowserMessageFilter { +class ProfilerMessageFilter : public BrowserMessageFilter { public: - ProfilerMessageFilter(); + explicit ProfilerMessageFilter(ProcessType process_type); virtual ~ProfilerMessageFilter(); // content::BrowserMessageFilter implementation. @@ -28,11 +31,15 @@ class ProfilerMessageFilter : public content::BrowserMessageFilter { private: // Message handlers. - void OnChildProfilerData(int sequence_number, - const base::DictionaryValue& profiler_data); + void OnChildProfilerData( + int sequence_number, + const tracked_objects::ProcessDataSnapshot& profiler_data); + + ProcessType process_type_; DISALLOW_COPY_AND_ASSIGN(ProfilerMessageFilter); }; -#endif // CONTENT_BROWSER_PROFILER_MESSAGE_FILTER_H_ +} // namespace content +#endif // CONTENT_BROWSER_PROFILER_MESSAGE_FILTER_H_ diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index e38a37f..0fae676 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -544,7 +544,8 @@ void RenderProcessHostImpl::CreateMessageFilters() { content::BrowserContext::GetQuotaManager(browser_context), content::GetContentClient()->browser()->CreateQuotaPermissionContext())); channel_->AddFilter(new content::GamepadBrowserMessageFilter(this)); - channel_->AddFilter(new ProfilerMessageFilter()); + channel_->AddFilter(new content::ProfilerMessageFilter( + content::PROCESS_TYPE_RENDERER)); } int RenderProcessHostImpl::GetNextRoutingID() { diff --git a/content/common/child_process_messages.h b/content/common/child_process_messages.h index ab56be0..363c556 100644 --- a/content/common/child_process_messages.h +++ b/content/common/child_process_messages.h @@ -14,6 +14,44 @@ IPC_ENUM_TRAITS(tracked_objects::ThreadData::Status) +IPC_STRUCT_TRAITS_BEGIN(tracked_objects::LocationSnapshot) + IPC_STRUCT_TRAITS_MEMBER(file_name) + IPC_STRUCT_TRAITS_MEMBER(function_name) + IPC_STRUCT_TRAITS_MEMBER(line_number) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(tracked_objects::BirthOnThreadSnapshot) + IPC_STRUCT_TRAITS_MEMBER(location) + IPC_STRUCT_TRAITS_MEMBER(thread_name) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(tracked_objects::DeathDataSnapshot) + IPC_STRUCT_TRAITS_MEMBER(count) + IPC_STRUCT_TRAITS_MEMBER(run_duration_sum) + IPC_STRUCT_TRAITS_MEMBER(run_duration_max) + IPC_STRUCT_TRAITS_MEMBER(run_duration_sample) + IPC_STRUCT_TRAITS_MEMBER(queue_duration_sum) + IPC_STRUCT_TRAITS_MEMBER(queue_duration_max) + IPC_STRUCT_TRAITS_MEMBER(queue_duration_sample) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(tracked_objects::TaskSnapshot) + IPC_STRUCT_TRAITS_MEMBER(birth) + IPC_STRUCT_TRAITS_MEMBER(death_data) + IPC_STRUCT_TRAITS_MEMBER(death_thread_name) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(tracked_objects::ParentChildPairSnapshot) + IPC_STRUCT_TRAITS_MEMBER(parent) + IPC_STRUCT_TRAITS_MEMBER(child) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(tracked_objects::ProcessDataSnapshot) + IPC_STRUCT_TRAITS_MEMBER(tasks) + IPC_STRUCT_TRAITS_MEMBER(descendants) + IPC_STRUCT_TRAITS_MEMBER(process_id) +IPC_STRUCT_TRAITS_END() + #undef IPC_MESSAGE_EXPORT #define IPC_MESSAGE_EXPORT CONTENT_EXPORT @@ -48,9 +86,8 @@ IPC_MESSAGE_CONTROL1(ChildProcessMsg_SetProfilerStatus, // Send to all the child processes to send back profiler data (ThreadData in // tracked_objects). -IPC_MESSAGE_CONTROL2(ChildProcessMsg_GetChildProfilerData, - int, /* sequence number. */ - std::string /* pickled Value of process type. */) +IPC_MESSAGE_CONTROL1(ChildProcessMsg_GetChildProfilerData, + int /* sequence number */) // Sent to child processes to dump their handle table. IPC_MESSAGE_CONTROL0(ChildProcessMsg_DumpHandles) @@ -80,8 +117,8 @@ IPC_MESSAGE_CONTROL1(ChildProcessHostMsg_TraceBufferPercentFullReply, // Send back profiler data (ThreadData in tracked_objects). IPC_MESSAGE_CONTROL2(ChildProcessHostMsg_ChildProfilerData, - int, /* sequence number. */ - DictionaryValue /* profiler data. */) + int, /* sequence number */ + tracked_objects::ProcessDataSnapshot /* profiler data */) // Reply to ChildProcessMsg_DumpHandles when handle table dump is complete. IPC_MESSAGE_CONTROL0(ChildProcessHostMsg_DumpHandlesDone) diff --git a/content/common/child_thread.cc b/content/common/child_thread.cc index c4061b3..9277364 100644 --- a/content/common/child_thread.cc +++ b/content/common/child_thread.cc @@ -7,7 +7,6 @@ #include "base/command_line.h" #include "base/message_loop.h" #include "base/process.h" -#include "base/process_util.h" #include "base/string_util.h" #include "base/tracked_objects.h" #include "content/common/child_process.h" @@ -28,6 +27,8 @@ #include "content/common/handle_enumerator_win.h" #endif +using tracked_objects::ThreadData; + ChildThread::ChildThread() { channel_name_ = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kProcessChannelID); @@ -219,21 +220,16 @@ void ChildThread::OnSetIPCLoggingEnabled(bool enable) { } #endif // IPC_MESSAGE_LOG_ENABLED -void ChildThread::OnSetProfilerStatus( - tracked_objects::ThreadData::Status status) { - tracked_objects::ThreadData::InitializeAndSetTrackingStatus(status); +void ChildThread::OnSetProfilerStatus(ThreadData::Status status) { + ThreadData::InitializeAndSetTrackingStatus(status); } -void ChildThread::OnGetChildProfilerData( - int sequence_number, - const std::string& process_type) { - scoped_ptr<base::DictionaryValue> value( - tracked_objects::ThreadData::ToValue(false)); - value->SetString("process_type", process_type); - value->SetInteger("process_id", base::GetCurrentProcId()); +void ChildThread::OnGetChildProfilerData(int sequence_number) { + tracked_objects::ProcessDataSnapshot process_data; + ThreadData::Snapshot(false, &process_data); - Send(new ChildProcessHostMsg_ChildProfilerData( - sequence_number, *value.get())); + Send(new ChildProcessHostMsg_ChildProfilerData(sequence_number, + process_data)); } void ChildThread::OnDumpHandles() { diff --git a/content/common/child_thread.h b/content/common/child_thread.h index a63e87c..2be2b57 100644 --- a/content/common/child_thread.h +++ b/content/common/child_thread.h @@ -100,8 +100,7 @@ class CONTENT_EXPORT ChildThread : public IPC::Channel::Listener, #endif virtual void OnSetProfilerStatus(tracked_objects::ThreadData::Status status); - virtual void OnGetChildProfilerData(int sequence_number, - const std::string& process_type); + virtual void OnGetChildProfilerData(int sequence_number); virtual void OnDumpHandles(); diff --git a/content/public/browser/profiler_controller.h b/content/public/browser/profiler_controller.h index 9627f52..e9fbe6a 100644 --- a/content/public/browser/profiler_controller.h +++ b/content/public/browser/profiler_controller.h @@ -39,18 +39,12 @@ class CONTENT_EXPORT ProfilerController { // Unregister the subscriber so that it will not be called when for example // OnProfilerDataCollected is returning profiler data from a child process. // Safe to call even if caller is not the current subscriber. - virtual void Unregister(ProfilerSubscriber* subscriber) = 0; + virtual void Unregister(const ProfilerSubscriber* subscriber) = 0; // Contact all processes and get their profiler data. virtual void GetProfilerData(int sequence_number) = 0; - - // Contact all processes and set profiler status to |enable|. - virtual void SetProfilerStatus( - tracked_objects::ThreadData::Status status) = 0; - }; } // namespace content #endif // CONTENT_PUBLIC_BROWSER_PROFILER_CONTROLLER_H_ - diff --git a/content/public/browser/profiler_subscriber.h b/content/public/browser/profiler_subscriber.h index f37947a..f6679d9 100644 --- a/content/public/browser/profiler_subscriber.h +++ b/content/public/browser/profiler_subscriber.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,9 +6,10 @@ #define CONTENT_PUBLIC_BROWSER_PROFILER_SUBSCRIBER_H_ #include "content/common/content_export.h" +#include "content/public/common/process_type.h" -namespace base { -class DictionaryValue; +namespace tracked_objects { +struct ProcessDataSnapshot; } namespace content { @@ -19,16 +20,17 @@ class CONTENT_EXPORT ProfilerSubscriber { virtual ~ProfilerSubscriber() {} // Send number of pending processes to subscriber. |end| is set to true if it - // is the last time. This is called on UI thread. + // is the last time. This is called on the UI thread. virtual void OnPendingProcesses(int sequence_number, int pending_processes, bool end) = 0; - // Send profiler_data back to subscriber. - // This is called on UI thread. + // Send |profiler_data| back to subscriber. + // This is called on the UI thread. virtual void OnProfilerDataCollected( int sequence_number, - base::DictionaryValue* profiler_data) = 0; + const tracked_objects::ProcessDataSnapshot& profiler_data, + ProcessType process_type) = 0; }; } // namespace content diff --git a/remoting/host/plugin/host_script_object.cc b/remoting/host/plugin/host_script_object.cc index 334df058..7370f93 100644 --- a/remoting/host/plugin/host_script_object.cc +++ b/remoting/host/plugin/host_script_object.cc @@ -13,6 +13,7 @@ #include "base/sys_string_conversions.h" #include "base/threading/platform_thread.h" #include "base/utf_string_conversions.h" +#include "base/values.h" #include "net/base/net_util.h" #include "remoting/base/auth_token_util.h" #include "remoting/host/chromoting_host.h" |