blob: fa6b998dda6c5562a606a715681c2110d7f8277f [file] [log] [blame]
// 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/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/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/include/perfetto/tracing/core/tracing_service.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:
void SetUp() override {
perfetto_service_ = std::make_unique<PerfettoService>(
base::SequencedTaskRunnerHandle::Get());
// The actual Perfetto service is created async on the given task_runner;
// wait until that's done.
RunUntilIdle();
ProducerClient::ResetTaskRunnerForTesting();
}
void TearDown() override { perfetto_service_.reset(); }
PerfettoService* perfetto_service() const { return perfetto_service_.get(); }
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
private:
std::unique_ptr<PerfettoService> perfetto_service_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
};
class TestDataSource {
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_; }
void CreateDataSourceInstance(
uint64_t id,
mojom::DataSourceConfigPtr data_source_config) override {
enabled_data_source_ = 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 {
enabled_data_source_.reset();
if (client_disabled_callback_) {
std::move(client_disabled_callback_).Run();
}
}
void CommitData(const perfetto::CommitDataRequest& commit,
CommitDataCallback callback = {}) override {
// Only write out commits that have actual data in it; Perfetto
// might send two commits from different threads (one always empty),
// which causes TSan to complain.
if (commit.chunks_to_patch_size() || commit.chunks_to_move_size()) {
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);
}
const std::string& all_client_commit_data_requests() const {
return all_client_commit_data_requests_;
}
TestDataSource* data_source() { return enabled_data_source_.get(); }
private:
base::OnceClosure client_enabled_callback_;
base::OnceClosure client_disabled_callback_;
const size_t send_packet_count_;
std::string all_client_commit_data_requests_;
std::unique_ptr<TestDataSource> enabled_data_source_;
};
class MockConsumer : public perfetto::Consumer {
public:
using PacketReceivedCallback = std::function<void(bool)>;
MockConsumer(perfetto::TracingService* 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;
EXPECT_TRUE(encoded_packet.Decode(&packet));
if (packet.for_testing().str() == kPerfettoTestString) {
received_packets_++;
}
}
packet_received_callback_(has_more);
}
private:
std::unique_ptr<perfetto::TracingService::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) {
if (!commit_data_request.chunks_to_patch_size() &&
!commit_data_request.chunks_to_move_size()) {
return;
}
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;
}
void OnMessagepipesReadyCallback(
perfetto::TracingService* perfetto_service,
mojom::ProducerClientPtr producer_client_pipe,
mojom::ProducerHostRequest producer_host_pipe) {
Initialize(std::move(producer_client_pipe), std::move(producer_host_pipe),
perfetto_service, kPerfettoProducerName);
}
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());
dummy_client->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(new_producer.get()),
base::Unretained(perfetto_service()->GetService())));
producer_initialized_runloop.Run();
ProducerClient::DeleteSoonForTesting(std::move(dummy_client));
}
TEST_F(PerfettoIntegrationTest, ClientEnabledAndDisabled) {
base::RunLoop on_trace_packets;
MockConsumer consumer(perfetto_service()->GetService(),
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);
client->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(producer.get()),
base::Unretained(perfetto_service()->GetService())));
client_enabled_callback.Run();
RunUntilIdle();
consumer.StopTracing();
client_disabled_callback.Run();
on_trace_packets.Run();
EXPECT_EQ(0u, consumer.received_packets());
ProducerClient::DeleteSoonForTesting(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);
client->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(producer.get()),
base::Unretained(perfetto_service()->GetService())));
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->GetService(),
kPerfettoTestDataSourceName,
[&no_more_packets_runloop](bool has_more) {
if (!has_more) {
no_more_packets_runloop.Quit();
}
});
client_enabled_callback.Run();
RunUntilIdle();
consumer.StopTracing();
client_disabled_callback.Run();
no_more_packets_runloop.Run();
EXPECT_EQ(kNumPackets, consumer.received_packets());
ProducerClient::DeleteSoonForTesting(std::move(client));
}
TEST_F(PerfettoIntegrationTest, PacketsEndToEndConsumerFirst) {
const size_t kNumPackets = 10;
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->GetService(),
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);
client->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(new_producer.get()),
base::Unretained(perfetto_service()->GetService())));
client_enabled_callback.Run();
RunUntilIdle();
consumer.StopTracing();
no_more_packets_runloop.Run();
EXPECT_EQ(kNumPackets, consumer.received_packets());
ProducerClient::DeleteSoonForTesting(std::move(client));
}
#if defined(THREAD_SANITIZER)
#define MAYBE_CommitDataRequestIsMaybeComplete \
DISABLED_CommitDataRequestIsMaybeComplete
#else
#define MAYBE_CommitDataRequestIsMaybeComplete CommitDataRequestIsMaybeComplete
#endif
TEST_F(PerfettoIntegrationTest, MAYBE_CommitDataRequestIsMaybeComplete) {
const size_t kNumPackets = 100;
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->GetService(),
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);
client->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(new_producer.get()),
base::Unretained(perfetto_service()->GetService())));
client_enabled_callback.Run();
client->data_source()->WritePacketBigly();
RunUntilIdle();
consumer.StopTracing();
no_more_packets_runloop.Run();
EXPECT_EQ(client->all_client_commit_data_requests(),
new_producer->all_host_commit_data_requests());
ProducerClient::DeleteSoonForTesting(std::move(client));
}
TEST_F(PerfettoIntegrationTest, TracingRestarted) {
const size_t kNumPackets = 10;
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->GetService(),
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);
client->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(new_producer.get()),
base::Unretained(perfetto_service()->GetService())));
client_enabled_callback.Run();
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();
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::DeleteSoonForTesting(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());
client->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(new_producer.get()),
base::Unretained(perfetto_service()->GetService())));
producer_initialized_runloop.Run();
base::RunLoop no_more_packets_runloop;
MockConsumer consumer(perfetto_service()->GetService(),
kPerfettoTestDataSourceName,
[&no_more_packets_runloop](bool has_more) {
if (!has_more) {
no_more_packets_runloop.Quit();
}
});
RunUntilIdle();
consumer.StopTracing();
no_more_packets_runloop.Run();
EXPECT_EQ(0u, consumer.received_packets());
ProducerClient::DeleteSoonForTesting(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);
client1->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(producer1.get()),
base::Unretained(perfetto_service()->GetService())));
auto producer2 = std::make_unique<MockProducer>(kPerfettoTestDataSourceName);
client2->CreateMojoMessagepipes(
base::BindOnce(&MockProducer::OnMessagepipesReadyCallback,
base::Unretained(producer2.get()),
base::Unretained(perfetto_service()->GetService())));
MockConsumer consumer(perfetto_service()->GetService(),
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::DeleteSoonForTesting(std::move(client1));
ProducerClient::DeleteSoonForTesting(std::move(client2));
}
} // namespace
} // namespace tracing