• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/syscall.h>
17 #include <atomic>
18 
19 #include "coroutines/coroutine_stats.h"
20 #include "libpandabase/macros.h"
21 #include "os/thread.h"
22 #include "runtime/tooling/sampler/sampling_profiler.h"
23 #include "runtime/include/managed_thread.h"
24 #include "runtime/thread_manager.h"
25 #include "runtime/tooling/sampler/stack_walker_base.h"
26 #include "runtime/tooling/pt_thread_info.h"
27 #include "runtime/signal_handler.h"
28 #include "runtime/coroutines/coroutine.h"
29 
30 namespace ark::tooling::sampler {
31 
32 static std::atomic<int> g_sCurrentHandlersCounter = 0;
33 
34 /* static */
35 Sampler *Sampler::instance_ = nullptr;
36 
37 static std::atomic<size_t> g_sLostSamples = 0;
38 static std::atomic<size_t> g_sLostSegvSamples = 0;
39 static std::atomic<size_t> g_sLostInvalidSamples = 0;
40 static std::atomic<size_t> g_sLostNotFindSamples = 0;
41 static std::atomic<size_t> g_sTotalSamples = 0;
42 
43 class ScopedThreadSampling {
44 public:
ScopedThreadSampling(ThreadSamplingInfo * samplingInfo)45     explicit ScopedThreadSampling(ThreadSamplingInfo *samplingInfo) : samplingInfo_(samplingInfo)
46     {
47         ASSERT(samplingInfo_ != nullptr);
48         ASSERT(samplingInfo_->IsThreadSampling() == false);
49         samplingInfo_->SetThreadSampling(true);
50     }
51 
~ScopedThreadSampling()52     ~ScopedThreadSampling()
53     {
54         ASSERT(samplingInfo_->IsThreadSampling() == true);
55         samplingInfo_->SetThreadSampling(false);
56     }
57 
58 private:
59     ThreadSamplingInfo *samplingInfo_;
60 
61     NO_COPY_SEMANTIC(ScopedThreadSampling);
62     NO_MOVE_SEMANTIC(ScopedThreadSampling);
63 };
64 
65 class ScopedHandlersCounting {
66 public:
ScopedHandlersCounting()67     explicit ScopedHandlersCounting()
68     {
69         ++g_sCurrentHandlersCounter;
70     }
71 
~ScopedHandlersCounting()72     ~ScopedHandlersCounting()
73     {
74         --g_sCurrentHandlersCounter;
75     }
76 
77     NO_COPY_SEMANTIC(ScopedHandlersCounting);
78     NO_MOVE_SEMANTIC(ScopedHandlersCounting);
79 };
80 
81 /* static */
Create()82 Sampler *Sampler::Create()
83 {
84     /*
85      * Sampler can be created only once and managed by one thread
86      * Runtime::Tools owns it ptr after it's created
87      */
88     ASSERT(instance_ == nullptr);
89     instance_ = new Sampler;
90 
91     /**
92      * As soon as the sampler is created, we subscribe to the events
93      * This is done so that start and stop do not depend on the runtime
94      * Internal issue #13780
95      */
96     ASSERT(Runtime::GetCurrent() != nullptr);
97 
98     Runtime::GetCurrent()->GetNotificationManager()->AddListener(instance_,
99                                                                  RuntimeNotificationManager::Event::THREAD_EVENTS);
100     Runtime::GetCurrent()->GetNotificationManager()->AddListener(instance_,
101                                                                  RuntimeNotificationManager::Event::LOAD_MODULE);
102     /**
103      * Collect threads and modules which were created before sampler start
104      * If we collect them before add listeners then new thread can be created (or new module can be loaded)
105      * so we will lose this thread (or module)
106      */
107     instance_->CollectThreads();
108     instance_->CollectModules();
109 
110     return Sampler::instance_;
111 }
112 
LogProfilerStats()113 static void LogProfilerStats()
114 {
115     LOG(INFO, PROFILER) << "Total samples: " << g_sTotalSamples;
116     LOG(INFO, PROFILER) << "Lost samples: " << g_sLostSamples;
117     LOG(INFO, PROFILER) << "Lost samples(Invalid method ptr): " << g_sLostInvalidSamples;
118     LOG(INFO, PROFILER) << "Lost samples(Invalid pf ptr): " << g_sLostNotFindSamples;
119     LOG(INFO, PROFILER) << "Lost samples(SIGSEGV occured): " << g_sLostSegvSamples;
120 }
121 
122 /* static */
Destroy(Sampler * sampler)123 void Sampler::Destroy(Sampler *sampler)
124 {
125     ASSERT(instance_ != nullptr);
126     ASSERT(instance_ == sampler);
127 
128     // Atomic with acquire order reason: To ensure start/stop load correctly
129     ASSERT(!sampler->isActive_.load(std::memory_order_acquire));
130 
131     Runtime::GetCurrent()->GetNotificationManager()->RemoveListener(instance_,
132                                                                     RuntimeNotificationManager::Event::THREAD_EVENTS);
133     Runtime::GetCurrent()->GetNotificationManager()->RemoveListener(instance_,
134                                                                     RuntimeNotificationManager::Event::LOAD_MODULE);
135 
136     instance_->ClearManagedThreadSet();
137     instance_->ClearLoadedPfs();
138 
139     delete sampler;
140     instance_ = nullptr;
141 
142     LOG(INFO, PROFILER) << "Sampling profiler destroyed";
143     LogProfilerStats();
144 }
145 
Sampler()146 Sampler::Sampler() : runtime_(Runtime::GetCurrent()), sampleInterval_(DEFAULT_SAMPLE_INTERVAL_US)
147 {
148     ASSERT_NATIVE_CODE();
149 }
150 
AddThreadHandle(ManagedThread * thread)151 void Sampler::AddThreadHandle(ManagedThread *thread)
152 {
153     os::memory::LockHolder holder(managedThreadsLock_);
154     managedThreads_.insert(thread->GetId());
155 }
156 
EraseThreadHandle(ManagedThread * thread)157 void Sampler::EraseThreadHandle(ManagedThread *thread)
158 {
159     os::memory::LockHolder holder(managedThreadsLock_);
160     managedThreads_.erase(thread->GetId());
161 }
162 
ThreadStart(ManagedThread * managedThread)163 void Sampler::ThreadStart(ManagedThread *managedThread)
164 {
165     AddThreadHandle(managedThread);
166 }
167 
ThreadEnd(ManagedThread * managedThread)168 void Sampler::ThreadEnd(ManagedThread *managedThread)
169 {
170     EraseThreadHandle(managedThread);
171 }
172 
LoadModule(std::string_view name)173 void Sampler::LoadModule(std::string_view name)
174 {
175     auto callback = [this, name](const panda_file::File &pf) {
176         if (pf.GetFilename() == name) {
177             auto ptrId = reinterpret_cast<uintptr_t>(&pf);
178             FileInfo pfModule;
179             pfModule.ptr = ptrId;
180             pfModule.pathname = pf.GetFullFileName();
181             pfModule.checksum = pf.GetHeader()->checksum;
182             if (!loadedPfsQueue_.FindValue(ptrId)) {
183                 loadedPfsQueue_.Push(pfModule);
184             }
185             os::memory::LockHolder holder(loadedPfsLock_);
186             this->loadedPfs_.push_back(pfModule);
187             return false;
188         }
189         return true;
190     };
191     runtime_->GetClassLinker()->EnumeratePandaFiles(callback, false);
192 }
193 
Start(std::unique_ptr<StreamWriter> && writer)194 bool Sampler::Start(std::unique_ptr<StreamWriter> &&writer)
195 {
196     // Atomic with acquire order reason: To ensure start/stop load correctly
197     if (isActive_.load(std::memory_order_acquire)) {
198         LOG(ERROR, PROFILER) << "Attemp to start sampling profiler while it's already started";
199         return false;
200     }
201 
202     if (UNLIKELY(!communicator_.Init())) {
203         LOG(ERROR, PROFILER) << "Failed to create pipes for sampling listener. Profiler cannot be started";
204         return false;
205     }
206 
207     // Atomic with release order reason: To ensure start store correctly
208     isActive_.store(true, std::memory_order_release);
209 
210     listenerThread_ = std::make_unique<std::thread>(&Sampler::ListenerThreadEntry, this, std::move(writer));
211     listenerTid_ = listenerThread_->native_handle();
212 
213     // All prepairing actions should be done before this thread is started
214     samplerThread_ = std::make_unique<std::thread>(&Sampler::SamplerThreadEntry, this);
215     samplerTid_ = samplerThread_->native_handle();
216     LOG(INFO, PROFILER) << "Sampling profiler started";
217     return true;
218 }
219 
Stop()220 bool Sampler::Stop()
221 {
222     // Atomic with acquire order reason: To ensure start/stop load correctly
223     if (!isActive_.load(std::memory_order_acquire)) {
224         LOG(ERROR, PROFILER) << "Attemp to stop sampling profiler, but it was not started";
225         return false;
226     }
227     if (!samplerThread_->joinable()) {
228         LOG(FATAL, PROFILER) << "Sampling profiler thread unexpectedly disappeared";
229         UNREACHABLE();
230     }
231     if (!listenerThread_->joinable()) {
232         LOG(FATAL, PROFILER) << "Listener profiler thread unexpectedly disappeared";
233         UNREACHABLE();
234     }
235 
236     // Atomic with release order reason: To ensure stop store correctly
237     isActive_.store(false, std::memory_order_release);
238     samplerThread_->join();
239     listenerThread_->join();
240 
241     // After threads are stopped we can clear all sampler info
242     samplerThread_.reset();
243     listenerThread_.reset();
244     samplerTid_ = 0;
245     listenerTid_ = 0;
246 
247     LOG(INFO, PROFILER) << "Sampling profiler stopped";
248     LogProfilerStats();
249     return true;
250 }
251 
WriteLoadedPandaFiles(StreamWriter * writerPtr)252 void Sampler::WriteLoadedPandaFiles(StreamWriter *writerPtr)
253 {
254     os::memory::LockHolder holder(loadedPfsLock_);
255     if (LIKELY(loadedPfs_.empty())) {
256         return;
257     }
258     for (const auto &module : loadedPfs_) {
259         if (!writerPtr->IsModuleWritten(module)) {
260             writerPtr->WriteModule(module);
261         }
262     }
263     loadedPfs_.clear();
264 }
265 
CollectThreads()266 void Sampler::CollectThreads()
267 {
268     auto tManager = runtime_->GetPandaVM()->GetThreadManager();
269     if (UNLIKELY(tManager == nullptr)) {
270         // NOTE(m.strizhak): make it for languages without thread_manager
271         LOG(FATAL, PROFILER) << "Thread manager is nullptr";
272         UNREACHABLE();
273     }
274 
275     tManager->EnumerateThreads(
276         [this](ManagedThread *thread) {
277             AddThreadHandle(thread);
278             return true;
279         },
280         static_cast<unsigned int>(EnumerationFlag::ALL), static_cast<unsigned int>(EnumerationFlag::VM_THREAD));
281 }
282 
CollectModules()283 void Sampler::CollectModules()
284 {
285     auto callback = [this](const panda_file::File &pf) {
286         auto ptrId = reinterpret_cast<uintptr_t>(&pf);
287         FileInfo pfModule;
288 
289         pfModule.ptr = ptrId;
290         pfModule.pathname = pf.GetFullFileName();
291         pfModule.checksum = pf.GetHeader()->checksum;
292 
293         if (!loadedPfsQueue_.FindValue(ptrId)) {
294             loadedPfsQueue_.Push(pfModule);
295         }
296 
297         os::memory::LockHolder holder(loadedPfsLock_);
298         this->loadedPfs_.push_back(pfModule);
299 
300         return true;
301     };
302     runtime_->GetClassLinker()->EnumeratePandaFiles(callback, false);
303 }
304 
GetThreadStatus(ManagedThread * mthread)305 static SampleInfo::ThreadStatus GetThreadStatus(ManagedThread *mthread)
306 {
307     ASSERT(mthread != nullptr);
308 
309     auto threadStatus = mthread->GetStatus();
310     if (threadStatus == ThreadStatus::RUNNING) {
311         return SampleInfo::ThreadStatus::RUNNING;
312     }
313 
314     bool isCoroutineRunning = false;
315     if (Coroutine::ThreadIsCoroutine(mthread)) {
316         isCoroutineRunning = Coroutine::CastFromThread(mthread)->GetCoroutineStatus() == Coroutine::Status::RUNNING;
317     }
318     if (threadStatus == ThreadStatus::NATIVE && isCoroutineRunning) {
319         return SampleInfo::ThreadStatus::RUNNING;
320     }
321 
322     return SampleInfo::ThreadStatus::SUSPENDED;
323 }
324 
325 struct SamplerFrameInfo {
326     Frame *frame;
327     bool isCompiled;
328 };
329 
330 /**
331  * @brief Collects samples from boundary frames.
332  * @returns true if bypass frame was found, false otherwise.
333  */
CollectBoundaryFrames(SamplerFrameInfo & frameInfo,SampleInfo & sample,size_t & stackCounter)334 static bool CollectBoundaryFrames(SamplerFrameInfo &frameInfo, SampleInfo &sample, size_t &stackCounter)
335 {
336     ASSERT(frameInfo.frame != nullptr);
337 
338     bool isFrameBoundary = true;
339     while (isFrameBoundary) {
340         auto *prevFrame = frameInfo.frame->GetPrevFrame();
341         const auto *method = frameInfo.frame->GetMethod();
342         if (StackWalkerBase::IsMethodInI2CFrame(method)) {
343             sample.stackInfo.managedStack[stackCounter].pandaFilePtr = helpers::ToUnderlying(FrameKind::BRIDGE);
344             sample.stackInfo.managedStack[stackCounter].fileId = helpers::ToUnderlying(FrameKind::BRIDGE);
345             ++stackCounter;
346 
347             frameInfo.frame = prevFrame;
348             frameInfo.isCompiled = false;
349         } else if (StackWalkerBase::IsMethodInC2IFrame(method)) {
350             sample.stackInfo.managedStack[stackCounter].pandaFilePtr = helpers::ToUnderlying(FrameKind::BRIDGE);
351             sample.stackInfo.managedStack[stackCounter].fileId = helpers::ToUnderlying(FrameKind::BRIDGE);
352             ++stackCounter;
353 
354             frameInfo.frame = prevFrame;
355             frameInfo.isCompiled = true;
356         } else if (StackWalkerBase::IsMethodInBPFrame(method)) {
357             g_sLostSamples++;
358             return true;
359         } else {
360             isFrameBoundary = false;
361         }
362     }
363     return false;
364 }
365 
ProcessCompiledTopFrame(SamplerFrameInfo & frameInfo,SampleInfo & sample,size_t & stackCounter,void * signalContextPtr)366 static void ProcessCompiledTopFrame(SamplerFrameInfo &frameInfo, SampleInfo &sample, size_t &stackCounter,
367                                     void *signalContextPtr)
368 {
369     CFrame cframe(frameInfo.frame);
370     if (cframe.IsNative()) {
371         return;
372     }
373 
374     auto signalContext = SignalContext(signalContextPtr);
375     auto fp = signalContext.GetFP();
376     if (fp == nullptr) {
377         sample.stackInfo.managedStack[stackCounter].pandaFilePtr = helpers::ToUnderlying(FrameKind::BRIDGE);
378         sample.stackInfo.managedStack[stackCounter].fileId = helpers::ToUnderlying(FrameKind::BRIDGE);
379         ++stackCounter;
380 
381         // fp is not set yet, so cframe not finished, currently in bridge, previous frame iframe
382         frameInfo.isCompiled = false;
383         return;
384     }
385 
386     auto pc = signalContext.GetPC();
387     bool pcInCompiledCode = InAllocatedCodeRange(pc);
388     if (pcInCompiledCode) {
389         // Currently in compiled method so get it from fp
390         frameInfo.frame = reinterpret_cast<Frame *>(fp);
391     } else {
392         const LockFreeQueue &pfsQueue = Sampler::GetSampleQueuePF();
393         auto pfId = reinterpret_cast<uintptr_t>(frameInfo.frame->GetMethod()->GetPandaFile());
394         if (pfsQueue.FindValue(pfId)) {
395             sample.stackInfo.managedStack[stackCounter].pandaFilePtr = helpers::ToUnderlying(FrameKind::BRIDGE);
396             sample.stackInfo.managedStack[stackCounter].fileId = helpers::ToUnderlying(FrameKind::BRIDGE);
397             ++stackCounter;
398 
399             // pc not in jitted code, so fp is not up-to-date, currently not in cfame
400             frameInfo.isCompiled = false;
401         }
402     }
403 }
404 
405 /**
406  * @brief Walk stack frames and collect samples.
407  * @returns true if invalid frame was encountered, false otherwise.
408  */
CollectFrames(SamplerFrameInfo & frameInfo,SampleInfo & sample,size_t & stackCounter)409 static bool CollectFrames(SamplerFrameInfo &frameInfo, SampleInfo &sample, size_t &stackCounter)
410 {
411     const LockFreeQueue &pfsQueue = Sampler::GetSampleQueuePF();
412     auto stackWalker = StackWalkerBase(frameInfo.frame, frameInfo.isCompiled);
413     while (stackWalker.HasFrame()) {
414         auto *method = stackWalker.GetMethod();
415         if (method == nullptr || IsInvalidPointer(reinterpret_cast<uintptr_t>(method))) {
416             g_sLostSamples++;
417             g_sLostInvalidSamples++;
418             return true;
419         }
420 
421         auto *pf = method->GetPandaFile();
422         auto pfId = reinterpret_cast<uintptr_t>(pf);
423         if (!pfsQueue.FindValue(pfId)) {
424             g_sLostSamples++;
425             g_sLostNotFindSamples++;
426             return true;
427         }
428 
429         sample.stackInfo.managedStack[stackCounter].pandaFilePtr = pfId;
430         sample.stackInfo.managedStack[stackCounter].fileId = method->GetFileId().GetOffset();
431         sample.stackInfo.managedStack[stackCounter].bcOffset = stackWalker.GetBytecodePc();
432         ++stackCounter;
433         stackWalker.NextFrame();
434 
435         if (stackCounter == SampleInfo::StackInfo::MAX_STACK_DEPTH) {
436             // According to the limitations we should drop all frames that is higher than MAX_STACK_DEPTH
437             break;
438         }
439     }
440     sample.timeStamp = Sampler::GetMicrosecondsTimeStamp();
441     return false;
442 }
443 
SigProfSamplingProfilerHandler(int signum,siginfo_t * siginfo,void * ptr)444 void SigProfSamplingProfilerHandler([[maybe_unused]] int signum, [[maybe_unused]] siginfo_t *siginfo,
445                                     [[maybe_unused]] void *ptr)
446 {
447     if (g_sCurrentHandlersCounter == 0) {
448         // Sampling ended if S_CURRENT_HANDLERS_COUNTER is 0. Thread started executing handler for signal
449         // that was sent before end, so thread is late now and we should return from handler
450         return;
451     }
452     auto scopedHandlersCounting = ScopedHandlersCounting();
453 
454     Coroutine *coro = Coroutine::GetCurrent();
455     if (coro != nullptr && coro->GetCoroutineStatus() != Coroutine::Status::RUNNING) {
456         return;
457     }
458 
459     ManagedThread *mthread = coro == nullptr ? ManagedThread::GetCurrent() : coro;
460     ASSERT(mthread != nullptr);
461 
462     // Checking that code is being executed
463     auto *framePtr = reinterpret_cast<CFrame::SlotType *>(mthread->GetCurrentFrame());
464     if (framePtr == nullptr) {
465         return;
466     }
467 
468     g_sTotalSamples++;
469 
470     // Note that optimized variables may end up with incorrect value as a consequence of a longjmp() operation
471     // - see "local variable clobbering and setjmp".
472     // Variables below are not volatile because they are not used after longjmp() is done.
473     SamplerFrameInfo frameInfo {mthread->GetCurrentFrame(), mthread->IsCurrentFrameCompiled()};
474 
475     SampleInfo sample {};
476     // `mthread` is passed as non-const argument into `GetThreadStatus`, so call it before `setjmp`
477     // in order to bypass "variable might be clobbered by ‘longjmp’" compiler warning.
478     sample.threadInfo.threadStatus = GetThreadStatus(mthread);
479     sample.threadInfo.threadId = coro == nullptr ? os::thread::GetCurrentThreadId() : coro->GetCoroutineId();
480     size_t stackCounter = 0;
481 
482     ScopedThreadSampling scopedThreadSampling(mthread->GetPtThreadInfo()->GetSamplingInfo());
483 
484     auto &sigSegvJmpBuf = mthread->GetPtThreadInfo()->GetSamplingInfo()->GetSigSegvJmpEnv();
485     // NOLINTNEXTLINE(cert-err52-cpp)
486     if (setjmp(sigSegvJmpBuf) != 0) {
487         // This code executed after longjmp()
488         // In case of SIGSEGV we lose the sample
489         g_sLostSamples++;
490         g_sLostSegvSamples++;
491         return;
492     }
493 
494     if (StackWalkerBase::IsMethodInBoundaryFrame(frameInfo.frame->GetMethod())) {
495         auto foundBypassFrame = CollectBoundaryFrames(frameInfo, sample, stackCounter);
496         if (foundBypassFrame) {
497             return;
498         }
499     } else if (frameInfo.isCompiled) {
500         ProcessCompiledTopFrame(frameInfo, sample, stackCounter, ptr);
501     }
502 
503     auto lostSample = CollectFrames(frameInfo, sample, stackCounter);
504     if (lostSample) {
505         return;
506     }
507 
508     if (stackCounter == 0) {
509         return;
510     }
511     sample.stackInfo.managedStackSize = stackCounter;
512 
513     const ThreadCommunicator &communicator = Sampler::GetSampleCommunicator();
514     communicator.SendSample(sample);
515 }
516 
SamplerThreadEntry()517 void Sampler::SamplerThreadEntry()
518 {
519     struct sigaction action {};
520     action.sa_sigaction = &SigProfSamplingProfilerHandler;
521     action.sa_flags = SA_SIGINFO | SA_ONSTACK;
522     // Clear signal set
523     sigemptyset(&action.sa_mask);
524     // Ignore incoming sigprof if handler isn't completed
525     sigaddset(&action.sa_mask, SIGPROF);
526 
527     struct sigaction oldAction {};
528 
529     if (sigaction(SIGPROF, &action, &oldAction) == -1) {
530         LOG(FATAL, PROFILER) << "Sigaction failed, can't start profiling";
531         UNREACHABLE();
532     }
533 
534     // We keep handler assigned to SigProfSamplingProfilerHandler after sampling end because
535     // otherwice deadlock can happen if signal will be slow and reach thread after handler resignation
536     if (oldAction.sa_sigaction != nullptr && oldAction.sa_sigaction != SigProfSamplingProfilerHandler) {
537         LOG(FATAL, PROFILER) << "SIGPROF signal handler was overriden in sampling profiler";
538         UNREACHABLE();
539     }
540     ++g_sCurrentHandlersCounter;
541 
542     auto pid = getpid();
543     // Atomic with acquire order reason: To ensure start/stop load correctly
544     while (isActive_.load(std::memory_order_acquire)) {
545         {
546             os::memory::LockHolder holder(managedThreadsLock_);
547             for (const auto &threadId : managedThreads_) {
548                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
549                 if (syscall(SYS_tgkill, pid, threadId, SIGPROF) != 0) {
550                     LOG(ERROR, PROFILER) << "Can't send signal to thread";
551                 }
552             }
553         }
554         os::thread::NativeSleepUS(sampleInterval_);
555     }
556 
557     // Sending last sample on finish to avoid of deadlock in listener
558     SampleInfo lastSample;
559     lastSample.stackInfo.managedStackSize = 0;
560     communicator_.SendSample(lastSample);
561 
562     --g_sCurrentHandlersCounter;
563 
564     const unsigned int timeToSleepMs = 100;
565     do {
566         os::thread::NativeSleep(timeToSleepMs);
567     } while (g_sCurrentHandlersCounter != 0);
568 }
569 
570 // NOLINTNEXTLINE(performance-unnecessary-value-param)
ListenerThreadEntry(std::unique_ptr<StreamWriter> writerPtr)571 void Sampler::ListenerThreadEntry(std::unique_ptr<StreamWriter> writerPtr)
572 {
573     // Writing panda files that were loaded before sampler was created
574     WriteLoadedPandaFiles(writerPtr.get());
575 
576     SampleInfo bufferSample;
577     // Atomic with acquire order reason: To ensure start/stop load correctly
578     while (isActive_.load(std::memory_order_acquire)) {
579         WriteLoadedPandaFiles(writerPtr.get());
580         communicator_.ReadSample(&bufferSample);
581         if (LIKELY(bufferSample.stackInfo.managedStackSize != 0)) {
582             writerPtr->WriteSample(bufferSample);
583         }
584     }
585     // Writing all remaining samples
586     while (!communicator_.IsPipeEmpty()) {
587         WriteLoadedPandaFiles(writerPtr.get());
588         communicator_.ReadSample(&bufferSample);
589         if (LIKELY(bufferSample.stackInfo.managedStackSize != 0)) {
590             writerPtr->WriteSample(bufferSample);
591         }
592     }
593 }
594 
GetMicrosecondsTimeStamp()595 uint64_t Sampler::GetMicrosecondsTimeStamp()
596 {
597     static constexpr int USEC_PER_SEC = 1000 * 1000;
598     static constexpr int NSEC_PER_USEC = 1000;
599     struct timespec time {};
600     clock_gettime(CLOCK_MONOTONIC, &time);
601     return time.tv_sec * USEC_PER_SEC + time.tv_nsec / NSEC_PER_USEC;
602 }
603 
604 }  // namespace ark::tooling::sampler
605