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