xref: /aosp_15_r20/external/perfetto/src/traced_relay/relay_service_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/traced_relay/relay_service.h"
18 
19 #include <memory>
20 
21 #include "perfetto/ext/base/unix_socket.h"
22 #include "protos/perfetto/ipc/wire_protocol.gen.h"
23 #include "src/base/test/test_task_runner.h"
24 #include "src/ipc/buffered_frame_deserializer.h"
25 #include "test/gtest_and_gmock.h"
26 
27 // Disable tests on MacOS and Windows as neither abstract sockets nor pseudo
28 // boot ID are supported.
29 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
30     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
31 
32 namespace perfetto {
33 namespace {
34 
35 using ::testing::_;
36 using ::testing::Invoke;
37 
38 class TestEventListener : public base::UnixSocket::EventListener {
39  public:
40   MOCK_METHOD(void, OnDataAvailable, (base::UnixSocket*), (override));
41   MOCK_METHOD(void, OnConnect, (base::UnixSocket*, bool), (override));
42   MOCK_METHOD(void, OnNewIncomingConnection, (base::UnixSocket*));
43 
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> new_connection)44   void OnNewIncomingConnection(
45       base::UnixSocket*,
46       std::unique_ptr<base::UnixSocket> new_connection) override {
47     // Need to keep |new_connection| alive.
48     client_connection_ = std::move(new_connection);
49     OnNewIncomingConnection(client_connection_.get());
50   }
51 
52  private:
53   std::unique_ptr<base::UnixSocket> client_connection_;
54 };
55 
56 // Exercises the relay service and also validates that the relay service injects
57 // a SetPeerIdentity message:
58 //
59 // producer (client UnixSocket) <- @producer.sock -> relay service
60 // <- 127.0.0.1.* -> tcp_server (listening UnixSocet).
TEST(RelayServiceTest,SetPeerIdentity)61 TEST(RelayServiceTest, SetPeerIdentity) {
62   base::TestTaskRunner task_runner;
63   auto relay_service = std::make_unique<RelayService>(&task_runner);
64   // Disable the extra socket connection created by RelayClient.
65   relay_service->SetRelayClientDisabledForTesting(true);
66 
67   // Set up a server UnixSocket to find an unused TCP port.
68   // The TCP connection emulates the socket to the host traced.
69   TestEventListener tcp_listener;
70   auto tcp_server = base::UnixSocket::Listen(
71       "127.0.0.1:0", &tcp_listener, &task_runner, base::SockFamily::kInet,
72       base::SockType::kStream);
73   ASSERT_TRUE(tcp_server->is_listening());
74   auto tcp_sock_name = tcp_server->GetSockAddr();
75   auto* unix_sock_name = "@producer.sock";  // Use abstract socket for server.
76 
77   // Start the relay service.
78   relay_service->Start(unix_sock_name, tcp_sock_name.c_str());
79 
80   // Emulates the producer connection.
81   TestEventListener producer_listener;
82   auto producer = base::UnixSocket::Connect(
83       unix_sock_name, &producer_listener, &task_runner, base::SockFamily::kUnix,
84       base::SockType::kStream);
85   auto producer_connected = task_runner.CreateCheckpoint("producer_connected");
86   EXPECT_CALL(producer_listener, OnConnect(_, _))
87       .WillOnce(Invoke([&](base::UnixSocket* s, bool conn) {
88         EXPECT_TRUE(conn);
89         EXPECT_EQ(s, producer.get());
90         producer_connected();
91       }));
92   task_runner.RunUntilCheckpoint("producer_connected");
93 
94   // Add some producer data.
95   ipc::Frame test_frame;
96   test_frame.add_data_for_testing("test_data");
97   auto test_data = ipc::BufferedFrameDeserializer::Serialize(test_frame);
98   producer->SendStr(test_data);
99 
100   base::UnixSocket* tcp_client_connection = nullptr;
101   auto tcp_client_connected =
102       task_runner.CreateCheckpoint("tcp_client_connected");
103   EXPECT_CALL(tcp_listener, OnNewIncomingConnection(_))
104       .WillOnce(Invoke([&](base::UnixSocket* client) {
105         tcp_client_connection = client;
106         tcp_client_connected();
107       }));
108   task_runner.RunUntilCheckpoint("tcp_client_connected");
109 
110   // Asserts that we can receive the SetPeerIdentity message.
111   auto peer_identity_recv = task_runner.CreateCheckpoint("peer_identity_recv");
112   ipc::BufferedFrameDeserializer deserializer;
113   EXPECT_CALL(tcp_listener, OnDataAvailable(_))
114       .WillRepeatedly(Invoke([&](base::UnixSocket* tcp_conn) {
115         auto buf = deserializer.BeginReceive();
116         auto rsize = tcp_conn->Receive(buf.data, buf.size);
117         EXPECT_TRUE(deserializer.EndReceive(rsize));
118 
119         auto frame = deserializer.PopNextFrame();
120         EXPECT_TRUE(frame->has_set_peer_identity());
121 
122         const auto& set_peer_identity = frame->set_peer_identity();
123         EXPECT_EQ(set_peer_identity.pid(), getpid());
124         EXPECT_EQ(set_peer_identity.uid(), static_cast<int32_t>(geteuid()));
125         EXPECT_TRUE(set_peer_identity.has_machine_id_hint());
126 
127         frame = deserializer.PopNextFrame();
128         EXPECT_EQ(1u, frame->data_for_testing().size());
129         EXPECT_EQ(std::string("test_data"), frame->data_for_testing()[0]);
130 
131         peer_identity_recv();
132       }));
133   task_runner.RunUntilCheckpoint("peer_identity_recv");
134 }
135 
TEST(RelayServiceTest,MachineIDHint)136 TEST(RelayServiceTest, MachineIDHint) {
137   base::TestTaskRunner task_runner;
138   auto relay_service = std::make_unique<RelayService>(&task_runner);
139 
140   auto hint1 = relay_service->GetMachineIdHint();
141   auto hint2 =
142       relay_service->GetMachineIdHint(/*use_pseudo_boot_id_for_testing=*/true);
143   EXPECT_NE(hint1, hint2);
144 
145   // Add a short sleep to verify that pseudo boot ID isn't affected.
146   std::this_thread::sleep_for(std::chrono::milliseconds(1));
147 
148   relay_service = std::make_unique<RelayService>(&task_runner);
149   auto hint3 = relay_service->GetMachineIdHint();
150   auto hint4 =
151       relay_service->GetMachineIdHint(/*use_pseudo_boot_id_for_testing=*/true);
152   EXPECT_NE(hint3, hint4);
153 
154   EXPECT_FALSE(hint1.empty());
155 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
156   // This test can run on Android kernel 3.x, but pseudo boot ID uses statx(2)
157   // that requires kernel 4.11.
158   EXPECT_FALSE(hint2.empty());
159 #endif
160 
161   EXPECT_EQ(hint1, hint3);
162   EXPECT_EQ(hint2, hint4);
163 }
164 
165 // Test that the RelayClient notifies its usr with the callback on
166 // connection errors.
TEST(RelayClientTest,OnErrorCallback)167 TEST(RelayClientTest, OnErrorCallback) {
168   base::TestTaskRunner task_runner;
169 
170   // Set up a server UnixSocket to find an unused TCP port.
171   // The TCP connection emulates the socket to the host traced.
172   TestEventListener tcp_listener;
173   auto tcp_server = base::UnixSocket::Listen(
174       "127.0.0.1:0", &tcp_listener, &task_runner, base::SockFamily::kInet,
175       base::SockType::kStream);
176   ASSERT_TRUE(tcp_server->is_listening());
177   auto tcp_sock_name = tcp_server->GetSockAddr();
178 
179   auto on_relay_client_error =
180       task_runner.CreateCheckpoint("on_relay_client_error");
181   auto on_error_callback = [&]() { on_relay_client_error(); };
182   auto relay_client = std::make_unique<RelayClient>(
183       tcp_sock_name, "fake_machine_id_hint", &task_runner, on_error_callback);
184 
185   base::UnixSocket* tcp_client_connection = nullptr;
186   auto tcp_client_connected =
187       task_runner.CreateCheckpoint("tcp_client_connected");
188   EXPECT_CALL(tcp_listener, OnNewIncomingConnection(_))
189       .WillOnce(Invoke([&](base::UnixSocket* client) {
190         tcp_client_connection = client;
191         tcp_client_connected();
192       }));
193   task_runner.RunUntilCheckpoint("tcp_client_connected");
194 
195   // Just drain the data passed over the socket.
196   EXPECT_CALL(tcp_listener, OnDataAvailable(_))
197       .WillRepeatedly(Invoke([&](base::UnixSocket* tcp_conn) {
198         ::testing::IgnoreResult(tcp_conn->ReceiveString());
199       }));
200 
201   EXPECT_FALSE(relay_client->clock_synced_with_service_for_testing());
202   // Shutdown the connected connection. The RelayClient should notice this
203   // error.
204   tcp_client_connection->Shutdown(true);
205   task_runner.RunUntilCheckpoint("on_relay_client_error");
206 
207   // Shutdown the server. The RelayClient should notice that the connection is
208   // refused.
209   tcp_server->Shutdown(true);
210   on_relay_client_error =
211       task_runner.CreateCheckpoint("on_relay_client_error_2");
212   relay_client = std::make_unique<RelayClient>(
213       tcp_sock_name, "fake_machine_id_hint", &task_runner,
214       [&]() { on_relay_client_error(); });
215   task_runner.RunUntilCheckpoint("on_relay_client_error_2");
216 }
217 
TEST(RelayClientTest,SetPeerIdentity)218 TEST(RelayClientTest, SetPeerIdentity) {
219   base::TestTaskRunner task_runner;
220   // Set up a server UnixSocket to find an unused TCP port.
221   // The TCP connection emulates the socket to the host traced.
222   TestEventListener tcp_listener;
223   auto tcp_server = base::UnixSocket::Listen(
224       "127.0.0.1:0", &tcp_listener, &task_runner, base::SockFamily::kInet,
225       base::SockType::kStream);
226   ASSERT_TRUE(tcp_server->is_listening());
227   auto tcp_sock_name = tcp_server->GetSockAddr();
228   auto on_error_callback = [&]() { FAIL() << "Should not be called"; };
229   auto relay_service = std::make_unique<RelayClient>(
230       tcp_sock_name, "fake_machine_id_hint", &task_runner, on_error_callback);
231 
232   base::UnixSocket* tcp_client_connection = nullptr;
233   auto tcp_client_connected =
234       task_runner.CreateCheckpoint("tcp_client_connected");
235   EXPECT_CALL(tcp_listener, OnNewIncomingConnection(_))
236       .WillOnce(Invoke([&](base::UnixSocket* client) {
237         tcp_client_connection = client;
238         tcp_client_connected();
239       }));
240   task_runner.RunUntilCheckpoint("tcp_client_connected");
241 
242   // Asserts that we can receive the SetPeerIdentity message.
243   auto peer_identity_recv = task_runner.CreateCheckpoint("peer_identity_recv");
244   ipc::BufferedFrameDeserializer deserializer;
245   EXPECT_CALL(tcp_listener, OnDataAvailable(_))
246       .WillRepeatedly(Invoke([&](base::UnixSocket* tcp_conn) {
247         auto buf = deserializer.BeginReceive();
248         auto rsize = tcp_conn->Receive(buf.data, buf.size);
249         EXPECT_TRUE(deserializer.EndReceive(rsize));
250 
251         auto frame = deserializer.PopNextFrame();
252         EXPECT_TRUE(frame->has_set_peer_identity());
253 
254         const auto& set_peer_identity = frame->set_peer_identity();
255         EXPECT_EQ(set_peer_identity.pid(), getpid());
256         EXPECT_EQ(set_peer_identity.uid(), static_cast<int32_t>(geteuid()));
257         EXPECT_EQ(set_peer_identity.machine_id_hint(), "fake_machine_id_hint");
258 
259         peer_identity_recv();
260       }));
261   task_runner.RunUntilCheckpoint("peer_identity_recv");
262 }
263 
264 }  // namespace
265 }  // namespace perfetto
266 
267 #endif
268