• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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/stackinfo/js_stackgetter.h"
17 
18 #include "ecmascript/compiler/aot_file/aot_file_manager.h"
19 #include "ecmascript/compiler/assembler/assembler.h"
20 #include "ecmascript/frames.h"
21 #include "ecmascript/jspandafile/js_pandafile_manager.h"
22 
23 namespace panda::ecmascript {
24 class CpuProfiler;
25 class SamplesRecord;
26 
CheckFrameType(JSThread * thread,JSTaggedType * sp)27 bool JsStackGetter::CheckFrameType(JSThread *thread, JSTaggedType *sp)
28 {
29     FrameType type = FrameHandler::GetFrameType(sp);
30     if (type > FrameType::FRAME_TYPE_LAST || type < FrameType::FRAME_TYPE_FIRST) {
31         return false;
32     }
33 
34     FrameIterator iterator(sp, thread);
35     iterator.Advance();
36     JSTaggedType *preSp = iterator.GetSp();
37     if (preSp == nullptr) {
38         return true;
39     }
40 #if defined(PANDA_TARGET_64)
41     if (thread->IsAsmInterpreter() && !thread->IsLegalSp(reinterpret_cast<uintptr_t>(preSp))) {
42         return false;
43     }
44 #endif
45     type = FrameHandler::GetFrameType(preSp);
46     if (type > FrameType::FRAME_TYPE_LAST || type < FrameType::FRAME_TYPE_FIRST) {
47         return false;
48     }
49     return true;
50 }
51 
ParseMethodInfo(struct MethodKey & methodKey,const FrameIterator & it,const EcmaVM * vm,FrameInfoTemp & codeEntry,bool isCpuProfiler)52 bool JsStackGetter::ParseMethodInfo(struct MethodKey &methodKey,
53                                     const FrameIterator &it,
54                                     const EcmaVM *vm,
55                                     FrameInfoTemp &codeEntry,
56                                     bool isCpuProfiler)
57 {
58     auto method = it.CheckAndGetMethod();
59     const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
60     codeEntry.methodKey = methodKey;
61     if (method->IsNativeWithCallField()) {
62         FrameIterator itNext(it.GetSp(), it.GetThread());
63         itNext.Advance<GCVisitedFlag::IGNORED>();
64         GetNativeMethodCallPos(itNext, codeEntry);
65         GetNativeStack(vm, it, codeEntry.functionName, sizeof(codeEntry.functionName), isCpuProfiler);
66     } else {
67         EntityId methodId = reinterpret_cast<MethodLiteral *>(methodKey.methodIdentifier)->GetMethodId();
68         // function name
69         const char *functionName = MethodLiteral::GetMethodName(jsPandaFile, methodId);
70         uint8_t length = strlen(functionName);
71         if (length != 0 && functionName[0] == '#') {
72             uint8_t index = length - 1;
73             while (functionName[index] != '#') {
74                 index--;
75             }
76             functionName += (index + 1);
77         }
78         if (strlen(functionName) == 0) {
79             functionName = "anonymous";
80         }
81         if (!CheckAndCopy(codeEntry.functionName, sizeof(codeEntry.functionName), functionName)) {
82             return false;
83         }
84         // record name
85         const char *recordName = MethodLiteral::GetRecordNameWithSymbol(jsPandaFile, methodId);
86         if (strlen(recordName) != 0) {
87             if (!CheckAndCopy(codeEntry.recordName, sizeof(codeEntry.recordName), recordName)) {
88                 return false;
89             }
90         }
91 
92         DebugInfoExtractor *debugExtractor =
93             JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
94         if (debugExtractor == nullptr) {
95             return false;
96         }
97         // source file
98         const std::string &sourceFile = debugExtractor->GetSourceFile(methodId);
99         if (!sourceFile.empty()) {
100             if (!CheckAndCopy(codeEntry.url, sizeof(codeEntry.url), sourceFile.c_str())) {
101                 return false;
102             }
103         }
104         // line number and clomn number
105         codeEntry.lineNumber = debugExtractor->GetFristLine(methodId);
106         codeEntry.columnNumber = debugExtractor->GetFristColumn(methodId);
107     }
108     return true;
109 }
110 
CheckAndCopy(char * dest,size_t length,const char * src)111 bool JsStackGetter::CheckAndCopy(char *dest, size_t length, const char *src)
112 {
113     int srcLength = strlen(src);
114     if (length <= static_cast<size_t>(srcLength) || strcpy_s(dest, srcLength + 1, src) != EOK) {
115         LOG_ECMA(ERROR) << "JsStackGetter strcpy_s failed, maybe srcLength more than destLength";
116         return false;
117     }
118     dest[srcLength] = '\0';
119     return true;
120 }
121 
GetNativeStack(const EcmaVM * vm,const FrameIterator & it,char * functionName,size_t size,bool isCpuProfiler)122 void JsStackGetter::GetNativeStack(const EcmaVM *vm, const FrameIterator &it, char *functionName, size_t size,
123                                    bool isCpuProfiler)
124 {
125     std::stringstream stream;
126     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
127     JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo();
128     JSThread *thread = vm->GetJSThread();
129     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
130     JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
131     JSHandle<JSTaggedValue> func(thread, function);
132     JSHandle<JSTaggedValue> funcNameValue = JSObject::GetProperty(thread, func, nameKey).GetValue();
133     std::string methodNameStr;
134     if (funcNameValue->IsString()) {
135         JSHandle<EcmaString> methodName(funcNameValue);
136         methodNameStr = EcmaStringAccessor(methodName).ToStdString();
137     }
138     // napi method
139     if (isCpuProfiler && function->IsCallNapi() && extraInfoValue.CheckIsJSNativePointer()) {
140         JSNativePointer *extraInfo = JSNativePointer::Cast(extraInfoValue.GetTaggedObject());
141         auto cb = vm->GetNativePtrGetter();
142         if (cb != nullptr  && extraInfo != nullptr) {
143             if (!vm->GetJSThread()->CpuProfilerCheckJSTaggedType(extraInfoValue.GetRawData())) {
144                 return;
145             }
146             auto addr = cb(reinterpret_cast<void *>(extraInfo->GetData()));
147             stream << addr;
148             CheckAndCopy(functionName, size, methodNameStr.c_str());
149             const uint8_t methodNameStrLength = methodNameStr.size();
150             CheckAndCopy(functionName + methodNameStrLength, size - methodNameStrLength, "(");
151             const uint8_t napiBeginLength = 1; // 1:the length of "("
152             CheckAndCopy(functionName + methodNameStrLength + napiBeginLength,
153                 size - methodNameStrLength - napiBeginLength, stream.str().c_str());
154             uint8_t srcLength = stream.str().size();
155             CheckAndCopy(functionName + methodNameStrLength + napiBeginLength + srcLength,
156                 size - methodNameStrLength - napiBeginLength - srcLength, ")");
157             return;
158         }
159     }
160     CheckAndCopy(functionName, size, methodNameStr.c_str());
161 }
162 
GetRunningState(const FrameIterator & it,const EcmaVM * vm,bool isNative,bool topFrame,bool enableVMTag)163 RunningState JsStackGetter::GetRunningState(const FrameIterator &it, const EcmaVM *vm,
164                                             bool isNative, bool topFrame,
165                                             bool enableVMTag)
166 {
167     JSThread *thread = vm->GetAssociatedJSThread();
168     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
169 
170     if (enableVMTag) {
171         if (topFrame) {
172             if (thread->GetGcState()) {
173                 return RunningState::GC;
174             }
175             if (isNative && (it.IsLeaveFrame() || thread->GetRuntimeState())) {
176                 return RunningState::RUNTIME;
177             }
178         }
179 
180         if (function->IsCallNapi()) {
181             return RunningState::NAPI;
182         }
183         if (isNative) {
184             if (function->GetNativeFunctionExtraInfo().CheckIsJSNativePointer()) {
185                 return RunningState::ARKUI_ENGINE;
186             }
187             return RunningState::BUILTIN;
188         }
189         if (it.IsOptimizedJSFunctionFrame()) {
190             return RunningState::AOT;
191         }
192         if (thread->IsAsmInterpreter()) {
193             return RunningState::AINT;
194         }
195         return RunningState::CINT;
196     }
197 
198     if (topFrame) {
199         if (function->IsCallNapi()) {
200             return RunningState::NAPI;
201         }
202         if (thread->GetGcState()) {
203             return RunningState::GC;
204         }
205     }
206 
207     if (function->IsCallNapi()) {
208         return RunningState::NAPI;
209     }
210     if (isNative) {
211         if (function->GetNativeFunctionExtraInfo().CheckIsJSNativePointer()) {
212             return RunningState::ARKUI_ENGINE;
213         }
214         return RunningState::BUILTIN;
215     }
216 
217     return RunningState::OTHER;
218 }
219 
GetNativeMethodCallPos(FrameIterator & it,FrameInfoTemp & codeEntry)220 void JsStackGetter::GetNativeMethodCallPos(FrameIterator &it, FrameInfoTemp &codeEntry)
221 {
222     auto nextMethod = it.CheckAndGetMethod();
223     if (nextMethod == nullptr) {
224         return ;
225     }
226     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
227     JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo();
228     if (!extraInfoValue.CheckIsJSNativePointer() && nextMethod->GetJSPandaFile() != nullptr) {
229         DebugInfoExtractor *debugExtractor =
230             JSPandaFileManager::GetInstance()->GetJSPtExtractor(nextMethod->GetJSPandaFile());
231         if (debugExtractor == nullptr) {
232             return;
233         }
234         MethodLiteral *methodLiteral = nextMethod->GetMethodLiteral();
235         if (methodLiteral == nullptr) {
236             return;
237         }
238         panda_file::File::EntityId methodId = methodLiteral->GetMethodId();
239         const std::string &sourceFile = debugExtractor->GetSourceFile(methodId);
240         const char *tempVariable;
241         if (sourceFile.empty()) {
242             tempVariable = "";
243         } else {
244             tempVariable = sourceFile.c_str();
245         }
246         if (!CheckAndCopy(codeEntry.url, sizeof(codeEntry.url), tempVariable)) {
247             return;
248         }
249         int lineNumber = 0;
250         auto callbackLineFunc = [&lineNumber](int32_t line) -> bool {
251             lineNumber = line + 1;
252             return true;
253         };
254         int columnNumber = 0;
255         auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool {
256             columnNumber += column + 1;
257             return true;
258         };
259         uint32_t offset = it.GetBytecodeOffset();
260         if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset)) {
261             lineNumber = 0;
262         }
263         if (!debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
264             columnNumber = 0;
265         }
266         codeEntry.lineNumber = lineNumber;
267         codeEntry.columnNumber = columnNumber;
268     }
269 }
270 
GetMethodIdentifier(Method * method,const FrameIterator & it)271 void *JsStackGetter::GetMethodIdentifier(Method *method, const FrameIterator &it)
272 {
273     JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject());
274     JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo();
275     if (method->IsNativeWithCallField()) {
276         if (extraInfoValue.CheckIsJSNativePointer()) {
277             JSNativePointer *extraInfo = JSNativePointer::Cast(extraInfoValue.GetTaggedObject());
278             return reinterpret_cast<void *>(extraInfo->GetData());
279         }
280         return const_cast<void *>(method->GetNativePointer());
281     }
282 
283     MethodLiteral *methodLiteral = method->GetMethodLiteral();
284     return reinterpret_cast<void *>(methodLiteral);
285 }
286 } // namespace panda::ecmascript