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