1 /*
2 * Copyright (c) 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
17 #include <sys/time.h>
18
19 #include "ecmascript/dfx/stackinfo/js_stackinfo.h"
20 #include "ecmascript/platform/aot_crash_info.h"
21 #include "ecmascript/platform/os.h"
22 #include "ecmascript/stubs/runtime_stubs-inl.h"
23 #include "ecmascript/jit/jit.h"
24 #if defined(PANDA_TARGET_OHOS)
25 #include "ecmascript/extractortool/src/extractor.h"
26 #endif
27 #if defined(ENABLE_EXCEPTION_BACKTRACE)
28 #include "ecmascript/platform/backtrace.h"
29 #endif
30 namespace panda::ecmascript {
31 [[maybe_unused]] static bool g_needCheck = true;
32
33 std::unordered_map<EntityId, std::string> JsStackInfo::nameMap;
34 std::unordered_map<EntityId, std::vector<uint8>> JsStackInfo::machineCodeMap;
35 JSStackTrace *JSStackTrace::trace_ = nullptr;
36 std::mutex JSStackTrace::mutex_;
37 size_t JSStackTrace::count_ = 0;
38
IsFastJitFunctionFrame(const FrameType frameType)39 bool IsFastJitFunctionFrame(const FrameType frameType)
40 {
41 return frameType == FrameType::FASTJIT_FUNCTION_FRAME || frameType == FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME;
42 }
43
BuildMethodTrace(Method * method,uint32_t pcOffset,LastBuilderCache & lastCache,bool enableStackSourceFile)44 std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset, LastBuilderCache &lastCache,
45 bool enableStackSourceFile)
46 {
47 std::string data;
48 data.reserve(InitialLength);
49 data.append(" at ");
50 std::string name = method->ParseFunctionName();
51 if (name.empty()) {
52 data.append("anonymous (");
53 } else {
54 data.append(name).append(" (");
55 }
56 // source file
57 DebugInfoExtractor *debugExtractor = nullptr;
58 const JSPandaFile *pandaFile = method->GetJSPandaFile();
59 if (pandaFile == lastCache.pf) {
60 debugExtractor = lastCache.extractor;
61 } else {
62 debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(pandaFile);
63 lastCache.pf = pandaFile;
64 lastCache.extractor = debugExtractor;
65 }
66 if (enableStackSourceFile) {
67 const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
68 if (sourceFile.empty()) {
69 data.push_back('?');
70 } else {
71 data += sourceFile;
72 }
73 } else {
74 data.append("hidden");
75 }
76
77 data.push_back(':');
78 // line number and column number
79 auto callbackLineFunc = [&data](int32_t line) -> bool {
80 data += std::to_string(line + 1);
81 data.push_back(':');
82 return true;
83 };
84 auto callbackColumnFunc = [&data](int32_t column) -> bool {
85 data += std::to_string(column + 1);
86 return true;
87 };
88 panda_file::File::EntityId methodId = method->GetMethodId();
89 if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, pcOffset) ||
90 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, pcOffset)) {
91 data.push_back('?');
92 }
93 data.append(")\n");
94 return data;
95 }
96
BuildInlinedMethodTrace(const JSPandaFile * pf,std::map<uint32_t,uint32_t> & methodOffsets)97 std::string JsStackInfo::BuildInlinedMethodTrace(const JSPandaFile *pf, std::map<uint32_t, uint32_t> &methodOffsets)
98 {
99 std::string data;
100 std::map<uint32_t, uint32_t>::reverse_iterator it;
101 for (it = methodOffsets.rbegin(); it != methodOffsets.rend(); it++) {
102 uint32_t methodId = it->second;
103 std::string name;
104 if (methodId == 0) {
105 name = "unknown";
106 } else {
107 name = std::string(MethodLiteral::GetMethodName(pf, EntityId(methodId)));
108 if (name == "") {
109 name = "anonymous";
110 }
111 }
112 data.append(" at ");
113 data.append(name);
114 data.append(" (maybe inlined).");
115 data.append(" depth: ");
116 data.append(std::to_string(it->first));
117
118 data.push_back('\n');
119 }
120 return data;
121 }
122
DumpJitCode(JSThread * thread)123 void JsStackInfo::DumpJitCode(JSThread *thread)
124 {
125 JSTaggedType exception = thread->GetException().GetRawData();
126 auto &jitCodeMaps = thread->GetJitCodeMaps();
127 auto jitCode = jitCodeMaps.find(exception);
128 if (jitCode == jitCodeMaps.end()) {
129 return;
130 }
131 std::set<MachineCode*> memos;
132 JsJitDumpElf jitDumpElf;
133 jitDumpElf.Init();
134 int64 idx = 0;
135 size_t offset = 0;
136 auto jitCodeVec = jitCodeMaps[exception];
137 for (size_t i = 0; i < jitCodeVec->size(); i++) {
138 auto item = (*jitCodeVec)[i];
139 auto machineCode = std::get<0>(item);
140 std::string methodName = std::get<1>(item);
141 uintptr_t pcOffset = std::get<2>(item);
142 auto res = memos.insert(machineCode);
143 if (res.second) {
144 LOG_ECMA(ERROR) << "jit : js crash at method : " << methodName << ", offset :" << pcOffset;
145 char *funcAddr = reinterpret_cast<char *>(machineCode->GetFuncAddr());
146 size_t len = machineCode->GetTextSize();
147 std::vector<uint8> vec(len);
148 if (memmove_s(vec.data(), len, funcAddr, len) != EOK) {
149 LOG_ECMA(ERROR) << "Fail to get machineCode on function addr: " << funcAddr;
150 }
151 jitDumpElf.AppendData(vec);
152 jitDumpElf.AppendSymbolToSymTab(idx++, offset, len, methodName);
153 offset += len;
154 }
155 }
156 std::string fileName = "jitCode-" + std::to_string(getpid());
157 std::string realOutPath;
158 std::string sanboxPath = panda::os::file::File::GetExtendedFilePath(AotCrashInfo::GetSandBoxPath());
159 if (!ecmascript::RealPath(sanboxPath, realOutPath, false)) {
160 return;
161 }
162 std::string outFile = realOutPath + "/" + fileName;
163 int fd = open(outFile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
164 jitDumpElf.WriteJitElfFile(fd);
165 close(fd);
166 }
167
AssembleJitCodeMap(JSThread * thread,const JSHandle<JSObject> & jsErrorObj,JSFunction * func,Method * method,uintptr_t offset)168 void AssembleJitCodeMap(JSThread *thread, const JSHandle<JSObject> &jsErrorObj, JSFunction *func, Method *method,
169 uintptr_t offset)
170 {
171 ASSERT(!jsErrorObj.GetTaggedValue().IsUndefined());
172 JSTaggedValue machineCodeTagVal = func->GetMachineCode();
173 MachineCode *machineCode = MachineCode::Cast(machineCodeTagVal.GetTaggedObject());
174 std::string methodName = method->ParseFunctionName();
175 if (methodName.empty()) {
176 methodName = "anonymous";
177 }
178 thread->SetJitCodeMap(jsErrorObj.GetTaggedValue().GetRawData(), machineCode, methodName, offset);
179 }
180
BuildJsStackTrace(JSThread * thread,bool needNative,const JSHandle<JSObject> & jsErrorObj)181 std::string JsStackInfo::BuildJsStackTrace(JSThread *thread, bool needNative, const JSHandle<JSObject> &jsErrorObj)
182 {
183 std::string data;
184 data.reserve(InitialDeeps * InitialLength);
185 JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
186 FrameIterator it(current, thread);
187 uintptr_t baselineNativePc = 0;
188
189 LastBuilderCache lastCache;
190 for (; !it.Done(); it.Advance<GCVisitedFlag::HYBRID_STACK>()) {
191 if (it.GetFrameType() == FrameType::BASELINE_BUILTIN_FRAME) {
192 auto *frame = it.GetFrame<BaselineBuiltinFrame>();
193 baselineNativePc = frame->GetReturnAddr();
194 continue;
195 }
196 if (!it.IsJSFrame()) {
197 continue;
198 }
199 auto method = it.CheckAndGetMethod();
200 if (method == nullptr) {
201 continue;
202 }
203 if (!method->IsNativeWithCallField()) {
204 uint32_t pcOffset = 0;
205 if (it.GetFrameType() == FrameType::ASM_INTERPRETER_FRAME && baselineNativePc != 0) {
206 // the pcOffste in baseline frame slot is always uint64::max(), so pcOffset should be computed
207 JSHandle<JSFunction> function(thread, it.GetFunction());
208 pcOffset = RuntimeStubs::RuntimeGetBytecodePcOfstForBaseline(function, baselineNativePc);
209 baselineNativePc = 0;
210 } else {
211 pcOffset = it.GetBytecodeOffset();
212 }
213 data += BuildJsStackTraceInfo(thread, method, it, pcOffset, jsErrorObj, lastCache);
214 } else if (needNative) {
215 auto addr = method->GetNativePointer();
216 std::stringstream strm;
217 strm << addr;
218 data.append(" at native method (").append(strm.str()).append(")\n");
219 }
220 }
221 if (data.empty()) {
222 #if defined(ENABLE_EXCEPTION_BACKTRACE)
223 std::ostringstream stack;
224 Backtrace(stack);
225 data = stack.str();
226 #endif
227 }
228 return data;
229 }
230
BuildJsStackTraceInfo(JSThread * thread,Method * method,FrameIterator & it,uint32_t pcOffset,const JSHandle<JSObject> & jsErrorObj,LastBuilderCache & lastCache)231 std::string JsStackInfo::BuildJsStackTraceInfo(JSThread *thread, Method *method, FrameIterator &it,
232 uint32_t pcOffset, const JSHandle<JSObject> &jsErrorObj,
233 LastBuilderCache &lastCache)
234 {
235 const JSPandaFile *pf = method->GetJSPandaFile();
236 std::map<uint32_t, uint32_t> methodOffsets = it.GetInlinedMethodInfo();
237 FrameType frameType = it.GetFrameType();
238 if (IsFastJitFunctionFrame(frameType)) {
239 JSFunction *func = static_cast<JSFunction*>(it.GetFunction().GetTaggedObject());
240 if (!jsErrorObj.GetTaggedValue().IsUndefined()) {
241 AssembleJitCodeMap(thread, jsErrorObj, func, method, it.GetOptimizedReturnAddr());
242 }
243 }
244 return BuildInlinedMethodTrace(pf, methodOffsets) +
245 BuildMethodTrace(method, pcOffset, lastCache, thread->GetEnableStackSourceFile());
246 }
247
BuildCrashInfo(bool isJsCrash,uintptr_t pc,JSThread * thread)248 void JsStackInfo::BuildCrashInfo(bool isJsCrash, uintptr_t pc, JSThread *thread)
249 {
250 if (JsStackInfo::loader == nullptr || JsStackInfo::options == nullptr) {
251 return;
252 }
253 if (!JsStackInfo::loader->IsEnableAOT() && !Jit::GetInstance()->IsEnableFastJit() &&
254 !JsStackInfo::options->IsEnablePGOProfiler()) {
255 return;
256 }
257 ohos::RuntimeInfoType type;
258 if (isJsCrash) {
259 type = ohos::RuntimeInfoType::JS;
260 } else if (pc != 0 && JsStackInfo::loader != nullptr && JsStackInfo::loader->InsideAOT(pc)) {
261 type = ohos::RuntimeInfoType::AOT_CRASH;
262 } else {
263 type = ohos::RuntimeInfoType::OTHERS;
264 }
265 ohos::AotRuntimeInfo::GetInstance().BuildCrashRuntimeInfo(type);
266 if (isJsCrash && thread != nullptr) {
267 DumpJitCode(thread);
268 }
269 }
270
BuildJsStackInfo(JSThread * thread,bool currentStack)271 std::vector<struct JsFrameInfo> JsStackInfo::BuildJsStackInfo(JSThread *thread, bool currentStack)
272 {
273 std::vector<struct JsFrameInfo> jsFrame;
274 uintptr_t *native = nullptr;
275 JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
276 FrameIterator it(current, thread);
277 for (; !it.Done(); it.Advance<GCVisitedFlag::HYBRID_STACK>()) {
278 if (!it.IsJSFrame()) {
279 continue;
280 }
281 auto method = it.CheckAndGetMethod();
282 if (method == nullptr) {
283 continue;
284 }
285 struct JsFrameInfo frameInfo;
286 if (native != nullptr) {
287 frameInfo.nativePointer = native;
288 native = nullptr;
289 }
290 if (!method->IsNativeWithCallField()) {
291 std::string name = method->ParseFunctionName();
292 if (name.empty()) {
293 frameInfo.functionName = "anonymous";
294 } else {
295 frameInfo.functionName = name;
296 }
297 // source file
298 DebugInfoExtractor *debugExtractor =
299 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
300 const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
301 if (sourceFile.empty()) {
302 frameInfo.fileName = "?";
303 } else {
304 frameInfo.fileName = sourceFile;
305 }
306 // line number and column number
307 int lineNumber = 0;
308 auto callbackLineFunc = [&frameInfo, &lineNumber](int32_t line) -> bool {
309 lineNumber = line + 1;
310 frameInfo.pos = std::to_string(lineNumber) + ":";
311 return true;
312 };
313 auto callbackColumnFunc = [&frameInfo](int32_t column) -> bool {
314 frameInfo.pos += std::to_string(column + 1);
315 return true;
316 };
317 panda_file::File::EntityId methodId = method->GetMethodId();
318 uint32_t offset = it.GetBytecodeOffset();
319 if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
320 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
321 frameInfo.pos = "?";
322 }
323 jsFrame.push_back(std::move(frameInfo));
324 if (currentStack) {
325 return jsFrame;
326 }
327 } else {
328 JSTaggedValue function = it.GetFunction();
329 JSHandle<JSTaggedValue> extraInfoValue(
330 thread, JSFunction::Cast(function.GetTaggedObject())->GetFunctionExtraInfo());
331 if (extraInfoValue->IsJSNativePointer()) {
332 JSHandle<JSNativePointer> extraInfo(extraInfoValue);
333 native = reinterpret_cast<uintptr_t *>(extraInfo->GetData());
334 }
335 }
336 }
337 return jsFrame;
338 }
339
ReadUintptrFromAddr(int pid,uintptr_t addr,uintptr_t & value,bool needCheckRegion)340 bool ReadUintptrFromAddr(int pid, uintptr_t addr, uintptr_t &value, bool needCheckRegion)
341 {
342 if (pid == getpid()) {
343 if (needCheckRegion) {
344 bool flag = false;
345 auto callback = [addr, &flag](Region *region) {
346 uintptr_t regionBegin = region->GetBegin();
347 uintptr_t regionEnd = region->GetEnd();
348 if (regionBegin <= addr && addr <= regionEnd) {
349 flag = true;
350 }
351 };
352 if (JsStackInfo::loader != nullptr) {
353 const Heap *heap = JsStackInfo::loader->GetHeap();
354 if (heap != nullptr) {
355 heap->EnumerateRegions(callback);
356 }
357 }
358 if (!flag) {
359 LOG_ECMA(ERROR) << "addr not in Region, addr: " << addr;
360 return false;
361 }
362 }
363 value = *(reinterpret_cast<uintptr_t *>(addr));
364 return true;
365 }
366 long *retAddr = reinterpret_cast<long *>(&value);
367 // note: big endian
368 for (size_t i = 0; i < sizeof(uintptr_t) / sizeof(long); i++) {
369 *retAddr = PtracePeektext(pid, addr);
370 if (*retAddr == -1) {
371 LOG_ECMA(ERROR) << "ReadFromAddr ERROR, addr: " << addr;
372 return false;
373 }
374 addr += sizeof(long);
375 retAddr++;
376 }
377 return true;
378 }
379
GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType,uintptr_t & typeOffset,uintptr_t & prevOffset)380 bool GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType, uintptr_t &typeOffset, uintptr_t &prevOffset)
381 {
382 FrameType type = static_cast<FrameType>(frameType);
383 switch (type) {
384 case FrameType::OPTIMIZED_FRAME:
385 typeOffset = OptimizedFrame::GetTypeOffset();
386 prevOffset = OptimizedFrame::GetPrevOffset();
387 break;
388 case FrameType::OPTIMIZED_ENTRY_FRAME:
389 typeOffset = OptimizedEntryFrame::GetTypeOffset();
390 prevOffset = OptimizedEntryFrame::GetLeaveFrameFpOffset();
391 break;
392 case FrameType::BASELINE_BUILTIN_FRAME:
393 typeOffset = BaselineBuiltinFrame::GetTypeOffset();
394 prevOffset = BaselineBuiltinFrame::GetPrevOffset();
395 break;
396 case FrameType::ASM_BRIDGE_FRAME:
397 typeOffset = AsmBridgeFrame::GetTypeOffset();
398 prevOffset = AsmBridgeFrame::GetPrevOffset();
399 break;
400 case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME:
401 typeOffset = OptimizedJSFunctionUnfoldArgVFrame::GetTypeOffset();
402 prevOffset = OptimizedJSFunctionUnfoldArgVFrame::GetPrevOffset();
403 break;
404 case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
405 case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
406 case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
407 typeOffset = OptimizedJSFunctionFrame::GetTypeOffset();
408 prevOffset = OptimizedJSFunctionFrame::GetPrevOffset();
409 break;
410 case FrameType::LEAVE_FRAME:
411 typeOffset = OptimizedLeaveFrame::GetTypeOffset();
412 prevOffset = OptimizedLeaveFrame::GetPrevOffset();
413 break;
414 case FrameType::LEAVE_FRAME_WITH_ARGV:
415 typeOffset = OptimizedWithArgvLeaveFrame::GetTypeOffset();
416 prevOffset = OptimizedWithArgvLeaveFrame::GetPrevOffset();
417 break;
418 case FrameType::BUILTIN_CALL_LEAVE_FRAME:
419 typeOffset = OptimizedBuiltinLeaveFrame::GetTypeOffset();
420 prevOffset = OptimizedBuiltinLeaveFrame::GetPrevOffset();
421 break;
422 case FrameType::INTERPRETER_FRAME:
423 case FrameType::INTERPRETER_FAST_NEW_FRAME:
424 typeOffset = InterpretedFrame::GetTypeOffset();
425 prevOffset = InterpretedFrame::GetPrevOffset();
426 break;
427 case FrameType::INTERPRETER_BUILTIN_FRAME:
428 typeOffset = InterpretedBuiltinFrame::GetTypeOffset();
429 prevOffset = InterpretedBuiltinFrame::GetPrevOffset();
430 break;
431 case FrameType::INTERPRETER_CONSTRUCTOR_FRAME:
432 case FrameType::ASM_INTERPRETER_FRAME:
433 typeOffset = AsmInterpretedFrame::GetTypeOffset();
434 prevOffset = AsmInterpretedFrame::GetPrevOffset();
435 break;
436 case FrameType::BUILTIN_FRAME:
437 case FrameType::BUILTIN_ENTRY_FRAME:
438 typeOffset = BuiltinFrame::GetTypeOffset();
439 prevOffset = BuiltinFrame::GetPrevOffset();
440 break;
441 case FrameType::BUILTIN_FRAME_WITH_ARGV:
442 case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME:
443 typeOffset = BuiltinWithArgvFrame::GetTypeOffset();
444 prevOffset = BuiltinWithArgvFrame::GetPrevOffset();
445 break;
446 case FrameType::INTERPRETER_ENTRY_FRAME:
447 typeOffset = InterpretedEntryFrame::GetTypeOffset();
448 prevOffset = InterpretedEntryFrame::GetPrevOffset();
449 break;
450 case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
451 typeOffset = AsmInterpretedEntryFrame::GetTypeOffset();
452 prevOffset = AsmInterpretedEntryFrame::GetPrevOffset();
453 break;
454 case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
455 typeOffset = AsmInterpretedBridgeFrame::GetTypeOffset();
456 prevOffset = AsmInterpretedBridgeFrame::GetPrevOffset();
457 break;
458 case FrameType::FASTJIT_FUNCTION_FRAME:
459 case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME:
460 typeOffset = FASTJITFunctionFrame::GetTypeOffset();
461 prevOffset = FASTJITFunctionFrame::GetPrevOffset();
462 break;
463 default:
464 return false;
465 }
466 return true;
467 }
468
ArkFrameCheck(uintptr_t frameType)469 bool ArkFrameCheck(uintptr_t frameType)
470 {
471 return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME ||
472 static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_ENTRY_FRAME;
473 }
474
IsFunctionFrame(uintptr_t frameType)475 bool IsFunctionFrame(uintptr_t frameType)
476 {
477 return static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_FRAME ||
478 static_cast<FrameType>(frameType) == FrameType::INTERPRETER_CONSTRUCTOR_FRAME ||
479 static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FRAME ||
480 static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FAST_NEW_FRAME ||
481 static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_FRAME ||
482 static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME ||
483 static_cast<FrameType>(frameType) == FrameType::FASTJIT_FUNCTION_FRAME ||
484 static_cast<FrameType>(frameType) == FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME;
485 }
486
ReadMethodInfo(panda_file::MethodDataAccessor & mda)487 std::optional<MethodInfo> JSStackTrace::ReadMethodInfo(panda_file::MethodDataAccessor &mda)
488 {
489 uintptr_t methodId = mda.GetMethodId().GetOffset();
490 auto codeId = mda.GetCodeId();
491 if (!codeId) {
492 return std::nullopt;
493 }
494 panda_file::CodeDataAccessor cda(mda.GetPandaFile(), codeId.value());
495 uint32_t codeSize = cda.GetCodeSize();
496 uintptr_t codeBegin = reinterpret_cast<uintptr_t>(cda.GetInstructions());
497 return std::make_optional<MethodInfo>(methodId, codeBegin, codeSize);
498 }
499
ReadAllMethodInfos(std::shared_ptr<JSPandaFile> jsPandaFile)500 CVector<MethodInfo> JSStackTrace::ReadAllMethodInfos(std::shared_ptr<JSPandaFile> jsPandaFile)
501 {
502 CVector<MethodInfo> result;
503 if (jsPandaFile == nullptr) {
504 LOG_ECMA(ERROR) << "Read all methods info in file failed, file is nullptr.";
505 return result;
506 }
507 const panda_file::File *pf = jsPandaFile->GetPandaFile();
508 Span<const uint32_t> classIndexes = jsPandaFile->GetClasses();
509 for (const uint32_t index : classIndexes) {
510 panda_file::File::EntityId classId(index);
511 if (jsPandaFile->IsExternal(classId)) {
512 continue;
513 }
514 panda_file::ClassDataAccessor cda(*pf, classId);
515 cda.EnumerateMethods([&result, jsPandaFile](panda_file::MethodDataAccessor &mda) {
516 auto info = JSStackTrace::ReadMethodInfo(mda);
517 if (!info) {
518 return;
519 }
520 result.push_back(info.value());
521 });
522 }
523
524 std::sort(result.begin(), result.end());
525 return result;
526 }
527
TranslateByteCodePc(uintptr_t realPc,const CVector<MethodInfo> & vec)528 std::optional<CodeInfo> JSStackTrace::TranslateByteCodePc(uintptr_t realPc, const CVector<MethodInfo> &vec)
529 {
530 int32_t left = 0;
531 ASSERT(vec.size() > 0);
532 int32_t right = static_cast<int32_t>(vec.size()) - 1;
533 for (; left <= right;) {
534 int32_t mid = (left + right) / 2;
535 bool isRight = realPc >= (vec[mid].codeBegin + vec[mid].codeSize);
536 bool isLeft = realPc < vec[mid].codeBegin;
537 // codeBegin <= realPc < codeBegin + codeSize
538 if (!isRight && !isLeft) {
539 return std::make_optional<CodeInfo>(realPc - vec[mid].codeBegin, vec[mid].methodId, vec[mid].codeSize);
540 } else if (isRight) {
541 left = mid + 1;
542 } else {
543 right = mid -1;
544 }
545 }
546 return std::nullopt;
547 }
548
SaveFuncName(EntityId entityId,const std::string & name)549 void SaveFuncName(EntityId entityId, const std::string &name)
550 {
551 size_t length = 256; // maximum stack length
552 if (JsStackInfo::nameMap.size() > length) {
553 auto it = JsStackInfo::nameMap.begin();
554 JsStackInfo::nameMap.erase(it);
555 }
556 JsStackInfo::nameMap.emplace(entityId, name);
557 }
558
559 template<typename T>
ParseJsFrameInfo(JSPandaFile * jsPandaFile,DebugInfoExtractor * debugExtractor,EntityId methodId,uintptr_t offset,T & jsFrame,SourceMap * sourceMap=nullptr)560 void ParseJsFrameInfo(JSPandaFile *jsPandaFile, DebugInfoExtractor *debugExtractor,
561 EntityId methodId, uintptr_t offset, T &jsFrame, SourceMap *sourceMap = nullptr)
562 {
563 if (jsPandaFile == nullptr) {
564 LOG_ECMA(ERROR) << "Parse jsFrame info failed, jsPandaFile is nullptr.";
565 return;
566 }
567 std::string name = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
568 name = name.empty() ? "anonymous" : name;
569 SaveFuncName(methodId, name);
570 std::string url = debugExtractor->GetSourceFile(methodId);
571
572 // line number and column number
573 int lineNumber = 0;
574 int columnNumber = 0;
575 auto callbackLineFunc = [&lineNumber](int32_t line) -> bool {
576 lineNumber = line + 1;
577 return true;
578 };
579 auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool {
580 columnNumber = column + 1;
581 return true;
582 };
583
584 if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
585 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
586 lineNumber = 0;
587 columnNumber = 0;
588 }
589
590 if (sourceMap != nullptr) {
591 sourceMap->TranslateUrlPositionBySourceMap(url, lineNumber, columnNumber);
592 }
593
594 size_t urlSize = url.size() + 1;
595 size_t nameSize = name.size() + 1;
596 if (strcpy_s(jsFrame.url, urlSize, url.c_str()) != EOK ||
597 strcpy_s(jsFrame.functionName, nameSize, name.c_str()) != EOK) {
598 LOG_ECMA(FATAL) << "jsFrame strcpy_s failed";
599 UNREACHABLE();
600 }
601 jsFrame.line = lineNumber;
602 jsFrame.column = columnNumber;
603 }
604
ArkParseJsFrameInfo(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,uint8_t * data,uint64_t dataSize,uintptr_t extractorptr,JsFunction * jsFunction)605 bool ArkParseJsFrameInfo(uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase, uintptr_t loadOffset,
606 uint8_t *data, uint64_t dataSize, uintptr_t extractorptr, JsFunction *jsFunction)
607 {
608 if (data == nullptr) {
609 LOG_ECMA(ERROR) << "Parse JSframe info failed, buffer is nullptr.";
610 return false;
611 }
612 loadOffset = loadOffset % PageSize();
613 auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
614 if (extractor == nullptr) {
615 LOG_ECMA(ERROR) << "Parse JSframe info failed, extractor is nullptr.";
616 return false;
617 }
618 auto jsPandaFile = extractor->GetJSPandaFile(data, dataSize);
619 if (jsPandaFile == nullptr) {
620 LOG_ECMA(ERROR) << "Parse JSframe info failed, panda file is nullptr.";
621 return false;
622 }
623 auto debugExtractor = extractor->GetDebugExtractor();
624 auto methodInfos = extractor->GetMethodInfos();
625 if (methodInfos.empty()) {
626 LOG_ECMA(ERROR) << "Read all method info from JSPandaFile failed, methodInfos is empty.";
627 return false;
628 }
629 uintptr_t realOffset = byteCodePc - mapBase - loadOffset;
630 uintptr_t pfBasePtr = reinterpret_cast<uintptr_t>(jsPandaFile->GetBase());
631 auto codeInfo = JSStackTrace::TranslateByteCodePc(realOffset + pfBasePtr, methodInfos);
632 if (!codeInfo) {
633 LOG_ECMA(ERROR) << std::hex << "Failed to get methodId, pc: " << byteCodePc;
634 return false;
635 }
636 if (!methodId) {
637 methodId = codeInfo->methodId;
638 }
639 auto offset = codeInfo->offset;
640 ParseJsFrameInfo(jsPandaFile, debugExtractor, EntityId(methodId), offset, *jsFunction, extractor->GetSourceMap());
641
642 jsFunction->codeBegin = byteCodePc - offset;
643 jsFunction->codeSize = codeInfo->codeSize;
644 return true;
645 }
646
ArkTranslateJsFrameInfo(uint8_t * data,size_t dataSize,JsFunction * jsFunction)647 bool ArkTranslateJsFrameInfo(uint8_t *data, size_t dataSize, JsFunction *jsFunction)
648 {
649 SourceMap sourceMap;
650 std::string strUrl = jsFunction->url;
651 sourceMap.Init(data, dataSize);
652 bool ret = sourceMap.TranslateUrlPositionBySourceMap(strUrl, jsFunction->line, jsFunction->column);
653 size_t strUrlSize = strUrl.size() + 1;
654 if (strcpy_s(jsFunction->url, strUrlSize, strUrl.c_str()) != EOK) {
655 LOG_FULL(FATAL) << "strcpy_s failed";
656 UNREACHABLE();
657 }
658 return ret;
659 }
660
GetBytecodeOffset(void * ctx,ReadMemFunc readMem,uintptr_t frameType,uintptr_t currentPtr)661 uintptr_t GetBytecodeOffset(void *ctx, ReadMemFunc readMem, uintptr_t frameType, uintptr_t currentPtr)
662 {
663 // currentPtr points to the frametype.
664 uintptr_t bytecodePc = 0;
665 FrameType type = static_cast<FrameType>(frameType);
666 switch (type) {
667 case FrameType::ASM_INTERPRETER_FRAME:
668 case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
669 currentPtr -= AsmInterpretedFrame::GetTypeOffset();
670 currentPtr += AsmInterpretedFrame::GetPcOffset(false);
671 readMem(ctx, currentPtr, &bytecodePc);
672 return bytecodePc;
673 }
674 case FrameType::INTERPRETER_FRAME:
675 case FrameType::INTERPRETER_FAST_NEW_FRAME: {
676 currentPtr -= InterpretedFrame::GetTypeOffset();
677 currentPtr += InterpretedFrame::GetPcOffset(false);
678 readMem(ctx, currentPtr, &bytecodePc);
679 return bytecodePc;
680 }
681 // aot get native pc
682 case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
683 case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
684 currentPtr -= OptimizedJSFunctionFrame::GetTypeOffset();
685 currentPtr += OptimizedJSFunctionFrame::GetReturnAddrOffset();
686 readMem(ctx, currentPtr, &bytecodePc);
687 return bytecodePc;
688 }
689 case FrameType::FASTJIT_FUNCTION_FRAME:
690 case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
691 currentPtr -= FASTJITFunctionFrame::GetTypeOffset();
692 readMem(ctx, currentPtr, &bytecodePc);
693 return bytecodePc;
694 }
695 default: {
696 break;
697 }
698 }
699 return 0;
700 }
701
ArkGetFunction(void * ctx,ReadMemFunc readMem,uintptr_t currentPtr,uintptr_t frameType)702 uintptr_t ArkGetFunction(void *ctx, ReadMemFunc readMem, uintptr_t currentPtr, uintptr_t frameType)
703 {
704 FrameType type = static_cast<FrameType>(frameType);
705 uintptr_t funcAddr = currentPtr;
706 switch (type) {
707 case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
708 case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
709 funcAddr -= OptimizedJSFunctionFrame::GetTypeOffset();
710 funcAddr += OptimizedJSFunctionFrame::GetFunctionOffset();
711 break;
712 }
713 case FrameType::ASM_INTERPRETER_FRAME:
714 case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
715 funcAddr -= AsmInterpretedFrame::GetTypeOffset();
716 funcAddr += AsmInterpretedFrame::GetFunctionOffset(false);
717 break;
718 }
719 case FrameType::INTERPRETER_FRAME:
720 case FrameType::INTERPRETER_FAST_NEW_FRAME: {
721 funcAddr -= InterpretedFrame::GetTypeOffset();
722 funcAddr += InterpretedFrame::GetFunctionOffset();
723 break;
724 }
725 case FrameType::FASTJIT_FUNCTION_FRAME:
726 case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
727 funcAddr -= FASTJITFunctionFrame::GetTypeOffset();
728 funcAddr += FASTJITFunctionFrame::GetFunctionOffset();
729 break;
730 }
731 default: {
732 return 0;
733 }
734 }
735 uintptr_t function = 0;
736 if (!readMem(ctx, funcAddr, &function)) {
737 return 0;
738 }
739 return function;
740 }
741
ArkCheckIsJSFunctionBaseOrJSProxy(void * ctx,ReadMemFunc readMem,uintptr_t objAddr,bool & isJSFunctionBase)742 bool ArkCheckIsJSFunctionBaseOrJSProxy(void *ctx, ReadMemFunc readMem, uintptr_t objAddr, bool &isJSFunctionBase)
743 {
744 bool isHeapObj = ((objAddr & JSTaggedValue::TAG_HEAPOBJECT_MASK) == 0U);
745 bool isInvalidValue = (objAddr <= JSTaggedValue::INVALID_VALUE_LIMIT);
746 if (isHeapObj && !isInvalidValue) {
747 ASSERT_PRINT(((objAddr & JSTaggedValue::TAG_WEAK) == 0U),
748 "can not convert JSTaggedValue to HeapObject :" << std::hex << objAddr);
749 uintptr_t hclassAddr = objAddr + TaggedObject::HCLASS_OFFSET;
750 uintptr_t hclass = 0;
751 if (!readMem(ctx, hclassAddr, &hclass)) {
752 return false;
753 }
754 if (hclass != 0) {
755 uintptr_t bitsAddr = reinterpret_cast<uintptr_t>(hclass + JSHClass::BIT_FIELD_OFFSET);
756 uintptr_t bits = 0;
757 if (!readMem(ctx, bitsAddr, &bits)) {
758 return false;
759 }
760 JSType jsType = JSHClass::ObjectTypeBits::Decode(bits);
761 isJSFunctionBase = (jsType >= JSType::JS_FUNCTION_BASE && jsType <= JSType::JS_BOUND_FUNCTION);
762 bool isJSProxy = (jsType == JSType::JS_PROXY);
763 return isJSFunctionBase || isJSProxy;
764 }
765 }
766 return false;
767 }
768
ArkCheckAndGetMethod(void * ctx,ReadMemFunc readMem,uintptr_t value)769 uintptr_t ArkCheckAndGetMethod(void *ctx, ReadMemFunc readMem, uintptr_t value)
770 {
771 bool isJSFunctionBase = 0;
772 if (ArkCheckIsJSFunctionBaseOrJSProxy(ctx, readMem, value, isJSFunctionBase)) {
773 if (isJSFunctionBase) {
774 value += JSFunctionBase::METHOD_OFFSET;
775 } else {
776 value += JSProxy::METHOD_OFFSET;
777 }
778 uintptr_t method = 0;
779 if (!readMem(ctx, value, &method)) {
780 return 0;
781 }
782 return method;
783 }
784 return 0;
785 }
786
ArkGetMethodIdFromMethod(void * ctx,ReadMemFunc readMem,uintptr_t method,uintptr_t & methodId)787 bool ArkGetMethodIdFromMethod(void *ctx, ReadMemFunc readMem, uintptr_t method, uintptr_t &methodId)
788 {
789 uintptr_t methodLiteralAddr = method + Method::LITERAL_INFO_OFFSET;
790 uintptr_t methodLiteral = 0;
791 if (!readMem(ctx, methodLiteralAddr, &methodLiteral)) {
792 return false;
793 }
794 methodId = MethodLiteral::MethodIdBits::Decode(methodLiteral);
795 return true;
796 }
797
ArkGetMethodId(void * ctx,ReadMemFunc readMem,uintptr_t frameType,uintptr_t currentPtr,uintptr_t & methodId)798 bool ArkGetMethodId(void *ctx, ReadMemFunc readMem, uintptr_t frameType, uintptr_t currentPtr, uintptr_t &methodId)
799 {
800 uintptr_t function = ArkGetFunction(ctx, readMem, currentPtr, frameType);
801 if (!function) {
802 LOG_ECMA(DEBUG) << "Failed to get function";
803 return false;
804 }
805
806 uintptr_t method = ArkCheckAndGetMethod(ctx, readMem, function);
807 if (!method) {
808 LOG_ECMA(DEBUG) << std::hex << "Failed to get method: " << function;
809 return false;
810 }
811
812 if (!ArkGetMethodIdFromMethod(ctx, readMem, method, methodId)) {
813 LOG_ECMA(DEBUG) << std::hex << "ArkGetJsFrameDebugInfo failed, method: " << method;
814 return false;
815 }
816 return true;
817 }
818
ArkGetNextFrame(void * ctx,ReadMemFunc readMem,uintptr_t & currentPtr,uintptr_t & frameType,uintptr_t & pc,uintptr_t * methodId)819 bool ArkGetNextFrame(void *ctx, ReadMemFunc readMem, uintptr_t ¤tPtr,
820 uintptr_t &frameType, uintptr_t &pc, uintptr_t *methodId)
821 {
822 currentPtr -= sizeof(FrameType);
823 if (!readMem(ctx, currentPtr, &frameType)) {
824 return false;
825 }
826 if (ArkFrameCheck(frameType)) {
827 return true;
828 }
829 bool ret = false;
830 if (IsFunctionFrame(frameType)) {
831 pc = GetBytecodeOffset(ctx, readMem, frameType, currentPtr);
832 ret = true;
833 if (methodId != nullptr) {
834 ret = ArkGetMethodId(ctx, readMem, frameType, currentPtr, *methodId);
835 }
836 }
837
838 uintptr_t typeOffset = 0;
839 uintptr_t prevOffset = 0;
840 if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
841 return false;
842 }
843 currentPtr -= typeOffset;
844 currentPtr += prevOffset;
845 if (!readMem(ctx, currentPtr, ¤tPtr)) {
846 return false;
847 }
848
849 if (ret) {
850 return true;
851 }
852 return ArkGetNextFrame(ctx, readMem, currentPtr, frameType, pc, methodId);
853 }
854
ArkGetMethodIdWithJit(ArkUnwindParam * arkUnwindParam,uintptr_t frameType,uintptr_t currentPtr)855 bool ArkGetMethodIdWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t frameType, uintptr_t currentPtr)
856 {
857 uintptr_t function = ArkGetFunction(arkUnwindParam->ctx, arkUnwindParam->readMem, currentPtr, frameType);
858 if (!function) {
859 LOG_ECMA(DEBUG) << "Failed to get function";
860 return false;
861 }
862
863 uintptr_t method = ArkCheckAndGetMethod(arkUnwindParam->ctx, arkUnwindParam->readMem, function);
864 if (!method) {
865 LOG_ECMA(DEBUG) << std::hex << "Failed to get method: " << function;
866 return false;
867 }
868
869 if (!ArkGetMethodIdFromMethod(arkUnwindParam->ctx, arkUnwindParam->readMem, method, *arkUnwindParam->methodId)) {
870 LOG_ECMA(DEBUG) << std::hex << "ArkGetJsFrameDebugInfo failed, method: " << method;
871 return false;
872 }
873
874 if (IsFastJitFunctionFrame(static_cast<FrameType>(frameType))) {
875 uintptr_t machineCode = 0;
876 uintptr_t functionAddr = function + JSFunction::MACHINECODE_OFFSET;
877 arkUnwindParam->readMem(arkUnwindParam->ctx, functionAddr, &machineCode);
878 uintptr_t size = 0;
879 uintptr_t funcAddr = 0;
880 if (machineCode) {
881 arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::INSTRSIZ_OFFSET, &size);
882 arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::FUNCADDR_OFFSET, &funcAddr);
883 }
884 if (size && funcAddr) {
885 // take the lower four bytes
886 size &= 0xFFFFFFFF;
887 std::vector<uint8> codeVec;
888 for (size_t l = 0; l < size; l++) {
889 uintptr_t tmp = 0;
890 arkUnwindParam->readMem(arkUnwindParam->ctx, funcAddr + l, &tmp);
891 codeVec.push_back(tmp);
892 }
893 arkUnwindParam->jitCache.push_back(*arkUnwindParam->methodId);
894 JsStackInfo::machineCodeMap[EntityId(*arkUnwindParam->methodId)] = codeVec;
895 }
896 }
897 return true;
898 }
899
ArkGetNextFrameWithJit(ArkUnwindParam * arkUnwindParam,uintptr_t & currentPtr,uintptr_t & frameType)900 bool ArkGetNextFrameWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t ¤tPtr, uintptr_t &frameType)
901 {
902 currentPtr -= sizeof(FrameType);
903 if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, &frameType)) {
904 return false;
905 }
906 if (ArkFrameCheck(frameType)) {
907 return true;
908 }
909 bool ret = false;
910 if (IsFunctionFrame(frameType)) {
911 *arkUnwindParam->pc = GetBytecodeOffset(arkUnwindParam->ctx, arkUnwindParam->readMem, frameType, currentPtr);
912 ret = true;
913 if (arkUnwindParam->methodId != nullptr) {
914 ret = ArkGetMethodIdWithJit(arkUnwindParam, frameType, currentPtr);
915 }
916 }
917
918 uintptr_t typeOffset = 0;
919 uintptr_t prevOffset = 0;
920 if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
921 return false;
922 }
923 currentPtr -= typeOffset;
924 currentPtr += prevOffset;
925 if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, ¤tPtr)) {
926 return false;
927 }
928
929 if (ret) {
930 return true;
931 }
932 return ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType);
933 }
934
ArkWriteJitCode(void * ctx,ReadMemFunc readMem,int fd,const uintptr_t * const jitCodeArray,const size_t jitSize)935 bool ArkWriteJitCode([[maybe_unused]] void *ctx, [[maybe_unused]] ReadMemFunc readMem,
936 int fd, const uintptr_t *const jitCodeArray, const size_t jitSize)
937 {
938 JsJitDumpElf jitDumpElf;
939 jitDumpElf.Init();
940 std::set<uintptr_t> memos;
941 int64 idx = 0;
942 size_t offset = 0;
943 for (size_t i = 0; i < jitSize; i++) {
944 uintptr_t methodId = jitCodeArray[i];
945 auto res = memos.insert(methodId);
946 if (res.second) {
947 std::vector<uint8> codeVec = JsStackInfo::machineCodeMap[EntityId(methodId)];
948 std::string name = JsStackInfo::nameMap[EntityId(methodId)];
949 size_t len = codeVec.size();
950 jitDumpElf.AppendData(codeVec);
951 jitDumpElf.AppendSymbolToSymTab(idx++, offset, len, name);
952 offset += len;
953 }
954 }
955 jitDumpElf.WriteJitElfFile(fd);
956 JsStackInfo::nameMap.clear();
957 JsStackInfo::machineCodeMap.clear();
958 return true;
959 }
960
StepArkWithRecordJit(ArkUnwindParam * arkUnwindParam)961 bool StepArkWithRecordJit(ArkUnwindParam *arkUnwindParam)
962 {
963 constexpr size_t FP_SIZE = sizeof(uintptr_t);
964 uintptr_t currentPtr = *arkUnwindParam->fp;
965 if (currentPtr == 0) {
966 LOG_ECMA(ERROR) << "fp is nullptr in StepArkWithRecordJit()!";
967 return false;
968 }
969
970 uintptr_t frameType = 0;
971 if (ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType)) {
972 if (ArkFrameCheck(frameType)) {
973 currentPtr += sizeof(FrameType);
974 *arkUnwindParam->sp = currentPtr;
975 bool ret = arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->fp);
976 currentPtr += FP_SIZE;
977 ret &= arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->pc);
978 *arkUnwindParam->isJsFrame = false;
979 return ret;
980 } else {
981 *arkUnwindParam->fp = currentPtr;
982 *arkUnwindParam->sp = currentPtr;
983 *arkUnwindParam->isJsFrame = true;
984 }
985 } else {
986 LOG_ECMA(ERROR) << "ArkGetNextFrame failed, currentPtr: " << currentPtr << ", frameType: " << frameType;
987 return false;
988 }
989 return true;
990 }
991
StepArk(void * ctx,ReadMemFunc readMem,uintptr_t * fp,uintptr_t * sp,uintptr_t * pc,uintptr_t * methodId,bool * isJsFrame)992 bool StepArk(void *ctx, ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp,
993 uintptr_t *pc, uintptr_t *methodId, bool *isJsFrame)
994 {
995 constexpr size_t FP_SIZE = sizeof(uintptr_t);
996 uintptr_t currentPtr = *fp;
997 if (currentPtr == 0) {
998 LOG_ECMA(ERROR) << "fp is nullptr in StepArk()!";
999 return false;
1000 }
1001
1002 uintptr_t frameType = 0;
1003 if (ArkGetNextFrame(ctx, readMem, currentPtr, frameType, *pc, methodId)) {
1004 if (ArkFrameCheck(frameType)) {
1005 currentPtr += sizeof(FrameType);
1006 *sp = currentPtr;
1007 bool ret = readMem(ctx, currentPtr, fp);
1008 currentPtr += FP_SIZE;
1009 ret &= readMem(ctx, currentPtr, pc);
1010 *isJsFrame = false;
1011 return ret;
1012 } else {
1013 *fp = currentPtr;
1014 *sp = currentPtr;
1015 *isJsFrame = true;
1016 }
1017 } else {
1018 LOG_ECMA(ERROR) << std::hex << "ArkGetNextFrame failed, addr: " << currentPtr;
1019 return false;
1020 }
1021
1022 return true;
1023 }
1024
ArkGetFunction(int pid,uintptr_t currentPtr,uintptr_t frameType)1025 uintptr_t ArkGetFunction(int pid, uintptr_t currentPtr, uintptr_t frameType)
1026 {
1027 FrameType type = static_cast<FrameType>(frameType);
1028 uintptr_t funcAddr = currentPtr;
1029 switch (type) {
1030 case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
1031 case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
1032 funcAddr -= OptimizedJSFunctionFrame::GetTypeOffset();
1033 funcAddr += OptimizedJSFunctionFrame::GetFunctionOffset();
1034 break;
1035 }
1036 case FrameType::ASM_INTERPRETER_FRAME:
1037 case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
1038 funcAddr -= AsmInterpretedFrame::GetTypeOffset();
1039 funcAddr += AsmInterpretedFrame::GetFunctionOffset(false);
1040 break;
1041 }
1042 case FrameType::INTERPRETER_FRAME:
1043 case FrameType::INTERPRETER_FAST_NEW_FRAME: {
1044 funcAddr -= InterpretedFrame::GetTypeOffset();
1045 funcAddr += InterpretedFrame::GetFunctionOffset();
1046 break;
1047 }
1048 case FrameType::INTERPRETER_BUILTIN_FRAME: {
1049 funcAddr -= InterpretedBuiltinFrame::GetTypeOffset();
1050 funcAddr += InterpretedBuiltinFrame::GetFunctionOffset();
1051 break;
1052 }
1053 case FrameType::BUILTIN_FRAME_WITH_ARGV: {
1054 funcAddr += sizeof(FrameType);
1055 auto topAddress = funcAddr +
1056 (static_cast<int>(BuiltinWithArgvFrame::Index::StackArgsTopIndex) * sizeof(uintptr_t));
1057 uintptr_t argcAddress = static_cast<uintptr_t>(funcAddr + (static_cast<int>
1058 (BuiltinWithArgvFrame::Index::NumArgsIndex) * sizeof(uintptr_t)));
1059 if (!ReadUintptrFromAddr(pid, argcAddress, argcAddress, g_needCheck)) {
1060 return 0;
1061 }
1062 auto numberArgs = argcAddress + NUM_MANDATORY_JSFUNC_ARGS;
1063 funcAddr = topAddress - static_cast<uint32_t>(numberArgs) * sizeof(uintptr_t);
1064 break;
1065 }
1066 case FrameType::BUILTIN_ENTRY_FRAME:
1067 case FrameType::BUILTIN_FRAME: {
1068 funcAddr -= BuiltinFrame::GetTypeOffset();
1069 funcAddr += BuiltinFrame::GetStackArgsOffset();
1070 break;
1071 }
1072 case FrameType::BUILTIN_CALL_LEAVE_FRAME: {
1073 funcAddr -= OptimizedBuiltinLeaveFrame::GetTypeOffset();
1074 funcAddr += OptimizedBuiltinLeaveFrame::GetFunctionOffset();
1075 break;
1076 }
1077 case FrameType::FASTJIT_FUNCTION_FRAME:
1078 case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
1079 funcAddr -= FASTJITFunctionFrame::GetTypeOffset();
1080 funcAddr += FASTJITFunctionFrame::GetFunctionOffset();
1081 break;
1082 }
1083 case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME :
1084 case FrameType::OPTIMIZED_FRAME:
1085 case FrameType::OPTIMIZED_ENTRY_FRAME:
1086 case FrameType::ASM_BRIDGE_FRAME:
1087 case FrameType::LEAVE_FRAME:
1088 case FrameType::LEAVE_FRAME_WITH_ARGV:
1089 case FrameType::INTERPRETER_ENTRY_FRAME:
1090 case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
1091 case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
1092 case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
1093 case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
1094 return 0;
1095 }
1096 default: {
1097 LOG_FULL(FATAL) << "Unknown frame type: " << static_cast<uintptr_t>(type);
1098 UNREACHABLE();
1099 }
1100 }
1101 uintptr_t function = 0;
1102 if (!ReadUintptrFromAddr(pid, funcAddr, function, g_needCheck)) {
1103 return 0;
1104 }
1105 return function;
1106 }
1107
ArkCheckIsJSFunctionBaseOrJSProxy(int pid,uintptr_t objAddr,bool & isJSFunctionBase)1108 bool ArkCheckIsJSFunctionBaseOrJSProxy(int pid, uintptr_t objAddr, bool &isJSFunctionBase)
1109 {
1110 bool isHeapObj = ((objAddr & JSTaggedValue::TAG_HEAPOBJECT_MASK) == 0U);
1111 bool isInvalidValue = (objAddr <= JSTaggedValue::INVALID_VALUE_LIMIT);
1112 if (isHeapObj && !isInvalidValue) {
1113 ASSERT_PRINT(((objAddr & JSTaggedValue::TAG_WEAK) == 0U),
1114 "can not convert JSTaggedValue to HeapObject :" << std::hex << objAddr);
1115 uintptr_t hclassAddr = objAddr + TaggedObject::HCLASS_OFFSET;
1116 uintptr_t hclass = 0;
1117 if (!ReadUintptrFromAddr(pid, hclassAddr, hclass, g_needCheck)) {
1118 return false;
1119 }
1120 if (hclass != 0) {
1121 uintptr_t bitsAddr = reinterpret_cast<uintptr_t>(hclass + JSHClass::BIT_FIELD_OFFSET);
1122 uintptr_t bits = 0;
1123 if (!ReadUintptrFromAddr(pid, bitsAddr, bits, g_needCheck)) {
1124 return false;
1125 }
1126 JSType jsType = JSHClass::ObjectTypeBits::Decode(bits);
1127 isJSFunctionBase = (jsType >= JSType::JS_FUNCTION_BASE && jsType <= JSType::JS_BOUND_FUNCTION);
1128 bool isJSProxy = (jsType == JSType::JS_PROXY);
1129 return isJSFunctionBase || isJSProxy;
1130 }
1131 }
1132 return false;
1133 }
1134
ArkCheckAndGetMethod(int pid,uintptr_t value)1135 uintptr_t ArkCheckAndGetMethod(int pid, uintptr_t value)
1136 {
1137 bool isJSFunctionBase = 0;
1138 if (ArkCheckIsJSFunctionBaseOrJSProxy(pid, value, isJSFunctionBase)) {
1139 if (isJSFunctionBase) {
1140 value += JSFunctionBase::METHOD_OFFSET;
1141 } else {
1142 value += JSProxy::METHOD_OFFSET;
1143 }
1144 uintptr_t method = 0;
1145 if (!ReadUintptrFromAddr(pid, value, method, g_needCheck)) {
1146 return 0;
1147 }
1148 return method;
1149 }
1150 return 0;
1151 }
1152
ArkGetMethodIdandJSPandaFileAddr(int pid,uintptr_t method,uintptr_t & methodId,uintptr_t & jsPandaFileAddr)1153 bool ArkGetMethodIdandJSPandaFileAddr(int pid, uintptr_t method, uintptr_t &methodId, uintptr_t &jsPandaFileAddr)
1154 {
1155 uintptr_t methodLiteralAddr = method + Method::LITERAL_INFO_OFFSET;
1156 uintptr_t methodLiteral = 0;
1157 if (!ReadUintptrFromAddr(pid, methodLiteralAddr, methodLiteral, g_needCheck)) {
1158 return false;
1159 }
1160 methodId = MethodLiteral::MethodIdBits::Decode(methodLiteral);
1161 uintptr_t constantpoolAddr = method + Method::CONSTANT_POOL_OFFSET;
1162 uintptr_t constantpool = 0;
1163 if (!ReadUintptrFromAddr(pid, constantpoolAddr, constantpool, g_needCheck)) {
1164 return false;
1165 }
1166 if (constantpool == JSTaggedValue::VALUE_UNDEFINED) {
1167 return false;
1168 }
1169 uintptr_t lengthAddr = constantpool + TaggedArray::LENGTH_OFFSET;
1170 uintptr_t length = 0;
1171 if (!ReadUintptrFromAddr(pid, lengthAddr, length, g_needCheck)) {
1172 return false;
1173 }
1174 jsPandaFileAddr = constantpool + TaggedArray::DATA_OFFSET +
1175 JSTaggedValue::TaggedTypeSize() * (length - ConstantPool::JS_PANDA_FILE_INDEX);
1176 if (!ReadUintptrFromAddr(pid, jsPandaFileAddr, jsPandaFileAddr, g_needCheck)) {
1177 return false;
1178 }
1179 return true;
1180 }
1181
ArkGetOffsetFromMethod(int pid,uintptr_t currentPtr,uintptr_t method)1182 uint32_t ArkGetOffsetFromMethod(int pid, uintptr_t currentPtr, uintptr_t method)
1183 {
1184 uintptr_t pc = 0;
1185 if (!ReadUintptrFromAddr(pid, currentPtr, pc, g_needCheck)) {
1186 return 0;
1187 }
1188 uintptr_t byteCodeArrayAddr = method + Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET;
1189 uintptr_t byteCodeArray = 0;
1190 if (!ReadUintptrFromAddr(pid, byteCodeArrayAddr, byteCodeArray, g_needCheck)) {
1191 return 0;
1192 }
1193 uintptr_t offset = pc - byteCodeArray;
1194 return static_cast<uint32_t>(offset);
1195 }
1196
ArkGetBytecodeOffset(int pid,uintptr_t method,uintptr_t frameType,uintptr_t currentPtr)1197 uint32_t ArkGetBytecodeOffset(int pid, uintptr_t method, uintptr_t frameType, uintptr_t currentPtr)
1198 {
1199 FrameType type = static_cast<FrameType>(frameType);
1200 switch (type) {
1201 case FrameType::ASM_INTERPRETER_FRAME:
1202 case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
1203 currentPtr -= AsmInterpretedFrame::GetTypeOffset();
1204 currentPtr += AsmInterpretedFrame::GetPcOffset(false);
1205 return ArkGetOffsetFromMethod(pid, currentPtr, method);
1206 }
1207 case FrameType::INTERPRETER_FRAME:
1208 case FrameType::INTERPRETER_FAST_NEW_FRAME: {
1209 currentPtr -= InterpretedFrame::GetTypeOffset();
1210 currentPtr += InterpretedFrame::GetPcOffset(false);
1211 return ArkGetOffsetFromMethod(pid, currentPtr, method);
1212 }
1213 // aot need stackmaps
1214 case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
1215 case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
1216 case FrameType::FASTJIT_FUNCTION_FRAME:
1217 case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
1218 break;
1219 }
1220 default: {
1221 break;
1222 }
1223 }
1224 return 0;
1225 }
1226
ArkGetFilePath(std::string & fileName)1227 std::string ArkGetFilePath(std::string &fileName)
1228 {
1229 auto lastSlash = fileName.rfind("/");
1230 if (lastSlash == std::string::npos) {
1231 LOG_ECMA(ERROR) << "ArkGetFilePath can't find fisrt /: " << fileName;
1232 return "";
1233 }
1234 if (lastSlash == 0) {
1235 LOG_ECMA(ERROR) << "ArkGetFilePath can't find second /: " << fileName;
1236 return "";
1237 }
1238
1239 auto secondLastSlash = fileName.rfind("/", lastSlash - 1);
1240 if (secondLastSlash == std::string::npos) {
1241 LOG_ECMA(ERROR) << "ArkGetFilePath can't find second /: " << fileName;
1242 return "";
1243 }
1244
1245 std::string mapPath = fileName.substr(secondLastSlash + 1);
1246 return mapPath;
1247 }
1248
ArkIsNativeWithCallField(int pid,uintptr_t method)1249 bool ArkIsNativeWithCallField(int pid, uintptr_t method)
1250 {
1251 uintptr_t callFieldAddr = method + Method::CALL_FIELD_OFFSET;
1252 uintptr_t callField = 0;
1253 if (!ReadUintptrFromAddr(pid, callFieldAddr, callField, g_needCheck)) {
1254 return true;
1255 }
1256 return Method::IsNativeBit::Decode(callField);
1257 }
1258
ArkReadCStringFromAddr(int pid,uintptr_t descAddr)1259 std::string ArkReadCStringFromAddr(int pid, uintptr_t descAddr)
1260 {
1261 std::string name;
1262 bool key = true;
1263 while (key) {
1264 uintptr_t desc = 0;
1265 if (!ReadUintptrFromAddr(pid, descAddr, desc, g_needCheck)) {
1266 LOG_ECMA(ERROR) << "ArkReadCStringFromAddr failed, descAddr: " << descAddr;
1267 return name;
1268 }
1269 size_t shiftAmount = 8;
1270 for (size_t i = 0; i < sizeof(long); i++) {
1271 char bottomEightBits = static_cast<char>(desc);
1272 desc = desc >> shiftAmount;
1273 if (!bottomEightBits) {
1274 key = false;
1275 break;
1276 }
1277 name += bottomEightBits;
1278 }
1279 if (!key) {
1280 break;
1281 }
1282 descAddr += sizeof(long);
1283 }
1284 return name;
1285 }
1286
ArkGetFileName(int pid,uintptr_t jsPandaFileAddr,std::string & hapPath)1287 std::string ArkGetFileName(int pid, uintptr_t jsPandaFileAddr, std::string &hapPath)
1288 {
1289 size_t size = sizeof(JSPandaFile) / sizeof(long);
1290 uintptr_t *jsPandaFilePart = new uintptr_t[size]();
1291 if (jsPandaFilePart == nullptr) {
1292 LOG_ECMA(FATAL) << "ArkGetFileName:jsPandaFilePart is nullptr";
1293 }
1294 for (size_t i = 0; i < size; i++) {
1295 if (!ReadUintptrFromAddr(pid, jsPandaFileAddr, jsPandaFilePart[i], g_needCheck)) {
1296 LOG_ECMA(ERROR) << "ArkGetFilePath failed, jsPandaFileAddr: " << jsPandaFileAddr;
1297 delete []jsPandaFilePart;
1298 return "";
1299 }
1300 jsPandaFileAddr += sizeof(long);
1301 }
1302 JSPandaFile *jsPandaFile = reinterpret_cast<JSPandaFile *>(jsPandaFilePart);
1303
1304 uintptr_t hapPathAddr = reinterpret_cast<uintptr_t>(
1305 const_cast<char *>(jsPandaFile->GetJSPandaFileHapPath().c_str()));
1306 hapPath = ArkReadCStringFromAddr(pid, hapPathAddr);
1307
1308 uintptr_t descAddr = reinterpret_cast<uintptr_t>(
1309 const_cast<char *>(jsPandaFile->GetJSPandaFileDesc().c_str()));
1310 delete []jsPandaFilePart;
1311 return ArkReadCStringFromAddr(pid, descAddr);
1312 }
1313
ArkReadData(const std::string & hapPath,const std::string & fileName,size_t & dataSize)1314 std::unique_ptr<uint8_t[]> ArkReadData([[maybe_unused]] const std::string &hapPath,
1315 [[maybe_unused]] const std::string &fileName,
1316 [[maybe_unused]] size_t &dataSize)
1317 {
1318 std::unique_ptr<uint8_t[]> dataPtr = nullptr;
1319 #if defined(PANDA_TARGET_OHOS)
1320 bool newCreate = false;
1321 std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(
1322 ExtractorUtil::GetLoadFilePath(hapPath), newCreate);
1323 if (extractor == nullptr) {
1324 LOG_ECMA(ERROR) << "Ark read data failed, hapPath: " << hapPath;
1325 return dataPtr;
1326 }
1327 if (!extractor->ExtractToBufByName(fileName, dataPtr, dataSize)) {
1328 LOG_ECMA(ERROR) << "Ark read data failed, hap/hsp path: " << hapPath << ", file name: " << fileName;
1329 return dataPtr;
1330 }
1331 #endif
1332 return dataPtr;
1333 }
1334
OpenJSPandaFileByReadData(const std::string & hapPath,const std::string & fileName)1335 std::shared_ptr<JSPandaFile> OpenJSPandaFileByReadData(const std::string &hapPath, const std::string &fileName)
1336 {
1337 size_t dataSize = 0;
1338 auto data = ArkReadData(hapPath, fileName, dataSize);
1339 if (data == nullptr) {
1340 return nullptr;
1341 }
1342 auto pf = panda_file::OpenPandaFileFromMemory(data.get(), dataSize);
1343 if (pf == nullptr) {
1344 return nullptr;
1345 }
1346 return std::make_shared<JSPandaFile>(pf.release(), fileName.c_str());
1347 }
1348
ArkParseJsFrameDebugInfos(const std::vector<JsFrameDebugInfo> & JsFrameDebugInfos,size_t size,JsFrame * jsFrame,size_t & jsFrameIndex)1349 void ArkParseJsFrameDebugInfos([[maybe_unused]] const std::vector<JsFrameDebugInfo> &JsFrameDebugInfos,
1350 [[maybe_unused]] size_t size, [[maybe_unused]] JsFrame *jsFrame,
1351 [[maybe_unused]] size_t &jsFrameIndex)
1352 {
1353 #if defined(PANDA_TARGET_OHOS)
1354 jsFrameIndex = 0;
1355 size = JsFrameDebugInfos.size() > size ? size : JsFrameDebugInfos.size();
1356 std::unordered_map<std::string, std::shared_ptr<JSPandaFile>> jsPandaFileTable;
1357 for (size_t i = 0; i < size; ++i) {
1358 auto fileIter = jsPandaFileTable.find(JsFrameDebugInfos[i].hapPath);
1359 if (fileIter == jsPandaFileTable.end()) {
1360 auto jsPandaFile = OpenJSPandaFileByReadData(JsFrameDebugInfos[i].hapPath, JsFrameDebugInfos[i].filePath);
1361 if (jsPandaFile != nullptr) {
1362 jsPandaFileTable.emplace(JsFrameDebugInfos[i].hapPath, jsPandaFile);
1363 auto debugExtractor = std::make_unique<DebugInfoExtractor>(jsPandaFile.get());
1364 ParseJsFrameInfo(jsPandaFile.get(), debugExtractor.get(), JsFrameDebugInfos[i].methodId,
1365 JsFrameDebugInfos[i].offset, jsFrame[jsFrameIndex]);
1366 jsFrameIndex++;
1367 }
1368 } else {
1369 auto jsPandaFile = fileIter->second;
1370 auto debugExtractor = std::make_unique<DebugInfoExtractor>(jsPandaFile.get());
1371 ParseJsFrameInfo(jsPandaFile.get(), debugExtractor.get(), JsFrameDebugInfos[i].methodId,
1372 JsFrameDebugInfos[i].offset, jsFrame[jsFrameIndex]);
1373 jsFrameIndex++;
1374 }
1375 }
1376 #endif
1377 }
1378
ArkGetJsFrameDebugInfo(int pid,uintptr_t currentPtr,uintptr_t frameType,std::vector<JsFrameDebugInfo> & JsFrameDebugInfos)1379 bool ArkGetJsFrameDebugInfo(int pid, uintptr_t currentPtr, uintptr_t frameType,
1380 std::vector<JsFrameDebugInfo> &JsFrameDebugInfos)
1381 {
1382 uintptr_t function = ArkGetFunction(pid, currentPtr, frameType);
1383 if (!function) {
1384 return false;
1385 }
1386
1387 uintptr_t method = ArkCheckAndGetMethod(pid, function);
1388 if (!method || ArkIsNativeWithCallField(pid, method)) {
1389 return false;
1390 }
1391 uintptr_t jsPandaFileAddr = 0;
1392 uintptr_t methodId = 0;
1393 if (!ArkGetMethodIdandJSPandaFileAddr(pid, method, methodId, jsPandaFileAddr)) {
1394 LOG_ECMA(ERROR) << "ArkGetJsFrameDebugInfo failed, method: " << method;
1395 return false;
1396 }
1397 uintptr_t offset = ArkGetBytecodeOffset(pid, method, frameType, currentPtr);
1398 std::string hapPath;
1399 std::string fileName = ArkGetFileName(pid, jsPandaFileAddr, hapPath);
1400 if (fileName.empty() || hapPath.empty()) {
1401 LOG_ECMA(DEBUG) << "ArkGetJsFrameDebugInfo get filename or hapPath failed, fileName: "
1402 << fileName << ", hapPath: "<< hapPath;
1403 return false;
1404 }
1405 std::string filePath = ArkGetFilePath(fileName);
1406 if (filePath.empty()) {
1407 return false;
1408 }
1409 JsFrameDebugInfo JsFrameDebugInfo(EntityId(methodId), offset, hapPath, filePath);
1410 JsFrameDebugInfos.push_back(std::move(JsFrameDebugInfo));
1411 return true;
1412 }
1413
ArkGetNextFrame(int pid,uintptr_t frameType,uintptr_t & currentPtr)1414 bool ArkGetNextFrame(int pid, uintptr_t frameType, uintptr_t ¤tPtr)
1415 {
1416 uintptr_t typeOffset = 0;
1417 uintptr_t prevOffset = 0;
1418 if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
1419 LOG_ECMA(ERROR) << "FrameType ERROR, addr: " << currentPtr << ", frameType: " << frameType;
1420 return false;
1421 }
1422 currentPtr -= typeOffset;
1423 currentPtr += prevOffset;
1424 if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr, g_needCheck)) {
1425 return false;
1426 }
1427 if (currentPtr == 0) {
1428 LOG_ECMA(ERROR) << "currentPtr is nullptr in GetArkNativeFrameInfo()!";
1429 return false;
1430 }
1431 return true;
1432 }
1433
GetArkNativeFrameInfo(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,JsFrame * jsFrame,size_t & size)1434 bool GetArkNativeFrameInfo([[maybe_unused]] int pid, [[maybe_unused]] uintptr_t *pc,
1435 [[maybe_unused]] uintptr_t *fp, [[maybe_unused]] uintptr_t *sp,
1436 [[maybe_unused]] JsFrame *jsFrame, [[maybe_unused]] size_t &size)
1437 {
1438 #if defined(PANDA_TARGET_OHOS)
1439 constexpr size_t FP_SIZE = sizeof(uintptr_t);
1440 uintptr_t currentPtr = *fp;
1441 if (pid == getpid()) {
1442 g_needCheck = false;
1443 }
1444 if (currentPtr == 0) {
1445 LOG_ECMA(ERROR) << "fp is nullptr in GetArkNativeFrameInfo()!";
1446 return false;
1447 }
1448
1449 if (pid == getpid() && JsStackInfo::loader != nullptr &&
1450 !JsStackInfo::loader->InsideStub(*pc) && !JsStackInfo::loader->InsideAOT(*pc)) {
1451 LOG_ECMA(ERROR) << "invalid pc in StepArkManagedNativeFrame()!";
1452 return false;
1453 }
1454
1455 std::vector<JsFrameDebugInfo> JsFrameDebugInfos;
1456 bool ret = true;
1457 while (true) {
1458 currentPtr -= sizeof(FrameType);
1459 uintptr_t frameType = 0;
1460 if (!ReadUintptrFromAddr(pid, currentPtr, frameType, g_needCheck)) {
1461 return false;
1462 }
1463 if (g_needCheck && IsFunctionFrame(frameType)) {
1464 ArkGetJsFrameDebugInfo(pid, currentPtr, frameType, JsFrameDebugInfos);
1465 } else if (ArkFrameCheck(frameType)) {
1466 currentPtr += sizeof(FrameType);
1467 *sp = currentPtr;
1468 ret &= ReadUintptrFromAddr(pid, currentPtr, *fp, g_needCheck);
1469 currentPtr += FP_SIZE;
1470 ret &= ReadUintptrFromAddr(pid, currentPtr, *pc, g_needCheck);
1471 break;
1472 }
1473
1474 if (!ArkGetNextFrame(pid, frameType, currentPtr)) {
1475 return false;
1476 }
1477 }
1478 if (g_needCheck && !JsFrameDebugInfos.empty()) {
1479 ArkParseJsFrameDebugInfos(JsFrameDebugInfos, size, jsFrame, size);
1480 } else {
1481 size = 0;
1482 }
1483 return ret;
1484 #else
1485 return false;
1486 #endif
1487 }
1488
GetData()1489 uint8_t* JSSymbolExtractor::GetData()
1490 {
1491 return data_;
1492 }
1493
GetLoadOffset()1494 uintptr_t JSSymbolExtractor::GetLoadOffset()
1495 {
1496 return loadOffset_;
1497 }
1498
GetDataSize()1499 uintptr_t JSSymbolExtractor::GetDataSize()
1500 {
1501 return dataSize_;
1502 }
1503
ParseHapFileData(std::string & hapName)1504 bool JSSymbolExtractor::ParseHapFileData([[maybe_unused]] std::string& hapName)
1505 {
1506 bool ret = false;
1507 #if defined(PANDA_TARGET_OHOS)
1508 if (hapName.empty()) {
1509 LOG_ECMA(ERROR) << "Get file data failed, path empty.";
1510 return false;
1511 }
1512 bool newCreate = false;
1513 std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(hapName, newCreate);
1514 if (extractor == nullptr) {
1515 LOG_ECMA(ERROR) << "GetExtractor failed, hap path: " << hapName;
1516 return false;
1517 }
1518
1519 std::string pandaFilePath = "ets/modules.abc";
1520 auto data = extractor->GetSafeData(pandaFilePath);
1521 if (!data) {
1522 LOG_ECMA(ERROR) << "GetSafeData failed, hap path: " << hapName;
1523 return false;
1524 }
1525
1526 data_ = data->GetDataPtr();
1527 dataSize_ = data->GetDataLen();
1528 loadOffset_ = static_cast<uintptr_t>(data->GetOffset());
1529 ret = true;
1530 auto zipFile = std::make_unique<ZipFile>(hapName);
1531 if (zipFile == nullptr || !zipFile->Open()) {
1532 return false;
1533 }
1534 auto &entrys = zipFile->GetAllEntries();
1535 if (ret) {
1536 std::string filePath = "ets/sourceMaps.map";
1537 if (entrys.find(filePath) == entrys.end()) {
1538 LOG_ECMA(INFO) << "Can't find sourceMaps.map in hap/hsp";
1539 return ret;
1540 }
1541 CreateSourceMap(hapName);
1542 }
1543 #endif
1544 return ret;
1545 }
1546
ArkParseJSFileInfo(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,const char * filePath,uintptr_t extractorptr,JsFunction * jsFunction)1547 bool ArkParseJSFileInfo([[maybe_unused]] uintptr_t byteCodePc, [[maybe_unused]] uintptr_t methodId,
1548 [[maybe_unused]] uintptr_t mapBase, [[maybe_unused]] const char* filePath,
1549 [[maybe_unused]] uintptr_t extractorptr, [[maybe_unused]] JsFunction *jsFunction)
1550 {
1551 bool ret = false;
1552 #if defined(PANDA_TARGET_OHOS)
1553 if (filePath == nullptr) {
1554 LOG_ECMA(ERROR) << "FilePath from dfx is nullptr.";
1555 return false;
1556 }
1557 auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
1558 if (extractor == nullptr) {
1559 LOG_ECMA(ERROR) << "Parse JSframe info failed, extractor is nullptr.";
1560 return false;
1561 }
1562 if (extractor->GetJSPandaFile() == nullptr) {
1563 std::string hapName = std::string(filePath);
1564 extractor->ParseHapFileData(hapName);
1565 extractor->CreateJSPandaFile();
1566 }
1567 ret = ArkParseJsFrameInfo(byteCodePc, methodId, mapBase, extractor->GetLoadOffset(),
1568 extractor->GetData(), extractor->GetDataSize(), extractorptr, jsFunction);
1569 #endif
1570 return ret;
1571 }
1572
~JSSymbolExtractor()1573 JSSymbolExtractor::~JSSymbolExtractor()
1574 {
1575 if (sourceMap_ != nullptr) {
1576 sourceMap_.reset();
1577 }
1578 if (debugExtractor_ != nullptr) {
1579 debugExtractor_.reset();
1580 }
1581 if (jsPandaFile_ != nullptr) {
1582 jsPandaFile_.reset();
1583 }
1584 methodInfo_.clear();
1585 }
1586
Create()1587 JSSymbolExtractor* JSSymbolExtractor::Create()
1588 {
1589 auto extractor = new JSSymbolExtractor();
1590 return extractor;
1591 }
1592
Destory(JSSymbolExtractor * extractor)1593 bool JSSymbolExtractor::Destory(JSSymbolExtractor *extractor)
1594 {
1595 if (extractor == nullptr) {
1596 LOG_ECMA(ERROR) << "Destory ark symbol extractor failed, extractor is nullptr.";
1597 return false;
1598 }
1599 delete extractor;
1600 extractor = nullptr;
1601 return true;
1602 }
1603
GetMethodInfos()1604 CVector<MethodInfo> JSSymbolExtractor::GetMethodInfos()
1605 {
1606 if (methodInfo_.empty()) {
1607 methodInfo_ = JSStackTrace::ReadAllMethodInfos(jsPandaFile_);
1608 }
1609
1610 return methodInfo_;
1611 }
1612
GetJSPandaFile(uint8_t * data,size_t dataSize)1613 JSPandaFile* JSSymbolExtractor::GetJSPandaFile(uint8_t *data, size_t dataSize)
1614 {
1615 if (jsPandaFile_ == nullptr && data != nullptr) {
1616 CreateJSPandaFile(data, dataSize);
1617 }
1618 return jsPandaFile_.get();
1619 }
1620
CreateJSPandaFile()1621 void JSSymbolExtractor::CreateJSPandaFile()
1622 {
1623 auto pf = panda_file::OpenPandaFileFromSecureMemory(data_, dataSize_);
1624 if (pf == nullptr) {
1625 LOG_ECMA(ERROR) << "Failed to open panda file.";
1626 return;
1627 }
1628 jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "");
1629 }
1630
CreateJSPandaFile(uint8_t * data,size_t dataSize)1631 void JSSymbolExtractor::CreateJSPandaFile(uint8_t *data, size_t dataSize)
1632 {
1633 auto pf = panda_file::OpenPandaFileFromSecureMemory(data, dataSize);
1634 if (pf == nullptr) {
1635 LOG_ECMA(ERROR) << "Failed to open panda file.";
1636 return;
1637 }
1638 jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "");
1639 }
1640
GetSourceMap(uint8_t * data,size_t dataSize)1641 SourceMap* JSSymbolExtractor::GetSourceMap(uint8_t *data, size_t dataSize)
1642 {
1643 if (sourceMap_ == nullptr && data != nullptr) {
1644 JSSymbolExtractor::CreateSourceMap(data, dataSize);
1645 }
1646 return sourceMap_.get();
1647 }
1648
CreateSourceMap(const std::string & hapPath)1649 void JSSymbolExtractor::CreateSourceMap([[maybe_unused]] const std::string &hapPath)
1650 {
1651 #if defined(PANDA_TARGET_OHOS)
1652 if (sourceMap_ == nullptr) {
1653 sourceMap_ = std::make_shared<SourceMap>();
1654 sourceMap_->Init(hapPath);
1655 }
1656 #endif
1657 }
1658
CreateSourceMap(uint8_t * data,size_t dataSize)1659 void JSSymbolExtractor::CreateSourceMap(uint8_t *data, size_t dataSize)
1660 {
1661 sourceMap_ = std::make_shared<SourceMap>();
1662 sourceMap_->Init(data, dataSize);
1663 }
1664
GetDebugExtractor()1665 DebugInfoExtractor* JSSymbolExtractor::GetDebugExtractor()
1666 {
1667 if (debugExtractor_ == nullptr) {
1668 JSSymbolExtractor::CreateDebugExtractor();
1669 }
1670 return debugExtractor_.get();
1671 }
1672
CreateDebugExtractor()1673 void JSSymbolExtractor::CreateDebugExtractor()
1674 {
1675 debugExtractor_ = std::make_unique<DebugInfoExtractor>(jsPandaFile_.get());
1676 }
1677
ArkCreateJSSymbolExtractor()1678 uintptr_t ArkCreateJSSymbolExtractor()
1679 {
1680 auto extractor = JSSymbolExtractor::Create();
1681 auto extractorptr = reinterpret_cast<uintptr_t>(extractor);
1682 return extractorptr;
1683 }
1684
ArkDestoryJSSymbolExtractor(uintptr_t extractorptr)1685 bool ArkDestoryJSSymbolExtractor(uintptr_t extractorptr)
1686 {
1687 auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
1688 return JSSymbolExtractor::Destory(extractor);
1689 }
1690
AddReference()1691 void JSStackTrace::AddReference()
1692 {
1693 std::unique_lock<std::mutex> lock(mutex_);
1694 if (count_ == 0) {
1695 trace_ = new JSStackTrace();
1696 }
1697 ++count_;
1698 LOG_ECMA(INFO) << "Add reference, count: " << count_;
1699 }
1700
ReleaseReference()1701 void JSStackTrace::ReleaseReference()
1702 {
1703 std::unique_lock<std::mutex> lock(mutex_);
1704 if (trace_ == nullptr) {
1705 return ;
1706 }
1707 --count_;
1708 LOG_ECMA(INFO) << "Release reference, count: " << count_;
1709 if (count_ == 0) {
1710 delete trace_;
1711 trace_ = nullptr;
1712 }
1713 }
1714
~JSStackTrace()1715 JSStackTrace::~JSStackTrace()
1716 {
1717 {
1718 std::unique_lock<std::shared_mutex> lock(infosMutex_);
1719 methodInfos_.clear();
1720 }
1721 {
1722 std::unique_lock<std::shared_mutex> lock(pfMutex_);
1723 jsPandaFiles_.clear();
1724 }
1725 }
1726
InitializeMethodInfo(uintptr_t mapBase)1727 bool JSStackTrace::InitializeMethodInfo(uintptr_t mapBase)
1728 {
1729 auto pandafile = FindJSpandaFile(mapBase);
1730 if (pandafile != nullptr) {
1731 return true;
1732 }
1733 pandafile =
1734 JSPandaFileManager::GetInstance()->FindJSPandaFileByMapBase(mapBase);
1735 if (pandafile == nullptr) {
1736 LOG_ECMA(ERROR) << "Find pandafile failed, mapBase: " << std::hex << mapBase;
1737 return false;
1738 }
1739 auto methodInfos = ReadAllMethodInfos(pandafile);
1740 SetMethodInfos(mapBase, methodInfos);
1741 SetJSpandaFile(mapBase, pandafile);
1742 return true;
1743 }
1744
FindJSpandaFile(uintptr_t mapBase)1745 std::shared_ptr<JSPandaFile> JSStackTrace::FindJSpandaFile(uintptr_t mapBase)
1746 {
1747 std::shared_lock<std::shared_mutex> lock(pfMutex_);
1748 auto iter = jsPandaFiles_.find(mapBase);
1749 if (iter == jsPandaFiles_.end()) {
1750 return nullptr;
1751 }
1752 return iter->second;
1753 }
1754
SetJSpandaFile(uintptr_t mapBase,std::shared_ptr<JSPandaFile> pandafile)1755 void JSStackTrace::SetJSpandaFile(uintptr_t mapBase, std::shared_ptr<JSPandaFile> pandafile)
1756 {
1757 std::unique_lock<std::shared_mutex> lock(pfMutex_);
1758 jsPandaFiles_.emplace(mapBase, pandafile);
1759 }
1760
FindMethodInfos(uintptr_t mapBase)1761 const CVector<MethodInfo> &JSStackTrace::FindMethodInfos(uintptr_t mapBase)
1762 {
1763 std::shared_lock<std::shared_mutex> lock(infosMutex_);
1764 auto iter = methodInfos_.find(mapBase);
1765 if (iter == methodInfos_.end()) {
1766 return methodInfo_;
1767 }
1768 return iter->second;
1769 }
1770
SetMethodInfos(uintptr_t mapBase,CVector<MethodInfo> & infos)1771 void JSStackTrace::SetMethodInfos(uintptr_t mapBase, CVector<MethodInfo> &infos)
1772 {
1773 std::unique_lock<std::shared_mutex> lock(infosMutex_);
1774 methodInfos_.emplace(mapBase, std::move(infos));
1775 }
1776
GetJsFrameInfo(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,JsFunction * jsFunction)1777 bool JSStackTrace::GetJsFrameInfo(uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase,
1778 uintptr_t loadOffset, JsFunction *jsFunction)
1779 {
1780 if (!InitializeMethodInfo(mapBase)) {
1781 return false;
1782 }
1783 loadOffset = loadOffset % PageSize();
1784 byteCodePc = byteCodePc - loadOffset;
1785 auto infos = FindMethodInfos(mapBase);
1786 auto codeInfo = TranslateByteCodePc(byteCodePc, infos);
1787 if (!codeInfo) {
1788 LOG_ECMA(ERROR) << std::hex << "Failed to get methodId, pc: " << byteCodePc;
1789 return false;
1790 }
1791 if (!methodId) {
1792 methodId = codeInfo->methodId;
1793 }
1794 auto offset = codeInfo->offset;
1795 auto pandafile = FindJSpandaFile(mapBase);
1796 auto debugInfoExtractor =
1797 JSPandaFileManager::GetInstance()->GetJSPtExtractor(pandafile.get());
1798 ParseJsFrameInfo(pandafile.get(), debugInfoExtractor, EntityId(methodId), offset, *jsFunction);
1799 jsFunction->codeBegin = byteCodePc - offset;
1800 jsFunction->codeSize = codeInfo->codeSize;
1801 return true;
1802 }
1803
ArkCreateLocal()1804 void ArkCreateLocal()
1805 {
1806 JSStackTrace::AddReference();
1807 }
1808
ArkParseJsFrameInfoLocal(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,JsFunction * jsFunction)1809 bool ArkParseJsFrameInfoLocal(uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase,
1810 uintptr_t loadOffset, JsFunction *jsFunction)
1811 {
1812 auto trace = JSStackTrace::GetInstance();
1813 if (trace == nullptr) {
1814 LOG_ECMA(ERROR) << "singleton is null, need create first.";
1815 return false;
1816 }
1817 return trace->GetJsFrameInfo(byteCodePc, methodId, mapBase, loadOffset, jsFunction);
1818 }
1819
ArkDestoryLocal()1820 void ArkDestoryLocal()
1821 {
1822 JSStackTrace::ReleaseReference();
1823 }
1824
1825 } // namespace panda::ecmascript
1826
ark_create_js_symbol_extractor(uintptr_t * extractorptr)1827 __attribute__((visibility("default"))) int ark_create_js_symbol_extractor(uintptr_t *extractorptr)
1828 {
1829 *extractorptr = panda::ecmascript::ArkCreateJSSymbolExtractor();
1830 return 1;
1831 }
1832
ark_destory_js_symbol_extractor(uintptr_t extractorptr)1833 __attribute__((visibility("default"))) int ark_destory_js_symbol_extractor(uintptr_t extractorptr)
1834 {
1835 if (panda::ecmascript::ArkDestoryJSSymbolExtractor(extractorptr)) {
1836 return 1;
1837 }
1838 return -1;
1839 }
1840
ark_destroy_local()1841 __attribute__((visibility("default"))) int ark_destroy_local()
1842 {
1843 panda::ecmascript::ArkDestoryLocal();
1844 return 1;
1845 }
1846
ark_create_local()1847 __attribute__((visibility("default"))) int ark_create_local()
1848 {
1849 panda::ecmascript::ArkCreateLocal();
1850 return 1;
1851 }
1852
step_ark_with_record_jit(panda::ecmascript::ArkUnwindParam * arkUnwindParam)1853 __attribute__((visibility("default"))) int step_ark_with_record_jit(panda::ecmascript::ArkUnwindParam *arkUnwindParam)
1854 {
1855 if (panda::ecmascript::StepArkWithRecordJit(arkUnwindParam)) {
1856 return 1;
1857 }
1858 return -1;
1859 }
1860
ark_write_jit_code(void * ctx,panda::ecmascript::ReadMemFunc readMem,int fd,const uintptr_t * const jitCodeArray,const size_t jitSize)1861 __attribute__((visibility("default"))) int ark_write_jit_code(
1862 void *ctx, panda::ecmascript::ReadMemFunc readMem, int fd, const uintptr_t *const jitCodeArray,
1863 const size_t jitSize)
1864 {
1865 if (panda::ecmascript::ArkWriteJitCode(ctx, readMem, fd, jitCodeArray, jitSize)) {
1866 return 1;
1867 }
1868 return -1;
1869 }
1870
step_ark(void * ctx,panda::ecmascript::ReadMemFunc readMem,uintptr_t * fp,uintptr_t * sp,uintptr_t * pc,uintptr_t * methodId,bool * isJsFrame)1871 __attribute__((visibility("default"))) int step_ark(
1872 void *ctx, panda::ecmascript::ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp,
1873 uintptr_t *pc, uintptr_t *methodId, bool *isJsFrame)
1874 {
1875 if (panda::ecmascript::StepArk(ctx, readMem, fp, sp, pc, methodId, isJsFrame)) {
1876 return 1;
1877 }
1878 return -1;
1879 }
1880
ark_parse_js_frame_info(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,uint8_t * data,uint64_t dataSize,uintptr_t extractorptr,panda::ecmascript::JsFunction * jsFunction)1881 __attribute__((visibility("default"))) int ark_parse_js_frame_info(
1882 uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase, uintptr_t loadOffset, uint8_t *data,
1883 uint64_t dataSize, uintptr_t extractorptr, panda::ecmascript::JsFunction *jsFunction)
1884 {
1885 if (panda::ecmascript::ArkParseJsFrameInfo(byteCodePc, methodId, mapBase, loadOffset, data,
1886 dataSize, extractorptr, jsFunction)) {
1887 return 1;
1888 }
1889 return -1;
1890 }
1891
ark_parse_js_file_info(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,const char * filePath,uintptr_t extractorptr,panda::ecmascript::JsFunction * jsFunction)1892 __attribute__((visibility("default"))) int ark_parse_js_file_info(
1893 uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase, const char* filePath, uintptr_t extractorptr,
1894 panda::ecmascript::JsFunction *jsFunction)
1895 {
1896 if (panda::ecmascript::ArkParseJSFileInfo(byteCodePc, methodId, mapBase, filePath, extractorptr, jsFunction)) {
1897 return 1;
1898 }
1899 return -1;
1900 }
1901
ark_translate_js_frame_info(uint8_t * data,size_t dataSize,panda::ecmascript::JsFunction * jsFunction)1902 __attribute__((visibility("default"))) int ark_translate_js_frame_info(
1903 uint8_t *data, size_t dataSize, panda::ecmascript::JsFunction *jsFunction)
1904 {
1905 if (panda::ecmascript::ArkTranslateJsFrameInfo(data, dataSize, jsFunction)) {
1906 return 1;
1907 }
1908 return -1;
1909 }
1910
get_ark_native_frame_info(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,panda::ecmascript::JsFrame * jsFrame,size_t & size)1911 __attribute__((visibility("default"))) int get_ark_native_frame_info(
1912 int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp,
1913 panda::ecmascript::JsFrame *jsFrame, size_t &size)
1914 {
1915 if (panda::ecmascript::GetArkNativeFrameInfo(pid, pc, fp, sp, jsFrame, size)) {
1916 return 1;
1917 }
1918 return -1;
1919 }
1920
ark_parse_js_frame_info_local(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,panda::ecmascript::JsFunction * jsFunction)1921 __attribute__((visibility("default"))) int ark_parse_js_frame_info_local(
1922 uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase, uintptr_t loadOffset,
1923 panda::ecmascript::JsFunction *jsFunction)
1924 {
1925 if (panda::ecmascript::ArkParseJsFrameInfoLocal(byteCodePc, methodId, mapBase, loadOffset, jsFunction)) {
1926 return 1;
1927 }
1928 return -1;
1929 }
1930