/* * Copyright (C) 2023 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 #include #include #include "perfetto/ext/base/unix_socket.h" #include "protos/perfetto/trace/remote_clock_sync.gen.h" #include "src/traced_relay/relay_service.h" #include "src/base/test/test_task_runner.h" #include "test/gtest_and_gmock.h" #include "test/test_helper.h" #include "protos/perfetto/config/test_config.gen.h" #include "protos/perfetto/config/trace_config.gen.h" #include "protos/perfetto/trace/test_event.gen.h" namespace perfetto { namespace { struct TestParams { std::string id; std::string tcp_sock_name; std::string unix_sock_name; std::string producer_name; std::unique_ptr relay_service; std::unique_ptr server_socket; std::unique_ptr producer_thread; }; TEST(TracedRelayIntegrationTest, BasicCase) { base::TestTaskRunner task_runner; std::string sock_name; { // Set up a server UnixSocket to find an unused TCP port. base::UnixSocket::EventListener event_listener; auto srv = base::UnixSocket::Listen("127.0.0.1:0", &event_listener, &task_runner, base::SockFamily::kInet, base::SockType::kStream); ASSERT_TRUE(srv->is_listening()); sock_name = srv->GetSockAddr(); // Shut down |srv| here to free the port. It's unlikely that the port will // be taken by another process so quickly before we reach the code below. } TestHelper helper(&task_runner, TestHelper::Mode::kStartDaemons, sock_name.c_str(), /*enable_relay_endpoint=*/true); ASSERT_EQ(helper.num_producers(), 1u); helper.StartServiceIfRequired(); auto relay_service = std::make_unique(&task_runner); // Don't let RelayClient interfere with the testing of relayed producers. relay_service->SetRelayClientDisabledForTesting(true); relay_service->Start("@traced_relay", sock_name.c_str()); auto producer_connected = task_runner.CreateCheckpoint("perfetto.FakeProducer.connected"); auto noop = []() {}; auto connected = [&]() { task_runner.PostTask(producer_connected); }; // We won't use the built-in fake producer and will start our own. auto producer_thread = std::make_unique( "@traced_relay", connected, noop, noop, "perfetto.FakeProducer"); producer_thread->Connect(); task_runner.RunUntilCheckpoint("perfetto.FakeProducer.connected"); helper.ConnectConsumer(); helper.WaitForConsumerConnect(); TraceConfig trace_config; trace_config.add_buffers()->set_size_kb(1024); trace_config.set_duration_ms(200); static constexpr uint32_t kMsgSize = 1024; static constexpr uint32_t kRandomSeed = 42; // Enable the producer. auto* ds_config = trace_config.add_data_sources()->mutable_config(); ds_config->set_name("perfetto.FakeProducer"); ds_config->set_target_buffer(0); ds_config->mutable_for_testing()->set_seed(kRandomSeed); ds_config->mutable_for_testing()->set_message_count(12); ds_config->mutable_for_testing()->set_message_size(kMsgSize); ds_config->mutable_for_testing()->set_send_batch_on_register(true); helper.StartTracing(trace_config); helper.WaitForTracingDisabled(); helper.ReadData(); helper.WaitForReadData(); const auto& packets = helper.trace(); ASSERT_EQ(packets.size(), 12u); // The producer is connected from this process. The relay service will inject // the SetPeerIdentity message using the pid and euid of the current process. auto pid = static_cast(getpid()); auto uid = static_cast(geteuid()); std::minstd_rand0 rnd_engine(kRandomSeed); for (const auto& packet : packets) { ASSERT_TRUE(packet.has_for_testing()); ASSERT_EQ(packet.trusted_pid(), pid); ASSERT_EQ(packet.trusted_uid(), uid); ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine()); // The tracing service should emit non-default machine ID in trace packets. ASSERT_NE(packet.machine_id(), 0u); } } TEST(TracedRelayIntegrationTest, MachineID_MultiRelayService) { base::TestTaskRunner task_runner; std::vector test_params(2); base::UnixSocket::EventListener event_listener; for (size_t i = 0; i < test_params.size(); i++) { auto& param = test_params[i]; param.id = std::to_string(i + 1); param.server_socket = base::UnixSocket::Listen( "127.0.0.1:0", &event_listener, &task_runner, base::SockFamily::kInet, base::SockType::kStream); ASSERT_TRUE(param.server_socket->is_listening()); param.tcp_sock_name = param.server_socket->GetSockAddr(); param.relay_service = std::make_unique(&task_runner); param.relay_service->SetMachineIdHintForTesting("test-machine-id-" + param.id); param.unix_sock_name = std::string("@traced_relay_") + param.id; param.producer_name = std::string("perfetto.FakeProducer.") + param.id; } for (auto& param : test_params) { // Shut down listening sockets to free the port. It's unlikely that the port // will be taken by another process so quickly before we reach the code // below. param.server_socket = nullptr; } auto relay_sock_name = test_params[0].tcp_sock_name + "," + test_params[1].tcp_sock_name; for (auto& param : test_params) { param.relay_service->Start(param.unix_sock_name.c_str(), param.tcp_sock_name.c_str()); // Don't let RelayClient interfere with the testing of relayed producers. param.relay_service->SetRelayClientDisabledForTesting(true); } TestHelper helper(&task_runner, TestHelper::Mode::kStartDaemons, relay_sock_name.c_str(), /*enable_relay_endpoint=*/true); ASSERT_EQ(helper.num_producers(), 2u); helper.StartServiceIfRequired(); for (auto& param : test_params) { auto checkpoint_name = "perfetto.FakeProducer.connected." + param.id; auto producer_connected = task_runner.CreateCheckpoint(checkpoint_name); auto noop = []() {}; auto connected = std::bind( [&](std::function checkpoint) { task_runner.PostTask(checkpoint); }, producer_connected); // We won't use the built-in fake producer and will start our own. param.producer_thread = std::make_unique( param.unix_sock_name, connected, noop, noop, param.producer_name); param.producer_thread->Connect(); task_runner.RunUntilCheckpoint(checkpoint_name); } helper.ConnectConsumer(); helper.WaitForConsumerConnect(); TraceConfig trace_config; trace_config.add_buffers()->set_size_kb(1024); trace_config.set_duration_ms(200); static constexpr uint32_t kMsgSize = 1024; static constexpr uint32_t kRandomSeed = 42; // Enable the 1st producer. auto* ds_config = trace_config.add_data_sources()->mutable_config(); ds_config->set_name("perfetto.FakeProducer.1"); ds_config->set_target_buffer(0); ds_config->mutable_for_testing()->set_message_count(12); ds_config->mutable_for_testing()->set_message_size(kMsgSize); ds_config->mutable_for_testing()->set_send_batch_on_register(true); // Enable the 2nd producer. ds_config = trace_config.add_data_sources()->mutable_config(); ds_config->set_name("perfetto.FakeProducer.2"); ds_config->set_target_buffer(0); ds_config->mutable_for_testing()->set_message_count(24); ds_config->mutable_for_testing()->set_message_size(kMsgSize); ds_config->mutable_for_testing()->set_send_batch_on_register(true); helper.StartTracing(trace_config); helper.WaitForTracingDisabled(); helper.ReadData(); helper.WaitForReadData(); const auto& packets = helper.trace(); ASSERT_EQ(packets.size(), 36u); // The producer is connected from this process. The relay service will inject // the SetPeerIdentity message using the pid and euid of the current process. auto pid = static_cast(getpid()); auto uid = static_cast(geteuid()); std::minstd_rand0 rnd_engine(kRandomSeed); std::map packets_counts; // machine ID => count. for (const auto& packet : packets) { ASSERT_TRUE(packet.has_for_testing()); ASSERT_EQ(packet.trusted_pid(), pid); ASSERT_EQ(packet.trusted_uid(), uid); packets_counts[packet.machine_id()]++; } // Fake producer (1, 2) either gets machine ID (1, 2), or (2, 1), depending on // which on is seen by the tracing service first. ASSERT_EQ(packets_counts.size(), 2u); auto count_1 = packets_counts.begin()->second; auto count_2 = packets_counts.rbegin()->second; ASSERT_TRUE(count_1 == 12u || count_1 == 24u); ASSERT_EQ(count_1 + count_2, 36u); for (auto& param : test_params) { param.producer_thread = nullptr; param.relay_service = nullptr; } } TEST(TracedRelayIntegrationTest, RelayClient) { base::TestTaskRunner task_runner; std::string sock_name; { // Set up a server UnixSocket to find an unused TCP port. base::UnixSocket::EventListener event_listener; auto srv = base::UnixSocket::Listen("127.0.0.1:0", &event_listener, &task_runner, base::SockFamily::kInet, base::SockType::kStream); ASSERT_TRUE(srv->is_listening()); sock_name = srv->GetSockAddr(); // Shut down |srv| here to free the port. It's unlikely that the port will // be taken by another process so quickly before we reach the code below. } TestHelper helper(&task_runner, TestHelper::Mode::kStartDaemons, sock_name.c_str(), /*enable_relay_endpoint=*/true); ASSERT_EQ(helper.num_producers(), 1u); helper.StartServiceIfRequired(); auto relay_service = std::make_unique(&task_runner); // This also starts the RelayClient. relay_service->Start("@traced_relay", sock_name.c_str()); ASSERT_TRUE(!!relay_service->relay_client_for_testing()); auto producer_connected = task_runner.CreateCheckpoint("perfetto.FakeProducer.connected"); auto noop = []() {}; auto connected = [&]() { task_runner.PostTask(producer_connected); }; // We won't use the built-in fake producer and will start our own. auto producer_thread = std::make_unique( "@traced_relay", connected, noop, noop, "perfetto.FakeProducer"); producer_thread->Connect(); task_runner.RunUntilCheckpoint("perfetto.FakeProducer.connected"); helper.ConnectConsumer(); helper.WaitForConsumerConnect(); while (!relay_service->relay_client_for_testing() ->clock_synced_with_service_for_testing()) task_runner.RunUntilIdle(); TraceConfig trace_config; trace_config.add_buffers()->set_size_kb(1024); trace_config.set_duration_ms(200); // // Enable the producer. auto* ds_config = trace_config.add_data_sources()->mutable_config(); ds_config->set_name("perfetto.FakeProducer"); ds_config->set_target_buffer(0); helper.StartTracing(trace_config); helper.WaitForTracingDisabled(); helper.ReadData(); helper.WaitForReadData(); const auto& packets = helper.trace(); bool clock_sync_packet_seen = false; for (auto& packet : packets) { if (!packet.has_remote_clock_sync()) continue; clock_sync_packet_seen = true; auto& synced_clocks = packet.remote_clock_sync().synced_clocks(); ASSERT_FALSE(synced_clocks.empty()); for (auto& clock_offset : synced_clocks) { ASSERT_TRUE(clock_offset.has_client_clocks()); ASSERT_TRUE(clock_offset.has_host_clocks()); } } ASSERT_TRUE(clock_sync_packet_seen); } } // namespace } // namespace perfetto