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