Commit 4df8a2dd authored by Oystein Eftevaag's avatar Oystein Eftevaag Committed by Commit Bot

Add a Mojo-based transportation layer for Perfetto

This adds a base layer that individual logging protocols (like tracing)
can use to send their data to Perfetto, using Mojo messaging and
shared memory.

First CL of several, after this comes the Chrome tracing layer: https://chromium-review.googlesource.com/c/chromium/src/+/981270

A preview of the full integration can be seen here: https://chromium-review.googlesource.com/c/chromium/src/+/852808

The design doc for the integration: https://docs.google.com/document/d/1a8lvN3zYWEyAsBjtuRRsy0fWOWdUdbw6bGnKLBscGdw/edit

R=primiano@chromium.org,skyostil@chromium.org

Change-Id: Ie1db5745ebffdcf1e2d74e2f6b12395f447c8552
Reviewed-on: https://chromium-review.googlesource.com/980578Reviewed-by: default avataroysteine <oysteine@google.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarPrimiano Tucci <primiano@chromium.org>
Commit-Queue: oysteine <oysteine@google.com>
Cr-Commit-Position: refs/heads/master@{#551019}
parent 79adc2fa
...@@ -842,7 +842,7 @@ deps = { ...@@ -842,7 +842,7 @@ deps = {
}, },
'src/third_party/perfetto': 'src/third_party/perfetto':
Var('android_git') + '/platform/external/perfetto.git' + '@' + '135841c8077f13f14c6b80e32d391da84d2ee131', Var('android_git') + '/platform/external/perfetto.git' + '@' + '197785b48b41ff490cb1682a2216109b98fc7722',
'src/third_party/perl': { 'src/third_party/perl': {
'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
......
...@@ -28,6 +28,24 @@ source_set("lib") { ...@@ -28,6 +28,24 @@ source_set("lib") {
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//services/tracing/public/cpp", "//services/tracing/public/cpp",
] ]
if (is_mac || is_linux || is_android) {
sources += [
"perfetto/perfetto_service.cc",
"perfetto/perfetto_service.h",
"perfetto/producer_host.cc",
"perfetto/producer_host.h",
]
deps = [
"//third_party/perfetto/protos/perfetto/trace:lite",
"//third_party/perfetto/protos/perfetto/trace:zero",
"//third_party/perfetto/protos/perfetto/trace/chrome:lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:zero",
]
public_deps += [ "//third_party/perfetto:libperfetto" ]
}
} }
service_manifest("manifest") { service_manifest("manifest") {
...@@ -75,6 +93,18 @@ source_set("tests") { ...@@ -75,6 +93,18 @@ source_set("tests") {
"//testing/gtest", "//testing/gtest",
] ]
if (is_mac || is_linux || is_android) {
sources += [ "perfetto/perfetto_integration_unittest.cc" ]
deps += [
"//third_party/perfetto/protos/perfetto/common",
"//third_party/perfetto/protos/perfetto/trace:lite",
"//third_party/perfetto/protos/perfetto/trace:zero",
"//third_party/perfetto/protos/perfetto/trace/chrome:lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:zero",
]
}
data_deps = [ data_deps = [
":tracing", ":tracing",
] ]
......
include_rules = [ include_rules = [
"+services/service_manager/public" "+services/service_manager/public",
"+third_party/perfetto/include",
"+third_party/perfetto/protos/perfetto",
] ]
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
"service_manager:connector": { "service_manager:connector": {
"provides": { "provides": {
"app": [ "app": [
"tracing::mojom::AgentRegistry" "tracing::mojom::AgentRegistry",
"tracing::mojom::PerfettoService"
], ],
"tracing": [ "tracing::mojom::Coordinator" ], "tracing": [ "tracing::mojom::Coordinator" ],
"tests": [ "*" ] "tests": [ "*" ]
......
// 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 <string>
#include <utility>
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/tracing/perfetto/perfetto_service.h"
#include "services/tracing/perfetto/producer_host.h"
#include "services/tracing/public/cpp/perfetto/producer_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h"
#include "third_party/perfetto/include/perfetto/tracing/core/consumer.h"
#include "third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h"
#include "third_party/perfetto/include/perfetto/tracing/core/service.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
#include "third_party/perfetto/protos/perfetto/common/commit_data_request.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/test_event.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
namespace tracing {
namespace {
const char kPerfettoTestDataSourceName[] =
"org.chromium.chrome_integration_unittest";
const char kPerfettoProducerName[] = "chrome_producer_test";
const char kPerfettoTestString[] = "d00df00d";
class PerfettoIntegrationTest : public testing::Test {
public:
PerfettoIntegrationTest() {
perfetto_service_ = std::make_unique<PerfettoService>();
}
PerfettoService* perfetto_service() const { return perfetto_service_.get(); }
private:
std::unique_ptr<PerfettoService> perfetto_service_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
};
class TestDataSource : public ProducerClient::DataSourceBase {
public:
TestDataSource(ProducerClient* producer_client,
size_t send_packet_count,
const std::string& trace_config,
uint32_t target_buffer)
: producer_client_(producer_client),
send_packet_count_(send_packet_count),
target_buffer_(target_buffer) {
if (send_packet_count_ > 0) {
std::unique_ptr<perfetto::TraceWriter> writer =
producer_client_->CreateTraceWriter(target_buffer);
CHECK(writer);
for (size_t i = 0; i < send_packet_count_; i++) {
writer->NewTracePacket()->set_for_testing()->set_str(
kPerfettoTestString);
}
}
}
void WritePacketBigly() {
const size_t kMessageSize = 10 * 1024;
std::unique_ptr<char[]> payload(new char[kMessageSize]);
memset(payload.get(), '.', kMessageSize);
payload.get()[kMessageSize - 1] = 0;
std::unique_ptr<perfetto::TraceWriter> writer =
producer_client_->CreateTraceWriter(target_buffer_);
CHECK(writer);
writer->NewTracePacket()->set_for_testing()->set_str(payload.get(),
kMessageSize);
}
private:
ProducerClient* producer_client_;
const size_t send_packet_count_;
uint32_t target_buffer_;
};
class MockProducerClient : public ProducerClient {
public:
MockProducerClient(
size_t send_packet_count,
base::OnceClosure client_enabled_callback = base::OnceClosure(),
base::OnceClosure client_disabled_callback = base::OnceClosure())
: client_enabled_callback_(std::move(client_enabled_callback)),
client_disabled_callback_(std::move(client_disabled_callback)),
send_packet_count_(send_packet_count) {}
size_t send_packet_count() const { return send_packet_count_; }
TestDataSource* enabled_data_source_instance() const {
return static_cast<TestDataSource*>(enabled_data_source_instance_.get());
}
void CreateDataSourceInstance(
uint64_t id,
mojom::DataSourceConfigPtr data_source_config) override {
enabled_data_source_instance_ = std::make_unique<TestDataSource>(
this, send_packet_count_, data_source_config->trace_config,
data_source_config->target_buffer);
if (client_enabled_callback_) {
std::move(client_enabled_callback_).Run();
}
}
void TearDownDataSourceInstance(uint64_t id) override {
ProducerClient::TearDownDataSourceInstance(id);
if (client_disabled_callback_) {
std::move(client_disabled_callback_).Run();
}
}
void CommitData(const perfetto::CommitDataRequest& commit,
CommitDataCallback callback = {}) override {
perfetto::protos::CommitDataRequest proto;
commit.ToProto(&proto);
std::string proto_string;
CHECK(proto.SerializeToString(&proto_string));
all_client_commit_data_requests_ += proto_string;
ProducerClient::CommitData(commit, callback);
}
void SetAgentEnabledCallback(base::OnceClosure client_enabled_callback) {
client_enabled_callback_ = std::move(client_enabled_callback);
}
void SetAgentDisabledCallback(base::OnceClosure client_disabled_callback) {
client_disabled_callback_ = std::move(client_disabled_callback);
}
void FlushTaskRunner() {
base::RunLoop wait_for_client_task_runner;
GetTaskRunner()->PostTask(FROM_HERE,
wait_for_client_task_runner.QuitClosure());
wait_for_client_task_runner.Run();
}
const std::string& all_client_commit_data_requests() const {
return all_client_commit_data_requests_;
}
private:
base::OnceClosure client_enabled_callback_;
base::OnceClosure client_disabled_callback_;
const size_t send_packet_count_;
std::string all_client_commit_data_requests_;
};
class MockConsumer : public perfetto::Consumer {
public:
using PacketReceivedCallback = std::function<void(bool)>;
MockConsumer(perfetto::Service* service,
std::string data_source_name,
PacketReceivedCallback packet_received_callback)
: packet_received_callback_(packet_received_callback),
data_source_name_(data_source_name) {
consumer_endpoint_ = service->ConnectConsumer(this);
}
void ReadBuffers() { consumer_endpoint_->ReadBuffers(); }
void StopTracing() {
ReadBuffers();
consumer_endpoint_->DisableTracing();
}
void StartTracing() {
perfetto::TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(4096 * 100);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name(data_source_name_);
ds_config->set_target_buffer(0);
consumer_endpoint_->EnableTracing(trace_config);
}
void FreeBuffers() { consumer_endpoint_->FreeBuffers(); }
size_t received_packets() const { return received_packets_; }
// perfetto::Consumer implementation
void OnConnect() override { StartTracing(); }
void OnDisconnect() override {}
void OnTracingDisabled() override {}
void OnTraceData(std::vector<perfetto::TracePacket> packets,
bool has_more) override {
for (auto& encoded_packet : packets) {
perfetto::protos::TracePacket packet;
DCHECK(encoded_packet.Decode(&packet));
if (packet.for_testing().str() == kPerfettoTestString) {
received_packets_++;
}
}
packet_received_callback_(has_more);
}
private:
std::unique_ptr<perfetto::Service::ConsumerEndpoint> consumer_endpoint_;
size_t received_packets_ = 0;
PacketReceivedCallback packet_received_callback_;
std::string data_source_name_;
};
class MockProducer : public ProducerHost {
public:
MockProducer(
std::string data_source_name,
base::OnceClosure datasource_registered_callback = base::OnceClosure())
: datasource_registered_callback_(
std::move(datasource_registered_callback)),
data_source_name_(data_source_name) {}
void OnConnect() override {
on_commit_callback_for_testing_ =
base::BindRepeating(&MockProducer::OnCommit, base::Unretained(this));
perfetto::DataSourceDescriptor descriptor;
descriptor.set_name(data_source_name_);
producer_endpoint_->RegisterDataSource(descriptor);
if (datasource_registered_callback_) {
std::move(datasource_registered_callback_).Run();
}
}
void OnCommit(const perfetto::CommitDataRequest& commit_data_request) {
perfetto::protos::CommitDataRequest proto;
commit_data_request.ToProto(&proto);
std::string proto_string;
CHECK(proto.SerializeToString(&proto_string));
all_host_commit_data_requests_ += proto_string;
}
const std::string& all_host_commit_data_requests() const {
return all_host_commit_data_requests_;
}
protected:
base::OnceClosure datasource_registered_callback_;
const std::string data_source_name_;
std::string all_host_commit_data_requests_;
};
TEST_F(PerfettoIntegrationTest, ProducerDatasourceInitialized) {
auto dummy_client =
std::make_unique<MockProducerClient>(0 /* send_packet_count */);
base::RunLoop producer_initialized_runloop;
auto new_producer = std::make_unique<MockProducer>(
kPerfettoTestDataSourceName, producer_initialized_runloop.QuitClosure());
new_producer->Initialize(dummy_client->CreateAndBindProducerClient(),
dummy_client->CreateProducerHostRequest(),
perfetto_service()->service(),
kPerfettoProducerName);
producer_initialized_runloop.Run();
ProducerClient::DeleteSoon(std::move(dummy_client));
}
TEST_F(PerfettoIntegrationTest, ClientEnabledAndDisabled) {
base::RunLoop on_trace_packets;
MockConsumer consumer(perfetto_service()->service(),
kPerfettoTestDataSourceName,
[&on_trace_packets](bool has_more) {
EXPECT_FALSE(has_more);
on_trace_packets.Quit();
});
base::RunLoop client_enabled_callback;
base::RunLoop client_disabled_callback;
auto client = std::make_unique<MockProducerClient>(
0 /* send_packet_count */, client_enabled_callback.QuitClosure(),
client_disabled_callback.QuitClosure());
auto producer = std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
producer->Initialize(client->CreateAndBindProducerClient(),
client->CreateProducerHostRequest(),
perfetto_service()->service(), kPerfettoProducerName);
client_enabled_callback.Run();
// Wait until all Mojo messages have been sent.
base::RunLoop().RunUntilIdle();
consumer.StopTracing();
client_disabled_callback.Run();
on_trace_packets.Run();
EXPECT_EQ(0u, consumer.received_packets());
ProducerClient::DeleteSoon(std::move(client));
}
TEST_F(PerfettoIntegrationTest, PacketsEndToEndProducerFirst) {
const size_t kNumPackets = 10;
base::RunLoop client_enabled_callback;
base::RunLoop client_disabled_callback;
auto client = std::make_unique<MockProducerClient>(
kNumPackets, client_enabled_callback.QuitClosure(),
client_disabled_callback.QuitClosure());
auto producer = std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
producer->Initialize(client->CreateAndBindProducerClient(),
client->CreateProducerHostRequest(),
perfetto_service()->service(), kPerfettoProducerName);
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->service(),
kPerfettoTestDataSourceName,
[&no_more_packets_runloop](bool has_more) {
if (!has_more) {
no_more_packets_runloop.Quit();
}
});
client_enabled_callback.Run();
// Wait until all Mojo messages have been sent.
base::RunLoop().RunUntilIdle();
consumer.StopTracing();
client_disabled_callback.Run();
no_more_packets_runloop.Run();
EXPECT_EQ(kNumPackets, consumer.received_packets());
ProducerClient::DeleteSoon(std::move(client));
}
TEST_F(PerfettoIntegrationTest, PacketsEndToEndConsumerFirst) {
const size_t kNumPackets = 10;
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->service(),
kPerfettoTestDataSourceName,
[&no_more_packets_runloop](bool has_more) {
if (!has_more) {
no_more_packets_runloop.Quit();
}
});
base::RunLoop client_enabled_callback;
auto client = std::make_unique<MockProducerClient>(
kNumPackets, client_enabled_callback.QuitClosure());
auto new_producer =
std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
new_producer->Initialize(client->CreateAndBindProducerClient(),
client->CreateProducerHostRequest(),
perfetto_service()->service(),
kPerfettoProducerName);
client_enabled_callback.Run();
// Wait until all Mojo messages have been sent.
base::RunLoop().RunUntilIdle();
consumer.StopTracing();
no_more_packets_runloop.Run();
EXPECT_EQ(kNumPackets, consumer.received_packets());
ProducerClient::DeleteSoon(std::move(client));
}
TEST_F(PerfettoIntegrationTest, CommitDataRequestIsMaybeComplete) {
const size_t kNumPackets = 100;
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->service(),
kPerfettoTestDataSourceName,
[&no_more_packets_runloop](bool has_more) {
if (!has_more) {
no_more_packets_runloop.Quit();
}
});
base::RunLoop client_enabled_callback;
auto client = std::make_unique<MockProducerClient>(
kNumPackets, client_enabled_callback.QuitClosure());
auto new_producer =
std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
new_producer->Initialize(client->CreateAndBindProducerClient(),
client->CreateProducerHostRequest(),
perfetto_service()->service(),
kPerfettoProducerName);
client_enabled_callback.Run();
client->enabled_data_source_instance()->WritePacketBigly();
client->FlushTaskRunner();
// Wait until all Mojo messages have been sent.
base::RunLoop().RunUntilIdle();
consumer.StopTracing();
no_more_packets_runloop.Run();
EXPECT_EQ(client->all_client_commit_data_requests(),
new_producer->all_host_commit_data_requests());
ProducerClient::DeleteSoon(std::move(client));
}
TEST_F(PerfettoIntegrationTest, TracingRestarted) {
const size_t kNumPackets = 10;
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->service(),
kPerfettoTestDataSourceName,
[&no_more_packets_runloop](bool has_more) {
if (!has_more) {
no_more_packets_runloop.Quit();
}
});
base::RunLoop client_enabled_callback;
auto client = std::make_unique<MockProducerClient>(
kNumPackets, client_enabled_callback.QuitClosure());
auto new_producer =
std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
new_producer->Initialize(client->CreateAndBindProducerClient(),
client->CreateProducerHostRequest(),
perfetto_service()->service(),
kPerfettoProducerName);
client_enabled_callback.Run();
// Wait until all Mojo messages have been sent.
base::RunLoop().RunUntilIdle();
perfetto::SharedMemory* first_session_shm = client->shared_memory();
consumer.StopTracing();
no_more_packets_runloop.Run();
EXPECT_EQ(kNumPackets, consumer.received_packets());
consumer.FreeBuffers();
base::RunLoop client_reenabled_callback;
client->SetAgentEnabledCallback(client_reenabled_callback.QuitClosure());
consumer.StartTracing();
client_reenabled_callback.Run();
// Wait until all Mojo messages have been sent.
base::RunLoop().RunUntilIdle();
// We should still be using the same shared memory.
EXPECT_EQ(first_session_shm, client->shared_memory());
base::RunLoop client_redisabled_callback;
client->SetAgentDisabledCallback(client_redisabled_callback.QuitClosure());
consumer.StopTracing();
client_redisabled_callback.Run();
EXPECT_EQ(kNumPackets * 2, consumer.received_packets());
ProducerClient::DeleteSoon(std::move(client));
}
TEST_F(PerfettoIntegrationTest, NoPacketsReceivedOnWrongSourceName) {
const size_t kNumPackets = 10;
base::RunLoop client_enabled_callback;
base::RunLoop client_disabled_callback;
auto client = std::make_unique<MockProducerClient>(
kNumPackets, client_enabled_callback.QuitClosure(),
client_disabled_callback.QuitClosure());
base::RunLoop producer_initialized_runloop;
auto new_producer = std::make_unique<MockProducer>(
"fake", producer_initialized_runloop.QuitClosure());
new_producer->Initialize(client->CreateAndBindProducerClient(),
client->CreateProducerHostRequest(),
perfetto_service()->service(),
kPerfettoProducerName);
producer_initialized_runloop.Run();
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->service(),
kPerfettoTestDataSourceName,
[&no_more_packets_runloop](bool has_more) {
if (!has_more) {
no_more_packets_runloop.Quit();
}
});
// Wait until all Mojo messages have been sent.
base::RunLoop().RunUntilIdle();
consumer.StopTracing();
no_more_packets_runloop.Run();
EXPECT_EQ(0u, consumer.received_packets());
ProducerClient::DeleteSoon(std::move(client));
}
TEST_F(PerfettoIntegrationTest,
DifferentSharedMemoryBuffersForDifferentAgents) {
base::RunLoop client1_enabled_callback;
base::RunLoop client2_enabled_callback;
auto client1 = std::make_unique<MockProducerClient>(
0 /* send_packet_count */, client1_enabled_callback.QuitClosure());
auto client2 = std::make_unique<MockProducerClient>(
0 /* send_packet_count */, client2_enabled_callback.QuitClosure());
auto producer1 = std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
producer1->Initialize(client1->CreateAndBindProducerClient(),
client1->CreateProducerHostRequest(),
perfetto_service()->service(), kPerfettoProducerName);
auto producer2 = std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
producer2->Initialize(client2->CreateAndBindProducerClient(),
client2->CreateProducerHostRequest(),
perfetto_service()->service(), kPerfettoProducerName);
MockConsumer consumer(perfetto_service()->service(),
kPerfettoTestDataSourceName, nullptr);
client1_enabled_callback.Run();
client2_enabled_callback.Run();
EXPECT_TRUE(client1->shared_memory());
EXPECT_TRUE(client2->shared_memory());
EXPECT_NE(client1->shared_memory(), client2->shared_memory());
ProducerClient::DeleteSoon(std::move(client1));
ProducerClient::DeleteSoon(std::move(client2));
}
} // namespace
} // namespace tracing
// 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/perfetto/perfetto_service.h"
#include <utility>
#include "base/task_scheduler/post_task.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
#include "services/tracing/perfetto/producer_host.h"
#include "services/tracing/public/cpp/perfetto/shared_memory.h"
#include "third_party/perfetto/include/perfetto/tracing/core/service.h"
namespace tracing {
namespace {
const char kPerfettoProducerName[] = "org.chromium.perfetto_producer";
PerfettoService* g_perfetto_service;
// Just used to destroy disconnected clients.
template <typename T>
void OnClientDisconnect(std::unique_ptr<T>) {}
} // namespace
// TODO(oysteine): Figure out if this is the correct TaskRunner to use.
PerfettoService::PerfettoService()
: perfetto_task_runner_(base::SequencedTaskRunnerHandle::Get()) {
service_ = perfetto::Service::CreateInstance(
std::make_unique<MojoSharedMemory::Factory>(), &perfetto_task_runner_);
DCHECK(service_);
DCHECK(!g_perfetto_service);
g_perfetto_service = this;
}
PerfettoService::~PerfettoService() {
DCHECK_EQ(g_perfetto_service, this);
g_perfetto_service = nullptr;
}
// static
PerfettoService* PerfettoService::GetInstance() {
return g_perfetto_service;
}
void PerfettoService::BindRequest(
mojom::PerfettoServiceRequest request,
const service_manager::BindSourceInfo& source_info) {
bindings_.AddBinding(this, std::move(request), source_info.identity);
}
void PerfettoService::ConnectToProducerHost(
mojom::ProducerClientPtr producer_client,
mojom::ProducerHostRequest producer_host) {
auto new_producer = std::make_unique<ProducerHost>();
new_producer->Initialize(std::move(producer_client), std::move(producer_host),
service_.get(), kPerfettoProducerName);
new_producer->set_connection_error_handler(base::BindOnce(
&OnClientDisconnect<ProducerHost>, std::move(new_producer)));
}
} // namespace tracing
// 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_PERFETTO_PERFETTO_SERVICE_H_
#define SERVICES_TRACING_PERFETTO_PERFETTO_SERVICE_H_
#include <memory>
#include <set>
#include "base/macros.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/service_manager/public/cpp/identity.h"
#include "services/tracing/public/cpp/perfetto/task_runner.h"
#include "services/tracing/public/mojom/perfetto_service.mojom.h"
namespace service_manager {
struct BindSourceInfo;
} // namespace service_manager
namespace perfetto {
class Service;
} // namespace perfetto
namespace tracing {
// This class serves two purposes: It wraps the use of the system-wide
// perfetto::Service instance, and serves as the main Mojo interface for
// connecting per-process ProducerClient with corresponding service-side
// ProducerHost.
class PerfettoService : public mojom::PerfettoService {
public:
PerfettoService();
~PerfettoService() override;
static PerfettoService* GetInstance();
void BindRequest(mojom::PerfettoServiceRequest request,
const service_manager::BindSourceInfo& source_info);
// mojom::PerfettoService implementation.
void ConnectToProducerHost(mojom::ProducerClientPtr producer_client,
mojom::ProducerHostRequest producer_host) override;
perfetto::Service* service() const { return service_.get(); }
private:
PerfettoTaskRunner perfetto_task_runner_;
std::unique_ptr<perfetto::Service> service_;
mojo::BindingSet<mojom::PerfettoService, service_manager::Identity> bindings_;
DISALLOW_COPY_AND_ASSIGN(PerfettoService);
};
} // namespace tracing
#endif // SERVICES_TRACING_PERFETTO_PERFETTO_SERVICE_H_
// 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/perfetto/producer_host.h"
#include <utility>
#include "services/tracing/public/cpp/perfetto/shared_memory.h"
#include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
namespace tracing {
ProducerHost::ProducerHost() = default;
ProducerHost::~ProducerHost() = default;
void ProducerHost::Initialize(mojom::ProducerClientPtr producer_client,
mojom::ProducerHostRequest producer_host,
perfetto::Service* service,
const std::string& name) {
DCHECK(service);
DCHECK(!producer_endpoint_);
producer_client_ = std::move(producer_client);
producer_client_.set_connection_error_handler(
base::BindOnce(&ProducerHost::OnConnectionError, base::Unretained(this)));
binding_ = std::make_unique<mojo::Binding<mojom::ProducerHost>>(
this, std::move(producer_host));
binding_->set_connection_error_handler(
base::BindOnce(&ProducerHost::OnConnectionError, base::Unretained(this)));
// TODO(oysteine): Figure out an uid once we need it.
producer_endpoint_ = service->ConnectProducer(this, 0 /* uid */, name);
DCHECK(producer_endpoint_);
}
void ProducerHost::OnConnectionError() {
// Manually reset to prevent any callbacks from the ProducerEndpoint
// when we're in a half-destructed state.
producer_endpoint_.reset();
std::move(connection_error_handler_).Run();
// This object is destroyed at this point.
}
void ProducerHost::OnConnect() {
// Register data sources with Perfetto here.
}
void ProducerHost::OnDisconnect() {
// Deliberately empty, this is invoked by the |service_| business logic after
// we destroy the |producer_endpoint|.
}
void ProducerHost::OnTracingSetup() {
MojoSharedMemory* shared_memory =
static_cast<MojoSharedMemory*>(producer_endpoint_->shared_memory());
DCHECK(shared_memory);
DCHECK(producer_client_);
mojo::ScopedSharedBufferHandle shm = shared_memory->Clone();
DCHECK(shm.is_valid());
producer_client_->OnTracingStart(std::move(shm));
}
void ProducerHost::CreateDataSourceInstance(
perfetto::DataSourceInstanceID id,
const perfetto::DataSourceConfig& config) {
// TODO(oysteine): Send full DataSourceConfig, not just the name/target_buffer
// and Chrome Tracing string.
auto data_source_config = mojom::DataSourceConfig::New();
data_source_config->name = config.name();
data_source_config->target_buffer = config.target_buffer();
data_source_config->trace_config = config.chrome_config().trace_config();
producer_client_->CreateDataSourceInstance(id, std::move(data_source_config));
}
void ProducerHost::TearDownDataSourceInstance(
perfetto::DataSourceInstanceID id) {
if (producer_client_) {
producer_client_->TearDownDataSourceInstance(id);
}
}
void ProducerHost::Flush(
perfetto::FlushRequestID id,
const perfetto::DataSourceInstanceID* raw_data_source_ids,
size_t num_data_sources) {
DCHECK(producer_client_);
std::vector<uint64_t> data_source_ids(raw_data_source_ids,
raw_data_source_ids + num_data_sources);
DCHECK_EQ(data_source_ids.size(), num_data_sources);
producer_client_->Flush(id, data_source_ids);
}
// This data can come from a malicious child process. We don't do any
// sanitization here because ProducerEndpoint::CommitData() (And any other
// ProducerEndpoint methods) are designed to deal with malformed / malicious
// inputs.
void ProducerHost::CommitData(mojom::CommitDataRequestPtr data_request) {
perfetto::CommitDataRequest native_data_request;
// TODO(oysteine): Set up a TypeTrait for this instead of manual conversion.
for (auto& chunk : data_request->chunks_to_move) {
auto* new_chunk = native_data_request.add_chunks_to_move();
new_chunk->set_page(chunk->page);
new_chunk->set_chunk(chunk->chunk);
new_chunk->set_target_buffer(chunk->target_buffer);
}
for (auto& chunk_patch : data_request->chunks_to_patch) {
auto* new_chunk_patch = native_data_request.add_chunks_to_patch();
new_chunk_patch->set_target_buffer(chunk_patch->target_buffer);
new_chunk_patch->set_writer_id(chunk_patch->writer_id);
new_chunk_patch->set_chunk_id(chunk_patch->chunk_id);
for (auto& patch : chunk_patch->patches) {
auto* new_patch = new_chunk_patch->add_patches();
new_patch->set_offset(patch->offset);
new_patch->set_data(patch->data);
}
new_chunk_patch->set_has_more_patches(chunk_patch->has_more_patches);
}
if (on_commit_callback_for_testing_) {
on_commit_callback_for_testing_.Run(native_data_request);
}
// TODO(oysteine): Pass through an optional callback for
// tests to know when a commit is completed.
producer_endpoint_->CommitData(native_data_request);
}
} // namespace tracing
// 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_PERFETTO_PRODUCER_HOST_H_
#define SERVICES_TRACING_PERFETTO_PRODUCER_HOST_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/macros.h"
#include "services/tracing/perfetto/producer_host.h"
#include "services/tracing/public/mojom/perfetto_service.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "third_party/perfetto/include/perfetto/tracing/core/producer.h"
#include "third_party/perfetto/include/perfetto/tracing/core/service.h"
namespace perfetto {
class CommitDataRequest;
} // namespace perfetto
namespace tracing {
// This class is the service-side part of the Perfetto Producer pair
// and is responsible for registering any available DataSources
// with Perfetto (like ChromeTracing) in OnConnect(). It will forward
// control messages from Perfetto to its per-process ProducerClient
// counterpart, like starting tracing with a specific shared memory buffer,
// create/teardown instances of specific data sources, etc.
// It's managed by PerfettoService which is responsible for
// creating a ProducerHost when a ProducerClient registers itself
// and connects them together.
class ProducerHost : public tracing::mojom::ProducerHost,
public perfetto::Producer {
public:
ProducerHost();
~ProducerHost() override;
void set_connection_error_handler(
base::OnceClosure connection_error_handler) {
connection_error_handler_ = std::move(connection_error_handler);
}
// Called by the ProducerService to register the
// Producer with Perfetto and connect to the
// corresponding remote ProducerClient.
void Initialize(mojom::ProducerClientPtr producer_client,
mojom::ProducerHostRequest producer_host,
perfetto::Service* service,
const std::string& name);
// perfetto::Producer implementation.
// Gets called by perfetto::Service to toggle specific data sources
// when requested by a Perfetto Consumer.
void OnConnect() override;
void OnDisconnect() override;
void CreateDataSourceInstance(
perfetto::DataSourceInstanceID id,
const perfetto::DataSourceConfig& config) override;
void TearDownDataSourceInstance(perfetto::DataSourceInstanceID) override;
void OnTracingSetup() override;
void Flush(perfetto::FlushRequestID,
const perfetto::DataSourceInstanceID* raw_data_source_ids,
size_t num_data_sources) override;
// mojom::ProducerHost implementation.
// This interface gets called by the per-process ProducerClients
// to signal that there's changes to be committed to the
// Shared Memory buffer (like finished chunks).
void CommitData(mojom::CommitDataRequestPtr data_request) override;
protected:
void OnConnectionError();
base::RepeatingCallback<void(const perfetto::CommitDataRequest&)>
on_commit_callback_for_testing_;
private:
mojom::ProducerClientPtr producer_client_;
std::unique_ptr<mojo::Binding<mojom::ProducerHost>> binding_;
base::OnceClosure connection_error_handler_;
protected:
// Perfetto guarantees that no OnXX callbacks are invoked on |this|
// immediately after |producer_endpoint_| is destroyed.
std::unique_ptr<perfetto::Service::ProducerEndpoint> producer_endpoint_;
DISALLOW_COPY_AND_ASSIGN(ProducerHost);
};
} // namespace tracing
#endif // SERVICES_TRACING_PERFETTO_PRODUCER_HOST_H_
...@@ -8,6 +8,8 @@ component("cpp") { ...@@ -8,6 +8,8 @@ component("cpp") {
"base_agent.h", "base_agent.h",
"chrome_trace_event_agent.cc", "chrome_trace_event_agent.cc",
"chrome_trace_event_agent.h", "chrome_trace_event_agent.h",
"tracing_features.cc",
"tracing_features.h",
] ]
defines = [ "IS_TRACING_CPP_IMPL" ] defines = [ "IS_TRACING_CPP_IMPL" ]
...@@ -19,4 +21,25 @@ component("cpp") { ...@@ -19,4 +21,25 @@ component("cpp") {
"//services/service_manager/public/cpp", "//services/service_manager/public/cpp",
"//services/tracing/public/mojom", "//services/tracing/public/mojom",
] ]
if (is_mac || is_linux || is_android) {
sources += [
"perfetto/producer_client.cc",
"perfetto/producer_client.h",
"perfetto/shared_memory.cc",
"perfetto/shared_memory.h",
"perfetto/task_runner.cc",
"perfetto/task_runner.h",
]
deps = [
# TODO(oysteine): Add a Perfetto build target which contains
# only the top-level proto to avoid pulling in unused messages.
"//third_party/perfetto/protos/perfetto/trace:zero",
"//third_party/perfetto/protos/perfetto/trace/chrome:lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:zero",
]
public_deps += [ "//third_party/perfetto:libperfetto" ]
}
} }
// 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/perfetto/producer_client.h"
#include <utility>
#include "base/task_scheduler/post_task.h"
#include "services/tracing/public/cpp/perfetto/shared_memory.h"
#include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h"
#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
namespace tracing {
// TODO(oysteine): Use a new sequence here once Perfetto handles multi-threading
// properly.
ProducerClient::ProducerClient()
: perfetto_task_runner_(base::SequencedTaskRunnerHandle::Get()) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ProducerClient::~ProducerClient() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// static
void ProducerClient::DeleteSoon(
std::unique_ptr<ProducerClient> producer_client) {
producer_client->GetTaskRunner()->DeleteSoon(FROM_HERE,
std::move(producer_client));
}
base::SequencedTaskRunner* ProducerClient::GetTaskRunner() {
return perfetto_task_runner_.task_runner();
}
// The Mojo binding should run on the same sequence as the one we get
// callbacks from Perfetto on, to avoid additional PostTasks.
mojom::ProducerClientPtr ProducerClient::CreateAndBindProducerClient() {
DCHECK(!binding_);
mojom::ProducerClientPtr producer_client;
GetTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&ProducerClient::BindOnSequence, base::Unretained(this),
mojo::MakeRequest(&producer_client)));
return producer_client;
}
mojom::ProducerHostRequest ProducerClient::CreateProducerHostRequest() {
return mojo::MakeRequest(&producer_host_);
}
void ProducerClient::BindOnSequence(mojom::ProducerClientRequest request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
binding_ = std::make_unique<mojo::Binding<mojom::ProducerClient>>(
this, std::move(request));
}
void ProducerClient::OnTracingStart(
mojo::ScopedSharedBufferHandle shared_memory) {
// TODO(oysteine): In next CLs plumb this through the service.
const size_t kShmemBufferPageSize = 4096;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(producer_host_);
if (!shared_memory_) {
shared_memory_ =
std::make_unique<MojoSharedMemory>(std::move(shared_memory));
shared_memory_arbiter_ = perfetto::SharedMemoryArbiter::CreateInstance(
shared_memory_.get(), kShmemBufferPageSize, this,
&perfetto_task_runner_);
} else {
// TODO(oysteine): This is assuming the SMB is the same, currently. Swapping
// out SharedMemoryBuffers would require more thread synchronization.
DCHECK_EQ(shared_memory_->shared_buffer()->value(), shared_memory->value());
}
}
void ProducerClient::CreateDataSourceInstance(
uint64_t id,
mojom::DataSourceConfigPtr data_source_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(oysteine): Create the relevant data source instance here.
}
void ProducerClient::TearDownDataSourceInstance(uint64_t id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
enabled_data_source_instance_.reset();
// TODO(oysteine): Yak shave: Can only destroy these once the TraceWriters
// are all cleaned up; have to figure out the TLS bits.
// shared_memory_arbiter_ = nullptr;
// shared_memory_ = nullptr;
}
void ProducerClient::Flush(uint64_t flush_request_id,
const std::vector<uint64_t>& data_source_ids) {
NOTREACHED();
}
void ProducerClient::RegisterDataSource(const perfetto::DataSourceDescriptor&) {
NOTREACHED();
}
void ProducerClient::UnregisterDataSource(const std::string& name) {
NOTREACHED();
}
void ProducerClient::CommitData(const perfetto::CommitDataRequest& commit,
CommitDataCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The CommitDataRequest which the SharedMemoryArbiter uses to
// signal Perfetto that individual chunks have finished being
// written and is ready for consumption, needs to be serialized
// into the corresponding Mojo class and sent over to the
// service-side.
auto new_data_request = mojom::CommitDataRequest::New();
for (auto& chunk : commit.chunks_to_move()) {
auto new_chunk = mojom::ChunksToMove::New();
new_chunk->page = chunk.page();
new_chunk->chunk = chunk.chunk();
new_chunk->target_buffer = chunk.target_buffer();
new_data_request->chunks_to_move.push_back(std::move(new_chunk));
}
for (auto& chunk_patch : commit.chunks_to_patch()) {
auto new_chunk_patch = mojom::ChunksToPatch::New();
new_chunk_patch->target_buffer = chunk_patch.target_buffer();
new_chunk_patch->writer_id = chunk_patch.writer_id();
new_chunk_patch->chunk_id = chunk_patch.chunk_id();
for (auto& patch : chunk_patch.patches()) {
auto new_patch = mojom::ChunkPatch::New();
new_patch->offset = patch.offset();
new_patch->data = patch.data();
new_chunk_patch->patches.push_back(std::move(new_patch));
}
new_chunk_patch->has_more_patches = chunk_patch.has_more_patches();
new_data_request->chunks_to_patch.push_back(std::move(new_chunk_patch));
}
producer_host_->CommitData(std::move(new_data_request));
}
perfetto::SharedMemory* ProducerClient::shared_memory() const {
return shared_memory_.get();
}
size_t ProducerClient::shared_buffer_page_size_kb() const {
NOTREACHED();
return 0;
}
void ProducerClient::NotifyFlushComplete(perfetto::FlushRequestID) {
NOTREACHED();
}
std::unique_ptr<perfetto::TraceWriter> ProducerClient::CreateTraceWriter(
perfetto::BufferID target_buffer) {
DCHECK(shared_memory_arbiter_);
return shared_memory_arbiter_->CreateTraceWriter(target_buffer);
}
} // namespace tracing
// 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_PERFETTO_PRODUCER_CLIENT_H_
#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PRODUCER_CLIENT_H_
#include <memory>
#include <string>
#include <vector>
#include "base/atomicops.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/tracing/public/cpp/perfetto/task_runner.h"
#include "services/tracing/public/mojom/perfetto_service.mojom.h"
#include "third_party/perfetto/include/perfetto/tracing/core/service.h"
namespace perfetto {
class SharedMemoryArbiter;
} // namespace perfetto
namespace tracing {
class MojoSharedMemory;
// This class is the per-process client side of the Perfetto
// producer, and is responsible for creating specific kinds
// of DataSources (like ChromeTracing) on demand, and provide
// them with TraceWriters and a configuration to start logging.
// Implementations of new DataSources should:
// * Implement ProducerClient::DataSourceBase.
// * Add a new data source name in perfetto_service.mojom.
// * Register the data source with Perfetto in ProducerHost::OnConnect.
// * Construct the new implementation when requested to
// in ProducerClient::CreateDataSourceInstance.
class COMPONENT_EXPORT(TRACING_CPP) ProducerClient
: public mojom::ProducerClient,
public perfetto::Service::ProducerEndpoint {
public:
// Base class for different DataSource implementations,
// i.e. something which is able to provide data in the
// form of protos of a given type to Perfetto (like trace events).
class DataSourceBase {
public:
virtual ~DataSourceBase() = default;
};
ProducerClient();
~ProducerClient() override;
static void DeleteSoon(std::unique_ptr<ProducerClient>);
// Connect to the service-side ProducerHost which will
// let Perfetto know we're ready to start logging
// our data.
mojom::ProducerClientPtr CreateAndBindProducerClient();
mojom::ProducerHostRequest CreateProducerHostRequest();
bool has_connected_producer_host_for_testing() const {
return !!producer_host_;
}
// mojom::ProducerClient implementation.
// Called through Mojo by the ProducerHost on the service-side to control
// tracing and toggle specific DataSources.
void OnTracingStart(mojo::ScopedSharedBufferHandle shared_memory) override;
void CreateDataSourceInstance(
uint64_t id,
mojom::DataSourceConfigPtr data_source_config) override;
void TearDownDataSourceInstance(uint64_t id) override;
void Flush(uint64_t flush_request_id,
const std::vector<uint64_t>& data_source_ids) override;
// perfetto::Service::ProducerEndpoint implementation.
// Used by the TraceWriters
// to signal Perfetto that shared memory chunks are ready
// for consumption.
void CommitData(const perfetto::CommitDataRequest& commit,
CommitDataCallback callback) override;
// Used by the DataSource implementations to create TraceWriters
// for writing their protobufs
std::unique_ptr<perfetto::TraceWriter> CreateTraceWriter(
perfetto::BufferID target_buffer) override;
perfetto::SharedMemory* shared_memory() const override;
// These ProducerEndpoint functions are only used on the service
// side and should not be called on the clients.
void RegisterDataSource(const perfetto::DataSourceDescriptor&) override;
void UnregisterDataSource(const std::string& name) override;
size_t shared_buffer_page_size_kb() const override;
void NotifyFlushComplete(perfetto::FlushRequestID) override;
protected:
base::SequencedTaskRunner* GetTaskRunner();
// TODO(oysteine): Allow multiple enabled data source instances.
std::unique_ptr<DataSourceBase> enabled_data_source_instance_;
private:
void BindOnSequence(mojom::ProducerClientRequest request);
// Keep the TaskRunner first in the member list so it outlives
// everything else and no dependent classes will try to use
// an invalid taskrunner.
PerfettoTaskRunner perfetto_task_runner_;
std::unique_ptr<mojo::Binding<mojom::ProducerClient>> binding_;
std::unique_ptr<perfetto::SharedMemoryArbiter> shared_memory_arbiter_;
mojom::ProducerHostPtr producer_host_;
std::unique_ptr<MojoSharedMemory> shared_memory_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(ProducerClient);
};
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PRODUCER_CLIENT_H_
// 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/perfetto/shared_memory.h"
#include <utility>
#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory.h"
namespace tracing {
std::unique_ptr<perfetto::SharedMemory>
MojoSharedMemory::Factory::CreateSharedMemory(size_t size) {
return std::make_unique<MojoSharedMemory>(size);
}
MojoSharedMemory::MojoSharedMemory(size_t size) {
shared_buffer_ = mojo::SharedBufferHandle::Create(size);
mapping_ = shared_buffer_->Map(size);
DCHECK(mapping_);
}
MojoSharedMemory::MojoSharedMemory(mojo::ScopedSharedBufferHandle shared_memory)
: shared_buffer_(std::move(shared_memory)) {
mapping_ = shared_buffer_->Map(shared_buffer_->GetSize());
DCHECK(mapping_);
}
MojoSharedMemory::~MojoSharedMemory() = default;
mojo::ScopedSharedBufferHandle MojoSharedMemory::Clone() {
return shared_buffer_->Clone(
mojo::SharedBufferHandle::AccessMode::READ_WRITE);
}
void* MojoSharedMemory::start() const {
return mapping_.get();
}
size_t MojoSharedMemory::size() const {
return shared_buffer_->GetSize();
}
} // namespace tracing
// 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_PERFETTO_SHARED_MEMORY_H_
#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_SHARED_MEMORY_H_
#include <memory>
#include "base/component_export.h"
#include "base/macros.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory.h"
namespace tracing {
// This wraps a Mojo SharedBuffer implementation to be
// able to provide it to Perfetto.
class COMPONENT_EXPORT(TRACING_CPP) MojoSharedMemory
: public perfetto::SharedMemory {
public:
class Factory : public perfetto::SharedMemory::Factory {
public:
std::unique_ptr<perfetto::SharedMemory> CreateSharedMemory(
size_t size) override;
};
explicit MojoSharedMemory(size_t size);
explicit MojoSharedMemory(mojo::ScopedSharedBufferHandle shared_memory);
~MojoSharedMemory() override;
// Create another wrapping instance of the same SharedMemory buffer,
// for sending to other processes.
mojo::ScopedSharedBufferHandle Clone();
const mojo::ScopedSharedBufferHandle& shared_buffer() const {
return shared_buffer_;
}
// perfetto::SharedMemory implementation. Called internally by Perfetto
// classes.
void* start() const override;
size_t size() const override;
private:
mojo::ScopedSharedBufferHandle shared_buffer_;
mojo::ScopedSharedBufferMapping mapping_;
DISALLOW_COPY_AND_ASSIGN(MojoSharedMemory);
};
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_SHARED_MEMORY_H_
// 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/perfetto/task_runner.h"
#include <memory>
#include "base/threading/sequenced_task_runner_handle.h"
namespace tracing {
PerfettoTaskRunner::PerfettoTaskRunner(
scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
PerfettoTaskRunner::~PerfettoTaskRunner() = default;
void PerfettoTaskRunner::PostTask(std::function<void()> task) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce([](std::function<void()> task) { task(); }, task));
}
void PerfettoTaskRunner::PostDelayedTask(std::function<void()> task,
uint32_t delay_ms) {
task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce([](std::function<void()> task) { task(); }, task),
base::TimeDelta::FromMilliseconds(delay_ms));
}
void PerfettoTaskRunner::AddFileDescriptorWatch(int fd, std::function<void()>) {
NOTREACHED();
}
void PerfettoTaskRunner::RemoveFileDescriptorWatch(int fd) {
NOTREACHED();
}
} // namespace tracing
// 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_PERFETTO_TASK_RUNNER_H_
#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TASK_RUNNER_H_
#include "base/component_export.h"
#include "base/macros.h"
#include "services/tracing/public/mojom/perfetto_service.mojom.h"
#include "third_party/perfetto/include/perfetto/base/task_runner.h"
namespace tracing {
// This wraps a base::TaskRunner implementation to be able
// to provide it to Perfetto.
class COMPONENT_EXPORT(TRACING_CPP) PerfettoTaskRunner
: public perfetto::base::TaskRunner {
public:
explicit PerfettoTaskRunner(
scoped_refptr<base::SequencedTaskRunner> task_runner);
~PerfettoTaskRunner() override;
// perfetto::base::TaskRunner implementation. Only called by
// the Perfetto implementation itself.
void PostTask(std::function<void()> task) override;
void PostDelayedTask(std::function<void()> task, uint32_t delay_ms) override;
// Not used in Chrome.
void AddFileDescriptorWatch(int fd, std::function<void()>) override;
void RemoveFileDescriptorWatch(int fd) override;
base::SequencedTaskRunner* task_runner() { return task_runner_.get(); }
private:
scoped_refptr<base::SequencedTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(PerfettoTaskRunner);
};
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TASK_RUNNER_H_
// 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/tracing_features.h"
#include <string>
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
namespace features {
const base::Feature kTracingPerfettoBackend{"TracingPerfettoBackend",
base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features
namespace tracing {
bool TracingUsesPerfettoBackend() {
return base::FeatureList::IsEnabled(features::kTracingPerfettoBackend);
}
} // namespace tracing
// 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.
// This file defines all the public base::FeatureList features for the
// services/tracing module.
#ifndef SERVICES_TRACING_PUBLIC_CPP_TRACING_FEATURES_H_
#define SERVICES_TRACING_PUBLIC_CPP_TRACING_FEATURES_H_
#include "base/component_export.h"
#include "base/feature_list.h"
namespace features {
// The features should be documented alongside the definition of their values
// in the .cc file.
extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature
kTracingPerfettoBackend;
} // namespace features
namespace tracing {
bool COMPONENT_EXPORT(TRACING_CPP) TracingUsesPerfettoBackend();
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_TRACING_FEATURES_H_
...@@ -10,6 +10,7 @@ mojom_component("mojom") { ...@@ -10,6 +10,7 @@ mojom_component("mojom") {
sources = [ sources = [
"constants.mojom", "constants.mojom",
"perfetto_service.mojom",
"tracing.mojom", "tracing.mojom",
] ]
......
// 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.
module tracing.mojom;
const string kTraceEventDataSourceName = "org.chromium.trace_event";
// Brief description of the flow: There's a per-process ProducerClient
// which connects to the central PerfettoService and establishes a two-way
// connection with a ProducerHost. The latter will then pass a
// SharedMemoryBuffer to the ProducerClient and tell it to start logging
// events of a given type into it. As chunks of the buffer gets filled
// up, the client will communicate this to the ProducerHost which will
// tell Perfetto to copy the finished chunks into its central storage
// and pass to any consumers.
// For a more complete explanation of a Perfetto tracing session, see
// https://android.googlesource.com/platform/external/perfetto/+/master/docs/life-of-a-tracing-session.md
// See https://android.googlesource.com/platform/external/perfetto/
// for the full documentation of Perfetto concepts and its shared memory ABI.
// Used by the CommitDataRequest() method (client process->service) to signal when
// a chunk is (segment/page of the shared memory buffer which is
// owned by a per-thread TraceWriter) the central Perfetto service that it's
// ready for consumption (flushed or fully written).
struct ChunksToMove {
// The page index within the producer:service shared memory buffer.
uint32 page;
// The chunk index within the given page.
uint32 chunk;
// The target ring-buffer in the service where the chunk should be copied into.
uint32 target_buffer;
};
// Used by the CommitDataRequest method (client process -> service) to
// patch previously written chunks (to fill in size fields when protos
// span multiple chunks, for example).
struct ChunkPatch {
// Offset relative to the chunk defined in ChunksToPatch.
uint32 offset;
string data;
};
struct ChunksToPatch {
// The triplet {target_buffer, writer_id, chunk_id} uniquely identified a chunk that has
// been copied over into the main, non-shared, trace buffer owned by the service.
uint32 target_buffer;
uint32 writer_id;
uint32 chunk_id;
array<ChunkPatch> patches;
// If false the chunk should be considered finalized and available to be read.
bool has_more_patches;
};
struct CommitDataRequest {
array<ChunksToMove> chunks_to_move;
array<ChunksToPatch> chunks_to_patch;
};
struct DataSourceConfig {
string name;
string trace_config;
uint32 target_buffer;
};
interface ProducerHost {
// Called by a ProducerClient to asks the service to:
// 1) Move data from the shared memory buffer into the final tracing buffer owned by the service
// (through the |chunks_to_move|).
// 2) Patch data (i.e. apply diff) that has been previously copied into the tracing buffer
// (if it's not been overwritten).
// The service is robust in terms of tolerating malformed or malicious requests.
CommitData(CommitDataRequest data_request);
};
interface ProducerClient {
OnTracingStart(handle<shared_buffer> shared_memory);
// TODO(oysteine): Make a TypeTrait for sending the full DataSourceConfig.
// Called by Perfetto (via ProducerHost) to request a data source to start logging.
CreateDataSourceInstance(uint64 id, DataSourceConfig data_source_config);
// Requesting a data source to stop logging again, with the id previously sent
// in the CreateDataSourceInstance call.
TearDownDataSourceInstance(uint64 id);
Flush(uint64 flush_request_id, array<uint64> data_source_ids);
};
interface PerfettoService {
ConnectToProducerHost(ProducerClient producer_client, ProducerHost& producer_host);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment