• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) {
251         return;
252     }
253     if (!JsStackInfo::loader->IsEnableAOT() && !Jit::GetInstance()->IsEnableFastJit() &&
254         !pgo::PGOProfilerManager::GetInstance()->IsEnable()) {
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     methodId = codeInfo->methodId;
637     auto offset = codeInfo->offset;
638     ParseJsFrameInfo(jsPandaFile, debugExtractor, EntityId(methodId), offset, *jsFunction, extractor->GetSourceMap());
639 
640     jsFunction->codeBegin = byteCodePc - offset;
641     jsFunction->codeSize = codeInfo->codeSize;
642     return true;
643 }
644 
ArkTranslateJsFrameInfo(uint8_t * data,size_t dataSize,JsFunction * jsFunction)645 bool ArkTranslateJsFrameInfo(uint8_t *data, size_t dataSize, JsFunction *jsFunction)
646 {
647     SourceMap sourceMap;
648     std::string strUrl = jsFunction->url;
649     sourceMap.Init(data, dataSize);
650     bool ret = sourceMap.TranslateUrlPositionBySourceMap(strUrl, jsFunction->line, jsFunction->column);
651     size_t strUrlSize = strUrl.size() + 1;
652     if (strcpy_s(jsFunction->url, strUrlSize, strUrl.c_str()) != EOK) {
653         LOG_FULL(FATAL) << "strcpy_s failed";
654         UNREACHABLE();
655     }
656     return ret;
657 }
658 
GetBytecodeOffset(void * ctx,ReadMemFunc readMem,uintptr_t frameType,uintptr_t currentPtr)659 uintptr_t GetBytecodeOffset(void *ctx, ReadMemFunc readMem, uintptr_t frameType, uintptr_t currentPtr)
660 {
661     // currentPtr points to the frametype.
662     uintptr_t bytecodePc = 0;
663     FrameType type = static_cast<FrameType>(frameType);
664     switch (type) {
665         case FrameType::ASM_INTERPRETER_FRAME:
666         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
667             currentPtr -= AsmInterpretedFrame::GetTypeOffset();
668             currentPtr += AsmInterpretedFrame::GetPcOffset(false);
669             readMem(ctx, currentPtr, &bytecodePc);
670             return bytecodePc;
671         }
672         case FrameType::INTERPRETER_FRAME:
673         case FrameType::INTERPRETER_FAST_NEW_FRAME: {
674             currentPtr -= InterpretedFrame::GetTypeOffset();
675             currentPtr += InterpretedFrame::GetPcOffset(false);
676             readMem(ctx, currentPtr, &bytecodePc);
677             return bytecodePc;
678         }
679         // aot get native pc
680         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
681         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
682             currentPtr -= OptimizedJSFunctionFrame::GetTypeOffset();
683             currentPtr += OptimizedJSFunctionFrame::GetReturnAddrOffset();
684             readMem(ctx, currentPtr, &bytecodePc);
685             return bytecodePc;
686         }
687         case FrameType::FASTJIT_FUNCTION_FRAME:
688         case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
689             currentPtr -= FASTJITFunctionFrame::GetTypeOffset();
690             readMem(ctx, currentPtr, &bytecodePc);
691             return bytecodePc;
692         }
693         default: {
694             break;
695         }
696     }
697     return 0;
698 }
699 
ArkGetFunction(void * ctx,ReadMemFunc readMem,uintptr_t currentPtr,uintptr_t frameType)700 uintptr_t ArkGetFunction(void *ctx, ReadMemFunc readMem, uintptr_t currentPtr, uintptr_t frameType)
701 {
702     FrameType type = static_cast<FrameType>(frameType);
703     uintptr_t funcAddr = currentPtr;
704     switch (type) {
705         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
706         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
707             funcAddr -= OptimizedJSFunctionFrame::GetTypeOffset();
708             funcAddr += OptimizedJSFunctionFrame::GetFunctionOffset();
709             break;
710         }
711         case FrameType::ASM_INTERPRETER_FRAME:
712         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
713             funcAddr -= AsmInterpretedFrame::GetTypeOffset();
714             funcAddr += AsmInterpretedFrame::GetFunctionOffset(false);
715             break;
716         }
717         case FrameType::INTERPRETER_FRAME:
718         case FrameType::INTERPRETER_FAST_NEW_FRAME: {
719             funcAddr -= InterpretedFrame::GetTypeOffset();
720             funcAddr += InterpretedFrame::GetFunctionOffset();
721             break;
722         }
723         case FrameType::FASTJIT_FUNCTION_FRAME:
724         case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
725             funcAddr -= FASTJITFunctionFrame::GetTypeOffset();
726             funcAddr += FASTJITFunctionFrame::GetFunctionOffset();
727             break;
728         }
729         default: {
730             return 0;
731         }
732     }
733     uintptr_t function = 0;
734     if (!readMem(ctx, funcAddr, &function)) {
735         return 0;
736     }
737     return function;
738 }
739 
ArkCheckIsJSFunctionBaseOrJSProxy(void * ctx,ReadMemFunc readMem,uintptr_t objAddr,bool & isJSFunctionBase)740 bool ArkCheckIsJSFunctionBaseOrJSProxy(void *ctx, ReadMemFunc readMem, uintptr_t objAddr, bool &isJSFunctionBase)
741 {
742     bool isHeapObj = ((objAddr & JSTaggedValue::TAG_HEAPOBJECT_MASK) == 0U);
743     bool isInvalidValue = (objAddr <= JSTaggedValue::INVALID_VALUE_LIMIT);
744     if (isHeapObj && !isInvalidValue) {
745         ASSERT_PRINT(((objAddr & JSTaggedValue::TAG_WEAK) == 0U),
746                      "can not convert JSTaggedValue to HeapObject :" << std::hex << objAddr);
747         uintptr_t hclassAddr = objAddr + TaggedObject::HCLASS_OFFSET;
748         uintptr_t hclass = 0;
749         if (!readMem(ctx, hclassAddr, &hclass)) {
750             return false;
751         }
752         if (hclass != 0) {
753             uintptr_t bitsAddr = reinterpret_cast<uintptr_t>(hclass + JSHClass::BIT_FIELD_OFFSET);
754             uintptr_t bits = 0;
755             if (!readMem(ctx, bitsAddr, &bits)) {
756                 return false;
757             }
758             JSType jsType = JSHClass::ObjectTypeBits::Decode(bits);
759             isJSFunctionBase = (jsType >= JSType::JS_FUNCTION_BASE && jsType <= JSType::JS_BOUND_FUNCTION);
760             bool isJSProxy = (jsType == JSType::JS_PROXY);
761             return isJSFunctionBase || isJSProxy;
762         }
763     }
764     return false;
765 }
766 
ArkCheckAndGetMethod(void * ctx,ReadMemFunc readMem,uintptr_t value)767 uintptr_t ArkCheckAndGetMethod(void *ctx, ReadMemFunc readMem, uintptr_t value)
768 {
769     bool isJSFunctionBase = 0;
770     if (ArkCheckIsJSFunctionBaseOrJSProxy(ctx, readMem, value, isJSFunctionBase)) {
771         if (isJSFunctionBase) {
772             value += JSFunctionBase::METHOD_OFFSET;
773         } else {
774             value += JSProxy::METHOD_OFFSET;
775         }
776         uintptr_t method = 0;
777         if (!readMem(ctx, value, &method)) {
778             return 0;
779         }
780         return method;
781     }
782     return 0;
783 }
784 
ArkGetMethodIdFromMethod(void * ctx,ReadMemFunc readMem,uintptr_t method,uintptr_t & methodId)785 bool ArkGetMethodIdFromMethod(void *ctx, ReadMemFunc readMem, uintptr_t method, uintptr_t &methodId)
786 {
787     uintptr_t methodLiteralAddr = method + Method::LITERAL_INFO_OFFSET;
788     uintptr_t methodLiteral = 0;
789     if (!readMem(ctx, methodLiteralAddr, &methodLiteral)) {
790         return false;
791     }
792     methodId = MethodLiteral::MethodIdBits::Decode(methodLiteral);
793     return true;
794 }
795 
ArkGetMethodId(void * ctx,ReadMemFunc readMem,uintptr_t frameType,uintptr_t currentPtr,uintptr_t & methodId)796 bool ArkGetMethodId(void *ctx, ReadMemFunc readMem, uintptr_t frameType, uintptr_t currentPtr, uintptr_t &methodId)
797 {
798     uintptr_t function = ArkGetFunction(ctx, readMem, currentPtr, frameType);
799     if (!function) {
800         LOG_ECMA(DEBUG) << "Failed to get function";
801         return false;
802     }
803 
804     uintptr_t method = ArkCheckAndGetMethod(ctx, readMem, function);
805     if (!method) {
806         LOG_ECMA(DEBUG) << std::hex << "Failed to get method: " << function;
807         return false;
808     }
809 
810     if (!ArkGetMethodIdFromMethod(ctx, readMem, method, methodId)) {
811         LOG_ECMA(DEBUG) << std::hex << "ArkGetJsFrameDebugInfo failed, method: " << method;
812         return false;
813     }
814     return true;
815 }
816 
ArkGetNextFrame(void * ctx,ReadMemFunc readMem,uintptr_t & currentPtr,uintptr_t & frameType,uintptr_t & pc,uintptr_t * methodId)817 bool ArkGetNextFrame(void *ctx, ReadMemFunc readMem, uintptr_t &currentPtr,
818                      uintptr_t &frameType, uintptr_t &pc, uintptr_t *methodId)
819 {
820     currentPtr -= sizeof(FrameType);
821     if (!readMem(ctx, currentPtr, &frameType)) {
822         return false;
823     }
824     if (ArkFrameCheck(frameType)) {
825         return true;
826     }
827     bool ret = false;
828     if (IsFunctionFrame(frameType)) {
829         pc = GetBytecodeOffset(ctx, readMem, frameType, currentPtr);
830         ret = true;
831         if (methodId != nullptr) {
832             ret = ArkGetMethodId(ctx, readMem, frameType, currentPtr, *methodId);
833         }
834     }
835 
836     uintptr_t typeOffset = 0;
837     uintptr_t prevOffset = 0;
838     if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
839         return false;
840     }
841     currentPtr -= typeOffset;
842     currentPtr += prevOffset;
843     if (!readMem(ctx, currentPtr, &currentPtr)) {
844         return false;
845     }
846 
847     if (ret) {
848         return true;
849     }
850     return ArkGetNextFrame(ctx, readMem, currentPtr, frameType, pc, methodId);
851 }
852 
ArkGetMethodIdWithJit(ArkUnwindParam * arkUnwindParam,uintptr_t frameType,uintptr_t currentPtr)853 bool ArkGetMethodIdWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t frameType, uintptr_t currentPtr)
854 {
855     uintptr_t function = ArkGetFunction(arkUnwindParam->ctx, arkUnwindParam->readMem, currentPtr, frameType);
856     if (!function) {
857         LOG_ECMA(DEBUG) << "Failed to get function";
858         return false;
859     }
860 
861     uintptr_t method = ArkCheckAndGetMethod(arkUnwindParam->ctx, arkUnwindParam->readMem, function);
862     if (!method) {
863         LOG_ECMA(DEBUG) << std::hex << "Failed to get method: " << function;
864         return false;
865     }
866 
867     if (!ArkGetMethodIdFromMethod(arkUnwindParam->ctx, arkUnwindParam->readMem, method, *arkUnwindParam->methodId)) {
868         LOG_ECMA(DEBUG) << std::hex << "ArkGetJsFrameDebugInfo failed, method: " << method;
869         return false;
870     }
871 
872     if (IsFastJitFunctionFrame(static_cast<FrameType>(frameType))) {
873         uintptr_t machineCode = 0;
874         uintptr_t functionAddr = function + JSFunction::MACHINECODE_OFFSET;
875         arkUnwindParam->readMem(arkUnwindParam->ctx, functionAddr, &machineCode);
876         uintptr_t size = 0;
877         uintptr_t funcAddr = 0;
878         if (machineCode) {
879             arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::INSTRSIZ_OFFSET, &size);
880             arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::FUNCADDR_OFFSET, &funcAddr);
881         }
882         if (size && funcAddr) {
883             // take the lower four bytes
884             size &= 0xFFFFFFFF;
885             std::vector<uint8> codeVec;
886             for (size_t l = 0; l < size; l++) {
887                 uintptr_t tmp = 0;
888                 arkUnwindParam->readMem(arkUnwindParam->ctx, funcAddr + l, &tmp);
889                 codeVec.push_back(tmp);
890             }
891             arkUnwindParam->jitCache.push_back(*arkUnwindParam->methodId);
892             JsStackInfo::machineCodeMap[EntityId(*arkUnwindParam->methodId)] = codeVec;
893         }
894     }
895     return true;
896 }
897 
ArkGetNextFrameWithJit(ArkUnwindParam * arkUnwindParam,uintptr_t & currentPtr,uintptr_t & frameType)898 bool ArkGetNextFrameWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t &currentPtr, uintptr_t &frameType)
899 {
900     currentPtr -= sizeof(FrameType);
901     if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, &frameType)) {
902         return false;
903     }
904     if (ArkFrameCheck(frameType)) {
905         return true;
906     }
907     bool ret = false;
908     if (IsFunctionFrame(frameType)) {
909         *arkUnwindParam->pc = GetBytecodeOffset(arkUnwindParam->ctx, arkUnwindParam->readMem, frameType, currentPtr);
910         ret = true;
911         if (arkUnwindParam->methodId != nullptr) {
912             ret = ArkGetMethodIdWithJit(arkUnwindParam, frameType, currentPtr);
913         }
914     }
915 
916     uintptr_t typeOffset = 0;
917     uintptr_t prevOffset = 0;
918     if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
919         return false;
920     }
921     currentPtr -= typeOffset;
922     currentPtr += prevOffset;
923     if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, &currentPtr)) {
924         return false;
925     }
926 
927     if (ret) {
928         return true;
929     }
930     return ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType);
931 }
932 
ArkWriteJitCode(void * ctx,ReadMemFunc readMem,int fd,const uintptr_t * const jitCodeArray,const size_t jitSize)933 bool ArkWriteJitCode([[maybe_unused]] void *ctx, [[maybe_unused]] ReadMemFunc readMem,
934                      int fd, const uintptr_t *const jitCodeArray, const size_t jitSize)
935 {
936     JsJitDumpElf jitDumpElf;
937     jitDumpElf.Init();
938     std::set<uintptr_t> memos;
939     int64 idx = 0;
940     size_t offset = 0;
941     for (size_t i = 0; i < jitSize; i++) {
942         uintptr_t methodId = jitCodeArray[i];
943         auto res = memos.insert(methodId);
944         if (res.second) {
945             std::vector<uint8> codeVec = JsStackInfo::machineCodeMap[EntityId(methodId)];
946             std::string name = JsStackInfo::nameMap[EntityId(methodId)];
947             size_t len = codeVec.size();
948             jitDumpElf.AppendData(codeVec);
949             jitDumpElf.AppendSymbolToSymTab(idx++, offset, len, name);
950             offset += len;
951         }
952     }
953     jitDumpElf.WriteJitElfFile(fd);
954     JsStackInfo::nameMap.clear();
955     JsStackInfo::machineCodeMap.clear();
956     return true;
957 }
958 
StepArkWithRecordJit(ArkUnwindParam * arkUnwindParam)959 bool StepArkWithRecordJit(ArkUnwindParam *arkUnwindParam)
960 {
961     constexpr size_t FP_SIZE = sizeof(uintptr_t);
962     uintptr_t currentPtr = *arkUnwindParam->fp;
963     if (currentPtr == 0) {
964         LOG_ECMA(ERROR) << "fp is nullptr in StepArkWithRecordJit()!";
965         return false;
966     }
967 
968     uintptr_t frameType = 0;
969     if (ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType)) {
970         if (ArkFrameCheck(frameType)) {
971             currentPtr += sizeof(FrameType);
972             *arkUnwindParam->sp = currentPtr;
973             bool ret = arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->fp);
974             currentPtr += FP_SIZE;
975             ret &= arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->pc);
976             *arkUnwindParam->isJsFrame = false;
977             return ret;
978         } else {
979             *arkUnwindParam->fp = currentPtr;
980             *arkUnwindParam->sp = currentPtr;
981             *arkUnwindParam->isJsFrame = true;
982         }
983     } else {
984         LOG_ECMA(ERROR) << "ArkGetNextFrame failed, currentPtr: " << currentPtr << ", frameType: " << frameType;
985         return false;
986     }
987     return true;
988 }
989 
StepArk(void * ctx,ReadMemFunc readMem,uintptr_t * fp,uintptr_t * sp,uintptr_t * pc,uintptr_t * methodId,bool * isJsFrame)990 bool StepArk(void *ctx, ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp,
991              uintptr_t *pc, uintptr_t *methodId, bool *isJsFrame)
992 {
993     constexpr size_t FP_SIZE = sizeof(uintptr_t);
994     uintptr_t currentPtr = *fp;
995     if (currentPtr == 0) {
996         LOG_ECMA(ERROR) << "fp is nullptr in StepArk()!";
997         return false;
998     }
999 
1000     uintptr_t frameType = 0;
1001     if (ArkGetNextFrame(ctx, readMem, currentPtr, frameType, *pc, methodId)) {
1002         if (ArkFrameCheck(frameType)) {
1003             currentPtr += sizeof(FrameType);
1004             *sp = currentPtr;
1005             bool ret = readMem(ctx, currentPtr, fp);
1006             currentPtr += FP_SIZE;
1007             ret &= readMem(ctx, currentPtr, pc);
1008             *isJsFrame = false;
1009             return ret;
1010         } else {
1011             *fp = currentPtr;
1012             *sp = currentPtr;
1013             *isJsFrame = true;
1014         }
1015     } else {
1016         LOG_ECMA(ERROR) << std::hex << "ArkGetNextFrame failed, addr: " << currentPtr;
1017         return false;
1018     }
1019 
1020     return true;
1021 }
1022 
ArkGetFunction(int pid,uintptr_t currentPtr,uintptr_t frameType)1023 uintptr_t ArkGetFunction(int pid, uintptr_t currentPtr, uintptr_t frameType)
1024 {
1025     FrameType type = static_cast<FrameType>(frameType);
1026     uintptr_t funcAddr = currentPtr;
1027     switch (type) {
1028         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
1029         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
1030             funcAddr -= OptimizedJSFunctionFrame::GetTypeOffset();
1031             funcAddr += OptimizedJSFunctionFrame::GetFunctionOffset();
1032             break;
1033         }
1034         case FrameType::ASM_INTERPRETER_FRAME:
1035         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
1036             funcAddr -= AsmInterpretedFrame::GetTypeOffset();
1037             funcAddr += AsmInterpretedFrame::GetFunctionOffset(false);
1038             break;
1039         }
1040         case FrameType::INTERPRETER_FRAME:
1041         case FrameType::INTERPRETER_FAST_NEW_FRAME: {
1042             funcAddr -= InterpretedFrame::GetTypeOffset();
1043             funcAddr += InterpretedFrame::GetFunctionOffset();
1044             break;
1045         }
1046         case FrameType::INTERPRETER_BUILTIN_FRAME: {
1047             funcAddr -= InterpretedBuiltinFrame::GetTypeOffset();
1048             funcAddr += InterpretedBuiltinFrame::GetFunctionOffset();
1049             break;
1050         }
1051         case FrameType::BUILTIN_FRAME_WITH_ARGV: {
1052             funcAddr += sizeof(FrameType);
1053             auto topAddress = funcAddr +
1054                 (static_cast<int>(BuiltinWithArgvFrame::Index::StackArgsTopIndex) * sizeof(uintptr_t));
1055             uintptr_t argcAddress = static_cast<uintptr_t>(funcAddr + (static_cast<int>
1056                                     (BuiltinWithArgvFrame::Index::NumArgsIndex) * sizeof(uintptr_t)));
1057             if (!ReadUintptrFromAddr(pid, argcAddress, argcAddress, g_needCheck)) {
1058                 return 0;
1059             }
1060             auto numberArgs = argcAddress + NUM_MANDATORY_JSFUNC_ARGS;
1061             funcAddr = topAddress - static_cast<uint32_t>(numberArgs) * sizeof(uintptr_t);
1062             break;
1063         }
1064         case FrameType::BUILTIN_ENTRY_FRAME:
1065         case FrameType::BUILTIN_FRAME: {
1066             funcAddr -= BuiltinFrame::GetTypeOffset();
1067             funcAddr += BuiltinFrame::GetStackArgsOffset();
1068             break;
1069         }
1070         case FrameType::BUILTIN_CALL_LEAVE_FRAME: {
1071             funcAddr -= OptimizedBuiltinLeaveFrame::GetTypeOffset();
1072             funcAddr += OptimizedBuiltinLeaveFrame::GetFunctionOffset();
1073             break;
1074         }
1075         case FrameType::FASTJIT_FUNCTION_FRAME:
1076         case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
1077             funcAddr -= FASTJITFunctionFrame::GetTypeOffset();
1078             funcAddr += FASTJITFunctionFrame::GetFunctionOffset();
1079             break;
1080         }
1081         case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME :
1082         case FrameType::OPTIMIZED_FRAME:
1083         case FrameType::OPTIMIZED_ENTRY_FRAME:
1084         case FrameType::ASM_BRIDGE_FRAME:
1085         case FrameType::LEAVE_FRAME:
1086         case FrameType::LEAVE_FRAME_WITH_ARGV:
1087         case FrameType::INTERPRETER_ENTRY_FRAME:
1088         case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
1089         case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
1090         case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
1091         case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
1092             return 0;
1093         }
1094         default: {
1095             LOG_FULL(FATAL) << "Unknown frame type: " << static_cast<uintptr_t>(type);
1096             UNREACHABLE();
1097         }
1098     }
1099     uintptr_t function = 0;
1100     if (!ReadUintptrFromAddr(pid, funcAddr, function, g_needCheck)) {
1101         return 0;
1102     }
1103     return function;
1104 }
1105 
ArkCheckIsJSFunctionBaseOrJSProxy(int pid,uintptr_t objAddr,bool & isJSFunctionBase)1106 bool ArkCheckIsJSFunctionBaseOrJSProxy(int pid, uintptr_t objAddr, bool &isJSFunctionBase)
1107 {
1108     bool isHeapObj = ((objAddr & JSTaggedValue::TAG_HEAPOBJECT_MASK) == 0U);
1109     bool isInvalidValue = (objAddr <= JSTaggedValue::INVALID_VALUE_LIMIT);
1110     if (isHeapObj && !isInvalidValue) {
1111         ASSERT_PRINT(((objAddr & JSTaggedValue::TAG_WEAK) == 0U),
1112                      "can not convert JSTaggedValue to HeapObject :" << std::hex << objAddr);
1113         uintptr_t hclassAddr = objAddr + TaggedObject::HCLASS_OFFSET;
1114         uintptr_t hclass = 0;
1115         if (!ReadUintptrFromAddr(pid, hclassAddr, hclass, g_needCheck)) {
1116             return false;
1117         }
1118         if (hclass != 0) {
1119             uintptr_t bitsAddr = reinterpret_cast<uintptr_t>(hclass + JSHClass::BIT_FIELD_OFFSET);
1120             uintptr_t bits = 0;
1121             if (!ReadUintptrFromAddr(pid, bitsAddr, bits, g_needCheck)) {
1122                 return false;
1123             }
1124             JSType jsType = JSHClass::ObjectTypeBits::Decode(bits);
1125             isJSFunctionBase = (jsType >= JSType::JS_FUNCTION_BASE && jsType <= JSType::JS_BOUND_FUNCTION);
1126             bool isJSProxy = (jsType == JSType::JS_PROXY);
1127             return isJSFunctionBase || isJSProxy;
1128         }
1129     }
1130     return false;
1131 }
1132 
ArkCheckAndGetMethod(int pid,uintptr_t value)1133 uintptr_t ArkCheckAndGetMethod(int pid, uintptr_t value)
1134 {
1135     bool isJSFunctionBase = 0;
1136     if (ArkCheckIsJSFunctionBaseOrJSProxy(pid, value, isJSFunctionBase)) {
1137         if (isJSFunctionBase) {
1138             value += JSFunctionBase::METHOD_OFFSET;
1139         } else {
1140             value += JSProxy::METHOD_OFFSET;
1141         }
1142         uintptr_t method = 0;
1143         if (!ReadUintptrFromAddr(pid, value, method, g_needCheck)) {
1144             return 0;
1145         }
1146         return method;
1147     }
1148     return 0;
1149 }
1150 
ArkGetMethodIdandJSPandaFileAddr(int pid,uintptr_t method,uintptr_t & methodId,uintptr_t & jsPandaFileAddr)1151 bool ArkGetMethodIdandJSPandaFileAddr(int pid, uintptr_t method, uintptr_t &methodId, uintptr_t &jsPandaFileAddr)
1152 {
1153     uintptr_t methodLiteralAddr = method + Method::LITERAL_INFO_OFFSET;
1154     uintptr_t methodLiteral = 0;
1155     if (!ReadUintptrFromAddr(pid, methodLiteralAddr, methodLiteral, g_needCheck)) {
1156         return false;
1157     }
1158     methodId = MethodLiteral::MethodIdBits::Decode(methodLiteral);
1159     uintptr_t constantpoolAddr = method + Method::CONSTANT_POOL_OFFSET;
1160     uintptr_t constantpool = 0;
1161     if (!ReadUintptrFromAddr(pid, constantpoolAddr, constantpool, g_needCheck)) {
1162         return false;
1163     }
1164     if (constantpool == JSTaggedValue::VALUE_UNDEFINED) {
1165         return false;
1166     }
1167     uintptr_t lengthAddr = constantpool + TaggedArray::LENGTH_OFFSET;
1168     uintptr_t length = 0;
1169     if (!ReadUintptrFromAddr(pid, lengthAddr, length, g_needCheck)) {
1170         return false;
1171     }
1172     jsPandaFileAddr = constantpool + TaggedArray::DATA_OFFSET +
1173                     JSTaggedValue::TaggedTypeSize() * (length - ConstantPool::JS_PANDA_FILE_INDEX);
1174     if (!ReadUintptrFromAddr(pid, jsPandaFileAddr, jsPandaFileAddr, g_needCheck)) {
1175         return false;
1176     }
1177     return true;
1178 }
1179 
ArkGetOffsetFromMethod(int pid,uintptr_t currentPtr,uintptr_t method)1180 uint32_t ArkGetOffsetFromMethod(int pid, uintptr_t currentPtr, uintptr_t method)
1181 {
1182     uintptr_t pc = 0;
1183     if (!ReadUintptrFromAddr(pid, currentPtr, pc, g_needCheck)) {
1184         return 0;
1185     }
1186     uintptr_t byteCodeArrayAddr = method + Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET;
1187     uintptr_t byteCodeArray = 0;
1188     if (!ReadUintptrFromAddr(pid, byteCodeArrayAddr, byteCodeArray, g_needCheck)) {
1189         return 0;
1190     }
1191     uintptr_t offset = pc - byteCodeArray;
1192     return static_cast<uint32_t>(offset);
1193 }
1194 
ArkGetBytecodeOffset(int pid,uintptr_t method,uintptr_t frameType,uintptr_t currentPtr)1195 uint32_t ArkGetBytecodeOffset(int pid, uintptr_t method, uintptr_t frameType, uintptr_t currentPtr)
1196 {
1197     FrameType type = static_cast<FrameType>(frameType);
1198     switch (type) {
1199         case FrameType::ASM_INTERPRETER_FRAME:
1200         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
1201             currentPtr -= AsmInterpretedFrame::GetTypeOffset();
1202             currentPtr += AsmInterpretedFrame::GetPcOffset(false);
1203             return ArkGetOffsetFromMethod(pid, currentPtr, method);
1204         }
1205         case FrameType::INTERPRETER_FRAME:
1206         case FrameType::INTERPRETER_FAST_NEW_FRAME: {
1207             currentPtr -= InterpretedFrame::GetTypeOffset();
1208             currentPtr += InterpretedFrame::GetPcOffset(false);
1209             return ArkGetOffsetFromMethod(pid, currentPtr, method);
1210         }
1211         // aot need stackmaps
1212         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
1213         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
1214         case FrameType::FASTJIT_FUNCTION_FRAME:
1215         case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
1216             break;
1217         }
1218         default: {
1219             break;
1220         }
1221     }
1222     return 0;
1223 }
1224 
ArkGetFilePath(std::string & fileName)1225 std::string ArkGetFilePath(std::string &fileName)
1226 {
1227     auto lastSlash = fileName.rfind("/");
1228     if (lastSlash == std::string::npos) {
1229         LOG_ECMA(ERROR) << "ArkGetFilePath can't find fisrt /: " << fileName;
1230         return "";
1231     }
1232     if (lastSlash == 0) {
1233         LOG_ECMA(ERROR) << "ArkGetFilePath can't find second /: " << fileName;
1234         return "";
1235     }
1236 
1237     auto secondLastSlash = fileName.rfind("/", lastSlash - 1);
1238     if (secondLastSlash == std::string::npos) {
1239         LOG_ECMA(ERROR) << "ArkGetFilePath can't find second /: " << fileName;
1240         return "";
1241     }
1242 
1243     std::string mapPath = fileName.substr(secondLastSlash + 1);
1244     return mapPath;
1245 }
1246 
ArkIsNativeWithCallField(int pid,uintptr_t method)1247 bool ArkIsNativeWithCallField(int pid, uintptr_t method)
1248 {
1249     uintptr_t callFieldAddr = method + Method::CALL_FIELD_OFFSET;
1250     uintptr_t callField = 0;
1251     if (!ReadUintptrFromAddr(pid, callFieldAddr, callField, g_needCheck)) {
1252         return true;
1253     }
1254     return Method::IsNativeBit::Decode(callField);
1255 }
1256 
ArkReadCStringFromAddr(int pid,uintptr_t descAddr)1257 std::string ArkReadCStringFromAddr(int pid, uintptr_t descAddr)
1258 {
1259     std::string name;
1260     bool key = true;
1261     while (key) {
1262         uintptr_t desc = 0;
1263         if (!ReadUintptrFromAddr(pid, descAddr, desc, g_needCheck)) {
1264             LOG_ECMA(ERROR) << "ArkReadCStringFromAddr failed, descAddr: " << descAddr;
1265             return name;
1266         }
1267         size_t shiftAmount = 8;
1268         for (size_t i = 0; i < sizeof(long); i++) {
1269             char bottomEightBits = static_cast<char>(desc);
1270             desc = desc >> shiftAmount;
1271             if (!bottomEightBits) {
1272                 key = false;
1273                 break;
1274             }
1275             name += bottomEightBits;
1276         }
1277         if (!key) {
1278             break;
1279         }
1280         descAddr += sizeof(long);
1281     }
1282     return name;
1283 }
1284 
ArkGetFileName(int pid,uintptr_t jsPandaFileAddr,std::string & hapPath)1285 std::string ArkGetFileName(int pid, uintptr_t jsPandaFileAddr, std::string &hapPath)
1286 {
1287     size_t size = sizeof(JSPandaFile) / sizeof(long);
1288     uintptr_t *jsPandaFilePart = new uintptr_t[size]();
1289     if (jsPandaFilePart == nullptr) {
1290         LOG_ECMA(FATAL) << "ArkGetFileName:jsPandaFilePart is nullptr";
1291     }
1292     for (size_t i = 0; i < size; i++) {
1293         if (!ReadUintptrFromAddr(pid, jsPandaFileAddr, jsPandaFilePart[i], g_needCheck)) {
1294             LOG_ECMA(ERROR) << "ArkGetFilePath failed, jsPandaFileAddr: " << jsPandaFileAddr;
1295             delete []jsPandaFilePart;
1296             return "";
1297         }
1298         jsPandaFileAddr += sizeof(long);
1299     }
1300     JSPandaFile *jsPandaFile = reinterpret_cast<JSPandaFile *>(jsPandaFilePart);
1301 
1302     uintptr_t hapPathAddr = reinterpret_cast<uintptr_t>(
1303         const_cast<char *>(jsPandaFile->GetJSPandaFileHapPath().c_str()));
1304     hapPath = ArkReadCStringFromAddr(pid, hapPathAddr);
1305 
1306     uintptr_t descAddr = reinterpret_cast<uintptr_t>(
1307         const_cast<char *>(jsPandaFile->GetJSPandaFileDesc().c_str()));
1308     delete []jsPandaFilePart;
1309     return ArkReadCStringFromAddr(pid, descAddr);
1310 }
1311 
ArkReadData(const std::string & hapPath,const std::string & fileName,size_t & dataSize)1312 std::unique_ptr<uint8_t[]> ArkReadData([[maybe_unused]] const std::string &hapPath,
1313                                        [[maybe_unused]] const std::string &fileName,
1314                                        [[maybe_unused]] size_t &dataSize)
1315 {
1316     std::unique_ptr<uint8_t[]> dataPtr = nullptr;
1317 #if defined(PANDA_TARGET_OHOS)
1318     bool newCreate = false;
1319     std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(
1320         ExtractorUtil::GetLoadFilePath(hapPath), newCreate);
1321     if (extractor == nullptr) {
1322         LOG_ECMA(ERROR) << "Ark read data failed, hapPath: " << hapPath;
1323         return dataPtr;
1324     }
1325     if (!extractor->ExtractToBufByName(fileName, dataPtr, dataSize)) {
1326         LOG_ECMA(ERROR) << "Ark read data failed, hap/hsp path: " << hapPath << ", file name: " << fileName;
1327         return dataPtr;
1328     }
1329 #endif
1330     return dataPtr;
1331 }
1332 
OpenJSPandaFileByReadData(const std::string & hapPath,const std::string & fileName)1333 std::shared_ptr<JSPandaFile> OpenJSPandaFileByReadData(const std::string &hapPath, const std::string &fileName)
1334 {
1335     size_t dataSize = 0;
1336     auto data = ArkReadData(hapPath, fileName, dataSize);
1337     if (data == nullptr) {
1338         return nullptr;
1339     }
1340     auto pf = panda_file::OpenPandaFileFromMemory(data.get(), dataSize);
1341     if (pf == nullptr) {
1342         return nullptr;
1343     }
1344     return std::make_shared<JSPandaFile>(pf.release(), fileName.c_str());
1345 }
1346 
ArkParseJsFrameDebugInfos(const std::vector<JsFrameDebugInfo> & JsFrameDebugInfos,size_t size,JsFrame * jsFrame,size_t & jsFrameIndex)1347 void ArkParseJsFrameDebugInfos([[maybe_unused]] const std::vector<JsFrameDebugInfo> &JsFrameDebugInfos,
1348                                [[maybe_unused]] size_t size, [[maybe_unused]] JsFrame *jsFrame,
1349                                [[maybe_unused]] size_t &jsFrameIndex)
1350 {
1351 #if defined(PANDA_TARGET_OHOS)
1352     jsFrameIndex = 0;
1353     size = JsFrameDebugInfos.size() > size ? size : JsFrameDebugInfos.size();
1354     std::unordered_map<std::string, std::shared_ptr<JSPandaFile>> jsPandaFileTable;
1355     for (size_t i = 0; i < size; ++i) {
1356         auto fileIter = jsPandaFileTable.find(JsFrameDebugInfos[i].hapPath);
1357         if (fileIter == jsPandaFileTable.end()) {
1358             auto jsPandaFile = OpenJSPandaFileByReadData(JsFrameDebugInfos[i].hapPath, JsFrameDebugInfos[i].filePath);
1359             if (jsPandaFile != nullptr) {
1360                 jsPandaFileTable.emplace(JsFrameDebugInfos[i].hapPath, jsPandaFile);
1361                 auto debugExtractor = std::make_unique<DebugInfoExtractor>(jsPandaFile.get());
1362                 ParseJsFrameInfo(jsPandaFile.get(), debugExtractor.get(), JsFrameDebugInfos[i].methodId,
1363                     JsFrameDebugInfos[i].offset, jsFrame[jsFrameIndex]);
1364                 jsFrameIndex++;
1365             }
1366         } else {
1367             auto jsPandaFile = fileIter->second;
1368             auto debugExtractor = std::make_unique<DebugInfoExtractor>(jsPandaFile.get());
1369             ParseJsFrameInfo(jsPandaFile.get(), debugExtractor.get(), JsFrameDebugInfos[i].methodId,
1370                 JsFrameDebugInfos[i].offset, jsFrame[jsFrameIndex]);
1371             jsFrameIndex++;
1372         }
1373     }
1374 #endif
1375 }
1376 
ArkGetJsFrameDebugInfo(int pid,uintptr_t currentPtr,uintptr_t frameType,std::vector<JsFrameDebugInfo> & JsFrameDebugInfos)1377 bool ArkGetJsFrameDebugInfo(int pid, uintptr_t currentPtr, uintptr_t frameType,
1378                             std::vector<JsFrameDebugInfo> &JsFrameDebugInfos)
1379 {
1380     uintptr_t function = ArkGetFunction(pid, currentPtr, frameType);
1381     if (!function) {
1382         return false;
1383     }
1384 
1385     uintptr_t method = ArkCheckAndGetMethod(pid, function);
1386     if (!method || ArkIsNativeWithCallField(pid, method)) {
1387         return false;
1388     }
1389     uintptr_t jsPandaFileAddr = 0;
1390     uintptr_t methodId = 0;
1391     if (!ArkGetMethodIdandJSPandaFileAddr(pid, method, methodId, jsPandaFileAddr)) {
1392         LOG_ECMA(ERROR) << "ArkGetJsFrameDebugInfo failed, method: " << method;
1393         return false;
1394     }
1395     uintptr_t offset = ArkGetBytecodeOffset(pid, method, frameType, currentPtr);
1396     std::string hapPath;
1397     std::string fileName = ArkGetFileName(pid, jsPandaFileAddr, hapPath);
1398     if (fileName.empty() || hapPath.empty()) {
1399         LOG_ECMA(DEBUG) << "ArkGetJsFrameDebugInfo get filename or hapPath failed, fileName: "
1400                         << fileName << ", hapPath: "<< hapPath;
1401         return false;
1402     }
1403     std::string filePath = ArkGetFilePath(fileName);
1404     if (filePath.empty()) {
1405         return false;
1406     }
1407     JsFrameDebugInfo JsFrameDebugInfo(EntityId(methodId), offset, hapPath, filePath);
1408     JsFrameDebugInfos.push_back(std::move(JsFrameDebugInfo));
1409     return true;
1410 }
1411 
ArkGetNextFrame(int pid,uintptr_t frameType,uintptr_t & currentPtr)1412 bool ArkGetNextFrame(int pid, uintptr_t frameType, uintptr_t &currentPtr)
1413 {
1414     uintptr_t typeOffset = 0;
1415     uintptr_t prevOffset = 0;
1416     if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
1417         LOG_ECMA(ERROR) << "FrameType ERROR, addr: " << currentPtr << ", frameType: " << frameType;
1418         return false;
1419     }
1420     currentPtr -= typeOffset;
1421     currentPtr += prevOffset;
1422     if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr, g_needCheck)) {
1423         return false;
1424     }
1425     if (currentPtr == 0) {
1426         LOG_ECMA(ERROR) << "currentPtr is nullptr in GetArkNativeFrameInfo()!";
1427         return false;
1428     }
1429     return true;
1430 }
1431 
GetArkNativeFrameInfo(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,JsFrame * jsFrame,size_t & size)1432 bool GetArkNativeFrameInfo([[maybe_unused]] int pid, [[maybe_unused]] uintptr_t *pc,
1433                            [[maybe_unused]] uintptr_t *fp, [[maybe_unused]] uintptr_t *sp,
1434                            [[maybe_unused]] JsFrame *jsFrame, [[maybe_unused]] size_t &size)
1435 {
1436 #if defined(PANDA_TARGET_OHOS)
1437     constexpr size_t FP_SIZE = sizeof(uintptr_t);
1438     uintptr_t currentPtr = *fp;
1439     if (pid == getpid()) {
1440         g_needCheck = false;
1441     }
1442     if (currentPtr == 0) {
1443         LOG_ECMA(ERROR) << "fp is nullptr in GetArkNativeFrameInfo()!";
1444         return false;
1445     }
1446 
1447     if (pid == getpid() && JsStackInfo::loader != nullptr &&
1448         !JsStackInfo::loader->InsideStub(*pc) && !JsStackInfo::loader->InsideAOT(*pc)) {
1449         LOG_ECMA(ERROR) << "invalid pc in StepArkManagedNativeFrame()!";
1450         return false;
1451     }
1452 
1453     std::vector<JsFrameDebugInfo> JsFrameDebugInfos;
1454     bool ret = true;
1455     while (true) {
1456         currentPtr -= sizeof(FrameType);
1457         uintptr_t frameType = 0;
1458         if (!ReadUintptrFromAddr(pid, currentPtr, frameType, g_needCheck)) {
1459             return false;
1460         }
1461         if (g_needCheck && IsFunctionFrame(frameType)) {
1462             ArkGetJsFrameDebugInfo(pid, currentPtr, frameType, JsFrameDebugInfos);
1463         } else if (ArkFrameCheck(frameType)) {
1464             currentPtr += sizeof(FrameType);
1465             *sp = currentPtr;
1466             ret &= ReadUintptrFromAddr(pid, currentPtr, *fp, g_needCheck);
1467             currentPtr += FP_SIZE;
1468             ret &= ReadUintptrFromAddr(pid, currentPtr, *pc, g_needCheck);
1469             break;
1470         }
1471 
1472         if (!ArkGetNextFrame(pid, frameType, currentPtr)) {
1473             return false;
1474         }
1475     }
1476     if (g_needCheck && !JsFrameDebugInfos.empty()) {
1477         ArkParseJsFrameDebugInfos(JsFrameDebugInfos, size, jsFrame, size);
1478     } else {
1479         size = 0;
1480     }
1481     return ret;
1482 #else
1483     return false;
1484 #endif
1485 }
1486 
GetData()1487 uint8_t* JSSymbolExtractor::GetData()
1488 {
1489     return data_;
1490 }
1491 
GetLoadOffset()1492 uintptr_t JSSymbolExtractor::GetLoadOffset()
1493 {
1494     return loadOffset_;
1495 }
1496 
GetDataSize()1497 uintptr_t JSSymbolExtractor::GetDataSize()
1498 {
1499     return dataSize_;
1500 }
1501 
ParseHapFileData(std::string & hapName)1502 bool JSSymbolExtractor::ParseHapFileData([[maybe_unused]] std::string& hapName)
1503 {
1504     bool ret = false;
1505 #if defined(PANDA_TARGET_OHOS)
1506     if (hapName.empty()) {
1507         LOG_ECMA(ERROR) << "Get file data failed, path empty.";
1508         return false;
1509     }
1510     bool newCreate = false;
1511     std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(hapName, newCreate);
1512     if (extractor == nullptr) {
1513         LOG_ECMA(ERROR) << "GetExtractor failed, hap path: " << hapName;
1514         return false;
1515     }
1516 
1517     std::string pandaFilePath = "ets/modules.abc";
1518     auto data = extractor->GetSafeData(pandaFilePath);
1519     if (!data) {
1520         LOG_ECMA(ERROR) << "GetSafeData failed, hap path: " << hapName;
1521         return false;
1522     }
1523 
1524     data_ = data->GetDataPtr();
1525     dataSize_ = data->GetDataLen();
1526     loadOffset_ = static_cast<uintptr_t>(data->GetOffset());
1527     ret = true;
1528     auto zipFile = std::make_unique<ZipFile>(hapName);
1529     if (zipFile == nullptr || !zipFile->Open()) {
1530         return false;
1531     }
1532     auto &entrys = zipFile->GetAllEntries();
1533     if (ret) {
1534         std::string filePath = "ets/sourceMaps.map";
1535         if (entrys.find(filePath) == entrys.end()) {
1536             LOG_ECMA(INFO) << "Can't find sourceMaps.map in hap/hsp";
1537             return ret;
1538         }
1539         CreateSourceMap(hapName);
1540     }
1541 #endif
1542     return ret;
1543 }
1544 
ArkParseJSFileInfo(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,const char * filePath,uintptr_t extractorptr,JsFunction * jsFunction)1545 bool ArkParseJSFileInfo([[maybe_unused]] uintptr_t byteCodePc, [[maybe_unused]] uintptr_t methodId,
1546                         [[maybe_unused]] uintptr_t mapBase, [[maybe_unused]] const char* filePath,
1547                         [[maybe_unused]] uintptr_t extractorptr, [[maybe_unused]] JsFunction *jsFunction)
1548 {
1549     bool ret = false;
1550 #if defined(PANDA_TARGET_OHOS)
1551     if (filePath == nullptr) {
1552         LOG_ECMA(ERROR) << "FilePath from dfx is nullptr.";
1553         return false;
1554     }
1555     auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
1556     if (extractor == nullptr) {
1557         LOG_ECMA(ERROR) << "Parse JSframe info failed, extractor is nullptr.";
1558         return false;
1559     }
1560     if (extractor->GetJSPandaFile() == nullptr) {
1561         std::string hapName = std::string(filePath);
1562         extractor->ParseHapFileData(hapName);
1563         extractor->CreateJSPandaFile();
1564     }
1565     ret = ArkParseJsFrameInfo(byteCodePc, methodId, mapBase, extractor->GetLoadOffset(),
1566                               extractor->GetData(), extractor->GetDataSize(), extractorptr, jsFunction);
1567 #endif
1568     return ret;
1569 }
1570 
~JSSymbolExtractor()1571 JSSymbolExtractor::~JSSymbolExtractor()
1572 {
1573     if (sourceMap_ != nullptr) {
1574         sourceMap_.reset();
1575     }
1576     if (debugExtractor_ != nullptr) {
1577         debugExtractor_.reset();
1578     }
1579     if (jsPandaFile_ != nullptr) {
1580         jsPandaFile_.reset();
1581     }
1582     methodInfo_.clear();
1583 }
1584 
Create()1585 JSSymbolExtractor* JSSymbolExtractor::Create()
1586 {
1587     auto extractor = new JSSymbolExtractor();
1588     return extractor;
1589 }
1590 
Destory(JSSymbolExtractor * extractor)1591 bool JSSymbolExtractor::Destory(JSSymbolExtractor *extractor)
1592 {
1593     if (extractor == nullptr) {
1594         LOG_ECMA(ERROR) << "Destory ark symbol extractor failed, extractor is nullptr.";
1595         return false;
1596     }
1597     delete extractor;
1598     extractor = nullptr;
1599     return true;
1600 }
1601 
GetMethodInfos()1602 CVector<MethodInfo> JSSymbolExtractor::GetMethodInfos()
1603 {
1604     if (methodInfo_.empty()) {
1605         methodInfo_ = JSStackTrace::ReadAllMethodInfos(jsPandaFile_);
1606     }
1607 
1608     return methodInfo_;
1609 }
1610 
GetJSPandaFile(uint8_t * data,size_t dataSize)1611 JSPandaFile* JSSymbolExtractor::GetJSPandaFile(uint8_t *data, size_t dataSize)
1612 {
1613     if (jsPandaFile_ == nullptr && data != nullptr) {
1614         CreateJSPandaFile(data, dataSize);
1615     }
1616     return jsPandaFile_.get();
1617 }
1618 
CreateJSPandaFile()1619 void JSSymbolExtractor::CreateJSPandaFile()
1620 {
1621     auto pf = panda_file::OpenPandaFileFromSecureMemory(data_, dataSize_);
1622     if (pf == nullptr) {
1623         LOG_ECMA(ERROR) << "Failed to open panda file.";
1624         return;
1625     }
1626     jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "");
1627 }
1628 
CreateJSPandaFile(uint8_t * data,size_t dataSize)1629 void JSSymbolExtractor::CreateJSPandaFile(uint8_t *data, size_t dataSize)
1630 {
1631     auto pf = panda_file::OpenPandaFileFromSecureMemory(data, dataSize);
1632     if (pf == nullptr) {
1633         LOG_ECMA(ERROR) << "Failed to open panda file.";
1634         return;
1635     }
1636     jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "");
1637 }
1638 
GetSourceMap(uint8_t * data,size_t dataSize)1639 SourceMap* JSSymbolExtractor::GetSourceMap(uint8_t *data, size_t dataSize)
1640 {
1641     if (sourceMap_ == nullptr && data != nullptr) {
1642         JSSymbolExtractor::CreateSourceMap(data, dataSize);
1643     }
1644     return sourceMap_.get();
1645 }
1646 
CreateSourceMap(const std::string & hapPath)1647 void JSSymbolExtractor::CreateSourceMap([[maybe_unused]] const std::string &hapPath)
1648 {
1649 #if defined(PANDA_TARGET_OHOS)
1650     if (sourceMap_ == nullptr) {
1651         sourceMap_ = std::make_shared<SourceMap>();
1652         sourceMap_->Init(hapPath);
1653     }
1654 #endif
1655 }
1656 
CreateSourceMap(uint8_t * data,size_t dataSize)1657 void JSSymbolExtractor::CreateSourceMap(uint8_t *data, size_t dataSize)
1658 {
1659     sourceMap_ = std::make_shared<SourceMap>();
1660     sourceMap_->Init(data, dataSize);
1661 }
1662 
GetDebugExtractor()1663 DebugInfoExtractor* JSSymbolExtractor::GetDebugExtractor()
1664 {
1665     if (debugExtractor_ == nullptr) {
1666         JSSymbolExtractor::CreateDebugExtractor();
1667     }
1668     return debugExtractor_.get();
1669 }
1670 
CreateDebugExtractor()1671 void JSSymbolExtractor::CreateDebugExtractor()
1672 {
1673     debugExtractor_ = std::make_unique<DebugInfoExtractor>(jsPandaFile_.get());
1674 }
1675 
ArkCreateJSSymbolExtractor()1676 uintptr_t ArkCreateJSSymbolExtractor()
1677 {
1678     auto extractor = JSSymbolExtractor::Create();
1679     auto extractorptr = reinterpret_cast<uintptr_t>(extractor);
1680     return extractorptr;
1681 }
1682 
ArkDestoryJSSymbolExtractor(uintptr_t extractorptr)1683 bool ArkDestoryJSSymbolExtractor(uintptr_t extractorptr)
1684 {
1685     auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
1686     return JSSymbolExtractor::Destory(extractor);
1687 }
1688 
AddReference()1689 void JSStackTrace::AddReference()
1690 {
1691     std::unique_lock<std::mutex> lock(mutex_);
1692     if (count_ == 0) {
1693         trace_ = new JSStackTrace();
1694     }
1695     ++count_;
1696     LOG_ECMA(INFO) << "Add reference, count: " << count_;
1697 }
1698 
ReleaseReference()1699 void JSStackTrace::ReleaseReference()
1700 {
1701     std::unique_lock<std::mutex> lock(mutex_);
1702     if (trace_ == nullptr) {
1703         return ;
1704     }
1705     --count_;
1706     LOG_ECMA(INFO) << "Release reference, count: " << count_;
1707     if (count_ == 0) {
1708         delete trace_;
1709         trace_ = nullptr;
1710     }
1711 }
1712 
~JSStackTrace()1713 JSStackTrace::~JSStackTrace()
1714 {
1715     {
1716         std::unique_lock<std::shared_mutex> lock(infosMutex_);
1717         methodInfos_.clear();
1718     }
1719     {
1720         std::unique_lock<std::shared_mutex> lock(pfMutex_);
1721         jsPandaFiles_.clear();
1722     }
1723 }
1724 
InitializeMethodInfo(uintptr_t mapBase)1725 bool JSStackTrace::InitializeMethodInfo(uintptr_t mapBase)
1726 {
1727     auto pandafile = FindJSpandaFile(mapBase);
1728     if (pandafile != nullptr) {
1729         return true;
1730     }
1731     pandafile =
1732         JSPandaFileManager::GetInstance()->FindJSPandaFileByMapBase(mapBase);
1733     if (pandafile == nullptr) {
1734         LOG_ECMA(ERROR) << "Find pandafile failed, mapBase: " << std::hex << mapBase;
1735         return false;
1736     }
1737     auto methodInfos = ReadAllMethodInfos(pandafile);
1738     SetMethodInfos(mapBase, methodInfos);
1739     SetJSpandaFile(mapBase, pandafile);
1740     return true;
1741 }
1742 
FindJSpandaFile(uintptr_t mapBase)1743 std::shared_ptr<JSPandaFile> JSStackTrace::FindJSpandaFile(uintptr_t mapBase)
1744 {
1745     std::shared_lock<std::shared_mutex> lock(pfMutex_);
1746     auto iter = jsPandaFiles_.find(mapBase);
1747     if (iter == jsPandaFiles_.end()) {
1748         return nullptr;
1749     }
1750     return iter->second;
1751 }
1752 
SetJSpandaFile(uintptr_t mapBase,std::shared_ptr<JSPandaFile> pandafile)1753 void JSStackTrace::SetJSpandaFile(uintptr_t mapBase, std::shared_ptr<JSPandaFile> pandafile)
1754 {
1755     std::unique_lock<std::shared_mutex> lock(pfMutex_);
1756     jsPandaFiles_.emplace(mapBase, pandafile);
1757 }
1758 
FindMethodInfos(uintptr_t mapBase)1759 const CVector<MethodInfo> &JSStackTrace::FindMethodInfos(uintptr_t mapBase)
1760 {
1761     std::shared_lock<std::shared_mutex> lock(infosMutex_);
1762     auto iter = methodInfos_.find(mapBase);
1763     if (iter == methodInfos_.end()) {
1764         return methodInfo_;
1765     }
1766     return iter->second;
1767 }
1768 
SetMethodInfos(uintptr_t mapBase,CVector<MethodInfo> & infos)1769 void JSStackTrace::SetMethodInfos(uintptr_t mapBase, CVector<MethodInfo> &infos)
1770 {
1771     std::unique_lock<std::shared_mutex> lock(infosMutex_);
1772     methodInfos_.emplace(mapBase, std::move(infos));
1773 }
1774 
GetJsFrameInfo(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,JsFunction * jsFunction)1775 bool JSStackTrace::GetJsFrameInfo(uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase,
1776                                   uintptr_t loadOffset, JsFunction *jsFunction)
1777 {
1778     if (!InitializeMethodInfo(mapBase)) {
1779         return false;
1780     }
1781     loadOffset = loadOffset % PageSize();
1782     byteCodePc = byteCodePc - loadOffset;
1783     auto infos = FindMethodInfos(mapBase);
1784     auto codeInfo = TranslateByteCodePc(byteCodePc, infos);
1785     if (!codeInfo) {
1786         LOG_ECMA(ERROR) << std::hex << "Failed to get methodId, pc: " << byteCodePc;
1787         return false;
1788     }
1789     methodId = codeInfo->methodId;
1790     auto offset = codeInfo->offset;
1791     auto pandafile = FindJSpandaFile(mapBase);
1792     auto debugInfoExtractor =
1793         JSPandaFileManager::GetInstance()->GetJSPtExtractor(pandafile.get());
1794     ParseJsFrameInfo(pandafile.get(), debugInfoExtractor, EntityId(methodId), offset, *jsFunction);
1795     jsFunction->codeBegin = byteCodePc - offset;
1796     jsFunction->codeSize = codeInfo->codeSize;
1797     return true;
1798 }
1799 
ArkCreateLocal()1800 void ArkCreateLocal()
1801 {
1802     JSStackTrace::AddReference();
1803 }
1804 
ArkParseJsFrameInfoLocal(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,JsFunction * jsFunction)1805 bool ArkParseJsFrameInfoLocal(uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase,
1806                               uintptr_t loadOffset, JsFunction *jsFunction)
1807 {
1808     auto trace = JSStackTrace::GetInstance();
1809     if (trace == nullptr) {
1810         LOG_ECMA(ERROR) << "singleton is null, need create first.";
1811         return false;
1812     }
1813     return trace->GetJsFrameInfo(byteCodePc, methodId, mapBase, loadOffset, jsFunction);
1814 }
1815 
ArkDestoryLocal()1816 void ArkDestoryLocal()
1817 {
1818     JSStackTrace::ReleaseReference();
1819 }
1820 
1821 } // namespace panda::ecmascript
1822 
ark_create_js_symbol_extractor(uintptr_t * extractorptr)1823 __attribute__((visibility("default"))) int ark_create_js_symbol_extractor(uintptr_t *extractorptr)
1824 {
1825     *extractorptr = panda::ecmascript::ArkCreateJSSymbolExtractor();
1826     return 1;
1827 }
1828 
ark_destory_js_symbol_extractor(uintptr_t extractorptr)1829 __attribute__((visibility("default"))) int ark_destory_js_symbol_extractor(uintptr_t extractorptr)
1830 {
1831     if (panda::ecmascript::ArkDestoryJSSymbolExtractor(extractorptr)) {
1832         return 1;
1833     }
1834     return -1;
1835 }
1836 
ark_destroy_local()1837 __attribute__((visibility("default"))) int ark_destroy_local()
1838 {
1839     panda::ecmascript::ArkDestoryLocal();
1840     return 1;
1841 }
1842 
ark_create_local()1843 __attribute__((visibility("default"))) int ark_create_local()
1844 {
1845     panda::ecmascript::ArkCreateLocal();
1846     return 1;
1847 }
1848 
step_ark_with_record_jit(panda::ecmascript::ArkUnwindParam * arkUnwindParam)1849 __attribute__((visibility("default"))) int step_ark_with_record_jit(panda::ecmascript::ArkUnwindParam *arkUnwindParam)
1850 {
1851     if (panda::ecmascript::StepArkWithRecordJit(arkUnwindParam)) {
1852         return 1;
1853     }
1854     return -1;
1855 }
1856 
ark_write_jit_code(void * ctx,panda::ecmascript::ReadMemFunc readMem,int fd,const uintptr_t * const jitCodeArray,const size_t jitSize)1857 __attribute__((visibility("default"))) int ark_write_jit_code(
1858     void *ctx, panda::ecmascript::ReadMemFunc readMem, int fd, const uintptr_t *const jitCodeArray,
1859     const size_t jitSize)
1860 {
1861     if (panda::ecmascript::ArkWriteJitCode(ctx, readMem, fd, jitCodeArray, jitSize)) {
1862         return 1;
1863     }
1864     return -1;
1865 }
1866 
step_ark(void * ctx,panda::ecmascript::ReadMemFunc readMem,uintptr_t * fp,uintptr_t * sp,uintptr_t * pc,uintptr_t * methodId,bool * isJsFrame)1867 __attribute__((visibility("default"))) int step_ark(
1868     void *ctx, panda::ecmascript::ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp,
1869     uintptr_t *pc, uintptr_t *methodId, bool *isJsFrame)
1870 {
1871     if (panda::ecmascript::StepArk(ctx, readMem, fp, sp, pc, methodId, isJsFrame)) {
1872         return 1;
1873     }
1874     return -1;
1875 }
1876 
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)1877 __attribute__((visibility("default"))) int ark_parse_js_frame_info(
1878     uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase, uintptr_t loadOffset, uint8_t *data,
1879     uint64_t dataSize, uintptr_t extractorptr, panda::ecmascript::JsFunction *jsFunction)
1880 {
1881     if (panda::ecmascript::ArkParseJsFrameInfo(byteCodePc, methodId, mapBase, loadOffset, data,
1882                                                dataSize, extractorptr, jsFunction)) {
1883         return 1;
1884     }
1885     return -1;
1886 }
1887 
ark_parse_js_file_info(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,const char * filePath,uintptr_t extractorptr,panda::ecmascript::JsFunction * jsFunction)1888 __attribute__((visibility("default"))) int ark_parse_js_file_info(
1889     uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase, const char* filePath, uintptr_t extractorptr,
1890     panda::ecmascript::JsFunction *jsFunction)
1891 {
1892     if (panda::ecmascript::ArkParseJSFileInfo(byteCodePc, methodId, mapBase, filePath, extractorptr, jsFunction)) {
1893         return 1;
1894     }
1895     return -1;
1896 }
1897 
ark_translate_js_frame_info(uint8_t * data,size_t dataSize,panda::ecmascript::JsFunction * jsFunction)1898 __attribute__((visibility("default"))) int ark_translate_js_frame_info(
1899     uint8_t *data, size_t dataSize, panda::ecmascript::JsFunction *jsFunction)
1900 {
1901     if (panda::ecmascript::ArkTranslateJsFrameInfo(data, dataSize, jsFunction)) {
1902         return 1;
1903     }
1904     return -1;
1905 }
1906 
get_ark_native_frame_info(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,panda::ecmascript::JsFrame * jsFrame,size_t & size)1907 __attribute__((visibility("default"))) int get_ark_native_frame_info(
1908     int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp,
1909     panda::ecmascript::JsFrame *jsFrame, size_t &size)
1910 {
1911     if (panda::ecmascript::GetArkNativeFrameInfo(pid, pc, fp, sp, jsFrame, size)) {
1912         return 1;
1913     }
1914     return -1;
1915 }
1916 
ark_parse_js_frame_info_local(uintptr_t byteCodePc,uintptr_t methodId,uintptr_t mapBase,uintptr_t loadOffset,panda::ecmascript::JsFunction * jsFunction)1917 __attribute__((visibility("default"))) int ark_parse_js_frame_info_local(
1918     uintptr_t byteCodePc, uintptr_t methodId, uintptr_t mapBase, uintptr_t loadOffset,
1919     panda::ecmascript::JsFunction *jsFunction)
1920 {
1921     if (panda::ecmascript::ArkParseJsFrameInfoLocal(byteCodePc, methodId, mapBase, loadOffset, jsFunction)) {
1922         return 1;
1923     }
1924     return -1;
1925 }
1926