diff options
author | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-13 00:39:26 +0000 |
---|---|---|
committer | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-13 00:39:26 +0000 |
commit | 1cb05dbbcafd3f35999606af6c8316058ce7b93a (patch) | |
tree | dfa6794b8a9c83a91d4c1bb487ab7551e3514fe3 /chrome/browser/task_profiler | |
parent | 543f27572597201d5fae77066c9146a6a87d41d8 (diff) | |
download | chromium_src-1cb05dbbcafd3f35999606af6c8316058ce7b93a.zip chromium_src-1cb05dbbcafd3f35999606af6c8316058ce7b93a.tar.gz chromium_src-1cb05dbbcafd3f35999606af6c8316058ce7b93a.tar.bz2 |
[UMA] Use proper C++ objects to serialize tracked_objects across process boundaries.
See https://chromiumcodereview.appspot.com/9702014/ for the original code review. Uploading to a new issue due to an AppEngine error...
BUG=103480
TEST=none (refactoring, no functional change expected)
TBR=jam@chromium.org,jar@chromium.org,eroman@chromium.org,jhawkins@chromium.org,ajwong@chromium.org
Review URL: http://codereview.chromium.org/10077001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132109 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/task_profiler')
3 files changed, 303 insertions, 6 deletions
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\"" + "}"); + } +} |