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 
35 namespace panda::ecmascript {
36 Mutex CpuProfiler::synchronizationMutex_;
37 CMap<pthread_t, struct TaskInfo> CpuProfiler::profilerMap_ = CMap<pthread_t, struct TaskInfo>();
CpuProfiler(const EcmaVM *vm, const int interval)38 CpuProfiler::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 
RegisterGetStackSignal()56 bool 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 
StartCpuProfilerForInfo()72 bool 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 
StartCpuProfilerForFile(const std::string &fileName)119 bool 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 
StopCpuProfilerForInfo(std::unique_ptr<struct ProfileInfo> &profileInfo)177 bool 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 
SetCpuSamplingInterval(int interval)203 void CpuProfiler::SetCpuSamplingInterval(int interval)
204 {
205     interval_ = static_cast<uint32_t>(interval);
206 }
207 
StopCpuProfilerForFile()208 bool 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 
~CpuProfiler()236 CpuProfiler::~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 
GetStack(FrameIterator &it)257 void 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 
GetStackBeforeCallNapi(JSThread *thread)300 bool 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 
GetStackAfterCallNapi(JSThread *thread)314 void CpuProfiler::GetStackAfterCallNapi(JSThread *thread)
315 {
316     GetStackCallNapi(thread, false);
317 }
318 
GetStackCallNapi(JSThread *thread, bool beforeCallNapi)319 bool 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 
GetStackSignalHandler(int signal, [[maybe_unused]] siginfo_t *siginfo, void *context)374 void 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 
InHeaderOrTail(uint64_t pc, uint64_t entryBegin, uint64_t entryDuration, uint64_t headerSize, uint64_t tailSize) const469 bool 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 
IsEntryFrameHeaderOrTail(JSThread *thread, uint64_t pc) const482 bool 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 
GetPcFromContext(void *context)495 uint64_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 
IsAddrAtStubOrAot(uint64_t pc) const511 bool CpuProfiler::IsAddrAtStubOrAot(uint64_t pc) const
512 {
513     AOTFileManager *loader = vm_->GetAOTFileManager();
514     return loader->InsideStub(pc) || loader->InsideAOT(pc);
515 }
516 
CheckFileName(const std::string &fileName, std::string &absoluteFilePath) const517 bool 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 
SetBuildNapiStack(bool flag)545 void CpuProfiler::SetBuildNapiStack(bool flag)
546 {
547     isBuildNapiStack_.store(flag);
548 }
549 
GetBuildNapiStack()550 bool CpuProfiler::GetBuildNapiStack()
551 {
552     return isBuildNapiStack_.load();
553 }
554 
GetOutToFile()555 bool CpuProfiler::GetOutToFile()
556 {
557     return outToFile_;
558 }
559 
GetVmbyTid(pthread_t tid)560 EcmaVM* CpuProfiler::GetVmbyTid(pthread_t tid)
561 {
562     LockHolder lock(synchronizationMutex_);
563     return const_cast<EcmaVM *>(profilerMap_[tid].vm_);
564 }
565 } // namespace panda::ecmascript
566