summaryrefslogtreecommitdiff
path: root/chromium/services/tracing
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-03 13:42:47 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:27:51 +0000
commit8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (patch)
treed29d987c4d7b173cf853279b79a51598f104b403 /chromium/services/tracing
parent830c9e163d31a9180fadca926b3e1d7dfffb5021 (diff)
downloadqtwebengine-chromium-8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec.tar.gz
BASELINE: Update Chromium to 66.0.3359.156
Change-Id: I0c9831ad39911a086b6377b16f995ad75a51e441 Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/services/tracing')
-rw-r--r--chromium/services/tracing/BUILD.gn92
-rw-r--r--chromium/services/tracing/DEPS3
-rw-r--r--chromium/services/tracing/OWNERS8
-rw-r--r--chromium/services/tracing/agent_registry.cc143
-rw-r--r--chromium/services/tracing/agent_registry.h127
-rw-r--r--chromium/services/tracing/agent_registry_unittest.cc128
-rw-r--r--chromium/services/tracing/coordinator.cc586
-rw-r--r--chromium/services/tracing/coordinator.h119
-rw-r--r--chromium/services/tracing/coordinator_unittest.cc411
-rw-r--r--chromium/services/tracing/manifest.json21
-rw-r--r--chromium/services/tracing/public/cpp/BUILD.gn22
-rw-r--r--chromium/services/tracing/public/cpp/base_agent.cc57
-rw-r--r--chromium/services/tracing/public/cpp/base_agent.h51
-rw-r--r--chromium/services/tracing/public/cpp/chrome_trace_event_agent.cc122
-rw-r--r--chromium/services/tracing/public/cpp/chrome_trace_event_agent.h70
-rw-r--r--chromium/services/tracing/public/cpp/chrome_trace_event_agent_unittest.cc186
-rw-r--r--chromium/services/tracing/public/mojom/BUILD.gn22
-rw-r--r--chromium/services/tracing/public/mojom/OWNERS2
-rw-r--r--chromium/services/tracing/public/mojom/constants.mojom13
-rw-r--r--chromium/services/tracing/public/mojom/tracing.mojom77
-rw-r--r--chromium/services/tracing/recorder.cc47
-rw-r--r--chromium/services/tracing/recorder.h70
-rw-r--r--chromium/services/tracing/recorder_unittest.cc135
-rw-r--r--chromium/services/tracing/service_main.cc12
-rw-r--r--chromium/services/tracing/test_util.cc55
-rw-r--r--chromium/services/tracing/test_util.h55
-rw-r--r--chromium/services/tracing/tracing_service.cc47
-rw-r--r--chromium/services/tracing/tracing_service.h58
-rw-r--r--chromium/services/tracing/tracing_service_unittest.cc52
-rw-r--r--chromium/services/tracing/unittest_manifest.json11
30 files changed, 2802 insertions, 0 deletions
diff --git a/chromium/services/tracing/BUILD.gn b/chromium/services/tracing/BUILD.gn
new file mode 100644
index 00000000000..157a1a7bb8e
--- /dev/null
+++ b/chromium/services/tracing/BUILD.gn
@@ -0,0 +1,92 @@
+# Copyright 2018 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.
+
+# There should be only one tracing service. It is currently
+# in the browser process. So, only //content/browser should link to this target.
+# Others modules should only need the public targets.
+import("//services/catalog/public/tools/catalog.gni")
+import("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
+
+source_set("lib") {
+ sources = [
+ "agent_registry.cc",
+ "agent_registry.h",
+ "coordinator.cc",
+ "coordinator.h",
+ "recorder.cc",
+ "recorder.h",
+ "tracing_service.cc",
+ "tracing_service.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//mojo/common",
+ "//mojo/public/cpp/bindings",
+ "//services/tracing/public/cpp",
+ ]
+}
+
+service_manifest("manifest") {
+ name = "tracing"
+ source = "manifest.json"
+}
+
+service("tracing") {
+ sources = [
+ "service_main.cc",
+ ]
+
+ deps = [
+ ":lib",
+ "//mojo/public/cpp/system",
+ ]
+}
+
+source_set("tests") {
+ testonly = true
+
+ sources = [
+ "agent_registry_unittest.cc",
+ "coordinator_unittest.cc",
+ "public/cpp/chrome_trace_event_agent_unittest.cc",
+ "recorder_unittest.cc",
+ "test_util.cc",
+ "test_util.h",
+ ]
+
+ if (!is_android) {
+ sources += [ "tracing_service_unittest.cc" ]
+ }
+
+ deps = [
+ ":lib",
+ "//base",
+ "//base/test:test_support",
+ "//mojo/public/cpp/bindings",
+ "//services/service_manager/public/cpp",
+ "//services/service_manager/public/cpp:service_test_support",
+ "//services/service_manager/public/mojom",
+ "//services/tracing:lib",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+
+ data_deps = [
+ ":tracing",
+ ]
+}
+
+service_manifest("unittest_manifest") {
+ name = "tracing_unittests"
+ source = "unittest_manifest.json"
+}
+
+catalog("tests_catalog") {
+ testonly = true
+ embedded_services = [ ":unittest_manifest" ]
+ standalone_services = [ ":manifest" ]
+}
diff --git a/chromium/services/tracing/DEPS b/chromium/services/tracing/DEPS
new file mode 100644
index 00000000000..5f9afdbbf6f
--- /dev/null
+++ b/chromium/services/tracing/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+services/service_manager/public"
+]
diff --git a/chromium/services/tracing/OWNERS b/chromium/services/tracing/OWNERS
new file mode 100644
index 00000000000..041b283cbdc
--- /dev/null
+++ b/chromium/services/tracing/OWNERS
@@ -0,0 +1,8 @@
+file://base/trace_event/OWNERS
+chiniforooshan@chromium.org
+
+per-file manifest.json=set noparent
+per-file manifest.json=file://ipc/SECURITY_OWNERS
+
+per-file unittest_manifest.json=set noparent
+per-file unittest_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chromium/services/tracing/agent_registry.cc b/chromium/services/tracing/agent_registry.cc
new file mode 100644
index 00000000000..5dceb1513a8
--- /dev/null
+++ b/chromium/services/tracing/agent_registry.cc
@@ -0,0 +1,143 @@
+// Copyright 2017 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 "services/tracing/agent_registry.h"
+
+#include <string>
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/threading/thread_checker.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace {
+tracing::AgentRegistry* g_agent_registry;
+}
+
+namespace tracing {
+
+AgentRegistry::AgentEntry::AgentEntry(
+ std::unique_ptr<service_manager::ServiceContextRef> service_ref,
+ size_t id,
+ AgentRegistry* agent_registry,
+ mojom::AgentPtr agent,
+ const std::string& label,
+ mojom::TraceDataType type,
+ bool supports_explicit_clock_sync)
+ : id_(id),
+ agent_registry_(agent_registry),
+ agent_(std::move(agent)),
+ label_(label),
+ type_(type),
+ supports_explicit_clock_sync_(supports_explicit_clock_sync),
+ is_tracing_(false),
+ service_ref_(std::move(service_ref)) {
+ DCHECK(!label.empty());
+ agent_.set_connection_error_handler(base::BindRepeating(
+ &AgentRegistry::AgentEntry::OnConnectionError, AsWeakPtr()));
+}
+
+AgentRegistry::AgentEntry::~AgentEntry() = default;
+
+void AgentRegistry::AgentEntry::AddDisconnectClosure(
+ const void* closure_name,
+ base::OnceClosure closure) {
+ DCHECK_EQ(0u, closures_.count(closure_name));
+ closures_[closure_name] = std::move(closure);
+}
+
+bool AgentRegistry::AgentEntry::RemoveDisconnectClosure(
+ const void* closure_name) {
+ return closures_.erase(closure_name) > 0;
+}
+
+bool AgentRegistry::AgentEntry::HasDisconnectClosure(const void* closure_name) {
+ return closures_.count(closure_name) > 0;
+}
+
+void AgentRegistry::AgentEntry::OnConnectionError() {
+ // Run disconnect closures if there is any. We should mark |key_value.second|
+ // as movable so that the version of |Run| that takes an rvalue reference is
+ // selected not the version that takes a const reference. The former is for
+ // once callbacks and the latter is for repeating callbacks.
+ while (!closures_.empty()) {
+ auto iterator = closures_.begin();
+ auto callback = std::move(iterator->second);
+ const size_t closures_size = closures_.size();
+ std::move(callback).Run();
+ // Verify that the callback has removed itself.
+ DCHECK_EQ(1u, closures_size - closures_.size());
+ }
+ agent_registry_->UnregisterAgent(id_);
+}
+
+// static
+AgentRegistry* AgentRegistry::GetInstance() {
+ return g_agent_registry;
+}
+
+AgentRegistry::AgentRegistry(
+ service_manager::ServiceContextRefFactory* service_ref_factory)
+ : service_ref_factory_(service_ref_factory) {
+ DCHECK(!g_agent_registry);
+ g_agent_registry = this;
+}
+
+AgentRegistry::~AgentRegistry() {
+ // For testing only.
+ g_agent_registry = nullptr;
+}
+
+void AgentRegistry::BindAgentRegistryRequest(
+ mojom::AgentRegistryRequest request,
+ const service_manager::BindSourceInfo& source_info) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ bindings_.AddBinding(this, std::move(request), source_info.identity);
+}
+
+void AgentRegistry::SetAgentInitializationCallback(
+ const AgentInitializationCallback& callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ agent_initialization_callback_ = callback;
+ ForAllAgents([this](AgentEntry* agent_entry) {
+ agent_initialization_callback_.Run(agent_entry);
+ });
+}
+
+void AgentRegistry::RemoveAgentInitializationCallback() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ agent_initialization_callback_.Reset();
+}
+
+bool AgentRegistry::HasDisconnectClosure(const void* closure_name) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ for (const auto& key_value : agents_) {
+ if (key_value.second->HasDisconnectClosure(closure_name))
+ return true;
+ }
+ return false;
+}
+
+void AgentRegistry::RegisterAgent(mojom::AgentPtr agent,
+ const std::string& label,
+ mojom::TraceDataType type,
+ bool supports_explicit_clock_sync) {
+ auto id = next_agent_id_++;
+ auto entry = std::make_unique<AgentEntry>(service_ref_factory_->CreateRef(),
+ id, this, std::move(agent), label,
+ type, supports_explicit_clock_sync);
+ if (!agent_initialization_callback_.is_null())
+ agent_initialization_callback_.Run(entry.get());
+ auto result = agents_.insert(std::make_pair(id, std::move(entry)));
+ DCHECK(result.second);
+}
+
+void AgentRegistry::UnregisterAgent(size_t agent_id) {
+ size_t num_deleted = agents_.erase(agent_id);
+ DCHECK_EQ(1u, num_deleted);
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/agent_registry.h b/chromium/services/tracing/agent_registry.h
new file mode 100644
index 00000000000..91cb7b60363
--- /dev/null
+++ b/chromium/services/tracing/agent_registry.h
@@ -0,0 +1,127 @@
+// Copyright 2017 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 SERVICES_TRACING_AGENT_REGISTRY_H_
+#define SERVICES_TRACING_AGENT_REGISTRY_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/service_manager/public/cpp/identity.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace service_manager {
+struct BindSourceInfo;
+class ServiceContextRef;
+class ServiceContextRefFactory;
+} // namespace service_manager
+
+namespace tracing {
+
+class AgentRegistry : public mojom::AgentRegistry {
+ public:
+ class AgentEntry : public base::SupportsWeakPtr<AgentEntry> {
+ public:
+ AgentEntry(std::unique_ptr<service_manager::ServiceContextRef> service_ref,
+ size_t id,
+ AgentRegistry* agent_registry,
+ mojom::AgentPtr agent,
+ const std::string& label,
+ mojom::TraceDataType type,
+ bool supports_explicit_clock_sync);
+ ~AgentEntry();
+
+ void AddDisconnectClosure(const void* closure_name,
+ base::OnceClosure closure);
+ bool RemoveDisconnectClosure(const void* closure_name);
+ bool HasDisconnectClosure(const void* closure_name);
+ size_t num_disconnect_closures_for_testing() const {
+ return closures_.size();
+ }
+
+ mojom::Agent* agent() const { return agent_.get(); }
+ const std::string& label() const { return label_; }
+ mojom::TraceDataType type() const { return type_; }
+ bool supports_explicit_clock_sync() const {
+ return supports_explicit_clock_sync_;
+ }
+ bool is_tracing() const { return is_tracing_; }
+ void set_is_tracing(bool is_tracing) { is_tracing_ = is_tracing; }
+
+ private:
+ void OnConnectionError();
+
+ const size_t id_;
+ AgentRegistry* agent_registry_;
+ mojom::AgentPtr agent_;
+ const std::string label_;
+ const mojom::TraceDataType type_;
+ const bool supports_explicit_clock_sync_;
+ std::map<const void*, base::OnceClosure> closures_;
+ bool is_tracing_;
+ std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+
+ DISALLOW_COPY_AND_ASSIGN(AgentEntry);
+ };
+
+ // A function to be run for every agent that registers itself.
+ using AgentInitializationCallback =
+ base::RepeatingCallback<void(AgentEntry*)>;
+
+ static AgentRegistry* GetInstance();
+
+ explicit AgentRegistry(
+ service_manager::ServiceContextRefFactory* service_ref_factory);
+
+ void BindAgentRegistryRequest(
+ mojom::AgentRegistryRequest request,
+ const service_manager::BindSourceInfo& source_info);
+ void SetAgentInitializationCallback(
+ const AgentInitializationCallback& callback);
+ void RemoveAgentInitializationCallback();
+ bool HasDisconnectClosure(const void* closure_name);
+
+ template <typename FunctionType>
+ void ForAllAgents(FunctionType function) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ for (const auto& key_value : agents_) {
+ function(key_value.second.get());
+ }
+ }
+
+ private:
+ friend std::default_delete<AgentRegistry>;
+ friend class AgentRegistryTest; // For testing.
+ friend class CoordinatorTest; // For testing.
+
+ ~AgentRegistry() override;
+
+ // mojom::AgentRegistry
+ void RegisterAgent(mojom::AgentPtr agent,
+ const std::string& label,
+ mojom::TraceDataType type,
+ bool supports_explicit_clock_sync) override;
+
+ void UnregisterAgent(size_t agent_id);
+
+ mojo::BindingSet<mojom::AgentRegistry, service_manager::Identity> bindings_;
+ size_t next_agent_id_ = 0;
+ std::map<size_t, std::unique_ptr<AgentEntry>> agents_;
+ AgentInitializationCallback agent_initialization_callback_;
+ service_manager::ServiceContextRefFactory* service_ref_factory_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(AgentRegistry);
+};
+
+} // namespace tracing
+
+#endif // SERVICES_TRACING_AGENT_REGISTRY_H_
diff --git a/chromium/services/tracing/agent_registry_unittest.cc b/chromium/services/tracing/agent_registry_unittest.cc
new file mode 100644
index 00000000000..7a75d0049a9
--- /dev/null
+++ b/chromium/services/tracing/agent_registry_unittest.cc
@@ -0,0 +1,128 @@
+// Copyright 2017 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 "services/tracing/agent_registry.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+#include "services/tracing/test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+class AgentRegistryTest : public testing::Test {
+ public:
+ AgentRegistryTest() : service_ref_factory_(base::DoNothing()) {}
+
+ void SetUp() override {
+ message_loop_.reset(new base::MessageLoop());
+ registry_.reset(new AgentRegistry(&service_ref_factory_));
+ }
+
+ void TearDown() override {
+ registry_.reset();
+ message_loop_.reset();
+ }
+
+ void RegisterAgent(mojom::AgentPtr agent,
+ const std::string& label,
+ mojom::TraceDataType type,
+ bool supports_explicit_clock_sync) {
+ registry_->RegisterAgent(std::move(agent), label, type,
+ supports_explicit_clock_sync);
+ }
+
+ void RegisterAgent(mojom::AgentPtr agent) {
+ registry_->RegisterAgent(std::move(agent), "label",
+ mojom::TraceDataType::ARRAY, false);
+ }
+
+ std::unique_ptr<AgentRegistry> registry_;
+ service_manager::ServiceContextRefFactory service_ref_factory_;
+
+ private:
+ std::unique_ptr<base::MessageLoop> message_loop_;
+};
+
+TEST_F(AgentRegistryTest, RegisterAgent) {
+ MockAgent agent1;
+ RegisterAgent(agent1.CreateAgentPtr(), "TraceEvent",
+ mojom::TraceDataType::ARRAY, false);
+ size_t num_agents = 0;
+ registry_->ForAllAgents([&num_agents](AgentRegistry::AgentEntry* entry) {
+ num_agents++;
+ EXPECT_EQ("TraceEvent", entry->label());
+ EXPECT_EQ(mojom::TraceDataType::ARRAY, entry->type());
+ EXPECT_FALSE(entry->supports_explicit_clock_sync());
+ });
+ EXPECT_EQ(1u, num_agents);
+
+ MockAgent agent2;
+ RegisterAgent(agent2.CreateAgentPtr(), "Power", mojom::TraceDataType::STRING,
+ true);
+ num_agents = 0;
+ registry_->ForAllAgents([&num_agents](AgentRegistry::AgentEntry* entry) {
+ num_agents++;
+ // Properties of |agent1| is already verified.
+ if (entry->label() == "TraceEvent")
+ return;
+ EXPECT_EQ("Power", entry->label());
+ EXPECT_EQ(mojom::TraceDataType::STRING, entry->type());
+ EXPECT_TRUE(entry->supports_explicit_clock_sync());
+ });
+ EXPECT_EQ(2u, num_agents);
+}
+
+TEST_F(AgentRegistryTest, UnregisterAgent) {
+ base::RunLoop run_loop;
+ MockAgent agent1;
+ RegisterAgent(agent1.CreateAgentPtr());
+ {
+ MockAgent agent2;
+ RegisterAgent(agent2.CreateAgentPtr());
+ size_t num_agents = 0;
+ registry_->ForAllAgents(
+ [&num_agents](AgentRegistry::AgentEntry* entry) { num_agents++; });
+ EXPECT_EQ(2u, num_agents);
+ }
+ run_loop.RunUntilIdle();
+
+ // |agent2| is not alive anymore.
+ size_t num_agents = 0;
+ registry_->ForAllAgents(
+ [&num_agents](AgentRegistry::AgentEntry* entry) { num_agents++; });
+ EXPECT_EQ(1u, num_agents);
+}
+
+TEST_F(AgentRegistryTest, AgentInitialization) {
+ size_t num_calls = 0;
+ MockAgent agent1;
+ RegisterAgent(agent1.CreateAgentPtr());
+ registry_->SetAgentInitializationCallback(base::BindRepeating(
+ [](size_t* num_calls, tracing::AgentRegistry::AgentEntry* entry) {
+ (*num_calls)++;
+ },
+ base::Unretained(&num_calls)));
+ // Since an agent was already registered, the callback should be run once.
+ EXPECT_EQ(1u, num_calls);
+
+ // The callback should be run on future agents, too.
+ MockAgent agent2;
+ RegisterAgent(agent2.CreateAgentPtr());
+ EXPECT_EQ(2u, num_calls);
+
+ // The callback should not be run on future agents if it is removed.
+ registry_->RemoveAgentInitializationCallback();
+ MockAgent agent3;
+ RegisterAgent(agent3.CreateAgentPtr());
+ EXPECT_EQ(2u, num_calls);
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/coordinator.cc b/chromium/services/tracing/coordinator.cc
new file mode 100644
index 00000000000..795abd0f371
--- /dev/null
+++ b/chromium/services/tracing/coordinator.cc
@@ -0,0 +1,586 @@
+// Copyright 2017 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 "services/tracing/coordinator.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
+#include "base/guid.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_config.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "services/tracing/agent_registry.h"
+#include "services/tracing/public/mojom/constants.mojom.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+#include "services/tracing/recorder.h"
+
+namespace {
+
+const char kMetadataTraceLabel[] = "metadata";
+
+const char kGetCategoriesClosureName[] = "GetCategoriesClosure";
+const char kRequestBufferUsageClosureName[] = "RequestBufferUsageClosure";
+const char kRequestClockSyncMarkerClosureName[] =
+ "RequestClockSyncMarkerClosure";
+const char kStartTracingClosureName[] = "StartTracingClosure";
+
+tracing::Coordinator* g_coordinator = nullptr;
+
+} // namespace
+
+namespace tracing {
+
+class Coordinator::TraceStreamer : public base::SupportsWeakPtr<TraceStreamer> {
+ public:
+ // Constructed on |main_task_runner_|.
+ TraceStreamer(
+ mojo::ScopedDataPipeProducerHandle stream,
+ const std::string& agent_label,
+ const scoped_refptr<base::SequencedTaskRunner>& main_task_runner,
+ base::WeakPtr<Coordinator> coordinator)
+ : stream_(std::move(stream)),
+ agent_label_(agent_label),
+ main_task_runner_(main_task_runner),
+ coordinator_(coordinator),
+ metadata_(new base::DictionaryValue()),
+ stream_is_empty_(true),
+ json_field_name_written_(false) {}
+
+ // Destroyed on |background_task_runner_|.
+ ~TraceStreamer() = default;
+
+ // Called from |background_task_runner_|.
+ void CreateAndSendRecorder(
+ const std::string& label,
+ mojom::TraceDataType type,
+ base::WeakPtr<AgentRegistry::AgentEntry> agent_entry) {
+ mojom::RecorderPtr ptr;
+ auto recorder = std::make_unique<Recorder>(
+ MakeRequest(&ptr), type,
+ base::BindRepeating(&Coordinator::TraceStreamer::OnRecorderDataChange,
+ AsWeakPtr(), label));
+ recorders_[label].insert(std::move(recorder));
+ DCHECK(type != mojom::TraceDataType::STRING ||
+ recorders_[label].size() == 1);
+
+ // Tracing agent proxies are bound on the main thread and should be called
+ // from the main thread.
+ main_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&Coordinator::SendRecorder, coordinator_,
+ agent_entry, std::move(ptr)));
+ }
+
+ // Called from |background_task_runner_| to close the recorder proxy on the
+ // correct task runner.
+ void CloseRecorder(mojom::RecorderPtr recorder) {}
+
+ // Called from |main_task_runner_| either after flushing is complete or at
+ // shutdown. We either will not write to the stream afterwards or do not care
+ // what happens to what we try to write.
+ void CloseStream() {
+ DCHECK(stream_.is_valid());
+ stream_.reset();
+ }
+
+ // Called from |main_task_runner_| after flushing is completed. So we are sure
+ // there is no race in accessing metadata_.
+ std::unique_ptr<base::DictionaryValue> GetMetadata() {
+ return std::move(metadata_);
+ }
+
+ private:
+ // Called from |background_task_runner_|.
+ void OnRecorderDataChange(const std::string& label) {
+ // Bail out if we are in the middle of writing events for another label to
+ // the stream, since we do not want to interleave chunks for different
+ // fields. For example, we do not want to mix |traceEvent| chunks with
+ // |battor| chunks.
+ //
+ // If we receive a |battor| chunk from an agent while writing |traceEvent|
+ // chunks to the stream, we wait until all agents that send |traceEvent|
+ // chunks are done, and then, we start writing |battor| chunks.
+ if (!streaming_label_.empty() && streaming_label_ != label)
+ return;
+
+ while (streaming_label_.empty() || !StreamEventsForCurrentLabel()) {
+ // We are not waiting for data from any particular label now. So, we look
+ // at the recorders that have some data available and select the next
+ // label to stream.
+ streaming_label_.clear();
+ bool all_finished = true;
+ for (const auto& key_value : recorders_) {
+ for (const auto& recorder : key_value.second) {
+ all_finished &= !recorder->is_recording();
+ if (!recorder->data().empty()) {
+ streaming_label_ = key_value.first;
+ json_field_name_written_ = false;
+ break;
+ }
+ }
+ if (!streaming_label_.empty())
+ break;
+ }
+
+ if (streaming_label_.empty()) {
+ // No recorder has any data for us, right now.
+ if (all_finished) {
+ StreamMetadata();
+ if (!stream_is_empty_ && agent_label_.empty()) {
+ mojo::common::BlockingCopyFromString("}", stream_);
+ stream_is_empty_ = false;
+ }
+ // Recorder connections should be closed on their binding thread.
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Coordinator::OnFlushDone, coordinator_));
+ }
+ return;
+ }
+ }
+ }
+
+ // Called from |background_task_runner_|.
+ bool StreamEventsForCurrentLabel() {
+ bool waiting_for_agents = false;
+ mojom::TraceDataType data_type =
+ (*recorders_[streaming_label_].begin())->data_type();
+ for (const auto& recorder : recorders_[streaming_label_]) {
+ waiting_for_agents |= recorder->is_recording();
+ if (!agent_label_.empty() && streaming_label_ != agent_label_)
+ recorder->clear_data();
+ if (recorder->data().empty())
+ continue;
+
+ std::string prefix;
+ if (!json_field_name_written_ && agent_label_.empty()) {
+ prefix = (stream_is_empty_ ? "{\"" : ",\"") + streaming_label_ + "\":";
+ switch (data_type) {
+ case mojom::TraceDataType::ARRAY:
+ prefix += "[";
+ break;
+ case mojom::TraceDataType::OBJECT:
+ prefix += "{";
+ break;
+ case mojom::TraceDataType::STRING:
+ prefix += "\"";
+ break;
+ default:
+ NOTREACHED();
+ }
+ json_field_name_written_ = true;
+ }
+ if (data_type == mojom::TraceDataType::STRING) {
+ // Escape characters if needed for string data.
+ std::string escaped;
+ base::EscapeJSONString(recorder->data(), false /* put_in_quotes */,
+ &escaped);
+ mojo::common::BlockingCopyFromString(prefix + escaped, stream_);
+ } else {
+ if (prefix.empty() && !stream_is_empty_)
+ prefix = ",";
+ mojo::common::BlockingCopyFromString(prefix + recorder->data(),
+ stream_);
+ }
+ stream_is_empty_ = false;
+ recorder->clear_data();
+ }
+ if (!waiting_for_agents) {
+ if (json_field_name_written_) {
+ switch (data_type) {
+ case mojom::TraceDataType::ARRAY:
+ mojo::common::BlockingCopyFromString("]", stream_);
+ break;
+ case mojom::TraceDataType::OBJECT:
+ mojo::common::BlockingCopyFromString("}", stream_);
+ break;
+ case mojom::TraceDataType::STRING:
+ mojo::common::BlockingCopyFromString("\"", stream_);
+ break;
+ default:
+ NOTREACHED();
+ }
+ stream_is_empty_ = false;
+ }
+ }
+ return waiting_for_agents;
+ }
+
+ // Called from |background_task_runner_|.
+ void StreamMetadata() {
+ if (!agent_label_.empty())
+ return;
+
+ for (const auto& key_value : recorders_) {
+ for (const auto& recorder : key_value.second) {
+ metadata_->MergeDictionary(&(recorder->metadata()));
+ }
+ }
+
+ std::string metadataJSON;
+ if (!metadata_->empty() &&
+ base::JSONWriter::Write(*metadata_, &metadataJSON)) {
+ std::string prefix = stream_is_empty_ ? "{\"" : ",\"";
+ mojo::common::BlockingCopyFromString(
+ prefix + std::string(kMetadataTraceLabel) + "\":" + metadataJSON,
+ stream_);
+ stream_is_empty_ = false;
+ }
+ }
+
+ // The stream to which trace events from different agents should be
+ // serialized, eventually. This is set when tracing is stopped.
+ mojo::ScopedDataPipeProducerHandle stream_;
+ std::string agent_label_;
+ scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
+ base::WeakPtr<Coordinator> coordinator_;
+
+ std::map<std::string, std::set<std::unique_ptr<Recorder>>> recorders_;
+
+ // If |streaming_label_| is not empty, it shows the label for which we are
+ // writing chunks to the output stream.
+ std::string streaming_label_;
+ std::unique_ptr<base::DictionaryValue> metadata_;
+ bool stream_is_empty_;
+ bool json_field_name_written_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceStreamer);
+};
+
+// static
+Coordinator* Coordinator::GetInstance() {
+ DCHECK(g_coordinator);
+ return g_coordinator;
+}
+
+Coordinator::Coordinator(
+ service_manager::ServiceContextRefFactory* service_ref_factory)
+ : binding_(this),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ agent_registry_(AgentRegistry::GetInstance()),
+ service_ref_(service_ref_factory->CreateRef()),
+ weak_ptr_factory_(this) {
+ DCHECK(!g_coordinator);
+ DCHECK(agent_registry_);
+ g_coordinator = this;
+ constexpr base::TaskTraits traits = {base::MayBlock(),
+ base::WithBaseSyncPrimitives(),
+ base::TaskPriority::BACKGROUND};
+ background_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(traits);
+}
+
+Coordinator::~Coordinator() {
+ if (!stop_and_flush_callback_.is_null()) {
+ base::ResetAndReturn(&stop_and_flush_callback_)
+ .Run(std::make_unique<base::DictionaryValue>());
+ }
+ if (!start_tracing_callback_.is_null())
+ base::ResetAndReturn(&start_tracing_callback_).Run(false);
+ if (!request_buffer_usage_callback_.is_null())
+ base::ResetAndReturn(&request_buffer_usage_callback_).Run(false, 0, 0);
+ if (!get_categories_callback_.is_null())
+ base::ResetAndReturn(&get_categories_callback_).Run(false, "");
+
+ if (trace_streamer_) {
+ // We are in the middle of flushing trace data. We need to
+ // 1- Close the stream so that the TraceStreamer does not block on writing
+ // to it.
+ // 2- Delete the TraceStreamer on the background task runner; it owns
+ // recorders that should be destructed on the background task runner
+ // because they are bound on the background task runner.
+ trace_streamer_->CloseStream();
+ background_task_runner_->DeleteSoon(FROM_HERE, trace_streamer_.release());
+ }
+
+ g_coordinator = nullptr;
+}
+
+void Coordinator::BindCoordinatorRequest(
+ mojom::CoordinatorRequest request,
+ const service_manager::BindSourceInfo& source_info) {
+ binding_.Bind(std::move(request));
+}
+
+void Coordinator::StartTracing(const std::string& config,
+ const StartTracingCallback& callback) {
+ if (is_tracing_) {
+ // Cannot change the config while tracing is enabled.
+ callback.Run(config == config_);
+ return;
+ }
+
+ is_tracing_ = true;
+ config_ = config;
+ agent_registry_->SetAgentInitializationCallback(base::BindRepeating(
+ &Coordinator::SendStartTracingToAgent, weak_ptr_factory_.GetWeakPtr()));
+ if (!agent_registry_->HasDisconnectClosure(&kStartTracingClosureName)) {
+ callback.Run(true);
+ return;
+ }
+ start_tracing_callback_ = callback;
+}
+
+void Coordinator::SendStartTracingToAgent(
+ AgentRegistry::AgentEntry* agent_entry) {
+ DCHECK(!agent_entry->is_tracing());
+ agent_entry->AddDisconnectClosure(
+ &kStartTracingClosureName,
+ base::BindOnce(&Coordinator::OnTracingStarted,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(agent_entry), false));
+ agent_entry->agent()->StartTracing(
+ config_, base::TimeTicks::Now(),
+ base::BindRepeating(&Coordinator::OnTracingStarted,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(agent_entry)));
+}
+
+void Coordinator::OnTracingStarted(AgentRegistry::AgentEntry* agent_entry,
+ bool success) {
+ agent_entry->set_is_tracing(success);
+ bool removed =
+ agent_entry->RemoveDisconnectClosure(&kStartTracingClosureName);
+ DCHECK(removed);
+
+ if (!agent_registry_->HasDisconnectClosure(&kStartTracingClosureName) &&
+ !start_tracing_callback_.is_null()) {
+ base::ResetAndReturn(&start_tracing_callback_).Run(true);
+ }
+}
+
+void Coordinator::StopAndFlush(mojo::ScopedDataPipeProducerHandle stream,
+ const StopAndFlushCallback& callback) {
+ StopAndFlushAgent(std::move(stream), "", callback);
+}
+
+void Coordinator::StopAndFlushAgent(mojo::ScopedDataPipeProducerHandle stream,
+ const std::string& agent_label,
+ const StopAndFlushCallback& callback) {
+ if (!is_tracing_) {
+ stream.reset();
+ callback.Run(std::make_unique<base::DictionaryValue>());
+ return;
+ }
+ DCHECK(!trace_streamer_);
+ DCHECK(stream.is_valid());
+ is_tracing_ = false;
+
+ // Do not send |StartTracing| to agents that connect from now on.
+ agent_registry_->RemoveAgentInitializationCallback();
+ trace_streamer_.reset(new Coordinator::TraceStreamer(
+ std::move(stream), agent_label, task_runner_,
+ weak_ptr_factory_.GetWeakPtr()));
+ stop_and_flush_callback_ = callback;
+ StopAndFlushInternal();
+}
+
+void Coordinator::StopAndFlushInternal() {
+ if (agent_registry_->HasDisconnectClosure(&kStartTracingClosureName)) {
+ // We received a |StopAndFlush| command before receiving |StartTracing| acks
+ // from all agents. Let's retry after a delay.
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindRepeating(&Coordinator::StopAndFlushInternal,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(
+ mojom::kStopTracingRetryTimeMilliseconds));
+ return;
+ }
+
+ agent_registry_->ForAllAgents([this](AgentRegistry::AgentEntry* agent_entry) {
+ if (!agent_entry->is_tracing() ||
+ !agent_entry->supports_explicit_clock_sync()) {
+ return;
+ }
+ const std::string sync_id = base::GenerateGUID();
+ agent_entry->AddDisconnectClosure(
+ &kRequestClockSyncMarkerClosureName,
+ base::BindOnce(&Coordinator::OnRequestClockSyncMarkerResponse,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(agent_entry), sync_id,
+ base::TimeTicks(), base::TimeTicks()));
+ agent_entry->agent()->RequestClockSyncMarker(
+ sync_id,
+ base::BindRepeating(&Coordinator::OnRequestClockSyncMarkerResponse,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(agent_entry), sync_id));
+ });
+ if (!agent_registry_->HasDisconnectClosure(
+ &kRequestClockSyncMarkerClosureName)) {
+ StopAndFlushAfterClockSync();
+ }
+}
+
+void Coordinator::OnRequestClockSyncMarkerResponse(
+ AgentRegistry::AgentEntry* agent_entry,
+ const std::string& sync_id,
+ base::TimeTicks issue_ts,
+ base::TimeTicks issue_end_ts) {
+ bool removed =
+ agent_entry->RemoveDisconnectClosure(&kRequestClockSyncMarkerClosureName);
+ DCHECK(removed);
+
+ // TODO(charliea): Change this function so that it can accept a boolean
+ // success indicator instead of having to rely on sentinel issue_ts and
+ // issue_end_ts values to signal failure.
+ if (!(issue_ts == base::TimeTicks() || issue_end_ts == base::TimeTicks()))
+ TRACE_EVENT_CLOCK_SYNC_ISSUER(sync_id, issue_ts, issue_end_ts);
+
+ if (!agent_registry_->HasDisconnectClosure(
+ &kRequestClockSyncMarkerClosureName)) {
+ StopAndFlushAfterClockSync();
+ }
+}
+
+void Coordinator::StopAndFlushAfterClockSync() {
+ bool has_tracing_agents = false;
+ agent_registry_->ForAllAgents(
+ [this, &has_tracing_agents](AgentRegistry::AgentEntry* agent_entry) {
+ if (!agent_entry->is_tracing())
+ return;
+ has_tracing_agents = true;
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Coordinator::TraceStreamer::CreateAndSendRecorder,
+ trace_streamer_->AsWeakPtr(), agent_entry->label(),
+ agent_entry->type(), agent_entry->AsWeakPtr()));
+ });
+ if (!has_tracing_agents)
+ OnFlushDone();
+}
+
+void Coordinator::SendRecorder(
+ base::WeakPtr<AgentRegistry::AgentEntry> agent_entry,
+ mojom::RecorderPtr recorder) {
+ if (agent_entry) {
+ agent_entry->agent()->StopAndFlush(std::move(recorder));
+ } else {
+ // Recorders are created and closed on |background_task_runner_|.
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Coordinator::TraceStreamer::CloseRecorder,
+ trace_streamer_->AsWeakPtr(), std::move(recorder)));
+ }
+}
+
+void Coordinator::OnFlushDone() {
+ base::ResetAndReturn(&stop_and_flush_callback_)
+ .Run(trace_streamer_->GetMetadata());
+ background_task_runner_->DeleteSoon(FROM_HERE, trace_streamer_.release());
+ agent_registry_->ForAllAgents([this](AgentRegistry::AgentEntry* agent_entry) {
+ agent_entry->set_is_tracing(false);
+ });
+ is_tracing_ = false;
+}
+
+void Coordinator::IsTracing(const IsTracingCallback& callback) {
+ callback.Run(is_tracing_);
+}
+
+void Coordinator::RequestBufferUsage(
+ const RequestBufferUsageCallback& callback) {
+ if (!request_buffer_usage_callback_.is_null()) {
+ callback.Run(false, 0, 0);
+ return;
+ }
+
+ maximum_trace_buffer_usage_ = 0;
+ approximate_event_count_ = 0;
+ request_buffer_usage_callback_ = callback;
+ agent_registry_->ForAllAgents([this](AgentRegistry::AgentEntry* agent_entry) {
+ agent_entry->AddDisconnectClosure(
+ &kRequestBufferUsageClosureName,
+ base::BindOnce(&Coordinator::OnRequestBufferStatusResponse,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(agent_entry), 0 /* capacity */,
+ 0 /* count */));
+ agent_entry->agent()->RequestBufferStatus(base::BindRepeating(
+ &Coordinator::OnRequestBufferStatusResponse,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(agent_entry)));
+ });
+}
+
+void Coordinator::OnRequestBufferStatusResponse(
+ AgentRegistry::AgentEntry* agent_entry,
+ uint32_t capacity,
+ uint32_t count) {
+ bool removed =
+ agent_entry->RemoveDisconnectClosure(&kRequestBufferUsageClosureName);
+ DCHECK(removed);
+
+ if (capacity > 0) {
+ float percent_full =
+ static_cast<float>(static_cast<double>(count) / capacity);
+ maximum_trace_buffer_usage_ =
+ std::max(maximum_trace_buffer_usage_, percent_full);
+ approximate_event_count_ += count;
+ }
+
+ if (!agent_registry_->HasDisconnectClosure(&kRequestBufferUsageClosureName)) {
+ base::ResetAndReturn(&request_buffer_usage_callback_)
+ .Run(true, maximum_trace_buffer_usage_, approximate_event_count_);
+ }
+}
+
+void Coordinator::GetCategories(const GetCategoriesCallback& callback) {
+ if (is_tracing_) {
+ callback.Run(false, "");
+ }
+
+ DCHECK(get_categories_callback_.is_null());
+ is_tracing_ = true;
+ category_set_.clear();
+ get_categories_callback_ = callback;
+ agent_registry_->ForAllAgents([this](AgentRegistry::AgentEntry* agent_entry) {
+ agent_entry->AddDisconnectClosure(
+ &kGetCategoriesClosureName,
+ base::BindOnce(&Coordinator::OnGetCategoriesResponse,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(agent_entry), ""));
+ agent_entry->agent()->GetCategories(base::BindRepeating(
+ &Coordinator::OnGetCategoriesResponse, weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(agent_entry)));
+ });
+}
+
+void Coordinator::OnGetCategoriesResponse(
+ AgentRegistry::AgentEntry* agent_entry,
+ const std::string& categories) {
+ bool removed =
+ agent_entry->RemoveDisconnectClosure(&kGetCategoriesClosureName);
+ DCHECK(removed);
+
+ std::vector<std::string> split = base::SplitString(
+ categories, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ for (const auto& category : split) {
+ category_set_.insert(category);
+ }
+
+ if (!agent_registry_->HasDisconnectClosure(&kGetCategoriesClosureName)) {
+ std::vector<std::string> category_vector(category_set_.begin(),
+ category_set_.end());
+ base::ResetAndReturn(&get_categories_callback_)
+ .Run(true, base::JoinString(category_vector, ","));
+ is_tracing_ = false;
+ }
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/coordinator.h b/chromium/services/tracing/coordinator.h
new file mode 100644
index 00000000000..17e6867b04b
--- /dev/null
+++ b/chromium/services/tracing/coordinator.h
@@ -0,0 +1,119 @@
+// Copyright 2017 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 SERVICES_TRACING_COORDINATOR_H_
+#define SERVICES_TRACING_COORDINATOR_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/values.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "services/tracing/agent_registry.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+#include "services/tracing/recorder.h"
+
+namespace base {
+class TimeTicks;
+} // namespace base
+
+namespace service_manager {
+struct BindSourceInfo;
+} // namespace service_manager
+
+namespace tracing {
+
+// Note that this implementation of mojom::Coordinator assumes that agents
+// either respond to messages that expect a response or disconnect. Mojo
+// verifies this to some extend by DCHECKing if the callback is deleted by the
+// agent before being run. However, the agent should not store the callback and
+// never run it.
+//
+// If we see that the above-mentioned assumption does not hold in some cases, we
+// should guard against it using timeouts.
+class Coordinator : public mojom::Coordinator {
+ public:
+ static Coordinator* GetInstance();
+
+ explicit Coordinator(
+ service_manager::ServiceContextRefFactory* service_ref_factory);
+
+ void BindCoordinatorRequest(
+ mojom::CoordinatorRequest request,
+ const service_manager::BindSourceInfo& source_info);
+
+ private:
+ friend std::default_delete<Coordinator>;
+ friend class CoordinatorTest; // For testing.
+
+ class TraceStreamer;
+
+ ~Coordinator() override;
+
+ // mojom::Coordinator
+ void StartTracing(const std::string& config,
+ const StartTracingCallback& callback) override;
+ void StopAndFlush(mojo::ScopedDataPipeProducerHandle stream,
+ const StopAndFlushCallback& callback) override;
+ void StopAndFlushAgent(mojo::ScopedDataPipeProducerHandle stream,
+ const std::string& agent_label,
+ const StopAndFlushCallback& callback) override;
+ void IsTracing(const IsTracingCallback& callback) override;
+ void RequestBufferUsage(const RequestBufferUsageCallback& callback) override;
+ void GetCategories(const GetCategoriesCallback& callback) override;
+
+ // Internal methods for collecting events from agents.
+ void SendStartTracingToAgent(AgentRegistry::AgentEntry* agent_entry);
+ void OnTracingStarted(AgentRegistry::AgentEntry* agent_entry, bool success);
+ void StopAndFlushInternal();
+ void OnRequestClockSyncMarkerResponse(AgentRegistry::AgentEntry* agent_entry,
+ const std::string& sync_id,
+ base::TimeTicks issue_ts,
+ base::TimeTicks issue_end_ts);
+ void StopAndFlushAfterClockSync();
+ void SendRecorder(base::WeakPtr<AgentRegistry::AgentEntry> agent_entry,
+ mojom::RecorderPtr recorder);
+ void OnFlushDone();
+
+ void OnRequestBufferStatusResponse(AgentRegistry::AgentEntry* agent_entry,
+ uint32_t capacity,
+ uint32_t count);
+
+ void OnGetCategoriesResponse(AgentRegistry::AgentEntry* agent_entry,
+ const std::string& categories);
+
+ mojo::Binding<mojom::Coordinator> binding_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+ AgentRegistry* agent_registry_;
+ std::string config_;
+ bool is_tracing_ = false;
+
+ std::unique_ptr<TraceStreamer> trace_streamer_;
+ StartTracingCallback start_tracing_callback_;
+ StopAndFlushCallback stop_and_flush_callback_;
+
+ // For computing trace buffer usage.
+ float maximum_trace_buffer_usage_ = 0;
+ uint32_t approximate_event_count_ = 0;
+ RequestBufferUsageCallback request_buffer_usage_callback_;
+
+ // For getting categories.
+ std::set<std::string> category_set_;
+ GetCategoriesCallback get_categories_callback_;
+ std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+
+ base::WeakPtrFactory<Coordinator> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Coordinator);
+};
+
+} // namespace tracing
+#endif // SERVICES_TRACING_COORDINATOR_H_
diff --git a/chromium/services/tracing/coordinator_unittest.cc b/chromium/services/tracing/coordinator_unittest.cc
new file mode 100644
index 00000000000..28f6d31c9f1
--- /dev/null
+++ b/chromium/services/tracing/coordinator_unittest.cc
@@ -0,0 +1,411 @@
+// Copyright 2017 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 "services/tracing/coordinator.h"
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/run_loop.h"
+#include "base/strings/string_split.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/common/data_pipe_drainer.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+#include "services/tracing/test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+class CoordinatorTest : public testing::Test,
+ public mojo::common::DataPipeDrainer::Client {
+ public:
+ CoordinatorTest() : service_ref_factory_(base::DoNothing()) {}
+
+ // testing::Test
+ void SetUp() override {
+ agent_registry_.reset(new AgentRegistry(&service_ref_factory_));
+ coordinator_.reset(new Coordinator(&service_ref_factory_));
+ output_ = "";
+ }
+
+ // testing::Test
+ void TearDown() override {
+ agents_.clear();
+ coordinator_.reset();
+ agent_registry_.reset();
+ }
+
+ // mojo::common::DataPipeDrainer::Client
+ void OnDataAvailable(const void* data, size_t num_bytes) override {
+ output_.append(static_cast<const char*>(data), num_bytes);
+ }
+
+ // mojo::common::DataPipeDrainer::Client
+ void OnDataComplete() override { base::ResetAndReturn(&quit_closure_).Run(); }
+
+ MockAgent* AddArrayAgent() {
+ auto agent = std::make_unique<MockAgent>();
+ agent_registry_->RegisterAgent(agent->CreateAgentPtr(), "traceEvents",
+ mojom::TraceDataType::ARRAY, false);
+ agents_.push_back(std::move(agent));
+ return agents_.back().get();
+ }
+
+ MockAgent* AddObjectAgent() {
+ auto agent = std::make_unique<MockAgent>();
+ agent_registry_->RegisterAgent(agent->CreateAgentPtr(), "systemTraceEvents",
+ mojom::TraceDataType::OBJECT, false);
+ agents_.push_back(std::move(agent));
+ return agents_.back().get();
+ }
+
+ MockAgent* AddStringAgent() {
+ auto agent = std::make_unique<MockAgent>();
+ agent_registry_->RegisterAgent(agent->CreateAgentPtr(), "battor",
+ mojom::TraceDataType::STRING, false);
+ agents_.push_back(std::move(agent));
+ return agents_.back().get();
+ }
+
+ void StartTracing(std::string config,
+ bool expected_response,
+ bool stop_and_flush) {
+ base::RepeatingClosure closure;
+ if (stop_and_flush) {
+ closure = base::BindRepeating(&CoordinatorTest::StopAndFlush,
+ base::Unretained(this));
+ }
+
+ coordinator_->StartTracing(
+ config,
+ base::BindRepeating(
+ [](bool expected, base::RepeatingClosure closure, bool actual) {
+ EXPECT_EQ(expected, actual);
+ if (!closure.is_null())
+ closure.Run();
+ },
+ expected_response, closure));
+ }
+
+ void StartTracing(std::string config, bool expected_response) {
+ StartTracing(config, expected_response, false);
+ }
+
+ void StopAndFlush() {
+ mojo::DataPipe data_pipe;
+ auto dummy_callback = [](std::unique_ptr<base::DictionaryValue> metadata) {
+ };
+ coordinator_->StopAndFlush(std::move(data_pipe.producer_handle),
+ base::BindRepeating(dummy_callback));
+ drainer_.reset(new mojo::common::DataPipeDrainer(
+ this, std::move(data_pipe.consumer_handle)));
+ }
+
+ void IsTracing(bool expected_response) {
+ coordinator_->IsTracing(base::BindRepeating(
+ [](bool expected, bool actual) { EXPECT_EQ(expected, actual); },
+ expected_response));
+ }
+
+ void RequestBufferUsage(float expected_usage, uint32_t expected_count) {
+ coordinator_->RequestBufferUsage(base::BindRepeating(
+ [](float expected_usage, uint32_t expected_count, bool success,
+ float usage, uint32_t count) {
+ EXPECT_TRUE(success);
+ EXPECT_EQ(expected_usage, usage);
+ EXPECT_EQ(expected_count, count);
+ },
+ expected_usage, expected_count));
+ }
+
+ void CheckDisconnectClosures(size_t num_agents) {
+ // Verify that all disconnect closures are cleared up. This means that, for
+ // each agent, either the tracing service is notified that the agent is
+ // disconnected or the agent has answered to all requests.
+ size_t count = 0;
+ agent_registry_->ForAllAgents([&count](AgentRegistry::AgentEntry* entry) {
+ count++;
+ EXPECT_EQ(0u, entry->num_disconnect_closures_for_testing());
+ });
+ EXPECT_EQ(num_agents, count);
+ }
+
+ void GetCategories(bool expected_success,
+ std::set<std::string> expected_categories) {
+ coordinator_->GetCategories(base::BindRepeating(
+ [](bool expected_success, std::set<std::string> expected_categories,
+ bool success, const std::string& categories) {
+ EXPECT_EQ(expected_success, success);
+ if (!success)
+ return;
+ std::vector<std::string> category_vector = base::SplitString(
+ categories, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ EXPECT_EQ(expected_categories.size(), category_vector.size());
+ for (const auto& expected_category : expected_categories) {
+ EXPECT_EQ(1, std::count(category_vector.begin(),
+ category_vector.end(), expected_category));
+ }
+ },
+ expected_success, expected_categories));
+ }
+
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<AgentRegistry> agent_registry_;
+ std::unique_ptr<Coordinator> coordinator_;
+ std::vector<std::unique_ptr<MockAgent>> agents_;
+ std::unique_ptr<mojo::common::DataPipeDrainer> drainer_;
+ base::RepeatingClosure quit_closure_;
+ std::string output_;
+ service_manager::ServiceContextRefFactory service_ref_factory_;
+};
+
+TEST_F(CoordinatorTest, StartTracingSimple) {
+ base::RunLoop run_loop;
+ auto* agent = AddArrayAgent();
+ StartTracing("*", true);
+ run_loop.RunUntilIdle();
+
+ // The agent should have received exactly one call from the coordinator.
+ EXPECT_EQ(1u, agent->call_stat().size());
+ EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StartTracingTwoAgents) {
+ base::RunLoop run_loop;
+ auto* agent1 = AddArrayAgent();
+ StartTracing("*", true);
+ auto* agent2 = AddStringAgent();
+ run_loop.RunUntilIdle();
+
+ // Each agent should have received exactly one call from the coordinatr.
+ EXPECT_EQ(1u, agent1->call_stat().size());
+ EXPECT_EQ("StartTracing", agent1->call_stat()[0]);
+ EXPECT_EQ(1u, agent2->call_stat().size());
+ EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StartTracingWithDifferentConfigs) {
+ base::RunLoop run_loop;
+ auto* agent = AddArrayAgent();
+ StartTracing("config 1", true);
+ // The 2nd |StartTracing| should return false.
+ StartTracing("config 2", false);
+ run_loop.RunUntilIdle();
+
+ // The agent should have received exactly one call from the coordinator
+ // because the 2nd |StartTracing| was aborted.
+ EXPECT_EQ(1u, agent->call_stat().size());
+ EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StartTracingWithSameConfigs) {
+ base::RunLoop run_loop;
+ auto* agent = AddArrayAgent();
+ StartTracing("config", true);
+ // The 2nd |StartTracing| should return true when we are not trying to change
+ // the config.
+ StartTracing("config", true);
+ run_loop.RunUntilIdle();
+
+ // The agent should have received exactly one call from the coordinator
+ // because the 2nd |StartTracing| was a no-op.
+ EXPECT_EQ(1u, agent->call_stat().size());
+ EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StopAndFlushObjectAgent) {
+ base::RunLoop run_loop;
+ quit_closure_ = run_loop.QuitClosure();
+
+ auto* agent = AddObjectAgent();
+ agent->data_.push_back("\"content\":{\"a\":1}");
+ agent->data_.push_back("\"name\":\"etw\"");
+
+ StartTracing("config", true, true);
+ if (!quit_closure_.is_null())
+ run_loop.Run();
+
+ EXPECT_EQ("{\"systemTraceEvents\":{\"content\":{\"a\":1},\"name\":\"etw\"}}",
+ output_);
+
+ // Each agent should have received exactly two calls.
+ EXPECT_EQ(2u, agent->call_stat().size());
+ EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+ EXPECT_EQ("StopAndFlush", agent->call_stat()[1]);
+}
+
+TEST_F(CoordinatorTest, StopAndFlushTwoArrayAgents) {
+ base::RunLoop run_loop;
+ quit_closure_ = run_loop.QuitClosure();
+
+ auto* agent1 = AddArrayAgent();
+ agent1->data_.push_back("e1");
+ agent1->data_.push_back("e2");
+
+ auto* agent2 = AddArrayAgent();
+ agent2->data_.push_back("e3");
+ agent2->data_.push_back("e4");
+
+ StartTracing("config", true, true);
+ if (!quit_closure_.is_null())
+ run_loop.Run();
+
+ // |output_| should be of the form {"traceEvents":[ei,ej,ek,el]}, where
+ // ei,ej,ek,el is a permutation of e1,e2,e3,e4 such that e1 is before e2 and
+ // e3 is before e4 since e1 and 2 come from the same agent and their order
+ // should be preserved and, similarly, the order of e3 and e4 should be
+ // preserved, too.
+ EXPECT_TRUE(output_ == "{\"traceEvents\":[e1,e2,e3,e4]}" ||
+ output_ == "{\"traceEvents\":[e1,e3,e2,e4]}" ||
+ output_ == "{\"traceEvents\":[e1,e3,e4,e2]}" ||
+ output_ == "{\"traceEvents\":[e3,e1,e2,e4]}" ||
+ output_ == "{\"traceEvents\":[e3,e1,e4,e2]}" ||
+ output_ == "{\"traceEvents\":[e3,e4,e1,e2]}");
+
+ // Each agent should have received exactly two calls.
+ EXPECT_EQ(2u, agent1->call_stat().size());
+ EXPECT_EQ("StartTracing", agent1->call_stat()[0]);
+ EXPECT_EQ("StopAndFlush", agent1->call_stat()[1]);
+
+ EXPECT_EQ(2u, agent2->call_stat().size());
+ EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
+ EXPECT_EQ("StopAndFlush", agent2->call_stat()[1]);
+}
+
+TEST_F(CoordinatorTest, StopAndFlushDifferentTypeAgents) {
+ base::RunLoop run_loop;
+ quit_closure_ = run_loop.QuitClosure();
+
+ auto* agent1 = AddArrayAgent();
+ agent1->data_.push_back("e1");
+ agent1->data_.push_back("e2");
+
+ auto* agent2 = AddStringAgent();
+ agent2->data_.push_back("e3");
+ agent2->data_.push_back("e4");
+
+ StartTracing("config", true, true);
+ if (!quit_closure_.is_null())
+ run_loop.Run();
+
+ EXPECT_TRUE(output_ == "{\"traceEvents\":[e1,e2],\"battor\":\"e3e4\"}" ||
+ output_ == "{\"battor\":\"e3e4\",\"traceEvents\":[e1,e2]}");
+
+ // Each agent should have received exactly two calls.
+ EXPECT_EQ(2u, agent1->call_stat().size());
+ EXPECT_EQ("StartTracing", agent1->call_stat()[0]);
+ EXPECT_EQ("StopAndFlush", agent1->call_stat()[1]);
+
+ EXPECT_EQ(2u, agent2->call_stat().size());
+ EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
+ EXPECT_EQ("StopAndFlush", agent2->call_stat()[1]);
+}
+
+TEST_F(CoordinatorTest, StopAndFlushWithMetadata) {
+ base::RunLoop run_loop;
+ quit_closure_ = run_loop.QuitClosure();
+
+ auto* agent = AddArrayAgent();
+ agent->data_.push_back("event");
+ agent->metadata_.SetString("key", "value");
+
+ StartTracing("config", true, true);
+ if (!quit_closure_.is_null())
+ run_loop.Run();
+
+ // Metadata is written at after trace data.
+ EXPECT_EQ("{\"traceEvents\":[event],\"metadata\":{\"key\":\"value\"}}",
+ output_);
+ EXPECT_EQ(2u, agent->call_stat().size());
+ EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+ EXPECT_EQ("StopAndFlush", agent->call_stat()[1]);
+}
+
+TEST_F(CoordinatorTest, IsTracing) {
+ base::RunLoop run_loop;
+ StartTracing("config", true);
+ IsTracing(true);
+ run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, IsNotTracing) {
+ base::RunLoop run_loop;
+ IsTracing(false);
+ run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, RequestBufferUsage) {
+ auto* agent1 = AddArrayAgent();
+ agent1->trace_log_status_.event_capacity = 4;
+ agent1->trace_log_status_.event_count = 1;
+ RequestBufferUsage(0.25, 1);
+ base::RunLoop().RunUntilIdle();
+ CheckDisconnectClosures(1);
+
+ auto* agent2 = AddArrayAgent();
+ agent2->trace_log_status_.event_capacity = 8;
+ agent2->trace_log_status_.event_count = 1;
+ // The buffer usage of |agent2| is less than the buffer usage of |agent1| and
+ // so the total buffer usage, i.e 0.25, does not change. But, the approximage
+ // count will be increased from 1 to 2.
+ RequestBufferUsage(0.25, 2);
+ base::RunLoop().RunUntilIdle();
+ CheckDisconnectClosures(2);
+
+ base::RunLoop run_loop3;
+ auto* agent3 = AddArrayAgent();
+ agent3->trace_log_status_.event_capacity = 8;
+ agent3->trace_log_status_.event_count = 4;
+ // |agent3| has the worst buffer usage of 0.5.
+ RequestBufferUsage(0.5, 6);
+ base::RunLoop().RunUntilIdle();
+ CheckDisconnectClosures(3);
+
+ // At the end |agent1| receveis 3 calls, |agent2| receives 2 calls, and
+ // |agent3| receives 1 call.
+ EXPECT_EQ(3u, agent1->call_stat().size());
+ EXPECT_EQ(2u, agent2->call_stat().size());
+ EXPECT_EQ(1u, agent3->call_stat().size());
+}
+
+TEST_F(CoordinatorTest, GetCategoriesFail) {
+ base::RunLoop run_loop;
+ StartTracing("config", true);
+ std::set<std::string> expected_categories;
+ GetCategories(false, expected_categories);
+ run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, GetCategoriesSimple) {
+ base::RunLoop run_loop;
+ auto* agent = AddArrayAgent();
+ agent->categories_ = "cat2,cat1";
+ std::set<std::string> expected_categories;
+ expected_categories.insert("cat1");
+ expected_categories.insert("cat2");
+ GetCategories(true, expected_categories);
+ run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, GetCategoriesFromTwoAgents) {
+ base::RunLoop run_loop;
+ auto* agent1 = AddArrayAgent();
+ agent1->categories_ = "cat2,cat1";
+ auto* agent2 = AddArrayAgent();
+ agent2->categories_ = "cat3,cat2";
+ std::set<std::string> expected_categories;
+ expected_categories.insert("cat1");
+ expected_categories.insert("cat2");
+ expected_categories.insert("cat3");
+ GetCategories(true, expected_categories);
+ run_loop.RunUntilIdle();
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/manifest.json b/chromium/services/tracing/manifest.json
new file mode 100644
index 00000000000..7f5e1128fc2
--- /dev/null
+++ b/chromium/services/tracing/manifest.json
@@ -0,0 +1,21 @@
+{
+ "name": "tracing",
+ "display_name": "Tracing",
+ "interface_provider_specs": {
+ "service_manager:connector": {
+ "provides": {
+ "app": [
+ "tracing::mojom::AgentRegistry"
+ ],
+ "tracing": [ "tracing::mojom::Coordinator" ],
+ "tests": [ "*" ]
+ },
+ "requires": {
+ "service_manager": [
+ "service_manager:singleton",
+ "service_manager:service_manager"
+ ]
+ }
+ }
+ }
+}
diff --git a/chromium/services/tracing/public/cpp/BUILD.gn b/chromium/services/tracing/public/cpp/BUILD.gn
new file mode 100644
index 00000000000..d46b333bf70
--- /dev/null
+++ b/chromium/services/tracing/public/cpp/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2018 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.
+
+component("cpp") {
+ sources = [
+ "base_agent.cc",
+ "base_agent.h",
+ "chrome_trace_event_agent.cc",
+ "chrome_trace_event_agent.h",
+ ]
+
+ defines = [ "IS_TRACING_CPP_IMPL" ]
+ output_name = "tracing_cpp"
+
+ public_deps = [
+ "//base",
+ "//mojo/public/cpp/bindings",
+ "//services/service_manager/public/cpp",
+ "//services/tracing/public/mojom",
+ ]
+}
diff --git a/chromium/services/tracing/public/cpp/base_agent.cc b/chromium/services/tracing/public/cpp/base_agent.cc
new file mode 100644
index 00000000000..3df867d4e99
--- /dev/null
+++ b/chromium/services/tracing/public/cpp/base_agent.cc
@@ -0,0 +1,57 @@
+// Copyright 2018 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 "services/tracing/public/cpp/base_agent.h"
+
+#include <utility>
+
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/tracing/public/mojom/constants.mojom.h"
+
+namespace tracing {
+
+BaseAgent::BaseAgent(service_manager::Connector* connector,
+ const std::string& label,
+ mojom::TraceDataType type,
+ bool supports_explicit_clock_sync)
+ : binding_(this) {
+ // |connector| can be null in tests.
+ if (!connector)
+ return;
+ tracing::mojom::AgentRegistryPtr agent_registry;
+ connector->BindInterface(tracing::mojom::kServiceName, &agent_registry);
+
+ tracing::mojom::AgentPtr agent;
+ binding_.Bind(mojo::MakeRequest(&agent));
+ agent_registry->RegisterAgent(std::move(agent), label, type,
+ supports_explicit_clock_sync);
+}
+
+BaseAgent::~BaseAgent() = default;
+
+void BaseAgent::StartTracing(const std::string& config,
+ base::TimeTicks coordinator_time,
+ const Agent::StartTracingCallback& callback) {
+ callback.Run(true /* success */);
+}
+
+void BaseAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) {}
+
+void BaseAgent::RequestClockSyncMarker(
+ const std::string& sync_id,
+ const Agent::RequestClockSyncMarkerCallback& callback) {
+ NOTREACHED() << "The agent claims to support explicit clock sync but does "
+ << "not override BaseAgent::RequestClockSyncMarker()";
+}
+
+void BaseAgent::GetCategories(const Agent::GetCategoriesCallback& callback) {
+ callback.Run("" /* categories */);
+}
+
+void BaseAgent::RequestBufferStatus(
+ const Agent::RequestBufferStatusCallback& callback) {
+ callback.Run(0 /* capacity */, 0 /* count */);
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/public/cpp/base_agent.h b/chromium/services/tracing/public/cpp/base_agent.h
new file mode 100644
index 00000000000..ae6caf129d9
--- /dev/null
+++ b/chromium/services/tracing/public/cpp/base_agent.h
@@ -0,0 +1,51 @@
+// Copyright 2018 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 SERVICES_TRACING_PUBLIC_CPP_BASE_AGENT_H_
+#define SERVICES_TRACING_PUBLIC_CPP_BASE_AGENT_H_
+
+#include <string>
+
+#include "base/component_export.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace service_manager {
+class Connector;
+} // namespace service_manager
+
+// This class is a minimal implementation of mojom::Agent to reduce boilerplate
+// code in tracing agents. A tracing agent can inherit from this class and only
+// override methods that actually do something, in most cases only StartTracing
+// and StopAndFlush.
+namespace tracing {
+class COMPONENT_EXPORT(TRACING_CPP) BaseAgent : public mojom::Agent {
+ protected:
+ BaseAgent(service_manager::Connector* connector,
+ const std::string& label,
+ mojom::TraceDataType type,
+ bool supports_explicit_clock_sync);
+ ~BaseAgent() override;
+
+ private:
+ // tracing::mojom::Agent:
+ void StartTracing(const std::string& config,
+ base::TimeTicks coordinator_time,
+ const Agent::StartTracingCallback& callback) override;
+ void StopAndFlush(tracing::mojom::RecorderPtr recorder) override;
+ void RequestClockSyncMarker(
+ const std::string& sync_id,
+ const Agent::RequestClockSyncMarkerCallback& callback) override;
+ void GetCategories(const Agent::GetCategoriesCallback& callback) override;
+ void RequestBufferStatus(
+ const Agent::RequestBufferStatusCallback& callback) override;
+
+ mojo::Binding<tracing::mojom::Agent> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseAgent);
+};
+
+} // namespace tracing
+
+#endif // SERVICES_TRACING_PUBLIC_CPP_BASE_AGENT_H_
diff --git a/chromium/services/tracing/public/cpp/chrome_trace_event_agent.cc b/chromium/services/tracing/public/cpp/chrome_trace_event_agent.cc
new file mode 100644
index 00000000000..0def62c7a34
--- /dev/null
+++ b/chromium/services/tracing/public/cpp/chrome_trace_event_agent.cc
@@ -0,0 +1,122 @@
+// Copyright 2017 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 "services/tracing/public/cpp/chrome_trace_event_agent.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_log.h"
+#include "base/values.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/tracing/public/mojom/constants.mojom.h"
+
+namespace {
+
+const char kChromeTraceEventLabel[] = "traceEvents";
+
+tracing::ChromeTraceEventAgent* g_chrome_trace_event_agent;
+
+} // namespace
+
+namespace tracing {
+
+// static
+ChromeTraceEventAgent* ChromeTraceEventAgent::GetInstance() {
+ return g_chrome_trace_event_agent;
+}
+
+ChromeTraceEventAgent::ChromeTraceEventAgent(
+ service_manager::Connector* connector)
+ : BaseAgent(connector,
+ kChromeTraceEventLabel,
+ mojom::TraceDataType::ARRAY,
+ false /* supports_explicit_clock_sync */),
+ enabled_tracing_modes_(0) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!g_chrome_trace_event_agent);
+ g_chrome_trace_event_agent = this;
+}
+
+ChromeTraceEventAgent::~ChromeTraceEventAgent() {
+ DCHECK(!trace_log_needs_me_);
+ g_chrome_trace_event_agent = nullptr;
+}
+
+void ChromeTraceEventAgent::AddMetadataGeneratorFunction(
+ MetadataGeneratorFunction generator) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ metadata_generator_functions_.push_back(generator);
+}
+
+void ChromeTraceEventAgent::StartTracing(const std::string& config,
+ base::TimeTicks coordinator_time,
+ const StartTracingCallback& callback) {
+ DCHECK(!recorder_);
+#if defined(__native_client__)
+ // NaCl and system times are offset by a bit, so subtract some time from
+ // the captured timestamps. The value might be off by a bit due to messaging
+ // latency.
+ base::TimeDelta time_offset = base::TimeTicks::Now() - coordinator_time;
+ TraceLog::GetInstance()->SetTimeOffset(time_offset);
+#endif
+ enabled_tracing_modes_ = base::trace_event::TraceLog::RECORDING_MODE;
+ const base::trace_event::TraceConfig trace_config(config);
+ if (!trace_config.event_filters().empty())
+ enabled_tracing_modes_ |= base::trace_event::TraceLog::FILTERING_MODE;
+ base::trace_event::TraceLog::GetInstance()->SetEnabled(
+ trace_config, enabled_tracing_modes_);
+ callback.Run(true);
+}
+
+void ChromeTraceEventAgent::StopAndFlush(mojom::RecorderPtr recorder) {
+ DCHECK(!recorder_);
+ recorder_ = std::move(recorder);
+ base::trace_event::TraceLog::GetInstance()->SetDisabled(
+ enabled_tracing_modes_);
+ enabled_tracing_modes_ = 0;
+ for (const auto& generator : metadata_generator_functions_) {
+ auto metadata = generator.Run();
+ if (metadata)
+ recorder_->AddMetadata(std::move(metadata));
+ }
+ trace_log_needs_me_ = true;
+ base::trace_event::TraceLog::GetInstance()->Flush(base::Bind(
+ &ChromeTraceEventAgent::OnTraceLogFlush, base::Unretained(this)));
+}
+
+void ChromeTraceEventAgent::RequestBufferStatus(
+ const RequestBufferStatusCallback& callback) {
+ base::trace_event::TraceLogStatus status =
+ base::trace_event::TraceLog::GetInstance()->GetStatus();
+ callback.Run(status.event_capacity, status.event_count);
+}
+
+void ChromeTraceEventAgent::GetCategories(
+ const GetCategoriesCallback& callback) {
+ std::vector<std::string> category_vector;
+ base::trace_event::TraceLog::GetInstance()->GetKnownCategoryGroups(
+ &category_vector);
+ callback.Run(base::JoinString(category_vector, ","));
+}
+
+void ChromeTraceEventAgent::OnTraceLogFlush(
+ const scoped_refptr<base::RefCountedString>& events_str,
+ bool has_more_events) {
+ if (!events_str->data().empty())
+ recorder_->AddChunk(events_str->data());
+ if (!has_more_events) {
+ trace_log_needs_me_ = false;
+ recorder_.reset();
+ }
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/public/cpp/chrome_trace_event_agent.h b/chromium/services/tracing/public/cpp/chrome_trace_event_agent.h
new file mode 100644
index 00000000000..f2f0c57b812
--- /dev/null
+++ b/chromium/services/tracing/public/cpp/chrome_trace_event_agent.h
@@ -0,0 +1,70 @@
+// Copyright 2017 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 SERVICES_TRACING_PUBLIC_CPP_CHROME_TRACE_EVENT_AGENT_H_
+#define SERVICES_TRACING_PUBLIC_CPP_CHROME_TRACE_EVENT_AGENT_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/threading/thread_checker.h"
+#include "base/values.h"
+#include "services/tracing/public/cpp/base_agent.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace base {
+class TimeTicks;
+} // namespace base
+
+namespace service_manager {
+class Connector;
+} // namespace service_manager
+
+namespace tracing {
+
+class COMPONENT_EXPORT(TRACING_CPP) ChromeTraceEventAgent : public BaseAgent {
+ public:
+ using MetadataGeneratorFunction =
+ base::RepeatingCallback<std::unique_ptr<base::DictionaryValue>()>;
+
+ static ChromeTraceEventAgent* GetInstance();
+
+ explicit ChromeTraceEventAgent(service_manager::Connector* connector);
+
+ void AddMetadataGeneratorFunction(MetadataGeneratorFunction generator);
+
+ private:
+ friend std::default_delete<ChromeTraceEventAgent>; // For Testing
+ friend class ChromeTraceEventAgentTest; // For Testing
+
+ ~ChromeTraceEventAgent() override;
+
+ // mojom::Agent
+ void StartTracing(const std::string& config,
+ base::TimeTicks coordinator_time,
+ const StartTracingCallback& callback) override;
+ void StopAndFlush(mojom::RecorderPtr recorder) override;
+ void RequestBufferStatus(
+ const RequestBufferStatusCallback& callback) override;
+ void GetCategories(const GetCategoriesCallback& callback) override;
+
+ void OnTraceLogFlush(const scoped_refptr<base::RefCountedString>& events_str,
+ bool has_more_events);
+
+ uint8_t enabled_tracing_modes_;
+ mojom::RecorderPtr recorder_;
+ std::vector<MetadataGeneratorFunction> metadata_generator_functions_;
+ bool trace_log_needs_me_ = false;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeTraceEventAgent);
+};
+
+} // namespace tracing
+#endif // SERVICES_TRACING_PUBLIC_CPP_CHROME_TRACE_EVENT_AGENT_H_
diff --git a/chromium/services/tracing/public/cpp/chrome_trace_event_agent_unittest.cc b/chromium/services/tracing/public/cpp/chrome_trace_event_agent_unittest.cc
new file mode 100644
index 00000000000..ab829416ce7
--- /dev/null
+++ b/chromium/services/tracing/public/cpp/chrome_trace_event_agent_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright 2017 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 "services/tracing/public/cpp/chrome_trace_event_agent.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/trace_event_analyzer.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_config.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_log.h"
+#include "base/values.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+namespace {
+const char kTestCategory[] = "ChromeTraceEventAgentTestCategory";
+const char kTestMetadataKey[] = "ChromeTraceEventAgentTestMetadata";
+} // namespace
+
+class MockRecorder : public mojom::Recorder {
+ public:
+ explicit MockRecorder(mojom::RecorderRequest request)
+ : binding_(this, std::move(request)) {
+ binding_.set_connection_error_handler(base::BindRepeating(
+ &MockRecorder::OnConnectionError, base::Unretained(this)));
+ }
+
+ std::string events() const { return events_; }
+ std::string metadata() const { return metadata_; }
+ void set_quit_closure(base::Closure quit_closure) {
+ quit_closure_ = quit_closure;
+ }
+
+ private:
+ void Append(std::string* dest, const std::string& chunk) {
+ if (chunk.empty())
+ return;
+ if (!dest->empty())
+ dest->push_back(',');
+ dest->append(chunk);
+ }
+
+ void AddChunk(const std::string& chunk) override {
+ std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer(
+ trace_analyzer::TraceAnalyzer::Create("[" + chunk + "]"));
+ trace_analyzer::TraceEventVector events;
+ analyzer->FindEvents(trace_analyzer::Query::EventCategoryIs(kTestCategory),
+ &events);
+ for (const auto* event : events) {
+ Append(&events_, event->name);
+ }
+ }
+
+ void AddMetadata(std::unique_ptr<base::DictionaryValue> metadata) override {
+ base::DictionaryValue* dict = nullptr;
+ EXPECT_TRUE(metadata->GetAsDictionary(&dict));
+ std::string value;
+ if (dict->GetString(kTestMetadataKey, &value))
+ Append(&metadata_, value);
+ }
+
+ void OnConnectionError() {
+ if (quit_closure_)
+ quit_closure_.Run();
+ }
+
+ mojo::Binding<mojom::Recorder> binding_;
+ std::string events_;
+ std::string metadata_;
+ base::Closure quit_closure_;
+};
+
+class ChromeTraceEventAgentTest : public testing::Test {
+ public:
+ void SetUp() override {
+ message_loop_.reset(new base::MessageLoop());
+ agent_.reset(new ChromeTraceEventAgent(nullptr));
+ }
+
+ void TearDown() override {
+ base::trace_event::TraceLog::GetInstance()->SetDisabled();
+ recorder_.reset();
+ agent_.reset();
+ message_loop_.reset();
+ }
+
+ void StartTracing(const std::string& categories) {
+ agent_->StartTracing(
+ base::trace_event::TraceConfig(categories, "").ToString(),
+ base::TimeTicks::Now(),
+ base::BindRepeating([](bool success) { EXPECT_TRUE(success); }));
+ }
+
+ void StopAndFlush(base::Closure quit_closure) {
+ mojom::RecorderPtr recorder_ptr;
+ recorder_.reset(new MockRecorder(MakeRequest(&recorder_ptr)));
+ recorder_->set_quit_closure(quit_closure);
+ agent_->StopAndFlush(std::move(recorder_ptr));
+ }
+
+ void AddMetadataGeneratorFunction(
+ ChromeTraceEventAgent::MetadataGeneratorFunction generator) {
+ agent_->AddMetadataGeneratorFunction(generator);
+ }
+
+ void GetCategories(const std::string& expected_category,
+ base::Closure quit_closure) {
+ agent_->GetCategories(base::BindRepeating(
+ &ChromeTraceEventAgentTest::OnGetCategoriesReply,
+ base::Unretained(this), expected_category, quit_closure));
+ }
+
+ void OnGetCategoriesReply(const std::string& expected_category,
+ base::Closure quit_closure,
+ const std::string& categories) {
+ EXPECT_FALSE(categories.rfind(expected_category) == std::string::npos);
+ quit_closure.Run();
+ }
+
+ MockRecorder* recorder() const { return recorder_.get(); }
+
+ private:
+ std::unique_ptr<base::MessageLoop> message_loop_;
+ std::unique_ptr<ChromeTraceEventAgent> agent_;
+ std::unique_ptr<MockRecorder> recorder_;
+};
+
+TEST_F(ChromeTraceEventAgentTest, StartTracing) {
+ EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+ base::RunLoop run_loop;
+ StartTracing("*");
+ EXPECT_TRUE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+ StopAndFlush(run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+TEST_F(ChromeTraceEventAgentTest, StopAndFlushEvents) {
+ EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+ base::RunLoop run_loop;
+ StartTracing(kTestCategory);
+ TRACE_EVENT_INSTANT0(kTestCategory, "event1", TRACE_EVENT_SCOPE_THREAD);
+ TRACE_EVENT_INSTANT0(kTestCategory, "event2", TRACE_EVENT_SCOPE_THREAD);
+ StopAndFlush(run_loop.QuitClosure());
+ run_loop.Run();
+
+ auto* mock_recorder = recorder();
+ EXPECT_EQ("event1,event2", mock_recorder->events());
+ EXPECT_EQ("", mock_recorder->metadata());
+ EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+}
+
+TEST_F(ChromeTraceEventAgentTest, GetCategories) {
+ base::RunLoop run_loop;
+ TRACE_EVENT_INSTANT0(kTestCategory, "event1", TRACE_EVENT_SCOPE_THREAD);
+ GetCategories(kTestCategory, run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+TEST_F(ChromeTraceEventAgentTest, StopAndFlushMetadata) {
+ EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+ base::RunLoop run_loop;
+ AddMetadataGeneratorFunction(base::BindRepeating([] {
+ std::unique_ptr<base::DictionaryValue> metadata_dict(
+ new base::DictionaryValue());
+ metadata_dict->SetString(kTestMetadataKey, "test metadata");
+ return metadata_dict;
+ }));
+ StartTracing(kTestCategory);
+ StopAndFlush(run_loop.QuitClosure());
+ run_loop.Run();
+
+ auto* mock_recorder = recorder();
+ EXPECT_EQ("", mock_recorder->events());
+ EXPECT_EQ("test metadata", mock_recorder->metadata());
+ EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+}
+} // namespace tracing
diff --git a/chromium/services/tracing/public/mojom/BUILD.gn b/chromium/services/tracing/public/mojom/BUILD.gn
new file mode 100644
index 00000000000..ebfa6b95a02
--- /dev/null
+++ b/chromium/services/tracing/public/mojom/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2018 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom_component("mojom") {
+ output_prefix = "tracing_mojom"
+ macro_prefix = "TRACING_MOJOM"
+
+ sources = [
+ "constants.mojom",
+ "tracing.mojom",
+ ]
+
+ public_deps = [
+ "//mojo/common:common_custom_types",
+ ]
+
+ # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
+ use_once_callback = false
+}
diff --git a/chromium/services/tracing/public/mojom/OWNERS b/chromium/services/tracing/public/mojom/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/services/tracing/public/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/services/tracing/public/mojom/constants.mojom b/chromium/services/tracing/public/mojom/constants.mojom
new file mode 100644
index 00000000000..abf03fa664b
--- /dev/null
+++ b/chromium/services/tracing/public/mojom/constants.mojom
@@ -0,0 +1,13 @@
+// Copyright 2017 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.
+
+module tracing.mojom;
+
+const uint32 kStopTracingRetryTimeMilliseconds = 100;
+
+const string kServiceName = "tracing";
+
+// The label of agents that provide trace data of the format explained in
+// https://goo.gl/Dw8qPY.
+const string kChromeTraceEventLabel = "traceEvents";
diff --git a/chromium/services/tracing/public/mojom/tracing.mojom b/chromium/services/tracing/public/mojom/tracing.mojom
new file mode 100644
index 00000000000..39a74c1a9f2
--- /dev/null
+++ b/chromium/services/tracing/public/mojom/tracing.mojom
@@ -0,0 +1,77 @@
+// Copyright 2017 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.
+
+module tracing.mojom;
+
+import "mojo/common/time.mojom";
+import "mojo/common/values.mojom";
+
+// The JSON type of data coming from a tracing agents.
+//
+// - All agents with the same label should have the same type.
+// - There can be multiple agents with the same label, if their data type is
+// ARRAY or OBJECT. Their data will be concatenated together and separated by
+// commas.
+// - There can be only one agent with data type STRING.
+enum TraceDataType {
+ ARRAY,
+ OBJECT,
+ STRING
+};
+
+// Tracing agents, like |chrome|, |etw|, |battor|, and |cros|, use this
+// interface to register themselves to the tracing service.
+//
+// This is a separate interface from |Coordinator| for security and privacy
+// reasons: although we want to let almost every process be able to send tracing
+// data to the service, we do not want to let an untrusted child process be able
+// to collect traces from other processes using the |Coordinator| interface.
+interface AgentRegistry {
+ RegisterAgent(Agent agent, string label, TraceDataType type,
+ bool supports_explicit_clock_sync_);
+};
+
+// When the tracing service calls |StopAndFlush| on an agent, the agent begins
+// serializing data into the given recorder. When finished, the agent should
+// close the recorder connection to signal the tracing service that no more data
+// will be sent.
+interface Agent {
+ StartTracing(string config, mojo.common.mojom.TimeTicks coordinator_time) => (
+ bool success);
+ StopAndFlush(Recorder recorder);
+ RequestClockSyncMarker(string sync_id) => (
+ mojo.common.mojom.TimeTicks issue_ts,
+ mojo.common.mojom.TimeTicks issue_end_ts);
+ RequestBufferStatus() => (uint32 capacity, uint32 count);
+ GetCategories() => (string categories);
+};
+
+// An agent can make several calls to |AddChunk|. Chunks will be concatenated
+// with no separator (type STRING) or using comma as the separator (type ARRAY).
+// There should be only one agent of type STRING per agent label; otherwise
+// their trace data would be mixed up.
+interface Recorder {
+ AddChunk(string chunk);
+ AddMetadata(mojo.common.mojom.DictionaryValue metadata);
+};
+
+// A tracing controller uses this interface to coordinate trace data collection
+// from all registered agents. At any given time, there should be at most one
+// connected controller.
+interface Coordinator {
+ // The return value is false if tracing is already enabled with a different
+ // config. Otherwise, true is returned as soon as the service receives acks
+ // from all existing agents and agents that connect during |StartTracing|.
+ StartTracing(string config) => (bool success);
+ StopAndFlush(handle<data_pipe_producer> stream) => (
+ mojo.common.mojom.DictionaryValue metadata);
+ // Same as |StopAndFlush| but only write data from a certain |agent_label| to
+ // the |stream|.
+ StopAndFlushAgent(handle<data_pipe_producer> stream, string agent_label) => (
+ mojo.common.mojom.DictionaryValue metadata);
+ IsTracing() => (bool is_tracing);
+ RequestBufferUsage() => (bool success, float percent_full,
+ uint32 approximate_count);
+ GetCategories() => (bool success, string categories);
+};
diff --git a/chromium/services/tracing/recorder.cc b/chromium/services/tracing/recorder.cc
new file mode 100644
index 00000000000..4c25d9cf549
--- /dev/null
+++ b/chromium/services/tracing/recorder.cc
@@ -0,0 +1,47 @@
+// Copyright 2017 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 "services/tracing/recorder.h"
+
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace tracing {
+
+Recorder::Recorder(mojom::RecorderRequest request,
+ mojom::TraceDataType data_type,
+ const base::RepeatingClosure& on_data_change_callback)
+ : is_recording_(true),
+ data_type_(data_type),
+ on_data_change_callback_(on_data_change_callback),
+ binding_(this, std::move(request)),
+ weak_ptr_factory_(this) {
+ binding_.set_connection_error_handler(base::BindOnce(
+ &Recorder::OnConnectionError, weak_ptr_factory_.GetWeakPtr()));
+}
+
+Recorder::~Recorder() = default;
+
+void Recorder::AddChunk(const std::string& chunk) {
+ if (chunk.empty())
+ return;
+ if (data_type_ != mojom::TraceDataType::STRING && !data_.empty())
+ data_.append(",");
+ data_.append(chunk);
+ on_data_change_callback_.Run();
+}
+
+void Recorder::AddMetadata(std::unique_ptr<base::DictionaryValue> metadata) {
+ metadata_.MergeDictionary(metadata.get());
+}
+
+void Recorder::OnConnectionError() {
+ is_recording_ = false;
+ on_data_change_callback_.Run();
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/recorder.h b/chromium/services/tracing/recorder.h
new file mode 100644
index 00000000000..1178004ff50
--- /dev/null
+++ b/chromium/services/tracing/recorder.h
@@ -0,0 +1,70 @@
+// Copyright 2017 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 SERVICES_TRACING_RECORDER_H_
+#define SERVICES_TRACING_RECORDER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace tracing {
+
+class Recorder : public mojom::Recorder {
+ public:
+ // The tracing service creates instances of the |Recorder| class and send them
+ // to agents. The agents then use the recorder for sending trace data to the
+ // tracing service.
+ //
+ // |data_is_array| tells the recorder whether the data is of type array or
+ // string. Chunks of type array are concatenated using a comma as the
+ // separator; chuunks of type string are concatenated without a separator.
+ //
+ // |on_data_change_callback| is run whenever the recorder receives data from
+ // the agent or when the connection is lost to notify the tracing service of
+ // the data change.
+ Recorder(mojom::RecorderRequest request,
+ mojom::TraceDataType data_type,
+ const base::RepeatingClosure& on_data_change_callback);
+ ~Recorder() override;
+
+ const std::string& data() const { return data_; }
+
+ void clear_data() { data_.clear(); }
+
+ const base::DictionaryValue& metadata() const { return metadata_; }
+ bool is_recording() const { return is_recording_; }
+ mojom::TraceDataType data_type() const { return data_type_; }
+
+ private:
+ friend class RecorderTest; // For testing.
+ // mojom::Recorder
+ // These are called by agents for sending trace data to the tracing service.
+ void AddChunk(const std::string& chunk) override;
+ void AddMetadata(std::unique_ptr<base::DictionaryValue> metadata) override;
+
+ void OnConnectionError();
+
+ std::string data_;
+ base::DictionaryValue metadata_;
+ bool is_recording_;
+ mojom::TraceDataType data_type_;
+ base::RepeatingClosure on_data_change_callback_;
+ mojo::Binding<mojom::Recorder> binding_;
+
+ base::WeakPtrFactory<Recorder> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Recorder);
+};
+
+} // namespace tracing
+#endif // SERVICES_TRACING_RECORDER_H_
diff --git a/chromium/services/tracing/recorder_unittest.cc b/chromium/services/tracing/recorder_unittest.cc
new file mode 100644
index 00000000000..9462477e3e2
--- /dev/null
+++ b/chromium/services/tracing/recorder_unittest.cc
@@ -0,0 +1,135 @@
+// Copyright 2017 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 "services/tracing/recorder.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+class RecorderTest : public testing::Test {
+ public:
+ void SetUp() override { message_loop_.reset(new base::MessageLoop()); }
+
+ void TearDown() override {
+ recorder_.reset();
+ message_loop_.reset();
+ }
+
+ void CreateRecorder(mojom::RecorderRequest request,
+ mojom::TraceDataType data_type,
+ const base::Closure& callback) {
+ recorder_.reset(new Recorder(std::move(request), data_type, callback));
+ }
+
+ void CreateRecorder(mojom::TraceDataType data_type,
+ const base::Closure& callback) {
+ CreateRecorder(nullptr, data_type, callback);
+ }
+
+ void AddChunk(const std::string& chunk) { recorder_->AddChunk(chunk); }
+
+ void AddMetadata(std::unique_ptr<base::DictionaryValue> metadata) {
+ recorder_->AddMetadata(std::move(metadata));
+ }
+
+ std::unique_ptr<Recorder> recorder_;
+
+ private:
+ std::unique_ptr<base::MessageLoop> message_loop_;
+};
+
+TEST_F(RecorderTest, AddChunkArray) {
+ size_t num_calls = 0;
+ CreateRecorder(mojom::TraceDataType::ARRAY,
+ base::BindRepeating([](size_t* num_calls) { (*num_calls)++; },
+ base::Unretained(&num_calls)));
+ AddChunk("chunk1");
+ AddChunk("chunk2");
+ AddChunk("chunk3");
+ EXPECT_EQ("chunk1,chunk2,chunk3", recorder_->data());
+
+ // Verify that the recorder has called the callback every time it received a
+ // chunk.
+ EXPECT_EQ(3u, num_calls);
+}
+
+TEST_F(RecorderTest, AddChunkObject) {
+ size_t num_calls = 0;
+ CreateRecorder(mojom::TraceDataType::OBJECT,
+ base::BindRepeating([](size_t* num_calls) { (*num_calls)++; },
+ base::Unretained(&num_calls)));
+ AddChunk("chunk1");
+ AddChunk("chunk2");
+ AddChunk("chunk3");
+
+ // Objects are similar to arrays. Their chunks are separated by commas.
+ EXPECT_EQ("chunk1,chunk2,chunk3", recorder_->data());
+
+ // Verify that the recorder has called the callback every time it received a
+ // chunk.
+ EXPECT_EQ(3u, num_calls);
+}
+
+TEST_F(RecorderTest, AddChunkString) {
+ size_t num_calls = 0;
+ CreateRecorder(mojom::TraceDataType::STRING,
+ base::BindRepeating([](size_t* num_calls) { (*num_calls)++; },
+ base::Unretained(&num_calls)));
+ AddChunk("chunk1");
+ AddChunk("chunk2");
+ AddChunk("chunk3");
+ EXPECT_EQ("chunk1chunk2chunk3", recorder_->data());
+ EXPECT_EQ(3u, num_calls);
+}
+
+TEST_F(RecorderTest, AddMetadata) {
+ CreateRecorder(mojom::TraceDataType::ARRAY, base::BindRepeating([] {}));
+
+ auto dict1 = std::make_unique<base::DictionaryValue>();
+ dict1->SetString("network-type", "Ethernet");
+ AddMetadata(std::move(dict1));
+
+ auto dict2 = std::make_unique<base::DictionaryValue>();
+ dict2->SetString("os-name", "CrOS");
+ AddMetadata(std::move(dict2));
+
+ EXPECT_EQ(2u, recorder_->metadata().size());
+ std::string net;
+ EXPECT_TRUE(recorder_->metadata().GetString("network-type", &net));
+ EXPECT_EQ("Ethernet", net);
+ std::string os;
+ EXPECT_TRUE(recorder_->metadata().GetString("os-name", &os));
+ EXPECT_EQ("CrOS", os);
+}
+
+TEST_F(RecorderTest, OnConnectionError) {
+ base::RunLoop run_loop;
+ size_t num_calls = 0;
+ {
+ mojom::RecorderPtr ptr;
+ auto request = MakeRequest(&ptr);
+ CreateRecorder(std::move(request), mojom::TraceDataType::STRING,
+ base::BindRepeating(
+ [](size_t* num_calls, base::Closure quit_closure) {
+ (*num_calls)++;
+ quit_closure.Run();
+ },
+ base::Unretained(&num_calls), run_loop.QuitClosure()));
+ }
+ // |ptr| is deleted at this point and so the recorder should notify us that
+ // the client is not going to send any more data by running the callback.
+ run_loop.Run();
+ EXPECT_EQ(1u, num_calls);
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/service_main.cc b/chromium/services/tracing/service_main.cc
new file mode 100644
index 00000000000..98adace3336
--- /dev/null
+++ b/chromium/services/tracing/service_main.cc
@@ -0,0 +1,12 @@
+// Copyright 2018 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 "services/service_manager/public/c/main.h"
+#include "services/service_manager/public/cpp/service_runner.h"
+#include "services/tracing/tracing_service.h"
+
+MojoResult ServiceMain(MojoHandle service_request_handle) {
+ return service_manager::ServiceRunner(new tracing::TracingService())
+ .Run(service_request_handle);
+}
diff --git a/chromium/services/tracing/test_util.cc b/chromium/services/tracing/test_util.cc
new file mode 100644
index 00000000000..e60d7cab14a
--- /dev/null
+++ b/chromium/services/tracing/test_util.cc
@@ -0,0 +1,55 @@
+// Copyright 2017 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 "services/tracing/test_util.h"
+
+#include <string>
+
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace tracing {
+
+MockAgent::MockAgent() : binding_(this) {}
+
+MockAgent::~MockAgent() = default;
+
+mojom::AgentPtr MockAgent::CreateAgentPtr() {
+ mojom::AgentPtr agent_proxy;
+ binding_.Bind(mojo::MakeRequest(&agent_proxy));
+ return agent_proxy;
+}
+
+void MockAgent::StartTracing(const std::string& config,
+ base::TimeTicks coordinator_time,
+ const StartTracingCallback& cb) {
+ call_stat_.push_back("StartTracing");
+ cb.Run(true);
+}
+
+void MockAgent::StopAndFlush(mojom::RecorderPtr recorder) {
+ call_stat_.push_back("StopAndFlush");
+ if (!metadata_.empty())
+ recorder->AddMetadata(metadata_.CreateDeepCopy());
+ for (const auto& chunk : data_) {
+ recorder->AddChunk(chunk);
+ }
+}
+
+void MockAgent::RequestClockSyncMarker(
+ const std::string& sync_id,
+ const RequestClockSyncMarkerCallback& cb) {
+ call_stat_.push_back("RequestClockSyncMarker");
+}
+
+void MockAgent::GetCategories(const GetCategoriesCallback& cb) {
+ call_stat_.push_back("GetCategories");
+ cb.Run(categories_);
+}
+
+void MockAgent::RequestBufferStatus(const RequestBufferStatusCallback& cb) {
+ call_stat_.push_back("RequestBufferStatus");
+ cb.Run(trace_log_status_.event_capacity, trace_log_status_.event_count);
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/test_util.h b/chromium/services/tracing/test_util.h
new file mode 100644
index 00000000000..082b4de47a0
--- /dev/null
+++ b/chromium/services/tracing/test_util.h
@@ -0,0 +1,55 @@
+// Copyright 2017 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 SERVICES_TRACING_TEST_UTIL_H_
+#define SERVICES_TRACING_TEST_UTIL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/trace_event/trace_log.h"
+#include "base/values.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace base {
+class TimeTicks;
+} // namespace base
+
+namespace tracing {
+
+class MockAgent : public mojom::Agent {
+ public:
+ MockAgent();
+ ~MockAgent() override;
+
+ mojom::AgentPtr CreateAgentPtr();
+
+ std::vector<std::string> call_stat() const { return call_stat_; }
+
+ // Set these variables to configure the agent.
+ std::vector<std::string> data_;
+ base::DictionaryValue metadata_;
+ std::string categories_;
+ base::trace_event::TraceLogStatus trace_log_status_;
+
+ private:
+ // mojom::Agent
+ void StartTracing(const std::string& config,
+ base::TimeTicks coordinator_time,
+ const StartTracingCallback& cb) override;
+ void StopAndFlush(mojom::RecorderPtr recorder) override;
+ void RequestClockSyncMarker(
+ const std::string& sync_id,
+ const RequestClockSyncMarkerCallback& cb) override;
+ void GetCategories(const GetCategoriesCallback& cb) override;
+ void RequestBufferStatus(const RequestBufferStatusCallback& cb) override;
+
+ mojo::Binding<mojom::Agent> binding_;
+ std::vector<std::string> call_stat_;
+};
+
+} // namespace tracing
+
+#endif // SERVICES_TRACING_TEST_UTIL_H_
diff --git a/chromium/services/tracing/tracing_service.cc b/chromium/services/tracing/tracing_service.cc
new file mode 100644
index 00000000000..701ea444cda
--- /dev/null
+++ b/chromium/services/tracing/tracing_service.cc
@@ -0,0 +1,47 @@
+// Copyright 2018 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 "services/tracing/tracing_service.h"
+
+#include <utility>
+
+#include "base/timer/timer.h"
+#include "services/service_manager/public/cpp/service_context.h"
+#include "services/tracing/agent_registry.h"
+#include "services/tracing/coordinator.h"
+
+namespace tracing {
+
+std::unique_ptr<service_manager::Service> TracingService::Create() {
+ return std::make_unique<TracingService>();
+}
+
+TracingService::TracingService() : weak_factory_(this) {}
+
+TracingService::~TracingService() = default;
+
+void TracingService::OnStart() {
+ ref_factory_.reset(new service_manager::ServiceContextRefFactory(
+ context()->CreateQuitClosure()));
+
+ tracing_agent_registry_ = std::make_unique<AgentRegistry>(ref_factory_.get());
+ registry_.AddInterface(
+ base::BindRepeating(&AgentRegistry::BindAgentRegistryRequest,
+ base::Unretained(tracing_agent_registry_.get())));
+
+ tracing_coordinator_ = std::make_unique<Coordinator>(ref_factory_.get());
+ registry_.AddInterface(
+ base::BindRepeating(&Coordinator::BindCoordinatorRequest,
+ base::Unretained(tracing_coordinator_.get())));
+}
+
+void TracingService::OnBindInterface(
+ const service_manager::BindSourceInfo& source_info,
+ const std::string& interface_name,
+ mojo::ScopedMessagePipeHandle interface_pipe) {
+ registry_.BindInterface(interface_name, std::move(interface_pipe),
+ source_info);
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/tracing_service.h b/chromium/services/tracing/tracing_service.h
new file mode 100644
index 00000000000..e70a29027fa
--- /dev/null
+++ b/chromium/services/tracing/tracing_service.h
@@ -0,0 +1,58 @@
+// Copyright 2018 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 SERVICES_TRACING_TRACING_SERVICE_H_
+#define SERVICES_TRACING_TRACING_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "services/tracing/agent_registry.h"
+#include "services/tracing/coordinator.h"
+
+namespace tracing {
+
+class TracingService : public service_manager::Service {
+ public:
+ TracingService();
+ ~TracingService() override;
+
+ // service_manager::Service:
+ // Factory function for use as an embedded service.
+ static std::unique_ptr<service_manager::Service> Create();
+
+ // service_manager::Service:
+ void OnStart() override;
+ void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+ const std::string& interface_name,
+ mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+ service_manager::ServiceContextRefFactory* ref_factory() {
+ return ref_factory_.get();
+ }
+
+ private:
+ service_manager::BinderRegistryWithArgs<
+ const service_manager::BindSourceInfo&>
+ registry_;
+ std::unique_ptr<tracing::AgentRegistry> tracing_agent_registry_;
+ std::unique_ptr<tracing::Coordinator> tracing_coordinator_;
+ std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_;
+
+ // WeakPtrFactory members should always come last so WeakPtrs are destructed
+ // before other members.
+ base::WeakPtrFactory<TracingService> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TracingService);
+};
+
+} // namespace tracing
+
+#endif // SERVICES_TRACING_TRACING_SERVICE_H_
diff --git a/chromium/services/tracing/tracing_service_unittest.cc b/chromium/services/tracing/tracing_service_unittest.cc
new file mode 100644
index 00000000000..b71fc43ad46
--- /dev/null
+++ b/chromium/services/tracing/tracing_service_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2018 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 <memory>
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_test.h"
+#include "services/tracing/public/mojom/constants.mojom.h"
+#include "services/tracing/public/mojom/tracing.mojom.h"
+
+namespace tracing {
+
+class TracingServiceTest : public service_manager::test::ServiceTest {
+ public:
+ TracingServiceTest()
+ : service_manager::test::ServiceTest("tracing_unittests") {}
+ ~TracingServiceTest() override {}
+
+ protected:
+ void SetUp() override {
+ service_manager::test::ServiceTest::SetUp();
+ connector()->StartService(mojom::kServiceName);
+ }
+
+ void SetRunLoopToQuit(base::RunLoop* loop) { loop_ = loop; }
+
+ private:
+ base::RunLoop* loop_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(TracingServiceTest);
+};
+
+TEST_F(TracingServiceTest, TracingServiceInstantiate) {
+ mojom::AgentRegistryPtr agent_registry;
+ connector()->BindInterface(mojom::kServiceName,
+ mojo::MakeRequest(&agent_registry));
+
+ tracing::mojom::AgentPtr agent;
+ agent_registry->RegisterAgent(std::move(agent), "FOO",
+ mojom::TraceDataType::STRING,
+ false /*supports_explicit_clock_sync*/);
+
+ base::RunLoop loop;
+ SetRunLoopToQuit(&loop);
+ loop.Run();
+}
+
+} // namespace tracing
diff --git a/chromium/services/tracing/unittest_manifest.json b/chromium/services/tracing/unittest_manifest.json
new file mode 100644
index 00000000000..311385fd3ec
--- /dev/null
+++ b/chromium/services/tracing/unittest_manifest.json
@@ -0,0 +1,11 @@
+{
+ "name": "tracing_unittests",
+ "display_name": "Tracing Unittests",
+ "interface_provider_specs": {
+ "service_manager:connector": {
+ "requires": {
+ "tracing": [ "tests" ]
+ }
+ }
+ }
+}