1/*
2 * Copyright (c) 2021-2024 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 "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
17
18#include <atomic>
19#include <chrono>
20#include <climits>
21#include <fstream>
22
23#include "ecmascript/compiler/aot_file/aot_file_manager.h"
24#include "ecmascript/jspandafile/js_pandafile_manager.h"
25#include "ecmascript/platform/ffrt.h"
26
27#if defined(ENABLE_FFRT_INTERFACES)
28#include "c/executor_task.h"
29#endif
30
31#if !defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
32    #error "ECMASCRIPT_SUPPORT_CPUPROFILER not defined"
33#endif
34
35namespace panda::ecmascript {
36Mutex CpuProfiler::synchronizationMutex_;
37CMap<pthread_t, struct TaskInfo> CpuProfiler::profilerMap_ = CMap<pthread_t, struct TaskInfo>();
38CpuProfiler::CpuProfiler(const EcmaVM *vm, const int interval) : vm_(vm), interval_(interval)
39{
40    enableVMTag_ = const_cast<EcmaVM *>(vm)->GetJSOptions().EnableCpuProfilerVMTag();
41    generator_ = new SamplesRecord();
42    generator_->SetEnableVMTag(enableVMTag_);
43    generator_->SetSourceMapTranslateCallback(vm->GetSourceMapTranslateCallback());
44    generator_->NodeInit();
45    if (generator_->SemInit(0, 0, 0) != 0) {
46        LOG_ECMA(ERROR) << "sem_[0] init failed";
47    }
48    if (generator_->SemInit(1, 0, 0) != 0) {
49        LOG_ECMA(ERROR) << "sem_[1] init failed";
50    }
51    if (generator_->SemInit(2, 0, 0) != 0) { // 2: signal 2
52        LOG_ECMA(ERROR) << "sem_[2] init failed";
53    }
54}
55
56bool CpuProfiler::RegisterGetStackSignal()
57{
58    struct sigaction sa;
59    sa.sa_sigaction = &GetStackSignalHandler;
60    if (sigemptyset(&sa.sa_mask) != 0) {
61        LOG_ECMA(ERROR) << "CpuProfiler::RegisterGetStackSignal, sigemptyset failed, errno = " << errno;
62        return false;
63    }
64    sa.sa_flags = SA_RESTART | SA_SIGINFO;
65    if (sigaction(SIGPROF, &sa, nullptr) != 0) {
66        LOG_ECMA(ERROR) << "CpuProfiler::RegisterGetStackSignal, sigaction failed, errno = " << errno;
67        return false;
68    }
69    return true;
70}
71
72bool CpuProfiler::StartCpuProfilerForInfo()
73{
74    LOG_ECMA(INFO) << "CpuProfiler::StartCpuProfilerForInfo, sampling interval = " << interval_;
75    if (isProfiling_) {
76        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForInfo, can not start when CpuProfiler is Profiling";
77        return false;
78    }
79    if (!RegisterGetStackSignal()) {
80        return false;
81    }
82    // when the ffrt is enabled for the thread, the tid_ will be task id
83    tid_ = static_cast<pthread_t>(JSThread::GetCurrentThreadId());
84    void *taskHandle = nullptr;
85    // when the ffrt is enabled for the thread,
86    // we record the task handle, which can be used to obtain the running thread id in the task
87#if defined(ENABLE_FFRT_INTERFACES)
88    taskHandle = ffrt_get_cur_task();
89    TaskInfo taskInfo { vm_, taskHandle };
90#else
91    TaskInfo taskInfo { vm_, nullptr };
92#endif // defined(ENABLE_FFRT_INTERFACES)
93    {
94        LockHolder lock(synchronizationMutex_);
95        profilerMap_[tid_] = taskInfo;
96    }
97
98    JSPandaFileManager *pandaFileManager = JSPandaFileManager::GetInstance();
99    pandaFileManager->EnumerateJSPandaFiles([&](const std::shared_ptr<JSPandaFile> &file) -> bool {
100        pandaFileManager->CpuProfilerGetJSPtExtractor(file.get());
101        return true;
102    });
103
104    generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT);
105    generator_->SetIsStart(true);
106    uint64_t startTime = SamplingProcessor::GetMicrosecondsTimeStamp();
107    generator_->SetThreadStartTime(startTime);
108    params_ = new RunParams(generator_, static_cast<uint32_t>(interval_), pthread_self(), taskHandle);
109    if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params_) != 0) {
110        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForInfo, pthread_create failed, errno = " << errno;
111        return false;
112    }
113    isProfiling_ = true;
114    vm_->GetJSThread()->SetIsProfiling(true);
115    outToFile_ = false;
116    return true;
117}
118
119bool CpuProfiler::StartCpuProfilerForFile(const std::string &fileName)
120{
121    LOG_ECMA(INFO) << "CpuProfiler::StartCpuProfilerForFile, sampling interval = " << interval_;
122    if (isProfiling_) {
123        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForFile, can not start when CpuProfiler is Profiling";
124        return false;
125    }
126    std::string absoluteFilePath("");
127    if (!CheckFileName(fileName, absoluteFilePath)) {
128        return false;
129    }
130    fileName_ = absoluteFilePath;
131    generator_->SetFileName(fileName_);
132    generator_->fileHandle_.open(fileName_.c_str());
133    if (generator_->fileHandle_.fail()) {
134        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForFile, fileHandle_ open failed";
135        return false;
136    }
137    if (!RegisterGetStackSignal()) {
138        return false;
139    }
140    // when the ffrt is enabled for the thread, the tid_ will be task id
141    tid_ = static_cast<pthread_t>(JSThread::GetCurrentThreadId());
142    void *taskHandle = nullptr;
143    // when the ffrt is enabled for the thread,
144    // we record the task handle, which can be used to obtain the running thread id in the task
145#if defined(ENABLE_FFRT_INTERFACES)
146    taskHandle = ffrt_get_cur_task();
147    TaskInfo taskInfo { vm_, taskHandle };
148#else
149    TaskInfo taskInfo { vm_, nullptr };
150#endif // defined(ENABLE_FFRT_INTERFACES)
151    {
152        LockHolder lock(synchronizationMutex_);
153        profilerMap_[tid_] = taskInfo;
154    }
155
156    JSPandaFileManager *pandaFileManager = JSPandaFileManager::GetInstance();
157    pandaFileManager->EnumerateJSPandaFiles([&](const std::shared_ptr<JSPandaFile> &file) -> bool {
158        pandaFileManager->CpuProfilerGetJSPtExtractor(file.get());
159        return true;
160    });
161
162    generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT);
163    generator_->SetIsStart(true);
164    uint64_t startTime = SamplingProcessor::GetMicrosecondsTimeStamp();
165    generator_->SetThreadStartTime(startTime);
166    params_ = new RunParams(generator_, static_cast<uint32_t>(interval_), pthread_self(), taskHandle);
167    if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params_) != 0) {
168        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForFile, pthread_create failed, errno = " << errno;
169        return false;
170    }
171    isProfiling_ = true;
172    vm_->GetJSThread()->SetIsProfiling(true);
173    outToFile_ = true;
174    return true;
175}
176
177bool CpuProfiler::StopCpuProfilerForInfo(std::unique_ptr<struct ProfileInfo> &profileInfo)
178{
179    LOG_ECMA(INFO) << "CpuProfiler::StopCpuProfilerForInfo enter";
180    if (!isProfiling_) {
181        LOG_ECMA(WARN) << "CpuProfiler::StopCpuProfilerForInfo, not isProfiling_";
182        return true;
183    }
184    if (outToFile_) {
185        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForInfo, is outToFile_";
186        return false;
187    }
188    generator_->SetIsStart(false);
189    if (generator_->SemPost(0) != 0) {
190        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForInfo, sem_[0] post failed, errno = " << errno;
191        return false;
192    }
193    if (generator_->SemWait(1) != 0) {
194        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForInfo, sem_[1] wait failed, errno = " << errno;
195        return false;
196    }
197    isProfiling_ = false;
198    vm_->GetJSThread()->SetIsProfiling(false);
199    profileInfo = generator_->GetProfileInfo();
200    return true;
201}
202
203void CpuProfiler::SetCpuSamplingInterval(int interval)
204{
205    interval_ = static_cast<uint32_t>(interval);
206}
207
208bool CpuProfiler::StopCpuProfilerForFile()
209{
210    LOG_ECMA(INFO) << "CpuProfiler::StopCpuProfilerForFile enter";
211    if (!isProfiling_) {
212        LOG_ECMA(WARN) << "CpuProfiler::StopCpuProfilerForFile, not isProfiling_";
213        return true;
214    }
215    if (!outToFile_) {
216        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForFile, not outToFile_";
217        return false;
218    }
219    generator_->SetIsStart(false);
220    if (generator_->SemPost(0) != 0) {
221        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForFile, sem_[0] post failed, errno = " << errno;
222        return false;
223    }
224    if (generator_->SemWait(1) != 0) {
225        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForFile, sem_[1] wait failed, errno = " << errno;
226        return false;
227    }
228    isProfiling_ = false;
229    vm_->GetJSThread()->SetIsProfiling(false);
230    generator_->StringifySampleData();
231    std::string fileData = generator_->GetSampleData();
232    generator_->fileHandle_ << fileData;
233    return true;
234}
235
236CpuProfiler::~CpuProfiler()
237{
238    if (generator_->SemDestroy(0) != 0) {
239        LOG_ECMA(ERROR) << "sem_[0] destroy failed";
240    }
241    if (generator_->SemDestroy(1) != 0) {
242        LOG_ECMA(ERROR) << "sem_[1] destroy failed";
243    }
244    if (generator_->SemDestroy(2) != 0) { // 2: signal 2
245        LOG_ECMA(ERROR) << "sem_[2] destroy failed";
246    }
247    if (generator_ != nullptr) {
248        delete generator_;
249        generator_ = nullptr;
250    }
251    if (params_ != nullptr) {
252        delete params_;
253        params_ = nullptr;
254    }
255}
256
257void CpuProfiler::GetStack(FrameIterator &it)
258{
259    const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
260    bool topFrame = true;
261    generator_->ResetFrameLength();
262    for (; !it.Done(); it.Advance<>()) {
263        auto method = it.CheckAndGetMethod();
264        if (method == nullptr || !JSTaggedValue(method).IsMethod()) {
265            continue;
266        }
267        bool isNative = method->IsNativeWithCallField();
268        struct MethodKey methodKey;
269        methodKey.deoptType = method->GetDeoptType();
270        if (topFrame) {
271            methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, true, enableVMTag_);
272            topFrame = false;
273        } else {
274            methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, false, enableVMTag_);
275        }
276        if (isNative) {
277            JsStackGetter::GetCallLineNumber(it, methodKey.lineNumber);
278        }
279        void *methodIdentifier = JsStackGetter::GetMethodIdentifier(method, it);
280        if (methodIdentifier == nullptr) {
281            continue;
282        }
283        methodKey.methodIdentifier = methodIdentifier;
284        if (stackInfo.count(methodKey) == 0) {
285            struct FrameInfoTemp codeEntry;
286            if (UNLIKELY(!JsStackGetter::ParseMethodInfo(methodKey, it, vm_, codeEntry, true))) {
287                continue;
288            }
289            if (UNLIKELY(!generator_->PushStackInfo(codeEntry))) {
290                return;
291            }
292        }
293        if (UNLIKELY(!generator_->PushFrameStack(methodKey))) {
294            return;
295        }
296    }
297    generator_->PostFrame();
298}
299
300bool CpuProfiler::GetStackBeforeCallNapi(JSThread *thread)
301{
302    uint64_t tempTimeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
303    if (tempTimeStamp - beforeCallNapiTimeStamp_ < interval_) {
304        return false;
305    }
306
307    if (GetStackCallNapi(thread, true)) {
308        beforeCallNapiTimeStamp_ = tempTimeStamp;
309        return true;
310    }
311    return false;
312}
313
314void CpuProfiler::GetStackAfterCallNapi(JSThread *thread)
315{
316    GetStackCallNapi(thread, false);
317}
318
319bool CpuProfiler::GetStackCallNapi(JSThread *thread, bool beforeCallNapi)
320{
321    [[maybe_unused]] CallNapiScope scope(this);
322    const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
323    generator_->ClearNapiStack();
324    bool topFrame = true;
325    auto currentFrame = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
326    FrameIterator it(currentFrame, thread);
327    if (!beforeCallNapi) {
328        it.Advance<GCVisitedFlag::IGNORED>();
329    }
330    for (; !it.Done(); it.Advance<GCVisitedFlag::IGNORED>()) {
331        auto method = it.CheckAndGetMethod();
332        if (method == nullptr || !JSTaggedValue(method).IsMethod()) {
333            continue;
334        }
335
336        bool isNative = method->IsNativeWithCallField();
337        struct MethodKey methodKey;
338        methodKey.deoptType = method->GetDeoptType();
339        if (topFrame) {
340            if (beforeCallNapi) {
341                methodKey.state = RunningState::NAPI;
342            } else {
343                methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, true, enableVMTag_);
344            }
345            topFrame = false;
346        } else {
347            methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, false, enableVMTag_);
348        }
349        if (isNative) {
350            JsStackGetter::GetCallLineNumber(it, methodKey.lineNumber);
351        }
352        void *methodIdentifier = JsStackGetter::GetMethodIdentifier(method, it);
353        if (methodIdentifier == nullptr) {
354            continue;
355        }
356        methodKey.methodIdentifier = methodIdentifier;
357        if (stackInfo.count(methodKey) == 0) {
358            struct FrameInfoTemp codeEntry;
359            if (UNLIKELY(!JsStackGetter::ParseMethodInfo(methodKey, it, vm_, codeEntry, true))) {
360                continue;
361            }
362            if (UNLIKELY(!generator_->PushNapiStackInfo(codeEntry))) {
363                return false;
364            }
365        }
366        if (UNLIKELY(!generator_->PushNapiFrameStack(methodKey))) {
367            return false;
368        }
369    }
370    generator_->PostNapiFrame();
371    return true;
372}
373
374void CpuProfiler::GetStackSignalHandler(int signal, [[maybe_unused]] siginfo_t *siginfo, void *context)
375{
376    if (signal != SIGPROF) {
377        return;
378    }
379    CpuProfiler *profiler = nullptr;
380    JSThread *thread = nullptr;
381    {
382        LockHolder lock(synchronizationMutex_);
383        // If no task running in this thread, we get the id of the last task that ran in this thread
384        pthread_t tid = static_cast<pthread_t>(GetThreadIdOrCachedTaskId());
385        const EcmaVM *vm = profilerMap_[tid].vm_;
386        if (vm == nullptr) {
387            LOG_ECMA(ERROR) << "CpuProfiler GetStackSignalHandler vm is nullptr";
388            return;
389        }
390        profiler = vm->GetProfiler();
391        thread = vm->GetAssociatedJSThread();
392        if (profiler == nullptr) {
393            LOG_ECMA(ERROR) << "CpuProfiler GetStackSignalHandler profiler is nullptr";
394            return;
395        }
396    }
397    [[maybe_unused]] SignalStateScope scope(thread->GetEcmaVM()->GetJsDebuggerManager());
398
399    if (profiler->GetBuildNapiStack() || thread->GetGcState()) {
400        if (profiler->generator_->SemPost(0) != 0) {
401            LOG_ECMA(ERROR) << "sem_[0] post failed";
402        }
403        return;
404    }
405
406    uint64_t pc = 0;
407    if (thread->IsAsmInterpreter()) {
408        // If the attempt fails, the callback will be terminated directly to avoid the reentrancy deadlock,
409        // and a sampling will be abandoned. Failures are rare, so the impact on the overall sampling results
410        // is very limited.
411        if (!thread->GetEcmaVM()->GetAOTFileManager()->TryReadLock()) {
412            if (profiler->generator_->SemPost(0) != 0) {
413                LOG_ECMA(ERROR) << "sem_[0] post failed";
414            }
415            return;
416        }
417        pc = GetPcFromContext(context);
418    }
419    if (thread->IsAsmInterpreter() && profiler->IsAddrAtStubOrAot(pc) &&
420        !profiler->IsEntryFrameHeaderOrTail(thread, pc)) {
421        [[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
422        [[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
423        [[maybe_unused]] void *fp = nullptr;
424        [[maybe_unused]] void *sp = nullptr;
425#if defined(PANDA_TARGET_AMD64)
426        fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]);
427        sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]);
428#elif defined(PANDA_TARGET_ARM64)
429        fp = reinterpret_cast<void*>(mcontext.regs[29]); // FP is an alias for x29.
430        sp = reinterpret_cast<void*>(mcontext.sp);
431#else
432        LOG_FULL(FATAL) << "AsmInterpreter does not currently support other platforms, please run on x64 and arm64";
433        return;
434#endif
435        if (reinterpret_cast<uint64_t*>(sp) > reinterpret_cast<uint64_t*>(fp)) {
436            LOG_ECMA(ERROR) << "sp > fp, stack frame exception";
437            if (profiler->generator_->SemPost(0) != 0) {
438                LOG_ECMA(ERROR) << "sem_[0] post failed";
439            }
440            return;
441        }
442        if (JsStackGetter::CheckFrameType(thread, reinterpret_cast<JSTaggedType *>(fp))) {
443            FrameIterator it(reinterpret_cast<JSTaggedType *>(fp), thread);
444            profiler->GetStack(it);
445        }
446    } else if (thread->IsAsmInterpreter()) {
447        if (thread->GetLastLeaveFrame() != nullptr) {
448            JSTaggedType *leaveFrame = const_cast<JSTaggedType *>(thread->GetLastLeaveFrame());
449            if (JsStackGetter::CheckFrameType(thread, leaveFrame)) {
450                FrameIterator it(leaveFrame, thread);
451                profiler->GetStack(it);
452            }
453        }
454    } else {
455        if (thread->GetCurrentFrame() != nullptr) {
456            if (JsStackGetter::CheckFrameType(thread, const_cast<JSTaggedType *>(thread->GetCurrentFrame()))) {
457                FrameHandler frameHandler(thread);
458                FrameIterator it(frameHandler.GetSp(), thread);
459                profiler->GetStack(it);
460            }
461        }
462    }
463    if (profiler->generator_->SemPost(0) != 0) {
464        LOG_ECMA(ERROR) << "sem_[0] post failed";
465        return;
466    }
467}
468
469bool CpuProfiler::InHeaderOrTail(uint64_t pc, uint64_t entryBegin, uint64_t entryDuration, uint64_t headerSize,
470                                 uint64_t tailSize) const
471{
472    uintptr_t entryEnd = entryBegin + entryDuration;
473    if (pc >= entryBegin && pc <= (entryBegin + headerSize)) {
474        return true;
475    }
476    if (pc <= entryEnd && pc >= (entryEnd - tailSize)) {
477        return true;
478    }
479    return false;
480}
481
482bool CpuProfiler::IsEntryFrameHeaderOrTail(JSThread *thread, uint64_t pc) const
483{
484    uint64_t headerSize = 0;
485    uint64_t tailSize = 0;
486    uint64_t entryDuration = 0;
487    Assembler::GetFrameCompletionPos(headerSize, tailSize, entryDuration);
488    uintptr_t entryBegin = thread->GetRTInterface(kungfu::RuntimeStubCSigns::ID_AsmInterpreterEntry);
489    bool inAsmInterpreterEntry = InHeaderOrTail(pc, entryBegin, entryDuration, headerSize, tailSize);
490    entryBegin = thread->GetRTInterface(kungfu::RuntimeStubCSigns::ID_GeneratorReEnterAsmInterp);
491    bool inGeneratorReEnterAsmInterp = InHeaderOrTail(pc, entryBegin, entryDuration, headerSize, tailSize);
492    return (inAsmInterpreterEntry || inGeneratorReEnterAsmInterp);
493}
494
495uint64_t CpuProfiler::GetPcFromContext(void *context)
496{
497    [[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
498    [[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
499    uint64_t pc = 0;
500#if defined(PANDA_TARGET_AMD64)
501    pc = static_cast<uint64_t>(mcontext.gregs[REG_RIP]);
502#elif defined(PANDA_TARGET_ARM64)
503    pc = static_cast<uint64_t>(mcontext.pc);
504#else
505    LOG_FULL(FATAL) << "AsmInterpreter does not currently support other platforms, please run on x64 and arm64";
506    pc = 0;
507#endif
508    return pc;
509}
510
511bool CpuProfiler::IsAddrAtStubOrAot(uint64_t pc) const
512{
513    AOTFileManager *loader = vm_->GetAOTFileManager();
514    return loader->InsideStub(pc) || loader->InsideAOT(pc);
515}
516
517bool CpuProfiler::CheckFileName(const std::string &fileName, std::string &absoluteFilePath) const
518{
519    if (fileName.empty()) {
520        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, fileName is empty";
521        return false;
522    }
523
524    if (fileName.size() > PATH_MAX) {
525        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, fileName exceed PATH_MAX";
526        return false;
527    }
528
529    CVector<char> resolvedPath(PATH_MAX);
530    auto result = realpath(fileName.c_str(), resolvedPath.data());
531    if (result == nullptr) {
532        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, realpath fail, errno = " << errno;
533        return false;
534    }
535    std::ofstream file(resolvedPath.data());
536    if (!file.good()) {
537        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, file is not good, errno = " << errno;
538        return false;
539    }
540    file.close();
541    absoluteFilePath = resolvedPath.data();
542    return true;
543}
544
545void CpuProfiler::SetBuildNapiStack(bool flag)
546{
547    isBuildNapiStack_.store(flag);
548}
549
550bool CpuProfiler::GetBuildNapiStack()
551{
552    return isBuildNapiStack_.load();
553}
554
555bool CpuProfiler::GetOutToFile()
556{
557    return outToFile_;
558}
559
560EcmaVM* CpuProfiler::GetVmbyTid(pthread_t tid)
561{
562    LockHolder lock(synchronizationMutex_);
563    return const_cast<EcmaVM *>(profilerMap_[tid].vm_);
564}
565} // namespace panda::ecmascript
566