blob: 2ffe0bd61ff0b6e1bc437e90d04e7a21da491083 [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/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::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;
}
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);
}
}
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->WaitForDataSourceStart("data_source");
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->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->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, 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->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->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->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"));
}
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(1);
const uint64_t kMaxFileSize = 512;
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->WaitForDataSourceStart("data_source");
static const char kPayload[] = "1234567890abcdef-";
static const int kNumPackets = 10;
std::unique_ptr<TraceWriter> writer =
producer->CreateTraceWriter("data_source");
// All these packets should fit within kMaxFileSize.
for (int i = 0; i < kNumPackets; 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_GE(trace.packet_size(), kNumPackets);
int num_testing_packet = 0;
for (int i = 0; i < trace.packet_size(); i++) {
const protos::TracePacket& tp = trace.packet(i);
if (!tp.has_for_testing())
continue;
ASSERT_EQ(kPayload + std::to_string(num_testing_packet++),
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]->WaitForDataSourceStart("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();
}
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->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->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->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); // Will NOT reply to flush id == 1.
producer->WaitForFlush(nullptr); // Will NOT reply to flush id == 2.
producer->WaitForFlush(writer.get()); // Will reply only to flush id == 3.
producer->WaitForFlush(nullptr); // Will NOT reply to flush id == 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")))));
}
// 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->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->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->WaitForDataSourceStart("ds_1A");
producer1->WaitForDataSourceStart("ds_1B");
if (i == 0)
producer2->WaitForTracingSetup();
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->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(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, 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());
}
} // namespace perfetto