1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gmock/gmock.h>
17 #include <gtest/gtest.h>
18 #include <atomic>
19
20 #include "assembler/assembly-parser.h"
21 #include "libpandafile/file.h"
22 #include "libpandabase/trace/trace.h"
23 #include "libpandabase/panda_gen_options/generated/base_options.h"
24 #include "runtime/include/thread_scopes.h"
25 #include "runtime/include/runtime.h"
26 #include "runtime/tooling/sampler/sampling_profiler.h"
27 #include "runtime/interpreter/runtime_interface.h"
28 #include "tools/sampler/aspt_converter.h"
29
30 namespace ark::tooling::sampler::test {
31
Separator()32 inline std::string Separator()
33 {
34 #ifdef _WIN32
35 return "\\";
36 #else
37 return "/";
38 #endif
39 }
40
41 static const char *g_profilerFilename = "profiler_result.aspt";
42 static const char *g_pandaFileName = "sampling_profiler_test_ark_asm.abc";
43 static constexpr size_t TEST_CYCLE_THRESHOLD = 100;
44 static const std::shared_ptr<SamplesRecord> G_SAMPLES_RECORD = std::make_shared<SamplesRecord>();
45
46 // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
47 class SamplerTest : public testing::Test {
48 public:
49 // NOLINTNEXTLINE(readability-function-size)
SetUp()50 void SetUp() override
51 {
52 Logger::Initialize(base_options::Options(""));
53
54 RuntimeOptions options;
55 options.SetLoadRuntimes({"core"});
56 options.SetRunGcInPlace(true);
57 options.SetVerifyCallStack(false);
58 options.SetInterpreterType("cpp");
59 auto execPath = ark::os::file::File::GetExecutablePath();
60 std::string pandaStdLib =
61 execPath.Value() + Separator() + ".." + Separator() + "pandastdlib" + Separator() + "arkstdlib.abc";
62 options.SetBootPandaFiles({pandaStdLib});
63 Runtime::Create(options);
64
65 auto pf = panda_file::OpenPandaFileOrZip(g_pandaFileName);
66 Runtime::GetCurrent()->GetClassLinker()->AddPandaFile(std::move(pf));
67
68 thread_ = ark::MTManagedThread::GetCurrent();
69 }
70
TearDown()71 void TearDown() override
72 {
73 Runtime::Destroy();
74 }
75
FullfillFakeSample(SampleInfo * ps)76 void FullfillFakeSample(SampleInfo *ps)
77 {
78 for (uint32_t i = 0; i < SampleInfo::StackInfo::MAX_STACK_DEPTH; ++i) {
79 ps->stackInfo.managedStack[i] = {i, pfId_};
80 }
81 ps->threadInfo.threadId = GetThreadId();
82 ps->stackInfo.managedStackSize = SampleInfo::StackInfo::MAX_STACK_DEPTH;
83 }
84
85 // Friend wrappers for accesing samplers private fields
ExtractListenerTid(const Sampler * sPtr)86 static os::thread::NativeHandleType ExtractListenerTid(const Sampler *sPtr)
87 {
88 return sPtr->listenerTid_;
89 }
90
ExtractSamplerTid(const Sampler * sPtr)91 static os::thread::NativeHandleType ExtractSamplerTid(const Sampler *sPtr)
92 {
93 return sPtr->samplerTid_;
94 }
95
ExtractManagedThreads(Sampler * sPtr)96 static PandaSet<os::thread::ThreadId> ExtractManagedThreads(Sampler *sPtr)
97 {
98 // Sending a copy to avoid of datarace
99 os::memory::LockHolder holder(sPtr->managedThreadsLock_);
100 PandaSet<os::thread::ThreadId> managedThreadsCopy = sPtr->managedThreads_;
101 return managedThreadsCopy;
102 }
103
ExtractLoadedPFSize(Sampler * sPtr)104 static size_t ExtractLoadedPFSize(Sampler *sPtr)
105 {
106 os::memory::LockHolder holder(sPtr->loadedPfsLock_);
107 return sPtr->loadedPfs_.size();
108 }
109
ExtractPipes(const Sampler * sPtr)110 static std::array<int, 2> ExtractPipes(const Sampler *sPtr)
111 {
112 // Sending a copy to avoid of datarace
113 return sPtr->communicator_.listenerPipe_;
114 }
115
ExtractIsActive(const Sampler * sPtr)116 static bool ExtractIsActive(const Sampler *sPtr)
117 {
118 // Atomic with acquire order reason: To ensure start/stop load correctly
119 return sPtr->isActive_.load(std::memory_order_acquire);
120 }
121
GetThreadId()122 uint32_t GetThreadId()
123 {
124 return os::thread::GetCurrentThreadId();
125 }
126
127 protected:
128 ark::MTManagedThread *thread_ {nullptr};
129 uintptr_t pfId_ {0};
130 uint32_t checksum_ {0};
131 };
132 // NOLINTEND(misc-non-private-member-variables-in-classes)
133
TEST_F(SamplerTest,SamplerInitTest)134 TEST_F(SamplerTest, SamplerInitTest)
135 {
136 auto *sp = Sampler::Create();
137 ASSERT_NE(sp, nullptr);
138
139 ASSERT_EQ(ExtractListenerTid(sp), 0);
140 ASSERT_EQ(ExtractSamplerTid(sp), 0);
141 ASSERT_EQ(ExtractIsActive(sp), false);
142
143 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), true);
144 ASSERT_NE(ExtractListenerTid(sp), 0);
145 ASSERT_NE(ExtractSamplerTid(sp), 0);
146 ASSERT_EQ(ExtractIsActive(sp), true);
147
148 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), false);
149
150 sp->Stop();
151 ASSERT_EQ(ExtractListenerTid(sp), 0);
152 ASSERT_EQ(ExtractSamplerTid(sp), 0);
153 ASSERT_EQ(ExtractIsActive(sp), false);
154
155 // Second run
156 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), true);
157 ASSERT_NE(ExtractListenerTid(sp), 0);
158 ASSERT_NE(ExtractSamplerTid(sp), 0);
159 ASSERT_EQ(ExtractIsActive(sp), true);
160
161 sp->Stop();
162 ASSERT_EQ(ExtractListenerTid(sp), 0);
163 ASSERT_EQ(ExtractSamplerTid(sp), 0);
164 ASSERT_EQ(ExtractIsActive(sp), false);
165 Sampler::Destroy(sp);
166 }
167
TEST_F(SamplerTest,InspectorSamplerInitTest)168 TEST_F(SamplerTest, InspectorSamplerInitTest)
169 {
170 auto *sp = Sampler::Create();
171 ASSERT_NE(sp, nullptr);
172
173 ASSERT_EQ(ExtractListenerTid(sp), 0);
174 ASSERT_EQ(ExtractSamplerTid(sp), 0);
175 ASSERT_EQ(ExtractIsActive(sp), false);
176
177 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), true);
178 ASSERT_NE(ExtractListenerTid(sp), 0);
179 ASSERT_NE(ExtractSamplerTid(sp), 0);
180 ASSERT_EQ(ExtractIsActive(sp), true);
181
182 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), false);
183
184 sp->Stop();
185 ASSERT_EQ(ExtractListenerTid(sp), 0);
186 ASSERT_EQ(ExtractSamplerTid(sp), 0);
187 ASSERT_EQ(ExtractIsActive(sp), false);
188
189 // Second run
190 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), true);
191 ASSERT_NE(ExtractListenerTid(sp), 0);
192 ASSERT_NE(ExtractSamplerTid(sp), 0);
193 ASSERT_EQ(ExtractIsActive(sp), true);
194
195 sp->Stop();
196 ASSERT_EQ(ExtractListenerTid(sp), 0);
197 ASSERT_EQ(ExtractSamplerTid(sp), 0);
198 ASSERT_EQ(ExtractIsActive(sp), false);
199 Sampler::Destroy(sp);
200 }
201
RunManagedThread(std::atomic<bool> * syncFlag)202 void RunManagedThread(std::atomic<bool> *syncFlag)
203 {
204 auto *mThr = ark::MTManagedThread::Create(ark::Runtime::GetCurrent(), ark::Runtime::GetCurrent()->GetPandaVM());
205 mThr->ManagedCodeBegin();
206
207 *syncFlag = true;
208 while (*syncFlag) {
209 // Calling safepoint 'cause starting profiler required to stop all managed threads
210 interpreter::RuntimeInterface::Safepoint();
211 }
212
213 mThr->ManagedCodeEnd();
214 mThr->Destroy();
215 }
216
RunManagedThreadAndSaveThreadId(std::atomic<bool> * syncFlag,os::thread::ThreadId * id)217 void RunManagedThreadAndSaveThreadId(std::atomic<bool> *syncFlag, os::thread::ThreadId *id)
218 {
219 auto *mThr = ark::MTManagedThread::Create(ark::Runtime::GetCurrent(), ark::Runtime::GetCurrent()->GetPandaVM());
220 mThr->ManagedCodeBegin();
221
222 *id = os::thread::GetCurrentThreadId();
223 *syncFlag = true;
224 while (*syncFlag) {
225 // Calling safepoint 'cause starting profiler required to stop all managed threads
226 interpreter::RuntimeInterface::Safepoint();
227 }
228
229 mThr->ManagedCodeEnd();
230 mThr->Destroy();
231 }
232
RunNativeThread(std::atomic<bool> * syncFlag)233 void RunNativeThread(std::atomic<bool> *syncFlag)
234 {
235 auto *mThr = ark::MTManagedThread::Create(ark::Runtime::GetCurrent(), ark::Runtime::GetCurrent()->GetPandaVM());
236
237 *syncFlag = true;
238 while (*syncFlag) {
239 }
240
241 mThr->Destroy();
242 }
243
244 // Testing notification thread started/finished
TEST_F(SamplerTest,SamplerEventThreadNotificationTest)245 TEST_F(SamplerTest, SamplerEventThreadNotificationTest)
246 {
247 auto *sp = Sampler::Create();
248 ASSERT_NE(sp, nullptr);
249
250 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), true);
251 ASSERT_NE(ExtractListenerTid(sp), 0);
252 ASSERT_NE(ExtractSamplerTid(sp), 0);
253 ASSERT_EQ(ExtractIsActive(sp), true);
254
255 ASSERT_FALSE(ExtractManagedThreads(sp).empty());
256 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
257
258 std::atomic<bool> syncFlag1 = false;
259 std::atomic<bool> syncFlag2 = false;
260 std::atomic<bool> syncFlag3 = false;
261 std::thread managedThread1(RunManagedThread, &syncFlag1);
262 std::thread managedThread2(RunManagedThread, &syncFlag2);
263 std::thread managedThread3(RunManagedThread, &syncFlag3);
264
265 while (!syncFlag1 || !syncFlag2 || !syncFlag3) {
266 ;
267 }
268 ASSERT_EQ(ExtractManagedThreads(sp).size(), 4UL);
269
270 syncFlag1 = false;
271 syncFlag2 = false;
272 syncFlag3 = false;
273 managedThread1.join();
274 managedThread2.join();
275 managedThread3.join();
276
277 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
278
279 sp->Stop();
280 ASSERT_EQ(ExtractListenerTid(sp), 0);
281 ASSERT_EQ(ExtractSamplerTid(sp), 0);
282 ASSERT_EQ(ExtractIsActive(sp), false);
283 Sampler::Destroy(sp);
284 }
285
TEST_F(SamplerTest,InspectorSamplerEventThreadNotificationTest)286 TEST_F(SamplerTest, InspectorSamplerEventThreadNotificationTest)
287 {
288 auto *sp = Sampler::Create();
289 ASSERT_NE(sp, nullptr);
290
291 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), true);
292 ASSERT_NE(ExtractListenerTid(sp), 0);
293 ASSERT_NE(ExtractSamplerTid(sp), 0);
294 ASSERT_EQ(ExtractIsActive(sp), true);
295
296 ASSERT_FALSE(ExtractManagedThreads(sp).empty());
297 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
298
299 std::atomic<bool> syncFlag1 = false;
300 std::atomic<bool> syncFlag2 = false;
301 std::atomic<bool> syncFlag3 = false;
302 std::thread managedThread1(RunManagedThread, &syncFlag1);
303 std::thread managedThread2(RunManagedThread, &syncFlag2);
304 std::thread managedThread3(RunManagedThread, &syncFlag3);
305
306 while (!syncFlag1 || !syncFlag2 || !syncFlag3) {
307 ;
308 }
309 ASSERT_EQ(ExtractManagedThreads(sp).size(), 4UL);
310
311 syncFlag1 = false;
312 syncFlag2 = false;
313 syncFlag3 = false;
314 managedThread1.join();
315 managedThread2.join();
316 managedThread3.join();
317
318 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
319
320 sp->Stop();
321 ASSERT_EQ(ExtractListenerTid(sp), 0);
322 ASSERT_EQ(ExtractSamplerTid(sp), 0);
323 ASSERT_EQ(ExtractIsActive(sp), false);
324 Sampler::Destroy(sp);
325 }
326
327 // Testing notification thread started/finished
TEST_F(SamplerTest,SamplerCheckThreadIdTest)328 TEST_F(SamplerTest, SamplerCheckThreadIdTest)
329 {
330 auto *sp = Sampler::Create();
331 ASSERT_NE(sp, nullptr);
332
333 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), true);
334 ASSERT_NE(ExtractListenerTid(sp), 0);
335 ASSERT_NE(ExtractSamplerTid(sp), 0);
336 ASSERT_EQ(ExtractIsActive(sp), true);
337
338 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
339
340 std::atomic<bool> syncFlag1 = false;
341 os::thread::ThreadId mtId = 0;
342 std::thread managedThread1(RunManagedThreadAndSaveThreadId, &syncFlag1, &mtId);
343
344 while (!syncFlag1) {
345 ;
346 }
347 // only one additional managed thread must be running
348 ASSERT_EQ(ExtractManagedThreads(sp).size(), 2UL);
349 bool isPassed = false;
350
351 for (const auto &elem : ExtractManagedThreads(sp)) {
352 if (elem == mtId) {
353 isPassed = true;
354 break;
355 }
356 }
357 ASSERT_TRUE(isPassed);
358
359 syncFlag1 = false;
360 managedThread1.join();
361
362 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
363
364 sp->Stop();
365 ASSERT_EQ(ExtractListenerTid(sp), 0);
366 ASSERT_EQ(ExtractSamplerTid(sp), 0);
367 ASSERT_EQ(ExtractIsActive(sp), false);
368 Sampler::Destroy(sp);
369 }
370
TEST_F(SamplerTest,InspectorSamplerCheckThreadIdTest)371 TEST_F(SamplerTest, InspectorSamplerCheckThreadIdTest)
372 {
373 auto *sp = Sampler::Create();
374 ASSERT_NE(sp, nullptr);
375
376 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), true);
377 ASSERT_NE(ExtractListenerTid(sp), 0);
378 ASSERT_NE(ExtractSamplerTid(sp), 0);
379 ASSERT_EQ(ExtractIsActive(sp), true);
380
381 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
382
383 std::atomic<bool> syncFlag1 = false;
384 os::thread::ThreadId mtId = 0;
385 std::thread managedThread1(RunManagedThreadAndSaveThreadId, &syncFlag1, &mtId);
386
387 while (!syncFlag1) {
388 ;
389 }
390 // only one additional managed thread must be running
391 ASSERT_EQ(ExtractManagedThreads(sp).size(), 2UL);
392 bool isPassed = false;
393
394 for (const auto &elem : ExtractManagedThreads(sp)) {
395 if (elem == mtId) {
396 isPassed = true;
397 break;
398 }
399 }
400 ASSERT_TRUE(isPassed);
401
402 syncFlag1 = false;
403 managedThread1.join();
404
405 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
406
407 sp->Stop();
408 ASSERT_EQ(ExtractListenerTid(sp), 0);
409 ASSERT_EQ(ExtractSamplerTid(sp), 0);
410 ASSERT_EQ(ExtractIsActive(sp), false);
411 Sampler::Destroy(sp);
412 }
413
414 // Testing thread collection
TEST_F(SamplerTest,SamplerCollectThreadTest)415 TEST_F(SamplerTest, SamplerCollectThreadTest)
416 {
417 auto *sp = Sampler::Create();
418 ASSERT_NE(sp, nullptr);
419
420 std::atomic<bool> syncFlag1 = false;
421 std::atomic<bool> syncFlag2 = false;
422 std::atomic<bool> syncFlag3 = false;
423 std::thread managedThread1(RunManagedThread, &syncFlag1);
424 std::thread managedThread2(RunManagedThread, &syncFlag2);
425 std::thread managedThread3(RunManagedThread, &syncFlag3);
426
427 while (!syncFlag1 || !syncFlag2 || !syncFlag3) {
428 ;
429 }
430
431 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), true);
432 ASSERT_NE(ExtractListenerTid(sp), 0);
433 ASSERT_NE(ExtractSamplerTid(sp), 0);
434 ASSERT_EQ(ExtractIsActive(sp), true);
435
436 ASSERT_EQ(ExtractManagedThreads(sp).size(), 4UL);
437
438 syncFlag1 = false;
439 syncFlag2 = false;
440 syncFlag3 = false;
441 managedThread1.join();
442 managedThread2.join();
443 managedThread3.join();
444
445 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
446
447 sp->Stop();
448 ASSERT_EQ(ExtractListenerTid(sp), 0);
449 ASSERT_EQ(ExtractSamplerTid(sp), 0);
450 ASSERT_EQ(ExtractIsActive(sp), false);
451 Sampler::Destroy(sp);
452 }
453
TEST_F(SamplerTest,InspectorSamplerCollectThreadTest)454 TEST_F(SamplerTest, InspectorSamplerCollectThreadTest)
455 {
456 auto *sp = Sampler::Create();
457 ASSERT_NE(sp, nullptr);
458
459 std::atomic<bool> syncFlag1 = false;
460 std::atomic<bool> syncFlag2 = false;
461 std::atomic<bool> syncFlag3 = false;
462 std::thread managedThread1(RunManagedThread, &syncFlag1);
463 std::thread managedThread2(RunManagedThread, &syncFlag2);
464 std::thread managedThread3(RunManagedThread, &syncFlag3);
465
466 while (!syncFlag1 || !syncFlag2 || !syncFlag3) {
467 ;
468 }
469
470 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), true);
471 ASSERT_NE(ExtractListenerTid(sp), 0);
472 ASSERT_NE(ExtractSamplerTid(sp), 0);
473 ASSERT_EQ(ExtractIsActive(sp), true);
474
475 ASSERT_EQ(ExtractManagedThreads(sp).size(), 4UL);
476
477 syncFlag1 = false;
478 syncFlag2 = false;
479 syncFlag3 = false;
480 managedThread1.join();
481 managedThread2.join();
482 managedThread3.join();
483
484 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
485
486 sp->Stop();
487 ASSERT_EQ(ExtractListenerTid(sp), 0);
488 ASSERT_EQ(ExtractSamplerTid(sp), 0);
489 ASSERT_EQ(ExtractIsActive(sp), false);
490 Sampler::Destroy(sp);
491 }
492
493 // Testing native thread collection
TEST_F(SamplerTest,SamplerCollectNativeThreadTest)494 TEST_F(SamplerTest, SamplerCollectNativeThreadTest)
495 {
496 auto *sp = Sampler::Create();
497 ASSERT_NE(sp, nullptr);
498
499 std::atomic<bool> syncFlag1 = false;
500 std::atomic<bool> syncFlag2 = false;
501 std::atomic<bool> syncFlag3 = false;
502 std::thread managedThread1(RunManagedThread, &syncFlag1);
503 std::thread nativeThread2(RunNativeThread, &syncFlag2);
504
505 while (!syncFlag1 || !syncFlag2) {
506 ;
507 }
508
509 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), true);
510 ASSERT_NE(ExtractListenerTid(sp), 0);
511 ASSERT_NE(ExtractSamplerTid(sp), 0);
512 ASSERT_EQ(ExtractIsActive(sp), true);
513
514 // two additional threads must be running - a managed and a native one
515 ASSERT_EQ(ExtractManagedThreads(sp).size(), 3UL);
516 std::thread nativeThread3(RunNativeThread, &syncFlag3);
517 while (!syncFlag3) {
518 ;
519 }
520
521 ASSERT_EQ(ExtractManagedThreads(sp).size(), 4UL);
522
523 syncFlag1 = false;
524 syncFlag2 = false;
525 syncFlag3 = false;
526 managedThread1.join();
527 nativeThread2.join();
528 nativeThread3.join();
529
530 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
531
532 sp->Stop();
533 ASSERT_EQ(ExtractListenerTid(sp), 0);
534 ASSERT_EQ(ExtractSamplerTid(sp), 0);
535 ASSERT_EQ(ExtractIsActive(sp), false);
536 Sampler::Destroy(sp);
537 }
538
TEST_F(SamplerTest,InspectorSamplerCollectNativeThreadTest)539 TEST_F(SamplerTest, InspectorSamplerCollectNativeThreadTest)
540 {
541 auto *sp = Sampler::Create();
542 ASSERT_NE(sp, nullptr);
543
544 std::atomic<bool> syncFlag1 = false;
545 std::atomic<bool> syncFlag2 = false;
546 std::atomic<bool> syncFlag3 = false;
547 std::thread managedThread1(RunManagedThread, &syncFlag1);
548 std::thread nativeThread2(RunNativeThread, &syncFlag2);
549
550 while (!syncFlag1 || !syncFlag2) {
551 ;
552 }
553
554 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), true);
555 ASSERT_NE(ExtractListenerTid(sp), 0);
556 ASSERT_NE(ExtractSamplerTid(sp), 0);
557 ASSERT_EQ(ExtractIsActive(sp), true);
558
559 // two additional threads must be running - a managed and a native one
560 ASSERT_EQ(ExtractManagedThreads(sp).size(), 3UL);
561 std::thread nativeThread3(RunNativeThread, &syncFlag3);
562 while (!syncFlag3) {
563 ;
564 }
565
566 ASSERT_EQ(ExtractManagedThreads(sp).size(), 4UL);
567
568 syncFlag1 = false;
569 syncFlag2 = false;
570 syncFlag3 = false;
571 managedThread1.join();
572 nativeThread2.join();
573 nativeThread3.join();
574
575 ASSERT_EQ(ExtractManagedThreads(sp).size(), 1);
576
577 sp->Stop();
578 ASSERT_EQ(ExtractListenerTid(sp), 0);
579 ASSERT_EQ(ExtractSamplerTid(sp), 0);
580 ASSERT_EQ(ExtractIsActive(sp), false);
581 Sampler::Destroy(sp);
582 }
583
584 // Testing pipes
TEST_F(SamplerTest,SamplerPipesTest)585 TEST_F(SamplerTest, SamplerPipesTest)
586 {
587 auto *sp = Sampler::Create();
588 ASSERT_NE(sp, nullptr);
589 sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename));
590
591 ASSERT_NE(ExtractPipes(sp)[ThreadCommunicator::PIPE_READ_ID], 0);
592 ASSERT_NE(ExtractPipes(sp)[ThreadCommunicator::PIPE_WRITE_ID], 0);
593
594 sp->Stop();
595 Sampler::Destroy(sp);
596 }
597
TEST_F(SamplerTest,InspectorSamplerPipesTest)598 TEST_F(SamplerTest, InspectorSamplerPipesTest)
599 {
600 auto *sp = Sampler::Create();
601 ASSERT_NE(sp, nullptr);
602 sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD));
603
604 ASSERT_NE(ExtractPipes(sp)[ThreadCommunicator::PIPE_READ_ID], 0);
605 ASSERT_NE(ExtractPipes(sp)[ThreadCommunicator::PIPE_WRITE_ID], 0);
606
607 sp->Stop();
608 Sampler::Destroy(sp);
609 }
610
611 // Stress testing restart
TEST_F(SamplerTest,ProfilerRestartStressTest)612 TEST_F(SamplerTest, ProfilerRestartStressTest)
613 {
614 constexpr size_t CURRENT_TEST_THRESHOLD = TEST_CYCLE_THRESHOLD / 10;
615 auto *sp = Sampler::Create();
616 ASSERT_NE(sp, nullptr);
617
618 for (uint32_t i = 0; i < CURRENT_TEST_THRESHOLD; i++) {
619 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(g_profilerFilename)), true);
620 sp->Stop();
621 }
622
623 Sampler::Destroy(sp);
624 }
625
TEST_F(SamplerTest,InspectorProfilerRestartStressTest)626 TEST_F(SamplerTest, InspectorProfilerRestartStressTest)
627 {
628 constexpr size_t CURRENT_TEST_THRESHOLD = TEST_CYCLE_THRESHOLD / 10;
629 auto *sp = Sampler::Create();
630 ASSERT_NE(sp, nullptr);
631
632 for (uint32_t i = 0; i < CURRENT_TEST_THRESHOLD; i++) {
633 ASSERT_EQ(sp->Start(std::make_unique<InspectorStreamWriter>(G_SAMPLES_RECORD)), true);
634 sp->Stop();
635 }
636
637 Sampler::Destroy(sp);
638 }
639
TEST_F(SamplerTest,ThreadCommunicatorTest)640 TEST_F(SamplerTest, ThreadCommunicatorTest)
641 {
642 ThreadCommunicator communicator;
643
644 SampleInfo sampleInput;
645 SampleInfo sampleOutput;
646 FullfillFakeSample(&sampleInput);
647 ASSERT_TRUE(communicator.Init());
648 ASSERT_TRUE(communicator.SendSample(sampleInput));
649 ASSERT_TRUE(communicator.ReadSample(&sampleOutput));
650 ASSERT_EQ(sampleOutput, sampleInput);
651 }
652
CommunicatorStressWritterThread(const ThreadCommunicator * com,const SampleInfo & sample,uint32_t messagesAmount)653 static void CommunicatorStressWritterThread(const ThreadCommunicator *com, const SampleInfo &sample,
654 uint32_t messagesAmount)
655 {
656 for (uint32_t i = 0; i < messagesAmount; ++i) {
657 // If the sample write failed we retrying to send it
658 if (!com->SendSample(sample)) {
659 std::cerr << "Failed to send a sample" << std::endl;
660 Runtime::Abort();
661 }
662 }
663 }
664
665 static constexpr uint32_t MESSAGES_AMOUNT = TEST_CYCLE_THRESHOLD * 100;
666
TEST_F(SamplerTest,ThreadCommunicatorMultithreadTest)667 TEST_F(SamplerTest, ThreadCommunicatorMultithreadTest)
668 {
669 ThreadCommunicator communicator;
670 SampleInfo sampleOutput;
671 SampleInfo sampleInput;
672 FullfillFakeSample(&sampleInput);
673 ASSERT_TRUE(communicator.Init());
674
675 std::thread sender(CommunicatorStressWritterThread, &communicator, sampleInput, MESSAGES_AMOUNT);
676 for (uint32_t i = 0; i < MESSAGES_AMOUNT; ++i) {
677 // If the sample write failed we retrying to send it
678 if (!communicator.ReadSample(&sampleOutput)) {
679 std::cerr << "Failed to read a sample" << std::endl;
680 Runtime::Abort();
681 }
682 ASSERT_EQ(sampleOutput, sampleInput);
683 }
684 sender.join();
685 }
686
687 // Testing reader and writer by writing and reading from .aspt one sample
TEST_F(SamplerTest,StreamWriterReaderTest)688 TEST_F(SamplerTest, StreamWriterReaderTest)
689 {
690 const char *streamTestFilename = "stream_writer_reader_test.aspt";
691 SampleInfo sampleOutput;
692 SampleInfo sampleInput;
693
694 {
695 FileStreamWriter writer(streamTestFilename);
696 FullfillFakeSample(&sampleInput);
697
698 writer.WriteSample(sampleInput);
699 }
700
701 SampleReader reader(streamTestFilename);
702 ASSERT_TRUE(reader.GetNextSample(&sampleOutput));
703 ASSERT_EQ(sampleOutput, sampleInput);
704 ASSERT_FALSE(reader.GetNextSample(&sampleOutput));
705 ASSERT_FALSE(reader.GetNextModule(nullptr));
706 }
707
TEST_F(SamplerTest,InspectorStreamWriterReaderTest)708 TEST_F(SamplerTest, InspectorStreamWriterReaderTest)
709 {
710 const std::shared_ptr<SamplesRecord> samplesRecord = std::make_shared<SamplesRecord>();
711 SampleInfo sampleInput;
712
713 {
714 InspectorStreamWriter writer(samplesRecord);
715 FullfillFakeSample(&sampleInput);
716
717 writer.WriteSample(sampleInput);
718 }
719
720 auto allThreadsProfileInfos = samplesRecord->GetAllThreadsProfileInfos();
721 ASSERT_NE(allThreadsProfileInfos, nullptr);
722 ASSERT_EQ(allThreadsProfileInfos->size(), 1);
723 ASSERT_NE((*allThreadsProfileInfos)[0]->nodeCount, 0);
724 ASSERT_EQ((*allThreadsProfileInfos)[0]->startTime, 0);
725 ASSERT_EQ((*allThreadsProfileInfos)[0]->stopTime, 0);
726 ASSERT_EQ((*allThreadsProfileInfos)[0]->tid, GetThreadId());
727 }
728
729 // Testing reader and writer by writing and reading from .aspt lots of samples
TEST_F(SamplerTest,StreamWriterReaderLotsSamplesTest)730 TEST_F(SamplerTest, StreamWriterReaderLotsSamplesTest)
731 {
732 constexpr size_t CURRENT_TEST_THRESHOLD = TEST_CYCLE_THRESHOLD * 100;
733 const char *streamTestFilename = "stream_writer_reader_test_lots_samples.aspt";
734 SampleInfo sampleOutput;
735 SampleInfo sampleInput;
736
737 {
738 FileStreamWriter writer(streamTestFilename);
739 FullfillFakeSample(&sampleInput);
740
741 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
742 writer.WriteSample(sampleInput);
743 }
744 }
745
746 SampleReader reader(streamTestFilename);
747 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
748 ASSERT_TRUE(reader.GetNextSample(&sampleOutput));
749 ASSERT_EQ(sampleOutput, sampleInput);
750 }
751 ASSERT_FALSE(reader.GetNextSample(&sampleOutput));
752 ASSERT_FALSE(reader.GetNextModule(nullptr));
753 }
754
TEST_F(SamplerTest,InspectorStreamWriterReaderLotsSamplesTest)755 TEST_F(SamplerTest, InspectorStreamWriterReaderLotsSamplesTest)
756 {
757 constexpr size_t CURRENT_TEST_THRESHOLD = TEST_CYCLE_THRESHOLD * 100;
758 const std::shared_ptr<SamplesRecord> samplesRecord = std::make_shared<SamplesRecord>();
759 SampleInfo sampleInput;
760
761 {
762 InspectorStreamWriter writer(samplesRecord);
763 FullfillFakeSample(&sampleInput);
764
765 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
766 writer.WriteSample(sampleInput);
767 }
768 }
769
770 auto allThreadsProfileInfos = samplesRecord->GetAllThreadsProfileInfos();
771 ASSERT_NE(allThreadsProfileInfos, nullptr);
772 ASSERT_EQ(allThreadsProfileInfos->size(), 1);
773 ASSERT_NE((*allThreadsProfileInfos)[0]->nodeCount, 0);
774 ASSERT_EQ((*allThreadsProfileInfos)[0]->startTime, 0);
775 ASSERT_EQ((*allThreadsProfileInfos)[0]->stopTime, 0);
776 ASSERT_EQ((*allThreadsProfileInfos)[0]->tid, GetThreadId());
777 ASSERT_EQ((*allThreadsProfileInfos)[0]->timeDeltas.size(), CURRENT_TEST_THRESHOLD);
778 }
779
780 // Testing reader and writer by writing and reading from .aspt one module
TEST_F(SamplerTest,ModuleWriterReaderTest)781 TEST_F(SamplerTest, ModuleWriterReaderTest)
782 {
783 const char *streamTestFilename = "stream_module_test_filename.aspt";
784 FileInfo moduleInput = {pfId_, checksum_, "~/folder/folder/lib/panda_file.pa"};
785 FileInfo moduleOutput = {};
786
787 {
788 FileStreamWriter writer(streamTestFilename);
789 writer.WriteModule(moduleInput);
790 }
791
792 SampleReader reader(streamTestFilename);
793 ASSERT_TRUE(reader.GetNextModule(&moduleOutput));
794 ASSERT_EQ(moduleOutput, moduleInput);
795 ASSERT_FALSE(reader.GetNextModule(&moduleOutput));
796 ASSERT_FALSE(reader.GetNextSample(nullptr));
797 }
798
799 // Testing reader and writer by writing and reading from .aspt lots of modules
TEST_F(SamplerTest,ModuleWriterReaderLotsModulesTest)800 TEST_F(SamplerTest, ModuleWriterReaderLotsModulesTest)
801 {
802 constexpr size_t CURRENT_TEST_THRESHOLD = TEST_CYCLE_THRESHOLD * 100;
803 const char *streamTestFilename = "stream_lots_modules_test_filename.aspt";
804 FileInfo moduleInput = {pfId_, checksum_, "~/folder/folder/lib/panda_file.pa"};
805 FileInfo moduleOutput = {};
806
807 {
808 FileStreamWriter writer(streamTestFilename);
809 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
810 writer.WriteModule(moduleInput);
811 }
812 }
813
814 SampleReader reader(streamTestFilename);
815 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
816 ASSERT_TRUE(reader.GetNextModule(&moduleOutput));
817 ASSERT_EQ(moduleOutput, moduleInput);
818 }
819 ASSERT_FALSE(reader.GetNextModule(&moduleOutput));
820 ASSERT_FALSE(reader.GetNextSample(nullptr));
821 }
822
823 // Testing reader and writer by writing and reading from .aspt lots of modules
TEST_F(SamplerTest,WriterReaderLotsRowsModulesAndSamplesTest)824 TEST_F(SamplerTest, WriterReaderLotsRowsModulesAndSamplesTest)
825 {
826 constexpr size_t CURRENT_TEST_THRESHOLD = TEST_CYCLE_THRESHOLD * 100;
827 const char *streamTestFilename = "stream_lots_modules_and_samples_test_filename.aspt";
828 FileInfo moduleInput = {pfId_, checksum_, "~/folder/folder/lib/panda_file.pa"};
829 FileInfo moduleOutput = {};
830 SampleInfo sampleOutput;
831 SampleInfo sampleInput;
832
833 {
834 FileStreamWriter writer(streamTestFilename);
835 FullfillFakeSample(&sampleInput);
836 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
837 writer.WriteModule(moduleInput);
838 writer.WriteSample(sampleInput);
839 }
840 }
841
842 SampleReader reader(streamTestFilename);
843 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
844 ASSERT_TRUE(reader.GetNextModule(&moduleOutput));
845 ASSERT_EQ(moduleOutput, moduleInput);
846 }
847
848 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
849 ASSERT_TRUE(reader.GetNextSample(&sampleOutput));
850 ASSERT_EQ(sampleOutput, sampleInput);
851 }
852
853 ASSERT_FALSE(reader.GetNextModule(&moduleOutput));
854 ASSERT_FALSE(reader.GetNextSample(&sampleOutput));
855 }
856
857 // Send sample to listener and check it inside the file
TEST_F(SamplerTest,ListenerWriteFakeSampleTest)858 TEST_F(SamplerTest, ListenerWriteFakeSampleTest)
859 {
860 const char *streamTestFilename = "listener_write_fake_sample_test.aspt";
861 auto *sp = Sampler::Create();
862 ASSERT_NE(sp, nullptr);
863 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(streamTestFilename)), true);
864
865 SampleInfo sampleOutput;
866 SampleInfo sampleInput;
867 FullfillFakeSample(&sampleInput);
868 sp->GetCommunicator().SendSample(sampleInput);
869 sp->Stop();
870
871 bool status = true;
872 bool isPassed = false;
873 SampleReader reader(streamTestFilename);
874 while (status) {
875 status = reader.GetNextSample(&sampleOutput);
876 if (sampleOutput == sampleInput) {
877 isPassed = true;
878 break;
879 }
880 }
881
882 ASSERT_TRUE(isPassed);
883
884 Sampler::Destroy(sp);
885 }
886
887 // Send lots of sample to listener and check it inside the file
TEST_F(SamplerTest,ListenerWriteLotsFakeSampleTest)888 TEST_F(SamplerTest, ListenerWriteLotsFakeSampleTest)
889 {
890 constexpr size_t CURRENT_TEST_THRESHOLD = TEST_CYCLE_THRESHOLD * 100;
891 const char *streamTestFilename = "listener_write_lots_fake_sample_test.aspt";
892 auto *sp = Sampler::Create();
893 ASSERT_NE(sp, nullptr);
894 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(streamTestFilename)), true);
895
896 SampleInfo sampleOutput;
897 SampleInfo sampleInput;
898 size_t sentSamplesCounter = 0;
899 FullfillFakeSample(&sampleInput);
900 for (size_t i = 0; i < CURRENT_TEST_THRESHOLD; ++i) {
901 if (sp->GetCommunicator().SendSample(sampleInput)) {
902 ++sentSamplesCounter;
903 }
904 }
905 sp->Stop();
906
907 bool status = true;
908 size_t amountOfSamples = 0;
909 SampleReader reader(streamTestFilename);
910 while (status) {
911 if (sampleOutput == sampleInput) {
912 ++amountOfSamples;
913 }
914 status = reader.GetNextSample(&sampleOutput);
915 }
916
917 ASSERT_EQ(amountOfSamples, sentSamplesCounter);
918
919 Sampler::Destroy(sp);
920 }
921
922 // Checking that sampler collect panda files correctly
TEST_F(SamplerTest,CollectPandaFilesTest)923 TEST_F(SamplerTest, CollectPandaFilesTest)
924 {
925 const char *streamTestFilename = "collect_panda_file_test.aspt";
926 auto *sp = Sampler::Create();
927 ASSERT_NE(sp, nullptr);
928 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(streamTestFilename)), true);
929 sp->Stop();
930
931 FileInfo moduleInfo;
932 SampleReader reader(streamTestFilename);
933 bool status = false;
934 while (reader.GetNextModule(&moduleInfo)) {
935 auto pfPtr = reinterpret_cast<panda_file::File *>(moduleInfo.ptr);
936 ASSERT_EQ(pfPtr->GetFullFileName(), moduleInfo.pathname);
937 status = true;
938 }
939 ASSERT_TRUE(status);
940 Sampler::Destroy(sp);
941 }
942
943 // Checking that sampler collect panda files correctly
TEST_F(SamplerTest,WriteModuleEventTest)944 TEST_F(SamplerTest, WriteModuleEventTest)
945 {
946 const char *streamTestFilename = "collect_panda_file_test.aspt";
947 auto *sp = Sampler::Create();
948 ASSERT_NE(sp, nullptr);
949 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(streamTestFilename)), true);
950
951 auto execPath = ark::os::file::File::GetExecutablePath();
952 std::string pandafile =
953 execPath.Value() + Separator() + ".." + Separator() + "pandastdlib" + Separator() + "arkstdlib.abc";
954
955 auto pf = panda_file::OpenPandaFileOrZip(pandafile);
956 Runtime::GetCurrent()->GetClassLinker()->AddPandaFile(std::move(pf));
957 // NOTE:
958 // It's necessary to add assert that only one module is loaded
959 // But simply add assert on loaded size is UB, because such assert may fail if the WriteLoadedPandaFiles
960 // managed to dump the loaded module into the trace before the assert call occurs in this test
961
962 sp->Stop();
963
964 FileInfo moduleInfo;
965 SampleReader reader(streamTestFilename);
966 bool status = false;
967 while (reader.GetNextModule(&moduleInfo)) {
968 auto pfPtr = reinterpret_cast<panda_file::File *>(moduleInfo.ptr);
969 ASSERT_EQ(pfPtr->GetFullFileName(), moduleInfo.pathname);
970 status = true;
971 }
972 ASSERT_TRUE(status);
973
974 Sampler::Destroy(sp);
975 }
976
977 // Sampling big pandasm program and convert it
TEST_F(SamplerTest,ProfilerSamplerSignalHandlerTest)978 TEST_F(SamplerTest, ProfilerSamplerSignalHandlerTest)
979 {
980 const char *streamTestFilename = "sampler_signal_handler_test.aspt";
981 const char *resultTestFilename = "sampler_signal_handler_test.csv";
982 size_t sampleCounter = 0;
983
984 {
985 auto *sp = Sampler::Create();
986 ASSERT_NE(sp, nullptr);
987 ASSERT_EQ(sp->Start(std::make_unique<FileStreamWriter>(streamTestFilename)), true);
988
989 {
990 ASSERT_TRUE(Runtime::GetCurrent()->Execute("_GLOBAL::main", {}));
991 }
992 sp->Stop();
993
994 SampleInfo sample;
995 SampleReader reader(streamTestFilename);
996 bool isFind = false;
997
998 while (reader.GetNextSample(&sample)) {
999 ++sampleCounter;
1000 if (sample.stackInfo.managedStackSize == 2U) {
1001 isFind = true;
1002 continue;
1003 }
1004 ASSERT_NE(sample.stackInfo.managedStackSize, 0);
1005 ASSERT_EQ(GetThreadId(), sample.threadInfo.threadId);
1006 }
1007
1008 ASSERT_EQ(isFind, true);
1009
1010 Sampler::Destroy(sp);
1011 }
1012
1013 // Checking converter
1014 {
1015 AsptConverter conv(streamTestFilename);
1016 ASSERT_EQ(conv.CollectTracesStats(), sampleCounter);
1017 ASSERT_TRUE(conv.CollectModules());
1018 ASSERT_TRUE(conv.DumpResolvedTracesAsCSV(resultTestFilename));
1019 }
1020 }
1021
1022 } // namespace ark::tooling::sampler::test
1023