• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/assembler/assembler.h"
24 #include "ecmascript/dfx/cpu_profiler/sampling_processor.h"
25 #include "ecmascript/frames.h"
26 #include "ecmascript/aot_file_manager.h"
27 #include "ecmascript/jspandafile/js_pandafile_manager.h"
28 #include "ecmascript/taskpool/taskpool.h"
29 
30 namespace panda::ecmascript {
31 os::memory::Mutex CpuProfiler::synchronizationMutex_;
32 CMap<pthread_t, const EcmaVM *> CpuProfiler::profilerMap_ = CMap<pthread_t, const EcmaVM *>();
CpuProfiler(const EcmaVM * vm,const int interval)33 CpuProfiler::CpuProfiler(const EcmaVM *vm, const int interval) : vm_(vm), interval_(interval)
34 {
35     generator_ = new SamplesRecord();
36     generator_->SetEnableVMTag(const_cast<EcmaVM *>(vm)->GetJSOptions().EnableCpuProfilerVMTag());
37     if (generator_->SemInit(0, 0, 0) != 0) {
38         LOG_ECMA(ERROR) << "sem_[0] init failed";
39     }
40     if (generator_->SemInit(1, 0, 0) != 0) {
41         LOG_ECMA(ERROR) << "sem_[1] init failed";
42     }
43     if (generator_->SemInit(2, 0, 0) != 0) { // 2: signal 2
44         LOG_ECMA(ERROR) << "sem_[2] init failed";
45     }
46 }
47 
StartCpuProfilerForInfo()48 void CpuProfiler::StartCpuProfilerForInfo()
49 {
50     LOG_ECMA(INFO) << "StartCpuProfilerForInfo, Sampling interval is: " << interval_
51                    << " ,napi active record is: " << callNapiGetStack_;
52     if (isProfiling_) {
53         LOG_ECMA(ERROR) << "Can not StartCpuProfilerForInfo when CpuProfiler is Profiling";
54         return;
55     }
56     isProfiling_ = true;
57     struct sigaction sa;
58     sa.sa_sigaction = &GetStackSignalHandler;
59     if (sigemptyset(&sa.sa_mask) != 0) {
60         LOG_ECMA(ERROR) << "Parameter set signal set initialization and emptying failed";
61         isProfiling_ = false;
62         return;
63     }
64     sa.sa_flags = SA_RESTART | SA_SIGINFO;
65     if (sigaction(SIGPROF, &sa, nullptr) != 0) {
66         LOG_ECMA(ERROR) << "sigaction failed to set signal";
67         isProfiling_ = false;
68         return;
69     }
70     tid_ = static_cast<pthread_t>(syscall(SYS_gettid));
71     {
72         os::memory::LockHolder lock(synchronizationMutex_);
73         profilerMap_[tid_] = vm_;
74     }
75     vm_->GetJSThread()->SetCallNapiGetStack(callNapiGetStack_);
76     generator_->SetIsStart(true);
77     Taskpool::GetCurrentTaskpool()->PostTask(
78         std::make_unique<SamplingProcessor>(vm_->GetJSThread()->GetThreadId(), generator_, interval_));
79 }
80 
StartCpuProfilerForFile(const std::string & fileName)81 void CpuProfiler::StartCpuProfilerForFile(const std::string &fileName)
82 {
83     LOG_ECMA(INFO) << "StartCpuProfilerForFile, Sampling interval is: " << interval_
84                    << " ,napi active record is: " << callNapiGetStack_;
85     if (isProfiling_) {
86         LOG_ECMA(ERROR) << "Can not StartCpuProfilerForFile when CpuProfiler is Profiling";
87         return;
88     }
89     isProfiling_ = true;
90     std::string absoluteFilePath("");
91     if (!CheckFileName(fileName, absoluteFilePath)) {
92         LOG_ECMA(ERROR) << "The filename contains illegal characters";
93         isProfiling_ = false;
94         return;
95     }
96     fileName_ = absoluteFilePath;
97     if (fileName_.empty()) {
98         LOG_ECMA(ERROR) << "CpuProfiler filename is empty!";
99         isProfiling_ = false;
100         return;
101     }
102     generator_->SetFileName(fileName_);
103     generator_->fileHandle_.open(fileName_.c_str());
104     if (generator_->fileHandle_.fail()) {
105         LOG_ECMA(ERROR) << "File open failed";
106         isProfiling_ = false;
107         return;
108     }
109     struct sigaction sa;
110     sa.sa_sigaction = &GetStackSignalHandler;
111     if (sigemptyset(&sa.sa_mask) != 0) {
112         LOG_ECMA(ERROR) << "Parameter set signal set initialization and emptying failed";
113         isProfiling_ = false;
114         return;
115     }
116     sa.sa_flags = SA_RESTART | SA_SIGINFO;
117     if (sigaction(SIGPROF, &sa, nullptr) != 0) {
118         LOG_ECMA(ERROR) << "sigaction failed to set signal";
119         isProfiling_ = false;
120         return;
121     }
122     tid_ = static_cast<pthread_t>(syscall(SYS_gettid));
123     {
124         os::memory::LockHolder lock(synchronizationMutex_);
125         profilerMap_[tid_] = vm_;
126     }
127     outToFile_ = true;
128     vm_->GetJSThread()->SetCallNapiGetStack(callNapiGetStack_);
129     generator_->SetIsStart(true);
130     Taskpool::GetCurrentTaskpool()->PostTask(
131         std::make_unique<SamplingProcessor>(vm_->GetJSThread()->GetThreadId(), generator_, interval_));
132 }
133 
StopCpuProfilerForInfo()134 std::unique_ptr<struct ProfileInfo> CpuProfiler::StopCpuProfilerForInfo()
135 {
136     LOG_ECMA(INFO) << "StopCpuProfilerForInfo enter";
137     std::unique_ptr<struct ProfileInfo> profileInfo;
138     if (!isProfiling_) {
139         LOG_ECMA(ERROR) << "Do not execute stop cpuprofiler twice in a row or didn't execute the start\
140                                 or the sampling thread is not started";
141         return profileInfo;
142     }
143     if (outToFile_) {
144         LOG_ECMA(ERROR) << "Can not Stop a CpuProfiler sampling which is for file output by this stop method";
145         return profileInfo;
146     }
147     isProfiling_ = false;
148     vm_->GetJSThread()->SetCallNapiGetStack(false);
149     generator_->SetIsStart(false);
150     generator_->SetIsBreakSampleFlag(true);
151     if (generator_->SemPost(0) != 0) {
152         LOG_ECMA(ERROR) << "sem_[0] post failed";
153         return profileInfo;
154     }
155     if (generator_->SemWait(1) != 0) {
156         LOG_ECMA(ERROR) << "sem_[1] wait failed";
157         return profileInfo;
158     }
159     profileInfo = generator_->GetProfileInfo();
160     return profileInfo;
161 }
162 
SetCpuSamplingInterval(int interval)163 void CpuProfiler::SetCpuSamplingInterval(int interval)
164 {
165     interval_ = static_cast<uint32_t>(interval);
166 }
167 
SetCallNapiGetStack(bool getStack)168 void CpuProfiler::SetCallNapiGetStack(bool getStack)
169 {
170     callNapiGetStack_ = getStack;
171 }
172 
StopCpuProfilerForFile()173 void CpuProfiler::StopCpuProfilerForFile()
174 {
175     LOG_ECMA(INFO) << "StopCpuProfilerForFile enter";
176     if (!isProfiling_) {
177         LOG_ECMA(ERROR) << "Do not execute stop cpuprofiler twice in a row or didn't execute the start\
178                                 or the sampling thread is not started";
179         return;
180     }
181 
182     if (!outToFile_) {
183         LOG_ECMA(ERROR) << "Can not Stop a CpuProfiler sampling which is for return profile info by\
184                                 this stop method";
185         return;
186     }
187 
188     isProfiling_ = false;
189     vm_->GetJSThread()->SetCallNapiGetStack(false);
190     generator_->SetIsStart(false);
191     generator_->SetIsBreakSampleFlag(true);
192     if (generator_->SemPost(0) != 0) {
193         LOG_ECMA(ERROR) << "sem_[0] post failed";
194         return;
195     }
196     if (generator_->SemWait(1) != 0) {
197         LOG_ECMA(ERROR) << "sem_[1] wait failed";
198         return;
199     }
200     generator_->StringifySampleData();
201     std::string fileData = generator_->GetSampleData();
202     generator_->fileHandle_ << fileData;
203 }
204 
~CpuProfiler()205 CpuProfiler::~CpuProfiler()
206 {
207     if (generator_->SemDestroy(0) != 0) {
208         LOG_ECMA(ERROR) << "sem_[0] destroy failed";
209     }
210     if (generator_->SemDestroy(1) != 0) {
211         LOG_ECMA(ERROR) << "sem_[1] destroy failed";
212     }
213     if (generator_->SemDestroy(2) != 0) { // 2: signal 2
214         LOG_ECMA(ERROR) << "sem_[2] destroy failed";
215     }
216     if (generator_ != nullptr) {
217         delete generator_;
218         generator_ = nullptr;
219     }
220 }
221 
SetProfileStart(uint64_t nowTimeStamp)222 void CpuProfiler::SetProfileStart(uint64_t nowTimeStamp)
223 {
224     uint64_t ts = SamplingProcessor::GetMicrosecondsTimeStamp();
225     struct CurrentProcessInfo currentProcessInfo = {0};
226     GetCurrentProcessInfo(currentProcessInfo);
227     std::string data = "";
228     data = "[{\"args\":{\"data\":{\"frames\":[{\"processId\":" + std::to_string(currentProcessInfo.pid) + "}]"
229             + ",\"persistentIds\":true}},\"cat\":\"disabled-by-default-devtools.timeline\","
230             + "\"name\":\"TracingStartedInBrowser\",\"ph\":\"I\",\"pid\":"
231             + std::to_string(currentProcessInfo.pid) + ",\"s\":\"t\",\"tid\":"
232             + std::to_string(currentProcessInfo.tid) + ",\"ts\":"
233             + std::to_string(ts) + ",\"tts\":178460227},\n";
234     ts = SamplingProcessor::GetMicrosecondsTimeStamp();
235     data += "{\"args\":{\"data\":{\"startTime\":" + std::to_string(nowTimeStamp) + "}},"
236             + "\"cat\":\"disabled-by-default-ark.cpu_profiler\",\"id\":\"0x2\","
237             + "\"name\":\"Profile\",\"ph\":\"P\",\"pid\":"
238             + std::to_string(currentProcessInfo.pid) + ",\"tid\":"
239             + std::to_string(currentProcessInfo.tid) + ",\"ts\":"
240             + std::to_string(ts) + ",\"tts\":" + std::to_string(currentProcessInfo.tts)
241             + "},\n";
242     generator_->SetStartsampleData(data);
243 }
244 
GetCurrentProcessInfo(struct CurrentProcessInfo & currentProcessInfo)245 void CpuProfiler::GetCurrentProcessInfo(struct CurrentProcessInfo &currentProcessInfo)
246 {
247     currentProcessInfo.nowTimeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
248     currentProcessInfo.pid = getpid();
249     if (syscall(SYS_gettid) == -1) {
250         LOG_FULL(FATAL) << "syscall failed";
251         UNREACHABLE();
252     }
253     currentProcessInfo.tts = SamplingProcessor::GetMicrosecondsTimeStamp();
254 }
255 
GetRunningState(const FrameIterator & it,const JSPandaFile * jsPandaFile,bool topFrame) const256 RunningState CpuProfiler::GetRunningState(const FrameIterator &it,
257                                           const JSPandaFile *jsPandaFile, bool topFrame) const
258 {
259     JSThread *thread = vm_->GetAssociatedJSThread();
260     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
261 
262     if (topFrame && function->IsCallNapi()) {
263         return RunningState::NAPI;
264     }
265     if (topFrame && thread->GetGcState()) {
266         return RunningState::GC;
267     }
268     // napi method
269     if (function->IsCallNapi()) {
270         return RunningState::NAPI;
271     }
272     JSTaggedValue extraInfoValue = function->GetFunctionExtraInfo();
273     if (extraInfoValue.IsJSNativePointer() || jsPandaFile == nullptr) {
274         if (extraInfoValue.IsJSNativePointer()) {
275             return RunningState::ARKUI_ENGINE;
276         }
277         return RunningState::BUILTIN;
278     }
279     if (it.IsLeaveFrame()) {
280         return RunningState::RUNTIME;
281     }
282     if (it.IsOptimizedJSFunctionFrame()) {
283         return RunningState::AOT;
284     }
285     if (topFrame && thread->GetRuntimeState()) {
286         return RunningState::RUNTIME;
287     }
288     if (thread->IsAsmInterpreter()) {
289         return RunningState::AINT;
290     }
291     return RunningState::CINT;
292 }
293 
GetStack(FrameIterator & it)294 void CpuProfiler::GetStack(FrameIterator &it)
295 {
296     const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
297     bool topFrame = true;
298     generator_->ResetFrameLength();
299     for (; !it.Done(); it.Advance<>()) {
300         auto method = it.CheckAndGetMethod();
301         if (method == nullptr) {
302             continue;
303         }
304         const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
305         struct MethodKey methodKey;
306         methodKey.deoptType = method->GetDeoptType();
307         if (topFrame) {
308             methodKey.state = GetRunningState(it, jsPandaFile, true);
309             topFrame = false;
310         } else {
311             methodKey.state = GetRunningState(it, jsPandaFile, false);
312         }
313         void *methodIdentifier = GetMethodIdentifier(method, it);
314         if (methodIdentifier == nullptr) {
315             generator_->SetIsBreakSampleFlag(true);
316             return;
317         }
318         methodKey.methodIdentifier = methodIdentifier;
319         if (stackInfo.count(methodKey) == 0) {
320             if (UNLIKELY(!ParseMethodInfo(methodKey, it, jsPandaFile, false))) {
321                 generator_->SetIsBreakSampleFlag(true);
322                 return;
323             }
324         }
325         if (UNLIKELY(!generator_->PushFrameStack(methodKey))) {
326             generator_->SetIsBreakSampleFlag(true);
327             return;
328         }
329     }
330     generator_->PostFrame();
331 }
332 
GetStackCallNapi(JSThread * thread,bool beforeCallNapi)333 void CpuProfiler::GetStackCallNapi(JSThread *thread, bool beforeCallNapi)
334 {
335     uint64_t tempTimeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
336     if (beforeCallNapi) {
337         if (tempTimeStamp - beforeCallNapiTimeStamp_ < INTERVAL_OF_ACTIVE_SAMPLING) {
338             beforeCallNapiTimeStamp_ = tempTimeStamp;
339             return;
340         }
341         beforeCallNapiTimeStamp_ = tempTimeStamp;
342     } else {
343         if (tempTimeStamp - beforeCallNapiTimeStamp_ < CPUPROFILER_DEFAULT_INTERVAL) {
344             return;
345         }
346     }
347     [[maybe_unused]] CallNapiScope scope(this);
348     const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
349     generator_->ClearNapiStack();
350     bool topFrame = true;
351     auto currentFrame = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
352     FrameIterator it(currentFrame, thread);
353     if (!beforeCallNapi) {
354         it.Advance<GCVisitedFlag::IGNORED>();
355     }
356     for (; !it.Done(); it.Advance<GCVisitedFlag::IGNORED>()) {
357         auto method = it.CheckAndGetMethod();
358         if (method == nullptr) {
359             continue;
360         }
361 
362         struct MethodKey methodKey;
363         methodKey.deoptType = method->GetDeoptType();
364         if (topFrame) {
365             if (beforeCallNapi) {
366                 methodKey.state = RunningState::NAPI;
367             } else {
368                 methodKey.state = GetRunningState(it, method->GetJSPandaFile(), true);
369             }
370             topFrame = false;
371         } else {
372             methodKey.state = GetRunningState(it, method->GetJSPandaFile(), false);
373         }
374         void *methodIdentifier = GetMethodIdentifier(method, it);
375         if (methodIdentifier == nullptr) {
376             return;
377         }
378         methodKey.methodIdentifier = methodIdentifier;
379         if (stackInfo.count(methodKey) == 0) {
380             if (UNLIKELY(!ParseMethodInfo(methodKey, it, method->GetJSPandaFile(), true))) {
381                 return;
382             }
383         }
384         if (UNLIKELY(!generator_->PushNapiFrameStack(methodKey))) {
385             return;
386         }
387     }
388     generator_->PostNapiFrame();
389 }
390 
ParseMethodInfo(struct MethodKey & methodKey,const FrameIterator & it,const JSPandaFile * jsPandaFile,bool isCallNapi)391 bool CpuProfiler::ParseMethodInfo(struct MethodKey &methodKey,
392                                   const FrameIterator &it,
393                                   const JSPandaFile *jsPandaFile,
394                                   bool isCallNapi)
395 {
396     struct FrameInfoTemp codeEntry;
397     codeEntry.methodKey = methodKey;
398     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
399     JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo();
400     if (extraInfoValue.CheckIsJSNativePointer() || jsPandaFile == nullptr) {
401         if (!CheckAndCopy(codeEntry.codeType, sizeof(codeEntry.codeType), "other")) {
402             return false;
403         }
404         FrameIterator itNext(it.GetSp(), it.GetThread());
405         itNext.Advance<GCVisitedFlag::IGNORED>();
406         GetNativeMethodCallPos(itNext, codeEntry);
407         GetNativeStack(it, codeEntry.functionName, sizeof(codeEntry.functionName));
408     } else {
409         if (!CheckAndCopy(codeEntry.codeType, sizeof(codeEntry.codeType), "JS")) {
410             return false;
411         }
412         const char *tempVariable = MethodLiteral::GetMethodName(jsPandaFile,
413             reinterpret_cast<MethodLiteral *>(methodKey.methodIdentifier)->GetMethodId());
414         uint8_t length = strlen(tempVariable);
415         if (length != 0 && tempVariable[0] == '#') {
416             uint8_t index = length - 1;
417             while (tempVariable[index] != '#') {
418                 index--;
419             }
420             tempVariable += (index + 1);
421         }
422         if (strlen(tempVariable) == 0) {
423             tempVariable = "anonymous";
424         }
425         if (!CheckAndCopy(codeEntry.functionName, sizeof(codeEntry.functionName), tempVariable)) {
426             return false;
427         }
428         // source file
429         DebugInfoExtractor *debugExtractor =
430             JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
431         const std::string &sourceFile =
432             debugExtractor->GetSourceFile(reinterpret_cast<MethodLiteral *>(methodKey.methodIdentifier)->GetMethodId());
433         if (sourceFile.empty()) {
434             tempVariable = "";
435         } else {
436             tempVariable = sourceFile.c_str();
437         }
438         if (!CheckAndCopy(codeEntry.url, sizeof(codeEntry.url), tempVariable)) {
439             return false;
440         }
441         // line number and clomn number
442         panda_file::File::EntityId methodId =
443                                    reinterpret_cast<MethodLiteral *>(methodKey.methodIdentifier)->GetMethodId();
444         codeEntry.lineNumber = debugExtractor->GetFristLine(methodId);
445         codeEntry.columnNumber = debugExtractor->GetFristColumn(methodId);
446     }
447     if (isCallNapi) {
448         return generator_->PushNapiStackInfo(codeEntry);
449     }
450     return generator_->PushStackInfo(codeEntry);
451 }
452 
GetNativeStack(const FrameIterator & it,char * functionName,size_t size)453 void CpuProfiler::GetNativeStack(const FrameIterator &it, char *functionName, size_t size)
454 {
455     std::stringstream stream;
456     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
457     JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo();
458     JSThread *thread = vm_->GetJSThread();
459     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
460     JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
461     JSHandle<JSTaggedValue> func(thread, function);
462     JSHandle<JSTaggedValue> funcNameValue = JSObject::GetProperty(thread, func, nameKey).GetValue();
463     std::string methodNameStr;
464     if (funcNameValue->IsString()) {
465         JSHandle<EcmaString> methodName(funcNameValue);
466         methodNameStr = EcmaStringAccessor(methodName).ToStdString();
467     }
468     // napi method
469     if (function->IsCallNapi() && extraInfoValue.CheckIsJSNativePointer()) {
470         JSNativePointer *extraInfo = JSNativePointer::Cast(extraInfoValue.GetTaggedObject());
471         auto cb = vm_->GetNativePtrGetter();
472         if (cb != nullptr  && extraInfo != nullptr) {
473             if (!vm_->GetJSThread()->CpuProfilerCheckJSTaggedType(extraInfoValue.GetRawData())) {
474                 return;
475             }
476             auto addr = cb(reinterpret_cast<void *>(extraInfo->GetData()));
477             stream << addr;
478             CheckAndCopy(functionName, size, methodNameStr.c_str());
479             const uint8_t methodNameStrLength = methodNameStr.size();
480             CheckAndCopy(functionName + methodNameStrLength, size - methodNameStrLength, "(");
481             const uint8_t napiBeginLength = 1; // 1:the length of "("
482             CheckAndCopy(functionName + methodNameStrLength + napiBeginLength,
483                 size - methodNameStrLength - napiBeginLength, stream.str().c_str());
484             uint8_t srcLength = stream.str().size();
485             CheckAndCopy(functionName + methodNameStrLength + napiBeginLength + srcLength,
486                 size - methodNameStrLength - napiBeginLength - srcLength, ")");
487             return;
488         }
489     }
490     CheckAndCopy(functionName, size, methodNameStr.c_str());
491 }
492 
GetStackSignalHandler(int signal,siginfo_t * siginfo,void * context)493 void CpuProfiler::GetStackSignalHandler(int signal, [[maybe_unused]] siginfo_t *siginfo, void *context)
494 {
495     if (signal != SIGPROF) {
496         return;
497     }
498     CpuProfiler *profiler = nullptr;
499     JSThread *thread = nullptr;
500     {
501         os::memory::LockHolder lock(synchronizationMutex_);
502         pthread_t tid = static_cast<pthread_t>(syscall(SYS_gettid));
503         const EcmaVM *vm = profilerMap_[tid];
504         if (vm == nullptr) {
505             LOG_ECMA(ERROR) << "CpuProfiler GetStackSignalHandler vm is nullptr";
506             return;
507         }
508         profiler = vm->GetProfiler();
509         thread = vm->GetAssociatedJSThread();
510         if (profiler == nullptr) {
511             LOG_ECMA(ERROR) << "CpuProfiler GetStackSignalHandler profiler is nullptr";
512             return;
513         }
514     }
515 
516     if (profiler->GetBuildNapiStack()) {
517         if (profiler->generator_->SemPost(0) != 0) {
518             LOG_ECMA(ERROR) << "sem_[0] post failed";
519         }
520         return;
521     }
522 
523     uint64_t pc = 0;
524     if (thread->IsAsmInterpreter()) {
525         // If the attempt fails, the callback will be terminated directly to avoid the reentrancy deadlock,
526         // and a sampling will be abandoned. Failures are rare, so the impact on the overall sampling results
527         // is very limited.
528         if (!thread->GetEcmaVM()->GetAOTFileManager()->TryReadLock()) {
529             if (profiler->generator_->SemPost(0) != 0) {
530                 LOG_ECMA(ERROR) << "sem_[0] post failed";
531             }
532             return;
533         }
534         pc = GetPcFromContext(context);
535     }
536     if (thread->IsAsmInterpreter() && profiler->IsAddrAtStubOrAot(pc) &&
537         !profiler->IsEntryFrameHeaderOrTail(thread, pc)) {
538         [[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
539         [[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
540         [[maybe_unused]] void *fp = nullptr;
541         [[maybe_unused]] void *sp = nullptr;
542 #if defined(PANDA_TARGET_AMD64)
543         fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]);
544         sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]);
545 #elif defined(PANDA_TARGET_ARM64)
546         fp = reinterpret_cast<void*>(mcontext.regs[29]); // FP is an alias for x29.
547         sp = reinterpret_cast<void*>(mcontext.sp);
548 #else
549         LOG_FULL(FATAL) << "AsmInterpreter does not currently support other platforms, please run on x64 and arm64";
550         return;
551 #endif
552         if (reinterpret_cast<uint64_t*>(sp) > reinterpret_cast<uint64_t*>(fp)) {
553             LOG_ECMA(ERROR) << "sp > fp, stack frame exception";
554             if (profiler->generator_->SemPost(0) != 0) {
555                 LOG_ECMA(ERROR) << "sem_[0] post failed";
556                 return;
557             }
558             return;
559         }
560         if (profiler->CheckFrameType(thread, reinterpret_cast<JSTaggedType *>(fp))) {
561             FrameIterator it(reinterpret_cast<JSTaggedType *>(fp), thread);
562             profiler->GetStack(it);
563         }
564     } else if (thread->IsAsmInterpreter()) {
565         if (thread->GetLastLeaveFrame() != nullptr) {
566             JSTaggedType *leaveFrame = const_cast<JSTaggedType *>(thread->GetLastLeaveFrame());
567             if (profiler->CheckFrameType(thread, leaveFrame)) {
568                 FrameIterator it(leaveFrame, thread);
569                 profiler->GetStack(it);
570             }
571         }
572     } else {
573         if (thread->GetCurrentFrame() != nullptr) {
574             if (profiler->CheckFrameType(thread, const_cast<JSTaggedType *>(thread->GetCurrentFrame()))) {
575                 FrameHandler frameHandler(thread);
576                 FrameIterator it(frameHandler.GetSp(), thread);
577                 profiler->GetStack(it);
578             }
579         }
580     }
581     if (profiler->generator_->SemPost(0) != 0) {
582         LOG_ECMA(ERROR) << "sem_[0] post failed";
583         return;
584     }
585 }
586 
CheckFrameType(JSThread * thread,JSTaggedType * sp)587 bool CpuProfiler::CheckFrameType(JSThread *thread, JSTaggedType *sp)
588 {
589     FrameType type = FrameHandler::GetFrameType(sp);
590     if (type > FrameType::FRAME_TYPE_LAST || type < FrameType::FRAME_TYPE_FIRST) {
591         return false;
592     }
593 
594     FrameIterator iterator(sp, thread);
595     iterator.Advance();
596     JSTaggedType *preSp = iterator.GetSp();
597     if (preSp == nullptr) {
598         return true;
599     }
600 #if defined(PANDA_TARGET_64)
601     if (thread->IsAsmInterpreter() && !thread->IsLegalSp(reinterpret_cast<uintptr_t>(preSp))) {
602         return false;
603     }
604 #endif
605     type = FrameHandler::GetFrameType(preSp);
606     if (type > FrameType::FRAME_TYPE_LAST || type < FrameType::FRAME_TYPE_FIRST) {
607         return false;
608     }
609     return true;
610 }
611 
InHeaderOrTail(uint64_t pc,uint64_t entryBegin,uint64_t entryDuration,uint64_t headerSize,uint64_t tailSize) const612 bool CpuProfiler::InHeaderOrTail(uint64_t pc, uint64_t entryBegin, uint64_t entryDuration, uint64_t headerSize,
613                                  uint64_t tailSize) const
614 {
615     uintptr_t entryEnd = entryBegin + entryDuration;
616     if (pc >= entryBegin && pc <= (entryBegin + headerSize)) {
617         return true;
618     }
619     if (pc <= entryEnd && pc >= (entryEnd - tailSize)) {
620         return true;
621     }
622     return false;
623 }
624 
IsEntryFrameHeaderOrTail(JSThread * thread,uint64_t pc) const625 bool CpuProfiler::IsEntryFrameHeaderOrTail(JSThread *thread, uint64_t pc) const
626 {
627     uint64_t headerSize = 0;
628     uint64_t tailSize = 0;
629     uint64_t entryDuration = 0;
630     Assembler::GetFrameCompletionPos(headerSize, tailSize, entryDuration);
631     uintptr_t entryBegin = thread->GetRTInterface(kungfu::RuntimeStubCSigns::ID_AsmInterpreterEntry);
632     bool inAsmInterpreterEntry = InHeaderOrTail(pc, entryBegin, entryDuration, headerSize, tailSize);
633     entryBegin = thread->GetRTInterface(kungfu::RuntimeStubCSigns::ID_GeneratorReEnterAsmInterp);
634     bool inGeneratorReEnterAsmInterp = InHeaderOrTail(pc, entryBegin, entryDuration, headerSize, tailSize);
635     return (inAsmInterpreterEntry || inGeneratorReEnterAsmInterp);
636 }
637 
GetPcFromContext(void * context)638 uint64_t CpuProfiler::GetPcFromContext(void *context)
639 {
640     [[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
641     [[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
642     uint64_t pc = 0;
643 #if defined(PANDA_TARGET_AMD64)
644     pc = static_cast<uint64_t>(mcontext.gregs[REG_RIP]);
645 #elif defined(PANDA_TARGET_ARM64)
646     pc = static_cast<uint64_t>(mcontext.pc);
647 #else
648     LOG_FULL(FATAL) << "AsmInterpreter does not currently support other platforms, please run on x64 and arm64";
649     pc = 0;
650 #endif
651     return pc;
652 }
653 
IsAddrAtStubOrAot(uint64_t pc) const654 bool CpuProfiler::IsAddrAtStubOrAot(uint64_t pc) const
655 {
656     AOTFileManager *loader = vm_->GetAOTFileManager();
657     return loader->InsideStub(pc) || loader->InsideAOT(pc);
658 }
659 
CheckFileName(const std::string & fileName,std::string & absoluteFilePath) const660 bool CpuProfiler::CheckFileName(const std::string &fileName, std::string &absoluteFilePath) const
661 {
662     if (fileName.empty()) {
663         return true;
664     }
665 
666     if (fileName.size() > PATH_MAX) {
667         return false;
668     }
669 
670     CVector<char> resolvedPath(PATH_MAX);
671     auto result = realpath(fileName.c_str(), resolvedPath.data());
672     if (result == nullptr) {
673         LOG_ECMA(INFO) << "The file path does not exist";
674         return false;
675     }
676     std::ofstream file(resolvedPath.data());
677     if (!file.good()) {
678         return false;
679     }
680     file.close();
681     absoluteFilePath = resolvedPath.data();
682     return true;
683 }
684 
CheckAndCopy(char * dest,size_t length,const char * src) const685 bool CpuProfiler::CheckAndCopy(char *dest, size_t length, const char *src) const
686 {
687     int srcLength = strlen(src);
688     if (length <= static_cast<size_t>(srcLength) || strcpy_s(dest, srcLength + 1, src) != EOK) {
689         LOG_ECMA(ERROR) << "CpuProfiler parseMethodInfo strcpy_s failed, maybe srcLength more than destLength";
690         return false;
691     }
692     dest[srcLength] = '\0';
693     return true;
694 }
695 
GetMethodIdentifier(Method * method,const FrameIterator & it)696 void *CpuProfiler::GetMethodIdentifier(Method *method, const FrameIterator &it)
697 {
698     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
699     JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo();
700     if (extraInfoValue.CheckIsJSNativePointer()) {
701         JSNativePointer *extraInfo = JSNativePointer::Cast(extraInfoValue.GetTaggedObject());
702         return reinterpret_cast<void *>(extraInfo->GetData());
703     }
704 
705     if (method->GetJSPandaFile() == nullptr) {
706         return const_cast<void *>(method->GetNativePointer());
707     }
708 
709     MethodLiteral *methodLiteral = method->GetMethodLiteral();
710     return reinterpret_cast<void *>(methodLiteral);
711 }
712 
RecordCallNapiInfo(const std::string & methodAddr)713 void CpuProfiler::RecordCallNapiInfo(const std::string &methodAddr)
714 {
715     uint64_t currentTime = SamplingProcessor::GetMicrosecondsTimeStamp();
716     generator_->RecordCallNapiTime(currentTime);
717     generator_->RecordCallNapiAddr(methodAddr);
718 }
719 
GetNativeMethodCallPos(FrameIterator & it,FrameInfoTemp & codeEntry)720 void CpuProfiler::GetNativeMethodCallPos(FrameIterator &it, FrameInfoTemp &codeEntry)
721 {
722     auto nextMethod = it.CheckAndGetMethod();
723     if (nextMethod == nullptr) {
724         return ;
725     }
726     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
727     JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo();
728     if (!extraInfoValue.IsJSNativePointer() && nextMethod->GetJSPandaFile() != nullptr) {
729         DebugInfoExtractor *debugExtractor =
730             JSPandaFileManager::GetInstance()->GetJSPtExtractor(nextMethod->GetJSPandaFile());
731         panda_file::File::EntityId methodId = nextMethod->GetMethodLiteral()->GetMethodId();
732         const std::string &sourceFile = debugExtractor->GetSourceFile(methodId);
733         const char *tempVariable;
734         if (sourceFile.empty()) {
735             tempVariable = "";
736         } else {
737             tempVariable = sourceFile.c_str();
738         }
739         if (!CheckAndCopy(codeEntry.url, sizeof(codeEntry.url), tempVariable)) {
740             return;
741         }
742         int lineNumber = 0;
743         auto callbackLineFunc = [&lineNumber](int32_t line) -> bool {
744             lineNumber = line + 1;
745             return true;
746         };
747         int columnNumber = 0;
748         auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool {
749             columnNumber += column + 1;
750             return true;
751         };
752         uint32_t offset = it.GetBytecodeOffset();
753         if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset)) {
754             lineNumber = 0;
755         }
756         if (!debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
757             columnNumber = 0;
758         }
759         codeEntry.lineNumber = lineNumber;
760         codeEntry.columnNumber = columnNumber;
761     }
762 }
763 
SetBuildNapiStack(bool flag)764 void CpuProfiler::SetBuildNapiStack(bool flag)
765 {
766     isBuildNapiStack_.store(flag);
767 }
768 
GetBuildNapiStack()769 bool CpuProfiler::GetBuildNapiStack()
770 {
771     return isBuildNapiStack_.load();
772 }
773 } // namespace panda::ecmascript
774