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 <condition_variable>
18 #include <mutex>
19 #include <thread>
20
21 #include "perfetto/public/abi/data_source_abi.h"
22 #include "perfetto/public/abi/pb_decoder_abi.h"
23 #include "perfetto/public/data_source.h"
24 #include "perfetto/public/producer.h"
25 #include "perfetto/public/protos/trace/test_event.pzc.h"
26 #include "perfetto/public/protos/trace/trace.pzc.h"
27 #include "perfetto/public/protos/trace/trace_packet.pzc.h"
28
29 #include "test/gtest_and_gmock.h"
30
31 #include "src/shared_lib/reset_for_testing.h"
32 #include "src/shared_lib/test/utils.h"
33
34 // Tests for the perfetto shared library.
35
36 namespace {
37
38 using ::perfetto::shlib::test_utils::FieldView;
39 using ::perfetto::shlib::test_utils::IdFieldView;
40 using ::perfetto::shlib::test_utils::MsgField;
41 using ::perfetto::shlib::test_utils::PbField;
42 using ::perfetto::shlib::test_utils::StringField;
43 using ::perfetto::shlib::test_utils::TracingSession;
44 using ::testing::_;
45 using ::testing::DoAll;
46 using ::testing::ElementsAre;
47 using ::testing::InSequence;
48 using ::testing::NiceMock;
49 using ::testing::Return;
50 using ::testing::SaveArg;
51
52 constexpr char kDataSourceName1[] = "dev.perfetto.example_data_source";
53 struct PerfettoDs data_source_1 = PERFETTO_DS_INIT();
54
55 constexpr char kDataSourceName2[] = "dev.perfetto.example_data_source2";
56 struct PerfettoDs data_source_2 = PERFETTO_DS_INIT();
57 void* const kDataSource2UserArg = reinterpret_cast<void*>(0x555);
58
59 class MockDs2Callbacks : testing::Mock {
60 public:
61 MOCK_METHOD(void*,
62 OnSetup,
63 (PerfettoDsInstanceIndex inst_id,
64 void* ds_config,
65 size_t ds_config_size,
66 void* user_arg));
67 MOCK_METHOD(void,
68 OnStart,
69 (PerfettoDsInstanceIndex inst_id,
70 void* user_arg,
71 void* inst_ctx));
72 MOCK_METHOD(void,
73 OnStop,
74 (PerfettoDsInstanceIndex inst_id,
75 void* user_arg,
76 void* inst_ctx,
77 struct PerfettoDsOnStopArgs* args));
78 MOCK_METHOD(void*,
79 OnCreateTls,
80 (PerfettoDsInstanceIndex inst_id,
81 struct PerfettoDsTracerImpl* tracer,
82 void* user_arg));
83 MOCK_METHOD(void, OnDeleteTls, (void*));
84 MOCK_METHOD(void*,
85 OnCreateIncr,
86 (PerfettoDsInstanceIndex inst_id,
87 struct PerfettoDsTracerImpl* tracer,
88 void* user_arg));
89 MOCK_METHOD(void, OnDeleteIncr, (void*));
90 };
91
92 class Notification {
93 public:
94 Notification() = default;
Notify()95 void Notify() {
96 std::unique_lock<std::mutex> lock(m_);
97 notified_ = true;
98 cv_.notify_one();
99 }
WaitForNotification()100 bool WaitForNotification() {
101 std::unique_lock<std::mutex> lock(m_);
102 cv_.wait(lock, [this] { return notified_; });
103 return notified_;
104 }
Notified()105 bool Notified() {
106 std::unique_lock<std::mutex> lock(m_);
107 return notified_;
108 }
109
110 private:
111 std::mutex m_;
112 std::condition_variable cv_;
113 bool notified_ = false;
114 };
115
116 class SharedLibDataSourceTest : public testing::Test {
117 protected:
SetUp()118 void SetUp() override {
119 struct PerfettoProducerInitArgs args = {0};
120 args.backends = PERFETTO_BACKEND_IN_PROCESS;
121 PerfettoProducerInit(args);
122 PerfettoDsRegister(&data_source_1, kDataSourceName1,
123 PerfettoDsNoCallbacks());
124 RegisterDataSource2();
125 }
126
TearDown()127 void TearDown() override {
128 perfetto::shlib::ResetForTesting();
129 data_source_1.enabled = &perfetto_atomic_false;
130 perfetto::shlib::DsImplDestroy(data_source_1.impl);
131 data_source_1.impl = nullptr;
132 data_source_2.enabled = &perfetto_atomic_false;
133 perfetto::shlib::DsImplDestroy(data_source_2.impl);
134 data_source_2.impl = nullptr;
135 }
136
137 struct Ds2CustomState {
138 void* actual;
139 SharedLibDataSourceTest* thiz;
140 };
141
RegisterDataSource2()142 void RegisterDataSource2() {
143 static struct PerfettoDsCallbacks callbacks = {};
144 callbacks.on_setup_cb = [](PerfettoDsInstanceIndex inst_id, void* ds_config,
145 size_t ds_config_size, void* user_arg) -> void* {
146 auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
147 return thiz->ds2_callbacks_.OnSetup(inst_id, ds_config, ds_config_size,
148 thiz->ds2_user_arg_);
149 };
150 callbacks.on_start_cb = [](PerfettoDsInstanceIndex inst_id, void* user_arg,
151 void* inst_ctx) -> void {
152 auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
153 return thiz->ds2_callbacks_.OnStart(inst_id, thiz->ds2_user_arg_,
154 inst_ctx);
155 };
156 callbacks.on_stop_cb = [](PerfettoDsInstanceIndex inst_id, void* user_arg,
157 void* inst_ctx,
158 struct PerfettoDsOnStopArgs* args) {
159 auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
160 return thiz->ds2_callbacks_.OnStop(inst_id, thiz->ds2_user_arg_, inst_ctx,
161 args);
162 };
163 callbacks.on_create_tls_cb = [](PerfettoDsInstanceIndex inst_id,
164 struct PerfettoDsTracerImpl* tracer,
165 void* user_arg) -> void* {
166 auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
167 auto* state = new Ds2CustomState();
168 state->thiz = thiz;
169 state->actual = thiz->ds2_callbacks_.OnCreateTls(inst_id, tracer,
170 thiz->ds2_user_arg_);
171 return state;
172 };
173 callbacks.on_delete_tls_cb = [](void* ptr) {
174 auto* state = static_cast<Ds2CustomState*>(ptr);
175 state->thiz->ds2_callbacks_.OnDeleteTls(state->actual);
176 delete state;
177 };
178 callbacks.on_create_incr_cb = [](PerfettoDsInstanceIndex inst_id,
179 struct PerfettoDsTracerImpl* tracer,
180 void* user_arg) -> void* {
181 auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
182 auto* state = new Ds2CustomState();
183 state->thiz = thiz;
184 state->actual = thiz->ds2_callbacks_.OnCreateIncr(inst_id, tracer,
185 thiz->ds2_user_arg_);
186 return state;
187 };
188 callbacks.on_delete_incr_cb = [](void* ptr) {
189 auto* state = static_cast<Ds2CustomState*>(ptr);
190 state->thiz->ds2_callbacks_.OnDeleteIncr(state->actual);
191 delete state;
192 };
193 callbacks.user_arg = this;
194 PerfettoDsRegister(&data_source_2, kDataSourceName2, callbacks);
195 }
196
Ds2ActualCustomState(void * ptr)197 void* Ds2ActualCustomState(void* ptr) {
198 auto* state = static_cast<Ds2CustomState*>(ptr);
199 return state->actual;
200 }
201
202 NiceMock<MockDs2Callbacks> ds2_callbacks_;
203 void* ds2_user_arg_ = kDataSource2UserArg;
204 };
205
TEST_F(SharedLibDataSourceTest,DisabledNotExecuted)206 TEST_F(SharedLibDataSourceTest, DisabledNotExecuted) {
207 bool executed = false;
208
209 PERFETTO_DS_TRACE(data_source_1, ctx) {
210 executed = true;
211 }
212
213 EXPECT_FALSE(executed);
214 }
215
TEST_F(SharedLibDataSourceTest,EnabledOnce)216 TEST_F(SharedLibDataSourceTest, EnabledOnce) {
217 size_t executed = 0;
218 TracingSession tracing_session =
219 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
220
221 PERFETTO_DS_TRACE(data_source_1, ctx) {
222 executed++;
223 }
224
225 EXPECT_EQ(executed, 1u);
226 }
227
TEST_F(SharedLibDataSourceTest,EnabledTwice)228 TEST_F(SharedLibDataSourceTest, EnabledTwice) {
229 size_t executed = 0;
230 TracingSession tracing_session1 =
231 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
232 TracingSession tracing_session2 =
233 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
234
235 PERFETTO_DS_TRACE(data_source_1, ctx) {
236 executed++;
237 }
238
239 EXPECT_EQ(executed, 2u);
240 }
241
TEST_F(SharedLibDataSourceTest,Serialization)242 TEST_F(SharedLibDataSourceTest, Serialization) {
243 TracingSession tracing_session =
244 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
245
246 PERFETTO_DS_TRACE(data_source_1, ctx) {
247 struct PerfettoDsRootTracePacket trace_packet;
248 PerfettoDsTracerPacketBegin(&ctx, &trace_packet);
249
250 {
251 struct perfetto_protos_TestEvent for_testing;
252 perfetto_protos_TracePacket_begin_for_testing(&trace_packet.msg,
253 &for_testing);
254 {
255 struct perfetto_protos_TestEvent_TestPayload payload;
256 perfetto_protos_TestEvent_begin_payload(&for_testing, &payload);
257 perfetto_protos_TestEvent_TestPayload_set_cstr_str(&payload,
258 "ABCDEFGH");
259 perfetto_protos_TestEvent_end_payload(&for_testing, &payload);
260 }
261 perfetto_protos_TracePacket_end_for_testing(&trace_packet.msg,
262 &for_testing);
263 }
264 PerfettoDsTracerPacketEnd(&ctx, &trace_packet);
265 }
266 PERFETTO_DS_TRACE(data_source_1, ctx) {
267 struct PerfettoDsRootTracePacket trace_packet;
268 PerfettoDsTracerPacketBegin(&ctx, &trace_packet);
269 PerfettoDsTracerPacketEnd(&ctx, &trace_packet);
270 }
271
272 tracing_session.StopBlocking();
273 std::vector<uint8_t> data = tracing_session.ReadBlocking();
274 bool found_for_testing = false;
275 for (struct PerfettoPbDecoderField trace_field : FieldView(data)) {
276 ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number,
277 MsgField(_)));
278 IdFieldView for_testing(
279 trace_field, perfetto_protos_TracePacket_for_testing_field_number);
280 ASSERT_TRUE(for_testing.ok());
281 if (for_testing.size() == 0) {
282 continue;
283 }
284 found_for_testing = true;
285 ASSERT_EQ(for_testing.size(), 1u);
286 ASSERT_THAT(FieldView(for_testing.front()),
287 ElementsAre(PbField(
288 perfetto_protos_TestEvent_payload_field_number,
289 MsgField(ElementsAre(PbField(
290 perfetto_protos_TestEvent_TestPayload_str_field_number,
291 StringField("ABCDEFGH")))))));
292 }
293 EXPECT_TRUE(found_for_testing);
294 }
295
TEST_F(SharedLibDataSourceTest,Break)296 TEST_F(SharedLibDataSourceTest, Break) {
297 TracingSession tracing_session1 =
298 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
299 TracingSession tracing_session2 =
300 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
301
302 PERFETTO_DS_TRACE(data_source_1, ctx) {
303 struct PerfettoDsRootTracePacket trace_packet;
304 PerfettoDsTracerPacketBegin(&ctx, &trace_packet);
305
306 {
307 struct perfetto_protos_TestEvent for_testing;
308 perfetto_protos_TracePacket_begin_for_testing(&trace_packet.msg,
309 &for_testing);
310 perfetto_protos_TracePacket_end_for_testing(&trace_packet.msg,
311 &for_testing);
312 }
313 PerfettoDsTracerPacketEnd(&ctx, &trace_packet);
314 // Break: the packet will be emitted only on the first data source instance
315 // and therefore will not show up on `tracing_session2`.
316 PERFETTO_DS_TRACE_BREAK(data_source_1, ctx);
317 }
318 PERFETTO_DS_TRACE(data_source_1, ctx) {
319 struct PerfettoDsRootTracePacket trace_packet;
320 PerfettoDsTracerPacketBegin(&ctx, &trace_packet);
321 PerfettoDsTracerPacketEnd(&ctx, &trace_packet);
322 }
323
324 tracing_session1.StopBlocking();
325 std::vector<uint8_t> data1 = tracing_session1.ReadBlocking();
326 EXPECT_THAT(
327 FieldView(data1),
328 Contains(PbField(perfetto_protos_Trace_packet_field_number,
329 MsgField(Contains(PbField(
330 perfetto_protos_TracePacket_for_testing_field_number,
331 MsgField(_)))))));
332 tracing_session2.StopBlocking();
333 std::vector<uint8_t> data2 = tracing_session2.ReadBlocking();
334 EXPECT_THAT(
335 FieldView(data2),
336 Each(PbField(
337 perfetto_protos_Trace_packet_field_number,
338 MsgField(Not(Contains(PbField(
339 perfetto_protos_TracePacket_for_testing_field_number, _)))))));
340 }
341
TEST_F(SharedLibDataSourceTest,FlushCb)342 TEST_F(SharedLibDataSourceTest, FlushCb) {
343 TracingSession tracing_session =
344 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
345 Notification notification;
346
347 PERFETTO_DS_TRACE(data_source_1, ctx) {
348 PerfettoDsTracerFlush(
349 &ctx,
350 [](void* p_notification) {
351 static_cast<Notification*>(p_notification)->Notify();
352 },
353 ¬ification);
354 }
355
356 notification.WaitForNotification();
357 EXPECT_TRUE(notification.Notified());
358 }
359
TEST_F(SharedLibDataSourceTest,LifetimeCallbacks)360 TEST_F(SharedLibDataSourceTest, LifetimeCallbacks) {
361 void* const kInstancePtr = reinterpret_cast<void*>(0x44);
362 testing::InSequence seq;
363 PerfettoDsInstanceIndex setup_inst, start_inst, stop_inst;
364 EXPECT_CALL(ds2_callbacks_, OnSetup(_, _, _, kDataSource2UserArg))
365 .WillOnce(DoAll(SaveArg<0>(&setup_inst), Return(kInstancePtr)));
366 EXPECT_CALL(ds2_callbacks_, OnStart(_, kDataSource2UserArg, kInstancePtr))
367 .WillOnce(SaveArg<0>(&start_inst));
368
369 TracingSession tracing_session =
370 TracingSession::Builder().set_data_source_name(kDataSourceName2).Build();
371
372 EXPECT_CALL(ds2_callbacks_, OnStop(_, kDataSource2UserArg, kInstancePtr, _))
373 .WillOnce(SaveArg<0>(&stop_inst));
374
375 tracing_session.StopBlocking();
376
377 EXPECT_EQ(setup_inst, start_inst);
378 EXPECT_EQ(setup_inst, stop_inst);
379 }
380
TEST_F(SharedLibDataSourceTest,StopDone)381 TEST_F(SharedLibDataSourceTest, StopDone) {
382 TracingSession tracing_session =
383 TracingSession::Builder().set_data_source_name(kDataSourceName2).Build();
384
385 Notification stop_called;
386 struct PerfettoDsAsyncStopper* stopper;
387
388 EXPECT_CALL(ds2_callbacks_, OnStop(_, kDataSource2UserArg, _, _))
389 .WillOnce([&](PerfettoDsInstanceIndex, void*, void*,
390 struct PerfettoDsOnStopArgs* args) {
391 stopper = PerfettoDsOnStopArgsPostpone(args);
392 stop_called.Notify();
393 });
394
395 std::thread t([&]() { tracing_session.StopBlocking(); });
396
397 stop_called.WaitForNotification();
398 PerfettoDsStopDone(stopper);
399
400 t.join();
401 }
402
TEST_F(SharedLibDataSourceTest,ThreadLocalState)403 TEST_F(SharedLibDataSourceTest, ThreadLocalState) {
404 bool ignored = false;
405 void* const kTlsPtr = &ignored;
406 TracingSession tracing_session =
407 TracingSession::Builder().set_data_source_name(kDataSourceName2).Build();
408
409 EXPECT_CALL(ds2_callbacks_, OnCreateTls).WillOnce(Return(kTlsPtr));
410
411 void* tls_state = nullptr;
412 PERFETTO_DS_TRACE(data_source_2, ctx) {
413 tls_state = PerfettoDsGetCustomTls(&data_source_2, &ctx);
414 }
415 EXPECT_EQ(Ds2ActualCustomState(tls_state), kTlsPtr);
416
417 tracing_session.StopBlocking();
418
419 EXPECT_CALL(ds2_callbacks_, OnDeleteTls(kTlsPtr));
420
421 // The OnDelete callback will be called by
422 // DestroyStoppedTraceWritersForCurrentThread(). One way to trigger that is to
423 // trace with another data source.
424 TracingSession tracing_session_1 =
425 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
426 PERFETTO_DS_TRACE(data_source_1, ctx) {}
427 }
428
TEST_F(SharedLibDataSourceTest,IncrementalState)429 TEST_F(SharedLibDataSourceTest, IncrementalState) {
430 bool ignored = false;
431 void* const kIncrPtr = &ignored;
432 TracingSession tracing_session =
433 TracingSession::Builder().set_data_source_name(kDataSourceName2).Build();
434
435 EXPECT_CALL(ds2_callbacks_, OnCreateIncr).WillOnce(Return(kIncrPtr));
436
437 void* tls_state = nullptr;
438 PERFETTO_DS_TRACE(data_source_2, ctx) {
439 tls_state = PerfettoDsGetIncrementalState(&data_source_2, &ctx);
440 }
441 EXPECT_EQ(Ds2ActualCustomState(tls_state), kIncrPtr);
442
443 tracing_session.StopBlocking();
444
445 EXPECT_CALL(ds2_callbacks_, OnDeleteIncr(kIncrPtr));
446
447 // The OnDelete callback will be called by
448 // DestroyStoppedTraceWritersForCurrentThread(). One way to trigger that is to
449 // trace with another data source.
450 TracingSession tracing_session_1 =
451 TracingSession::Builder().set_data_source_name(kDataSourceName1).Build();
452 PERFETTO_DS_TRACE(data_source_1, ctx) {}
453 }
454
455 } // namespace
456