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