blob: 81f46345fb837bcd10a4b43232946b1933dfdbf2 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/tracing/core/tracing_service_impl.h"
#include <string.h>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/base/file_utils.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/base/utils.h"
#include "perfetto/tracing/core/consumer.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/producer.h"
#include "perfetto/tracing/core/shared_memory.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "perfetto/tracing/core/trace_writer.h"
#include "src/base/test/test_task_runner.h"
#include "src/tracing/core/shared_memory_arbiter_impl.h"
#include "src/tracing/core/trace_writer_impl.h"
#include "src/tracing/test/mock_consumer.h"
#include "src/tracing/test/mock_producer.h"
#include "src/tracing/test/test_shared_memory.h"
#include "perfetto/trace/test_event.pbzero.h"
#include "perfetto/trace/trace.pb.h"
#include "perfetto/trace/trace_packet.pb.h"
#include "perfetto/trace/trace_packet.pbzero.h"
using ::testing::_;
using ::testing::Contains;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::Mock;
using ::testing::Not;
using ::testing::Property;
using ::testing::StrictMock;
namespace perfetto {
namespace {
constexpr size_t kDefaultShmSizeKb = TracingServiceImpl::kDefaultShmSize / 1024;
constexpr size_t kMaxShmSizeKb = TracingServiceImpl::kMaxShmSize / 1024;
} // namespace
class TracingServiceImplTest : public testing::Test {
public:
TracingServiceImplTest() {
auto shm_factory =
std::unique_ptr<SharedMemory::Factory>(new TestSharedMemory::Factory());
svc.reset(static_cast<TracingServiceImpl*>(
TracingService::CreateInstance(std::move(shm_factory), &task_runner)
.release()));
svc->min_write_period_ms_ = 1;
}
std::unique_ptr<MockProducer> CreateMockProducer() {
return std::unique_ptr<MockProducer>(
new StrictMock<MockProducer>(&task_runner));
}
std::unique_ptr<MockConsumer> CreateMockConsumer() {
return std::unique_ptr<MockConsumer>(
new StrictMock<MockConsumer>(&task_runner));
}
ProducerID* last_producer_id() { return &svc->last_producer_id_; }
uid_t GetProducerUid(ProducerID producer_id) {
return svc->GetProducer(producer_id)->uid_;
}
TracingServiceImpl::TracingSession* tracing_session() {
auto* session = svc->GetTracingSession(svc->last_tracing_session_id_);
EXPECT_NE(nullptr, session);
return session;
}
const std::set<BufferID>& GetAllowedTargetBuffers(ProducerID producer_id) {
return svc->GetProducer(producer_id)->allowed_target_buffers_;
}
const std::map<WriterID, BufferID>& GetWriters(ProducerID producer_id) {
return svc->GetProducer(producer_id)->writers_;
}
std::unique_ptr<SharedMemoryArbiterImpl> TakeShmemArbiterForProducer(
ProducerID producer_id) {
return std::move(svc->GetProducer(producer_id)->inproc_shmem_arbiter_);
}
size_t GetNumPendingFlushes() {
return tracing_session()->pending_flushes.size();
}
void WaitForNextSyncMarker() {
tracing_session()->last_snapshot_time = base::TimeMillis(0);
static int attempt = 0;
while (tracing_session()->last_snapshot_time == base::TimeMillis(0)) {
auto checkpoint_name = "wait_snapshot_" + std::to_string(attempt++);
auto timer_expired = task_runner.CreateCheckpoint(checkpoint_name);
task_runner.PostDelayedTask([timer_expired] { timer_expired(); }, 1);
task_runner.RunUntilCheckpoint(checkpoint_name);
}
}
void WaitForTraceWritersChanged(ProducerID producer_id) {
static int i = 0;
auto checkpoint_name = "writers_changed_" + std::to_string(producer_id) +
"_" + std::to_string(i++);
auto writers_changed = task_runner.CreateCheckpoint(checkpoint_name);
auto writers = GetWriters(producer_id);
std::function<void()> task;
task = [&task, writers, writers_changed, producer_id, this]() {
if (writers != GetWriters(producer_id)) {
writers_changed();
return;
}
task_runner.PostDelayedTask(task, 1);
};
task_runner.PostDelayedTask(task, 1);
task_runner.RunUntilCheckpoint(checkpoint_name);
}
base::TestTaskRunner task_runner;
std::unique_ptr<TracingServiceImpl> svc;
};
TEST_F(TracingServiceImplTest, RegisterAndUnregister) {
std::unique_ptr<MockProducer> mock_producer_1 = CreateMockProducer();
std::unique_ptr<MockProducer> mock_producer_2 = CreateMockProducer();
mock_producer_1->Connect(svc.get(), "mock_producer_1", 123u /* uid */);
mock_producer_2->Connect(svc.get(), "mock_producer_2", 456u /* uid */);
ASSERT_EQ(2u, svc->num_producers());
ASSERT_EQ(mock_producer_1->endpoint(), svc->GetProducer(1));
ASSERT_EQ(mock_producer_2->endpoint(), svc->GetProducer(2));
ASSERT_EQ(123u, GetProducerUid(1));
ASSERT_EQ(456u, GetProducerUid(2));
mock_producer_1->RegisterDataSource("foo");
mock_producer_2->RegisterDataSource("bar");
mock_producer_1->UnregisterDataSource("foo");
mock_producer_2->UnregisterDataSource("bar");
mock_producer_1.reset();
ASSERT_EQ(1u, svc->num_producers());
ASSERT_EQ(nullptr, svc->GetProducer(1));
mock_producer_2.reset();
ASSERT_EQ(nullptr, svc->GetProducer(2));
ASSERT_EQ(0u, svc->num_producers());
}
TEST_F(TracingServiceImplTest, EnableAndDisableTracing) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Calling StartTracing() should be a noop (% a DLOG statement) because the
// trace config didn't have the |deferred_start| flag set.
consumer->StartTracing();
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
}
TEST_F(TracingServiceImplTest, LockdownMode) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer_sameuid", geteuid());
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
trace_config.set_lockdown_mode(
TraceConfig::LockdownModeOperation::LOCKDOWN_SET);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
std::unique_ptr<MockProducer> producer_otheruid = CreateMockProducer();
auto x = svc->ConnectProducer(producer_otheruid.get(), geteuid() + 1,
"mock_producer_ouid");
EXPECT_CALL(*producer_otheruid, OnConnect()).Times(0);
task_runner.RunUntilIdle();
Mock::VerifyAndClearExpectations(producer_otheruid.get());
consumer->DisableTracing();
consumer->FreeBuffers();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
trace_config.set_lockdown_mode(
TraceConfig::LockdownModeOperation::LOCKDOWN_CLEAR);
consumer->EnableTracing(trace_config);
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
std::unique_ptr<MockProducer> producer_otheruid2 = CreateMockProducer();
producer_otheruid->Connect(svc.get(), "mock_producer_ouid2", geteuid() + 1);
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
}
TEST_F(TracingServiceImplTest, ProducerNameFilterChange) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer1 = CreateMockProducer();
producer1->Connect(svc.get(), "mock_producer_1");
producer1->RegisterDataSource("data_source");
std::unique_ptr<MockProducer> producer2 = CreateMockProducer();
producer2->Connect(svc.get(), "mock_producer_2");
producer2->RegisterDataSource("data_source");
std::unique_ptr<MockProducer> producer3 = CreateMockProducer();
producer3->Connect(svc.get(), "mock_producer_3");
producer3->RegisterDataSource("data_source");
producer3->RegisterDataSource("unused_data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* data_source = trace_config.add_data_sources();
data_source->mutable_config()->set_name("data_source");
*data_source->add_producer_name_filter() = "mock_producer_1";
// Enable tracing with only mock_producer_1 enabled;
// the rest should not start up.
consumer->EnableTracing(trace_config);
producer1->WaitForTracingSetup();
producer1->WaitForDataSourceSetup("data_source");
producer1->WaitForDataSourceStart("data_source");
EXPECT_CALL(*producer2, OnConnect()).Times(0);
EXPECT_CALL(*producer3, OnConnect()).Times(0);
task_runner.RunUntilIdle();
Mock::VerifyAndClearExpectations(producer2.get());
Mock::VerifyAndClearExpectations(producer3.get());
// Enable mock_producer_2, the third one should still
// not get connected.
*data_source->add_producer_name_filter() = "mock_producer_2";
consumer->ChangeTraceConfig(trace_config);
producer2->WaitForTracingSetup();
producer2->WaitForDataSourceSetup("data_source");
producer2->WaitForDataSourceStart("data_source");
// Enable mock_producer_3 but also try to do an
// unsupported change (adding a new data source);
// mock_producer_3 should get enabled but not
// for the new data source.
*data_source->add_producer_name_filter() = "mock_producer_3";
auto* dummy_data_source = trace_config.add_data_sources();
dummy_data_source->mutable_config()->set_name("unused_data_source");
*dummy_data_source->add_producer_name_filter() = "mock_producer_3";
consumer->ChangeTraceConfig(trace_config);
producer3->WaitForTracingSetup();
EXPECT_CALL(*producer3, SetupDataSource(_, _)).Times(1);
EXPECT_CALL(*producer3, StartDataSource(_, _)).Times(1);
task_runner.RunUntilIdle();
Mock::VerifyAndClearExpectations(producer3.get());
consumer->DisableTracing();
consumer->FreeBuffers();
producer1->WaitForDataSourceStop("data_source");
producer2->WaitForDataSourceStop("data_source");
EXPECT_CALL(*producer3, StopDataSource(_)).Times(1);
consumer->WaitForTracingDisabled();
task_runner.RunUntilIdle();
Mock::VerifyAndClearExpectations(producer3.get());
}
TEST_F(TracingServiceImplTest, DisconnectConsumerWhileTracing) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Disconnecting the consumer while tracing should trigger data source
// teardown.
consumer.reset();
producer->WaitForDataSourceStop("data_source");
}
TEST_F(TracingServiceImplTest, ReconnectProducerWhileTracing) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Disconnecting and reconnecting a producer with a matching data source.
// The Producer should see that data source getting enabled again.
producer.reset();
producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer_2");
producer->RegisterDataSource("data_source");
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
}
TEST_F(TracingServiceImplTest, ProducerIDWrapping) {
std::vector<std::unique_ptr<MockProducer>> producers;
producers.push_back(nullptr);
auto connect_producer_and_get_id = [&producers,
this](const std::string& name) {
producers.emplace_back(CreateMockProducer());
producers.back()->Connect(svc.get(), "mock_producer_" + name);
return *last_producer_id();
};
// Connect producers 1-4.
for (ProducerID i = 1; i <= 4; i++)
ASSERT_EQ(i, connect_producer_and_get_id(std::to_string(i)));
// Disconnect producers 1,3.
producers[1].reset();
producers[3].reset();
*last_producer_id() = kMaxProducerID - 1;
ASSERT_EQ(kMaxProducerID, connect_producer_and_get_id("maxid"));
ASSERT_EQ(1u, connect_producer_and_get_id("1_again"));
ASSERT_EQ(3u, connect_producer_and_get_id("3_again"));
ASSERT_EQ(5u, connect_producer_and_get_id("5"));
ASSERT_EQ(6u, connect_producer_and_get_id("6"));
}
// Note: file_write_period_ms is set to a large enough to have exactly one flush
// of the tracing buffers (and therefore at most one synchronization section),
// unless the test runs unrealistically slowly, or the implementation of the
// tracing snapshot packets changes.
TEST_F(TracingServiceImplTest, WriteIntoFileAndStopOnMaxSize) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(4096);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
ds_config->set_target_buffer(0);
trace_config.set_write_into_file(true);
trace_config.set_file_write_period_ms(100000); // 100s
const uint64_t kMaxFileSize = 1024;
trace_config.set_max_file_size_bytes(kMaxFileSize);
base::TempFile tmp_file = base::TempFile::Create();
consumer->EnableTracing(trace_config, base::ScopedFile(dup(tmp_file.fd())));
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
static const int kNumPreamblePackets = 4;
static const int kNumTestPackets = 10;
static const char kPayload[] = "1234567890abcdef-";
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("data_source");
// Tracing service will emit a preamble of packets (a synchronization section,
// followed by a tracing config packet). The preamble and these test packets
// should fit within kMaxFileSize.
for (int i = 0; i < kNumTestPackets; i++) {
auto tp = writer->NewTracePacket();
std::string payload(kPayload);
payload.append(std::to_string(i));
tp->set_for_testing()->set_str(payload.c_str(), payload.size());
}
// Finally add a packet that overflows kMaxFileSize. This should cause the
// implicit stop of the trace and should *not* be written in the trace.
{
auto tp = writer->NewTracePacket();
char big_payload[kMaxFileSize] = "BIG!";
tp->set_for_testing()->set_str(big_payload, sizeof(big_payload));
}
writer->Flush();
writer.reset();
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
// Verify the contents of the file.
std::string trace_raw;
ASSERT_TRUE(base::ReadFile(tmp_file.path().c_str(), &trace_raw));
protos::Trace trace;
ASSERT_TRUE(trace.ParseFromString(trace_raw));
ASSERT_EQ(trace.packet_size(), kNumPreamblePackets + kNumTestPackets);
for (int i = 0; i < kNumTestPackets; i++) {
const protos::TracePacket& tp = trace.packet(kNumPreamblePackets + i);
ASSERT_EQ(kPayload + std::to_string(i++), tp.for_testing().str());
}
}
// Test the logic that allows the trace config to set the shm total size and
// page size from the trace config. Also check that, if the config doesn't
// specify a value we fall back on the hint provided by the producer.
TEST_F(TracingServiceImplTest, ProducerShmAndPageSizeOverriddenByTraceConfig) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
const size_t kConfigPageSizesKb[] = /****/ {16, 16, 4, 0, 16, 8, 3, 4096, 4};
const size_t kExpectedPageSizesKb[] = /**/ {16, 16, 4, 4, 16, 8, 4, 64, 4};
const size_t kConfigSizesKb[] = /**/ {0, 16, 0, 20, 32, 7, 0, 96, 4096000};
const size_t kHintSizesKb[] = /****/ {0, 0, 16, 32, 16, 0, 7, 96, 4096000};
const size_t kExpectedSizesKb[] = {
kDefaultShmSizeKb, // Both hint and config are 0, use default.
16, // Hint is 0, use config.
16, // Config is 0, use hint.
20, // Hint is takes precedence over the config.
32, // Ditto, even if config is higher than hint.
kDefaultShmSizeKb, // Config is invalid and hint is 0, use default.
kDefaultShmSizeKb, // Config is 0 and hint is invalid, use default.
kDefaultShmSizeKb, // 96 KB isn't a multiple of the page size (64 KB).
kMaxShmSizeKb // Too big, cap at kMaxShmSize.
};
const size_t kNumProducers = base::ArraySize(kHintSizesKb);
std::unique_ptr<MockProducer> producer[kNumProducers];
for (size_t i = 0; i < kNumProducers; i++) {
auto name = "mock_producer_" + std::to_string(i);
producer[i] = CreateMockProducer();
producer[i]->Connect(svc.get(), name, geteuid(), kHintSizesKb[i] * 1024);
producer[i]->RegisterDataSource("data_source");
}
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
for (size_t i = 0; i < kNumProducers; i++) {
auto* producer_config = trace_config.add_producers();
producer_config->set_producer_name("mock_producer_" + std::to_string(i));
producer_config->set_shm_size_kb(static_cast<uint32_t>(kConfigSizesKb[i]));
producer_config->set_page_size_kb(
static_cast<uint32_t>(kConfigPageSizesKb[i]));
}
consumer->EnableTracing(trace_config);
size_t actual_shm_sizes_kb[kNumProducers]{};
size_t actual_page_sizes_kb[kNumProducers]{};
for (size_t i = 0; i < kNumProducers; i++) {
producer[i]->WaitForTracingSetup();
producer[i]->WaitForDataSourceSetup("data_source");
actual_shm_sizes_kb[i] =
producer[i]->endpoint()->shared_memory()->size() / 1024;
actual_page_sizes_kb[i] =
producer[i]->endpoint()->shared_buffer_page_size_kb();
}
for (size_t i = 0; i < kNumProducers; i++) {
producer[i]->WaitForDataSourceStart("data_source");
}
ASSERT_THAT(actual_page_sizes_kb, ElementsAreArray(kExpectedPageSizesKb));
ASSERT_THAT(actual_shm_sizes_kb, ElementsAreArray(kExpectedSizesKb));
}
TEST_F(TracingServiceImplTest, ExplicitFlush) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("data_source");
{
auto tp = writer->NewTracePacket();
tp->set_for_testing()->set_str("payload");
}
auto flush_request = consumer->Flush();
producer->WaitForFlush(writer.get());
ASSERT_TRUE(flush_request.WaitForReply());
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
EXPECT_THAT(
consumer->ReadBuffers(),
Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload")))));
}
TEST_F(TracingServiceImplTest, ImplicitFlushOnTimedTraces) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
trace_config.set_duration_ms(1);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("data_source");
{
auto tp = writer->NewTracePacket();
tp->set_for_testing()->set_str("payload");
}
producer->WaitForFlush(writer.get());
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
EXPECT_THAT(
consumer->ReadBuffers(),
Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload")))));
}
// Tests the monotonic semantic of flush request IDs, i.e., once a producer
// acks flush request N, all flush requests <= N are considered successful and
// acked to the consumer.
TEST_F(TracingServiceImplTest, BatchFlushes) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("data_source");
{
auto tp = writer->NewTracePacket();
tp->set_for_testing()->set_str("payload");
}
auto flush_req_1 = consumer->Flush();
auto flush_req_2 = consumer->Flush();
auto flush_req_3 = consumer->Flush();
// We'll deliberately let the 4th flush request timeout. Use a lower timeout
// to keep test time short.
auto flush_req_4 = consumer->Flush(/*timeout_ms=*/10);
ASSERT_EQ(4u, GetNumPendingFlushes());
// Make the producer reply only to the 3rd flush request.
testing::InSequence seq;
producer->WaitForFlush(nullptr, /*reply=*/false); // Do NOT reply to flush 1.
producer->WaitForFlush(nullptr, /*reply=*/false); // Do NOT reply to flush 2.
producer->WaitForFlush(writer.get()); // Reply only to flush 3.
producer->WaitForFlush(nullptr, /*reply=*/false); // Do NOT reply to flush 4.
// Even if the producer explicily replied only to flush ID == 3, all the
// previous flushed < 3 should be implicitly acked.
ASSERT_TRUE(flush_req_1.WaitForReply());
ASSERT_TRUE(flush_req_2.WaitForReply());
ASSERT_TRUE(flush_req_3.WaitForReply());
// At this point flush id == 4 should still be pending and should fail because
// of reaching its timeout.
ASSERT_FALSE(flush_req_4.WaitForReply());
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
EXPECT_THAT(
consumer->ReadBuffers(),
Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload")))));
}
TEST_F(TracingServiceImplTest, PeriodicFlush) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.set_flush_period_ms(1);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("data_source");
const int kNumFlushes = 3;
auto checkpoint = task_runner.CreateCheckpoint("all_flushes_done");
int flushes_seen = 0;
EXPECT_CALL(*producer, Flush(_, _, _))
.WillRepeatedly(Invoke([&producer, &writer, &flushes_seen, checkpoint](
FlushRequestID flush_req_id,
const DataSourceInstanceID*, size_t) {
{
auto tp = writer->NewTracePacket();
char payload[32];
sprintf(payload, "f_%d", flushes_seen);
tp->set_for_testing()->set_str(payload);
}
writer->Flush();
producer->endpoint()->NotifyFlushComplete(flush_req_id);
if (++flushes_seen == kNumFlushes)
checkpoint();
}));
task_runner.RunUntilCheckpoint("all_flushes_done");
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
auto trace_packets = consumer->ReadBuffers();
for (int i = 0; i < kNumFlushes; i++) {
EXPECT_THAT(trace_packets,
Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("f_" + std::to_string(i))))));
}
}
// Creates a tracing session where some of the data sources set the
// |will_notify_on_stop| flag and checks that the OnTracingDisabled notification
// to the consumer is delayed until the acks are received.
TEST_F(TracingServiceImplTest, OnTracingDisabledWaitsForDataSourceStopAcks) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("ds_will_ack_1", /*ack_stop=*/true);
producer->RegisterDataSource("ds_wont_ack");
producer->RegisterDataSource("ds_will_ack_2", /*ack_stop=*/true);
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_data_sources()->mutable_config()->set_name("ds_will_ack_1");
trace_config.add_data_sources()->mutable_config()->set_name("ds_wont_ack");
trace_config.add_data_sources()->mutable_config()->set_name("ds_will_ack_2");
trace_config.set_duration_ms(1);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("ds_will_ack_1");
producer->WaitForDataSourceSetup("ds_wont_ack");
producer->WaitForDataSourceSetup("ds_will_ack_2");
producer->WaitForDataSourceStart("ds_will_ack_1");
producer->WaitForDataSourceStart("ds_wont_ack");
producer->WaitForDataSourceStart("ds_will_ack_2");
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("ds_wont_ack");
producer->WaitForFlush(writer.get());
DataSourceInstanceID id1 = producer->GetDataSourceInstanceId("ds_will_ack_1");
DataSourceInstanceID id2 = producer->GetDataSourceInstanceId("ds_will_ack_2");
producer->WaitForDataSourceStop("ds_will_ack_1");
producer->WaitForDataSourceStop("ds_wont_ack");
producer->WaitForDataSourceStop("ds_will_ack_2");
producer->endpoint()->NotifyDataSourceStopped(id1);
producer->endpoint()->NotifyDataSourceStopped(id2);
// Wait for at most half of the service timeout, so that this test fails if
// the service falls back on calling the OnTracingDisabled() because some of
// the expected acks weren't received.
consumer->WaitForTracingDisabled(
TracingServiceImpl::kDataSourceStopTimeoutMs / 2);
}
// Creates a tracing session where a second data source
// is added while the service is waiting for DisableTracing
// acks; the service should not enable the new datasource
// and should not hit any asserts when the consumer is
// subsequently destroyed.
TEST_F(TracingServiceImplTest, OnDataSourceAddedWhilePendingDisableAcks) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("ds_will_ack", /*ack_stop=*/true);
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_data_sources()->mutable_config()->set_name("ds_will_ack");
trace_config.add_data_sources()->mutable_config()->set_name("ds_wont_ack");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
consumer->DisableTracing();
producer->RegisterDataSource("ds_wont_ack");
consumer.reset();
}
// Similar to OnTracingDisabledWaitsForDataSourceStopAcks, but deliberately
// skips the ack and checks that the service invokes the OnTracingDisabled()
// after the timeout.
TEST_F(TracingServiceImplTest, OnTracingDisabledCalledAnywaysInCaseOfTimeout) {
svc->override_data_source_test_timeout_ms_for_testing = 1;
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source", /*ack_stop=*/true);
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_data_sources()->mutable_config()->set_name("data_source");
trace_config.set_duration_ms(1);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("data_source");
producer->WaitForFlush(writer.get());
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
}
// Tests the session_id logic. Two data sources in the same tracing session
// should see the same session id.
TEST_F(TracingServiceImplTest, SessionId) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer1 = CreateMockProducer();
producer1->Connect(svc.get(), "mock_producer1");
producer1->RegisterDataSource("ds_1A");
producer1->RegisterDataSource("ds_1B");
std::unique_ptr<MockProducer> producer2 = CreateMockProducer();
producer2->Connect(svc.get(), "mock_producer2");
producer2->RegisterDataSource("ds_2A");
testing::InSequence seq;
TracingSessionID last_session_id = 0;
for (int i = 0; i < 3; i++) {
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_data_sources()->mutable_config()->set_name("ds_1A");
trace_config.add_data_sources()->mutable_config()->set_name("ds_1B");
trace_config.add_data_sources()->mutable_config()->set_name("ds_2A");
trace_config.set_duration_ms(1);
consumer->EnableTracing(trace_config);
if (i == 0)
producer1->WaitForTracingSetup();
producer1->WaitForDataSourceSetup("ds_1A");
producer1->WaitForDataSourceSetup("ds_1B");
if (i == 0)
producer2->WaitForTracingSetup();
producer2->WaitForDataSourceSetup("ds_2A");
producer1->WaitForDataSourceStart("ds_1A");
producer1->WaitForDataSourceStart("ds_1B");
producer2->WaitForDataSourceStart("ds_2A");
auto* ds1 = producer1->GetDataSourceInstance("ds_1A");
auto* ds2 = producer1->GetDataSourceInstance("ds_1B");
auto* ds3 = producer2->GetDataSourceInstance("ds_2A");
ASSERT_EQ(ds1->session_id, ds2->session_id);
ASSERT_EQ(ds1->session_id, ds3->session_id);
ASSERT_NE(ds1->session_id, last_session_id);
last_session_id = ds1->session_id;
auto writer1 = producer1->CreateTraceWriter("ds_1A");
producer1->WaitForFlush(writer1.get());
auto writer2 = producer2->CreateTraceWriter("ds_2A");
producer2->WaitForFlush(writer2.get());
producer1->WaitForDataSourceStop("ds_1A");
producer1->WaitForDataSourceStop("ds_1B");
producer2->WaitForDataSourceStop("ds_2A");
consumer->WaitForTracingDisabled();
consumer->FreeBuffers();
}
}
// Writes a long trace and then tests that the trace parsed in partitions
// derived by the synchronization markers is identical to the whole trace parsed
// in one go.
TEST_F(TracingServiceImplTest, ResynchronizeTraceStreamUsingSyncMarker) {
// Setup tracing.
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(4096);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
trace_config.set_write_into_file(true);
trace_config.set_file_write_period_ms(1);
base::TempFile tmp_file = base::TempFile::Create();
consumer->EnableTracing(trace_config, base::ScopedFile(dup(tmp_file.fd())));
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Write some variable length payload, waiting for sync markers every now
// and then.
const int kNumMarkers = 5;
auto writer = producer->CreateTraceWriter("data_source");
for (int i = 1; i <= 100; i++) {
std::string payload(static_cast<size_t>(i), 'A' + (i % 25));
writer->NewTracePacket()->set_for_testing()->set_str(payload.c_str());
if (i % (100 / kNumMarkers) == 0) {
writer->Flush();
WaitForNextSyncMarker();
}
}
writer->Flush();
writer.reset();
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
std::string trace_raw;
ASSERT_TRUE(base::ReadFile(tmp_file.path().c_str(), &trace_raw));
const auto kMarkerSize = sizeof(TracingServiceImpl::kSyncMarker);
const std::string kSyncMarkerStr(
reinterpret_cast<const char*>(TracingServiceImpl::kSyncMarker),
kMarkerSize);
// Read back the trace in partitions derived from the marker.
// The trace should look like this:
// [uid, marker] [event] [event] [uid, marker] [event] [event]
size_t num_markers = 0;
size_t start = 0;
size_t end = 0;
protos::Trace merged_trace;
for (size_t pos = 0; pos != std::string::npos; start = end) {
pos = trace_raw.find(kSyncMarkerStr, pos + 1);
num_markers++;
end = (pos == std::string::npos) ? trace_raw.size() : pos + kMarkerSize;
int size = static_cast<int>(end - start);
ASSERT_GT(size, 0);
protos::Trace trace_partition;
ASSERT_TRUE(trace_partition.ParseFromArray(trace_raw.data() + start, size));
merged_trace.MergeFrom(trace_partition);
}
EXPECT_GE(num_markers, static_cast<size_t>(kNumMarkers));
protos::Trace whole_trace;
ASSERT_TRUE(whole_trace.ParseFromString(trace_raw));
ASSERT_EQ(whole_trace.packet_size(), merged_trace.packet_size());
EXPECT_EQ(whole_trace.SerializeAsString(), merged_trace.SerializeAsString());
}
// Creates a tracing session with |deferred_start| and checks that data sources
// are started only after calling StartTracing().
TEST_F(TracingServiceImplTest, DeferredStart) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
// Create two data sources but enable only one of them.
producer->RegisterDataSource("ds_1");
producer->RegisterDataSource("ds_2");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
trace_config.set_deferred_start(true);
trace_config.set_duration_ms(1);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("ds_1");
// Make sure we don't get unexpected DataSourceStart() notifications yet.
task_runner.RunUntilIdle();
consumer->StartTracing();
producer->WaitForDataSourceStart("ds_1");
auto writer1 = producer->CreateTraceWriter("ds_1");
producer->WaitForFlush(writer1.get());
producer->WaitForDataSourceStop("ds_1");
consumer->WaitForTracingDisabled();
}
TEST_F(TracingServiceImplTest, ProducerUIDsAndPacketSequenceIDs) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer1 = CreateMockProducer();
producer1->Connect(svc.get(), "mock_producer1", 123u /* uid */);
producer1->RegisterDataSource("data_source");
std::unique_ptr<MockProducer> producer2 = CreateMockProducer();
producer2->Connect(svc.get(), "mock_producer2", 456u /* uid */);
producer2->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer1->WaitForTracingSetup();
producer1->WaitForDataSourceSetup("data_source");
producer2->WaitForTracingSetup();
producer2->WaitForDataSourceSetup("data_source");
producer1->WaitForDataSourceStart("data_source");
producer2->WaitForDataSourceStart("data_source");
std::unique_ptr<TraceWriter> writer1a =
producer1->CreateTraceWriter("data_source");
std::unique_ptr<TraceWriter> writer1b =
producer1->CreateTraceWriter("data_source");
std::unique_ptr<TraceWriter> writer2a =
producer2->CreateTraceWriter("data_source");
{
auto tp = writer1a->NewTracePacket();
tp->set_for_testing()->set_str("payload1a1");
tp = writer1b->NewTracePacket();
tp->set_for_testing()->set_str("payload1b1");
tp = writer1a->NewTracePacket();
tp->set_for_testing()->set_str("payload1a2");
tp = writer2a->NewTracePacket();
tp->set_for_testing()->set_str("payload2a1");
tp = writer1b->NewTracePacket();
tp->set_for_testing()->set_str("payload1b2");
}
auto flush_request = consumer->Flush();
producer1->WaitForFlush({writer1a.get(), writer1b.get()});
producer2->WaitForFlush(writer2a.get());
ASSERT_TRUE(flush_request.WaitForReply());
consumer->DisableTracing();
producer1->WaitForDataSourceStop("data_source");
producer2->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
auto packets = consumer->ReadBuffers();
EXPECT_THAT(
packets,
Contains(AllOf(
Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload1a1"))),
Property(&protos::TracePacket::trusted_uid, Eq(123)),
Property(&protos::TracePacket::trusted_packet_sequence_id, Eq(2u)))));
EXPECT_THAT(
packets,
Contains(AllOf(
Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload1a2"))),
Property(&protos::TracePacket::trusted_uid, Eq(123)),
Property(&protos::TracePacket::trusted_packet_sequence_id, Eq(2u)))));
EXPECT_THAT(
packets,
Contains(AllOf(
Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload1b1"))),
Property(&protos::TracePacket::trusted_uid, Eq(123)),
Property(&protos::TracePacket::trusted_packet_sequence_id, Eq(3u)))));
EXPECT_THAT(
packets,
Contains(AllOf(
Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload1b2"))),
Property(&protos::TracePacket::trusted_uid, Eq(123)),
Property(&protos::TracePacket::trusted_packet_sequence_id, Eq(3u)))));
EXPECT_THAT(
packets,
Contains(AllOf(
Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload2a1"))),
Property(&protos::TracePacket::trusted_uid, Eq(456)),
Property(&protos::TracePacket::trusted_packet_sequence_id, Eq(4u)))));
}
TEST_F(TracingServiceImplTest, AllowedBuffers) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer1 = CreateMockProducer();
producer1->Connect(svc.get(), "mock_producer1");
ProducerID producer1_id = *last_producer_id();
producer1->RegisterDataSource("data_source1");
std::unique_ptr<MockProducer> producer2 = CreateMockProducer();
producer2->Connect(svc.get(), "mock_producer2");
ProducerID producer2_id = *last_producer_id();
producer2->RegisterDataSource("data_source2.1");
producer2->RegisterDataSource("data_source2.2");
producer2->RegisterDataSource("data_source2.3");
EXPECT_EQ(std::set<BufferID>(), GetAllowedTargetBuffers(producer1_id));
EXPECT_EQ(std::set<BufferID>(), GetAllowedTargetBuffers(producer2_id));
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config1 = trace_config.add_data_sources()->mutable_config();
ds_config1->set_name("data_source1");
ds_config1->set_target_buffer(0);
auto* ds_config21 = trace_config.add_data_sources()->mutable_config();
ds_config21->set_name("data_source2.1");
ds_config21->set_target_buffer(1);
auto* ds_config22 = trace_config.add_data_sources()->mutable_config();
ds_config22->set_name("data_source2.2");
ds_config22->set_target_buffer(2);
auto* ds_config23 = trace_config.add_data_sources()->mutable_config();
ds_config23->set_name("data_source2.3");
ds_config23->set_target_buffer(2); // same buffer as data_source2.2.
consumer->EnableTracing(trace_config);
ASSERT_EQ(3u, tracing_session()->num_buffers());
std::set<BufferID> expected_buffers_producer1 = {
tracing_session()->buffers_index[0]};
std::set<BufferID> expected_buffers_producer2 = {
tracing_session()->buffers_index[1], tracing_session()->buffers_index[2]};
EXPECT_EQ(expected_buffers_producer1, GetAllowedTargetBuffers(producer1_id));
EXPECT_EQ(expected_buffers_producer2, GetAllowedTargetBuffers(producer2_id));
producer1->WaitForTracingSetup();
producer1->WaitForDataSourceSetup("data_source1");
producer2->WaitForTracingSetup();
producer2->WaitForDataSourceSetup("data_source2.1");
producer2->WaitForDataSourceSetup("data_source2.2");
producer2->WaitForDataSourceSetup("data_source2.3");
producer1->WaitForDataSourceStart("data_source1");
producer2->WaitForDataSourceStart("data_source2.1");
producer2->WaitForDataSourceStart("data_source2.2");
producer2->WaitForDataSourceStart("data_source2.3");
producer2->UnregisterDataSource("data_source2.3");
producer2->WaitForDataSourceStop("data_source2.3");
// Should still be allowed to write to buffers 1 (data_source2.1) and 2
// (data_source2.2).
EXPECT_EQ(expected_buffers_producer2, GetAllowedTargetBuffers(producer2_id));
// Calling StartTracing() should be a noop (% a DLOG statement) because the
// trace config didn't have the |deferred_start| flag set.
consumer->StartTracing();
consumer->DisableTracing();
producer1->WaitForDataSourceStop("data_source1");
producer2->WaitForDataSourceStop("data_source2.1");
producer2->WaitForDataSourceStop("data_source2.2");
consumer->WaitForTracingDisabled();
consumer->FreeBuffers();
EXPECT_EQ(std::set<BufferID>(), GetAllowedTargetBuffers(producer1_id));
EXPECT_EQ(std::set<BufferID>(), GetAllowedTargetBuffers(producer2_id));
}
#if !PERFETTO_DCHECK_IS_ON()
TEST_F(TracingServiceImplTest, CommitToForbiddenBufferIsDiscarded) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
ProducerID producer_id = *last_producer_id();
producer->RegisterDataSource("data_source");
EXPECT_EQ(std::set<BufferID>(), GetAllowedTargetBuffers(producer_id));
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
ds_config->set_target_buffer(0);
consumer->EnableTracing(trace_config);
ASSERT_EQ(2u, tracing_session()->num_buffers());
std::set<BufferID> expected_buffers = {tracing_session()->buffers_index[0]};
EXPECT_EQ(expected_buffers, GetAllowedTargetBuffers(producer_id));
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Calling StartTracing() should be a noop (% a DLOG statement) because the
// trace config didn't have the |deferred_start| flag set.
consumer->StartTracing();
// Try to write to the correct buffer.
std::unique_ptr<TraceWriter> writer = producer->endpoint()->CreateTraceWriter(
tracing_session()->buffers_index[0]);
{
auto tp = writer->NewTracePacket();
tp->set_for_testing()->set_str("good_payload");
}
auto flush_request = consumer->Flush();
producer->WaitForFlush(writer.get());
ASSERT_TRUE(flush_request.WaitForReply());
// Try to write to the wrong buffer.
writer = producer->endpoint()->CreateTraceWriter(
tracing_session()->buffers_index[1]);
{
auto tp = writer->NewTracePacket();
tp->set_for_testing()->set_str("bad_payload");
}
flush_request = consumer->Flush();
producer->WaitForFlush(writer.get());
ASSERT_TRUE(flush_request.WaitForReply());
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
auto packets = consumer->ReadBuffers();
EXPECT_THAT(packets, Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("good_payload")))));
EXPECT_THAT(packets, Not(Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("bad_payload"))))));
consumer->FreeBuffers();
EXPECT_EQ(std::set<BufferID>(), GetAllowedTargetBuffers(producer_id));
}
#endif // !PERFETTO_DCHECK_IS_ON()
TEST_F(TracingServiceImplTest, RegisterAndUnregisterTraceWriter) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
ProducerID producer_id = *last_producer_id();
producer->RegisterDataSource("data_source");
EXPECT_TRUE(GetWriters(producer_id).empty());
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
ds_config->set_target_buffer(0);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Calling StartTracing() should be a noop (% a DLOG statement) because the
// trace config didn't have the |deferred_start| flag set.
consumer->StartTracing();
// Creating the trace writer should register it with the service.
std::unique_ptr<TraceWriter> writer = producer->endpoint()->CreateTraceWriter(
tracing_session()->buffers_index[0]);
WaitForTraceWritersChanged(producer_id);
std::map<WriterID, BufferID> expected_writers;
expected_writers[writer->writer_id()] = tracing_session()->buffers_index[0];
EXPECT_EQ(expected_writers, GetWriters(producer_id));
// Verify writing works.
{
auto tp = writer->NewTracePacket();
tp->set_for_testing()->set_str("payload");
}
auto flush_request = consumer->Flush();
producer->WaitForFlush(writer.get());
ASSERT_TRUE(flush_request.WaitForReply());
// Destroying the writer should unregister it.
writer.reset();
WaitForTraceWritersChanged(producer_id);
EXPECT_TRUE(GetWriters(producer_id).empty());
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
auto packets = consumer->ReadBuffers();
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload")))));
}
TEST_F(TracingServiceImplTest, ScrapeBuffersOnFlush) {
svc->SetSMBScrapingEnabled(true);
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
ProducerID producer_id = *last_producer_id();
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
ds_config->set_target_buffer(0);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Calling StartTracing() should be a noop (% a DLOG statement) because the
// trace config didn't have the |deferred_start| flag set.
consumer->StartTracing();
std::unique_ptr<TraceWriter> writer = producer->endpoint()->CreateTraceWriter(
tracing_session()->buffers_index[0]);
WaitForTraceWritersChanged(producer_id);
// Write a few trace packets.
writer->NewTracePacket()->set_for_testing()->set_str("payload1");
writer->NewTracePacket()->set_for_testing()->set_str("payload2");
writer->NewTracePacket()->set_for_testing()->set_str("payload3");
// Flush but don't actually flush the chunk from TraceWriter.
auto flush_request = consumer->Flush();
producer->WaitForFlush(nullptr, /*reply=*/true);
ASSERT_TRUE(flush_request.WaitForReply());
// Chunk with the packets should have been scraped. The service can't know
// whether the last packet was completed, so shouldn't read it.
auto packets = consumer->ReadBuffers();
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload1")))));
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload2")))));
EXPECT_THAT(packets, Not(Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("payload3"))))));
// Write some more packets.
writer->NewTracePacket()->set_for_testing()->set_str("payload4");
writer->NewTracePacket()->set_for_testing()->set_str("payload5");
// Don't reply to flush, causing a timeout. This should scrape again.
flush_request = consumer->Flush(/*timeout=*/100);
producer->WaitForFlush(nullptr, /*reply=*/false);
ASSERT_FALSE(flush_request.WaitForReply());
// Chunk with the packets should have been scraped again, overriding the
// original one. Again, the last packet should be ignored and the first two
// should not be read twice.
packets = consumer->ReadBuffers();
EXPECT_THAT(packets, Not(Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("payload1"))))));
EXPECT_THAT(packets, Not(Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("payload2"))))));
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload3")))));
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload4")))));
EXPECT_THAT(packets, Not(Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("payload5"))))));
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
}
// Test scraping on producer disconnect.
TEST_F(TracingServiceImplTest, ScrapeBuffersOnProducerDisconnect) {
svc->SetSMBScrapingEnabled(true);
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
ProducerID producer_id = *last_producer_id();
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
ds_config->set_target_buffer(0);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Calling StartTracing() should be a noop (% a DLOG statement) because the
// trace config didn't have the |deferred_start| flag set.
consumer->StartTracing();
std::unique_ptr<TraceWriter> writer = producer->endpoint()->CreateTraceWriter(
tracing_session()->buffers_index[0]);
WaitForTraceWritersChanged(producer_id);
// Write a few trace packets.
writer->NewTracePacket()->set_for_testing()->set_str("payload1");
writer->NewTracePacket()->set_for_testing()->set_str("payload2");
writer->NewTracePacket()->set_for_testing()->set_str("payload3");
// Disconnect the producer without committing the chunk. This should cause a
// scrape of the SMB. Avoid destroying the ShmemArbiter until writer is
// destroyed.
auto shmem_arbiter = TakeShmemArbiterForProducer(producer_id);
producer.reset();
// Chunk with the packets should have been scraped. The service can't know
// whether the last packet was completed, so shouldn't read it.
auto packets = consumer->ReadBuffers();
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload1")))));
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload2")))));
EXPECT_THAT(packets, Not(Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("payload3"))))));
// Cleanup writer without causing a crash because the producer already went
// away.
static_cast<TraceWriterImpl*>(writer.get())->ResetChunkForTesting();
writer.reset();
shmem_arbiter.reset();
consumer->DisableTracing();
consumer->WaitForTracingDisabled();
}
TEST_F(TracingServiceImplTest, ScrapeBuffersOnDisable) {
svc->SetSMBScrapingEnabled(true);
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
ProducerID producer_id = *last_producer_id();
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
ds_config->set_target_buffer(0);
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
// Calling StartTracing() should be a noop (% a DLOG statement) because the
// trace config didn't have the |deferred_start| flag set.
consumer->StartTracing();
std::unique_ptr<TraceWriter> writer = producer->endpoint()->CreateTraceWriter(
tracing_session()->buffers_index[0]);
WaitForTraceWritersChanged(producer_id);
// Write a few trace packets.
writer->NewTracePacket()->set_for_testing()->set_str("payload1");
writer->NewTracePacket()->set_for_testing()->set_str("payload2");
writer->NewTracePacket()->set_for_testing()->set_str("payload3");
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
// Chunk with the packets should have been scraped. The service can't know
// whether the last packet was completed, so shouldn't read it.
auto packets = consumer->ReadBuffers();
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload1")))));
EXPECT_THAT(packets, Contains(Property(
&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str, Eq("payload2")))));
EXPECT_THAT(packets, Not(Contains(Property(&protos::TracePacket::for_testing,
Property(&protos::TestEvent::str,
Eq("payload3"))))));
}
TEST_F(TracingServiceImplTest, AbortIfTraceDurationIsTooLong) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("datasource");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
trace_config.add_data_sources()->mutable_config()->set_name("datasource");
trace_config.set_duration_ms(0x7fffffff);
EXPECT_CALL(*producer, SetupDataSource(_, _)).Times(0);
consumer->EnableTracing(trace_config);
// The trace is aborted immediately, 5s here is just some slack for the thread
// ping-pongs for slow devices.
consumer->WaitForTracingDisabled(5000);
}
TEST_F(TracingServiceImplTest, GetTraceStats) {
std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
consumer->Connect(svc.get());
consumer->GetTraceStats();
consumer->WaitForTraceStats(false);
std::unique_ptr<MockProducer> producer = CreateMockProducer();
producer->Connect(svc.get(), "mock_producer");
producer->RegisterDataSource("data_source");
TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(128);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("data_source");
consumer->EnableTracing(trace_config);
producer->WaitForTracingSetup();
producer->WaitForDataSourceSetup("data_source");
producer->WaitForDataSourceStart("data_source");
consumer->GetTraceStats();
consumer->WaitForTraceStats(true);
consumer->DisableTracing();
producer->WaitForDataSourceStop("data_source");
consumer->WaitForTracingDisabled();
}
} // namespace perfetto