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