• 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 || 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 &currentPtr,
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, &currentPtr)) {
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 &currentPtr, 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, &currentPtr)) {
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 &currentPtr)
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