summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/location.cc24
-rw-r--r--base/location.h22
-rw-r--r--base/tracked_objects.cc278
-rw-r--r--base/tracked_objects.h234
-rw-r--r--base/tracked_objects_unittest.cc816
-rw-r--r--chrome/browser/metrics/tracking_synchronizer.cc126
-rw-r--r--chrome/browser/metrics/tracking_synchronizer.h48
-rw-r--r--chrome/browser/metrics/tracking_synchronizer_observer.h42
-rw-r--r--chrome/browser/resources/profiler/profiler.js6
-rw-r--r--chrome/browser/task_profiler/task_profiler_data_serializer.cc125
-rw-r--r--chrome/browser/task_profiler/task_profiler_data_serializer.h23
-rw-r--r--chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc161
-rw-r--r--chrome/browser/ui/webui/profiler_ui.cc32
-rw-r--r--chrome/browser/ui/webui/profiler_ui.h19
-rw-r--r--chrome/chrome_browser.gypi5
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chromeos/dbus/flimflam_client_unittest_base.cc1
-rw-r--r--chromeos/dbus/flimflam_ipconfig_client.cc1
-rw-r--r--chromeos/dbus/flimflam_manager_client.cc1
-rw-r--r--chromeos/dbus/flimflam_manager_client_unittest.cc1
-rw-r--r--chromeos/dbus/flimflam_network_client_unittest.cc1
-rw-r--r--chromeos/dbus/flimflam_profile_client.cc1
-rw-r--r--content/browser/browser_child_process_host_impl.cc2
-rw-r--r--content/browser/profiler_controller_impl.cc59
-rw-r--r--content/browser/profiler_controller_impl.h34
-rw-r--r--content/browser/profiler_message_filter.cc18
-rw-r--r--content/browser/profiler_message_filter.h23
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc3
-rw-r--r--content/common/child_process_messages.h47
-rw-r--r--content/common/child_thread.cc22
-rw-r--r--content/common/child_thread.h3
-rw-r--r--content/public/browser/profiler_controller.h8
-rw-r--r--content/public/browser/profiler_subscriber.h16
-rw-r--r--remoting/host/plugin/host_script_object.cc1
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_(&current) {
}
-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"