• 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 "ecmascript/dfx/stackinfo/js_stackinfo.h"
18 #include "ecmascript/platform/aot_crash_info.h"
19 #include "ecmascript/platform/os.h"
20 #include "ecmascript/stubs/runtime_stubs-inl.h"
21 #include "ecmascript/jit/jit.h"
22 #if defined(PANDA_TARGET_OHOS)
23 #include "ecmascript/extractortool/src/extractor.h"
24 #endif
25 #if defined(ENABLE_EXCEPTION_BACKTRACE)
26 #include "ecmascript/platform/backtrace.h"
27 #endif
28 
29 namespace panda::ecmascript {
30 std::unordered_map<EntityId, std::string> JsStackInfo::nameMap;
31 std::unordered_map<EntityId, std::vector<uint8>> JsStackInfo::machineCodeMap;
32 JSStackTrace *JSStackTrace::trace_ = nullptr;
33 std::mutex JSStackTrace::mutex_;
34 size_t JSStackTrace::count_ = 0;
35 
IsFastJitFunctionFrame(const FrameType frameType)36 bool IsFastJitFunctionFrame(const FrameType frameType)
37 {
38     return frameType == FrameType::FASTJIT_FUNCTION_FRAME || frameType == FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME;
39 }
40 
IsFastJitFunctionFrame(uintptr_t frameType)41 bool IsFastJitFunctionFrame(uintptr_t frameType)
42 {
43     return static_cast<FrameType>(frameType) == FrameType::FASTJIT_FUNCTION_FRAME ||
44            static_cast<FrameType>(frameType) == FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME;
45 }
46 
BuildMethodTrace(Method * method,uint32_t pcOffset,LastBuilderCache & lastCache,bool enableStackSourceFile)47 std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset, LastBuilderCache &lastCache,
48                                           bool enableStackSourceFile)
49 {
50     std::string data;
51     data.reserve(InitialLength);
52     data.append("    at ");
53     std::string name = method->ParseFunctionName();
54     if (name.empty()) {
55         data.append("anonymous (");
56     } else {
57         data.append(name).append(" (");
58     }
59     // source file
60     DebugInfoExtractor *debugExtractor = nullptr;
61     const JSPandaFile *pandaFile = method->GetJSPandaFile();
62     if (pandaFile == lastCache.pf) {
63         debugExtractor = lastCache.extractor;
64     } else {
65         debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(pandaFile);
66         lastCache.pf = pandaFile;
67         lastCache.extractor = debugExtractor;
68     }
69     if (enableStackSourceFile) {
70         const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
71         if (sourceFile.empty()) {
72             data.push_back('?');
73         } else {
74             data += sourceFile;
75         }
76     } else {
77         data.append("hidden");
78     }
79 
80     data.push_back(':');
81     // line number and column number
82     auto callbackLineFunc = [&data](int32_t line) -> bool {
83         data += std::to_string(line + 1);
84         data.push_back(':');
85         return true;
86     };
87     auto callbackColumnFunc = [&data](int32_t column) -> bool {
88         data += std::to_string(column + 1);
89         return true;
90     };
91     panda_file::File::EntityId methodId = method->GetMethodId();
92     if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, pcOffset) ||
93         !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, pcOffset)) {
94         data.push_back('?');
95     }
96     data.append(")\n");
97     return data;
98 }
99 
BuildInlinedMethodTrace(const JSPandaFile * pf,std::map<uint32_t,uint32_t> & methodOffsets)100 std::string JsStackInfo::BuildInlinedMethodTrace(const JSPandaFile *pf, std::map<uint32_t, uint32_t> &methodOffsets)
101 {
102     std::string data;
103     std::map<uint32_t, uint32_t>::reverse_iterator it;
104     for (it = methodOffsets.rbegin(); it != methodOffsets.rend(); it++) {
105         uint32_t methodId = it->second;
106         std::string name;
107         if (methodId == 0) {
108             name = "unknown";
109         } else {
110             name = std::string(MethodLiteral::GetMethodName(pf, EntityId(methodId)));
111             if (name == "") {
112                 name = "anonymous";
113             }
114         }
115         data.append("    at ");
116         data.append(name);
117         data.append(" (maybe inlined).");
118         data.append(" depth: ");
119         data.append(std::to_string(it->first));
120 
121         data.push_back('\n');
122     }
123     return data;
124 }
125 
DumpJitCode(JSThread * thread)126 void JsStackInfo::DumpJitCode(JSThread *thread)
127 {
128     JSTaggedType exception = thread->GetException().GetRawData();
129     auto &jitCodeMaps = thread->GetJitCodeMaps();
130     auto jitCode = jitCodeMaps.find(exception);
131     if (jitCode == jitCodeMaps.end()) {
132         return;
133     }
134     std::set<MachineCode*> memos;
135     JsJitDumpElf jitDumpElf;
136     jitDumpElf.Init();
137     int64 idx = 0;
138     size_t offset = 0;
139     auto jitCodeVec = jitCodeMaps[exception];
140     for (size_t i = 0; i < jitCodeVec->size(); i++) {
141         auto item = (*jitCodeVec)[i];
142         auto machineCode = std::get<0>(item);
143         std::string methodName = std::get<1>(item);
144         uintptr_t pcOffset = std::get<2>(item);
145         auto res = memos.insert(machineCode);
146         if (res.second) {
147             LOG_ECMA(ERROR) << "jit : js crash at method : " << methodName << ", offset :" << pcOffset;
148             char *funcAddr = reinterpret_cast<char *>(machineCode->GetFuncAddr());
149             size_t len = machineCode->GetTextSize();
150             std::vector<uint8> vec(len);
151             if (memmove_s(vec.data(), len, funcAddr, len) != EOK) {
152                 LOG_ECMA(ERROR) << "Fail to get machineCode on function addr: " << funcAddr;
153             }
154             jitDumpElf.AppendData(vec);
155             jitDumpElf.AppendSymbolToSymTab(idx++, offset, len, methodName);
156             offset += len;
157         }
158     }
159     std::string fileName = "jitCode-" + std::to_string(getpid());
160     std::string realOutPath;
161     std::string sanboxPath = panda::os::file::File::GetExtendedFilePath(AotCrashInfo::GetSandBoxPath());
162     if (!ecmascript::RealPath(sanboxPath, realOutPath, false)) {
163         return;
164     }
165     std::string outFile = realOutPath + "/" + fileName;
166     int fd = open(outFile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
167     jitDumpElf.WriteJitElfFile(fd);
168     close(fd);
169 }
170 
AssembleJitCodeMap(JSThread * thread,const JSHandle<JSObject> & jsErrorObj,JSFunction * func,Method * method,uintptr_t offset)171 void AssembleJitCodeMap(JSThread *thread, const JSHandle<JSObject> &jsErrorObj, JSFunction *func, Method *method,
172                         uintptr_t offset)
173 {
174     ASSERT(!jsErrorObj.GetTaggedValue().IsUndefined());
175     JSTaggedValue machineCodeTagVal = func->GetMachineCode();
176     MachineCode *machineCode = MachineCode::Cast(machineCodeTagVal.GetTaggedObject());
177     std::string methodName = method->ParseFunctionName();
178     if (methodName.empty()) {
179         methodName = "anonymous";
180     }
181     thread->SetJitCodeMap(jsErrorObj.GetTaggedValue().GetRawData(), machineCode, methodName, offset);
182 }
183 
BuildJsStackTrace(JSThread * thread,bool needNative,const JSHandle<JSObject> & jsErrorObj,bool needNativeStack)184 std::string JsStackInfo::BuildJsStackTrace(JSThread *thread, bool needNative,
185                                            const JSHandle<JSObject> &jsErrorObj, bool needNativeStack)
186 {
187     std::string data;
188     data.reserve(InitialDeeps * InitialLength);
189     JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
190     FrameIterator it(current, thread);
191     uintptr_t baselineNativePc = 0;
192 
193     LastBuilderCache lastCache;
194     for (; !it.Done(); it.Advance<GCVisitedFlag::HYBRID_STACK>()) {
195         if (it.GetFrameType() == FrameType::BASELINE_BUILTIN_FRAME) {
196             auto *frame = it.GetFrame<BaselineBuiltinFrame>();
197             baselineNativePc = frame->GetReturnAddr();
198             continue;
199         }
200         if (!it.IsJSFrame()) {
201             continue;
202         }
203         auto method = it.CheckAndGetMethod();
204         if (method == nullptr) {
205             continue;
206         }
207         if (!method->IsNativeWithCallField()) {
208             uint32_t pcOffset = 0;
209             if (it.GetFrameType() == FrameType::ASM_INTERPRETER_FRAME && baselineNativePc != 0) {
210                 // the pcOffste in baseline frame slot is always uint64::max(), so pcOffset should be computed
211                 JSHandle<JSFunction> function(thread, it.GetFunction());
212                 pcOffset = RuntimeStubs::RuntimeGetBytecodePcOfstForBaseline(function, baselineNativePc);
213                 baselineNativePc = 0;
214             } else {
215                 pcOffset = it.GetBytecodeOffset();
216             }
217             data += BuildJsStackTraceInfo(thread, method, it, pcOffset, jsErrorObj, lastCache);
218         } else if (needNative) {
219             auto addr = JSFunction::Cast(it.GetFunction().GetTaggedObject())->GetNativePointer();
220             std::stringstream strm;
221             strm << addr;
222             data.append("    at native method (").append(strm.str()).append(")\n");
223         }
224     }
225     if (data.empty() && needNativeStack) {
226 #if defined(ENABLE_EXCEPTION_BACKTRACE)
227         std::ostringstream stack;
228         Backtrace(stack, true);
229         data = stack.str();
230 #endif
231     }
232     return data;
233 }
234 
BuildJsStackTraceInfo(JSThread * thread,Method * method,FrameIterator & it,uint32_t pcOffset,const JSHandle<JSObject> & jsErrorObj,LastBuilderCache & lastCache)235 std::string JsStackInfo::BuildJsStackTraceInfo(JSThread *thread, Method *method, FrameIterator &it,
236                                                uint32_t pcOffset, const JSHandle<JSObject> &jsErrorObj,
237                                                LastBuilderCache &lastCache)
238 {
239     const JSPandaFile *pf = method->GetJSPandaFile();
240     std::map<uint32_t, uint32_t> methodOffsets = it.GetInlinedMethodInfo();
241     FrameType frameType = it.GetFrameType();
242     if (IsFastJitFunctionFrame(frameType)) {
243         JSFunction *func = static_cast<JSFunction*>(it.GetFunction().GetTaggedObject());
244         if (!jsErrorObj.GetTaggedValue().IsUndefined()) {
245             AssembleJitCodeMap(thread, jsErrorObj, func, method, it.GetOptimizedReturnAddr());
246         }
247     }
248     return BuildInlinedMethodTrace(pf, methodOffsets) +
249            BuildMethodTrace(method, pcOffset, lastCache, thread->GetEnableStackSourceFile());
250 }
251 
BuildCrashInfo(bool isJsCrash,uintptr_t pc,JSThread * thread)252 void JsStackInfo::BuildCrashInfo(bool isJsCrash, uintptr_t pc, JSThread *thread)
253 {
254     if (JsStackInfo::loader == nullptr) {
255         return;
256     }
257     if (!JsStackInfo::loader->IsEnableAOT() && !Jit::GetInstance()->IsEnableFastJit() &&
258         !pgo::PGOProfilerManager::GetInstance()->IsEnable()) {
259         return;
260     }
261     ohos::RuntimeInfoType type;
262     if (isJsCrash) {
263         type = ohos::RuntimeInfoType::JS;
264     } else if (pc != 0 && JsStackInfo::loader != nullptr && JsStackInfo::loader->InsideAOT(pc)) {
265         type = ohos::RuntimeInfoType::AOT_CRASH;
266     } else {
267         type = ohos::RuntimeInfoType::OTHERS;
268     }
269     ohos::AotRuntimeInfo::GetInstance().BuildCrashRuntimeInfo(type);
270     if (isJsCrash && thread != nullptr) {
271         DumpJitCode(thread);
272     }
273 }
274 
BuildJsStackInfo(JSThread * thread,bool currentStack)275 std::vector<struct JsFrameInfo> JsStackInfo::BuildJsStackInfo(JSThread *thread, bool currentStack)
276 {
277     std::vector<struct JsFrameInfo> jsFrame;
278     uintptr_t *native = nullptr;
279     JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
280     FrameIterator it(current, thread);
281     for (; !it.Done(); it.Advance<GCVisitedFlag::HYBRID_STACK>()) {
282         if (!it.IsJSFrame()) {
283             continue;
284         }
285         auto method = it.CheckAndGetMethod();
286         if (method == nullptr) {
287             continue;
288         }
289         struct JsFrameInfo frameInfo;
290         if (native != nullptr) {
291             frameInfo.nativePointer = native;
292             native = nullptr;
293         }
294         if (!method->IsNativeWithCallField()) {
295             std::string name = method->ParseFunctionName();
296             if (name.empty()) {
297                 frameInfo.functionName = "anonymous";
298             } else {
299                 frameInfo.functionName = name;
300             }
301             // source file
302             DebugInfoExtractor *debugExtractor =
303                 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
304             const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
305             if (sourceFile.empty()) {
306                 frameInfo.fileName = "?";
307             } else {
308                 frameInfo.fileName = sourceFile;
309             }
310             // line number and column number
311             int lineNumber = 0;
312             auto callbackLineFunc = [&frameInfo, &lineNumber](int32_t line) -> bool {
313                 lineNumber = line + 1;
314                 frameInfo.pos = std::to_string(lineNumber) + ":";
315                 return true;
316             };
317             auto callbackColumnFunc = [&frameInfo](int32_t column) -> bool {
318                 frameInfo.pos += std::to_string(column + 1);
319                 return true;
320             };
321             panda_file::File::EntityId methodId = method->GetMethodId();
322             uint32_t offset = it.GetBytecodeOffset();
323             if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
324                 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
325                 frameInfo.pos = "?";
326             }
327             jsFrame.push_back(std::move(frameInfo));
328             if (currentStack) {
329                 return jsFrame;
330             }
331         } else {
332             JSTaggedValue function = it.GetFunction();
333             JSHandle<JSTaggedValue> extraInfoValue(
334                 thread, JSFunctionBase::Cast(function.GetTaggedObject())->GetFunctionExtraInfo());
335             if (extraInfoValue->IsJSNativePointer()) {
336                 JSHandle<JSNativePointer> extraInfo(extraInfoValue);
337                 native = reinterpret_cast<uintptr_t *>(extraInfo->GetData());
338             }
339         }
340     }
341     return jsFrame;
342 }
343 
GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType,uintptr_t & typeOffset,uintptr_t & prevOffset)344 bool GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType, uintptr_t &typeOffset, uintptr_t &prevOffset)
345 {
346     FrameType type = static_cast<FrameType>(frameType);
347     switch (type) {
348         case FrameType::OPTIMIZED_FRAME:
349             typeOffset = OptimizedFrame::GetTypeOffset();
350             prevOffset = OptimizedFrame::GetPrevOffset();
351             break;
352         case FrameType::OPTIMIZED_ENTRY_FRAME:
353             typeOffset = OptimizedEntryFrame::GetTypeOffset();
354             prevOffset = OptimizedEntryFrame::GetLeaveFrameFpOffset();
355             break;
356         case FrameType::BASELINE_BUILTIN_FRAME:
357             typeOffset = BaselineBuiltinFrame::GetTypeOffset();
358             prevOffset = BaselineBuiltinFrame::GetPrevOffset();
359             break;
360         case FrameType::ASM_BRIDGE_FRAME:
361             typeOffset = AsmBridgeFrame::GetTypeOffset();
362             prevOffset = AsmBridgeFrame::GetPrevOffset();
363             break;
364         case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME:
365             typeOffset = OptimizedJSFunctionUnfoldArgVFrame::GetTypeOffset();
366             prevOffset = OptimizedJSFunctionUnfoldArgVFrame::GetPrevOffset();
367             break;
368         case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
369         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
370         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
371             typeOffset = OptimizedJSFunctionFrame::GetTypeOffset();
372             prevOffset = OptimizedJSFunctionFrame::GetPrevOffset();
373             break;
374         case FrameType::LEAVE_FRAME:
375             typeOffset = OptimizedLeaveFrame::GetTypeOffset();
376             prevOffset = OptimizedLeaveFrame::GetPrevOffset();
377             break;
378         case FrameType::LEAVE_FRAME_WITH_ARGV:
379             typeOffset = OptimizedWithArgvLeaveFrame::GetTypeOffset();
380             prevOffset = OptimizedWithArgvLeaveFrame::GetPrevOffset();
381             break;
382         case FrameType::BUILTIN_CALL_LEAVE_FRAME:
383             typeOffset = OptimizedBuiltinLeaveFrame::GetTypeOffset();
384             prevOffset = OptimizedBuiltinLeaveFrame::GetPrevOffset();
385             break;
386         case FrameType::INTERPRETER_FRAME:
387         case FrameType::INTERPRETER_FAST_NEW_FRAME:
388             typeOffset = InterpretedFrame::GetTypeOffset();
389             prevOffset = InterpretedFrame::GetPrevOffset();
390             break;
391         case FrameType::INTERPRETER_BUILTIN_FRAME:
392             typeOffset = InterpretedBuiltinFrame::GetTypeOffset();
393             prevOffset = InterpretedBuiltinFrame::GetPrevOffset();
394             break;
395         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME:
396         case FrameType::ASM_INTERPRETER_FRAME:
397             typeOffset = AsmInterpretedFrame::GetTypeOffset();
398             prevOffset = AsmInterpretedFrame::GetPrevOffset();
399             break;
400         case FrameType::BUILTIN_FRAME:
401         case FrameType::BUILTIN_ENTRY_FRAME:
402             typeOffset = BuiltinFrame::GetTypeOffset();
403             prevOffset = BuiltinFrame::GetPrevOffset();
404             break;
405         case FrameType::BUILTIN_FRAME_WITH_ARGV:
406         case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME:
407             typeOffset = BuiltinWithArgvFrame::GetTypeOffset();
408             prevOffset = BuiltinWithArgvFrame::GetPrevOffset();
409             break;
410         case FrameType::INTERPRETER_ENTRY_FRAME:
411             typeOffset = InterpretedEntryFrame::GetTypeOffset();
412             prevOffset = InterpretedEntryFrame::GetPrevOffset();
413             break;
414         case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
415             typeOffset = AsmInterpretedEntryFrame::GetTypeOffset();
416             prevOffset = AsmInterpretedEntryFrame::GetPrevOffset();
417             break;
418         case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
419             typeOffset = AsmInterpretedBridgeFrame::GetTypeOffset();
420             prevOffset = AsmInterpretedBridgeFrame::GetPrevOffset();
421             break;
422         case FrameType::FASTJIT_FUNCTION_FRAME:
423         case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME:
424             typeOffset = FASTJITFunctionFrame::GetTypeOffset();
425             prevOffset = FASTJITFunctionFrame::GetPrevOffset();
426             break;
427         default:
428             return false;
429     }
430     return true;
431 }
432 
ArkFrameCheck(uintptr_t frameType)433 bool ArkFrameCheck(uintptr_t frameType)
434 {
435     return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME ||
436            static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_ENTRY_FRAME;
437 }
438 
IsJsFunctionFrame(uintptr_t frameType)439 bool IsJsFunctionFrame(uintptr_t frameType)
440 {
441     return static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_FRAME ||
442            static_cast<FrameType>(frameType) == FrameType::INTERPRETER_CONSTRUCTOR_FRAME ||
443            static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FRAME ||
444            static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FAST_NEW_FRAME;
445 }
446 
IsNativeFunctionFrame(uintptr_t frameType)447 bool IsNativeFunctionFrame(uintptr_t frameType)
448 {
449     return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_FRAME ||
450            static_cast<FrameType>(frameType) == FrameType::BASELINE_BUILTIN_FRAME ||
451            static_cast<FrameType>(frameType) == FrameType::ASM_BRIDGE_FRAME ||
452            static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME ||
453            static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME ||
454            static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME ||
455            static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_FRAME ||
456            static_cast<FrameType>(frameType) == FrameType::LEAVE_FRAME ||
457            static_cast<FrameType>(frameType) == FrameType::LEAVE_FRAME_WITH_ARGV ||
458            static_cast<FrameType>(frameType) == FrameType::BUILTIN_CALL_LEAVE_FRAME ||
459            static_cast<FrameType>(frameType) == FrameType::BUILTIN_FRAME ||
460            static_cast<FrameType>(frameType) == FrameType::BUILTIN_ENTRY_FRAME ||
461            static_cast<FrameType>(frameType) == FrameType::BUILTIN_FRAME_WITH_ARGV ||
462            static_cast<FrameType>(frameType) == FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME ||
463            static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_BRIDGE_FRAME;
464 }
465 
IsAotFunctionFrame(uintptr_t frameType)466 bool IsAotFunctionFrame(uintptr_t frameType)
467 {
468     return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_FRAME ||
469            static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME;
470 }
471 
ReadMethodInfo(panda_file::MethodDataAccessor & mda)472 std::optional<MethodInfo> JSStackTrace::ReadMethodInfo(panda_file::MethodDataAccessor &mda)
473 {
474     uintptr_t methodId = mda.GetMethodId().GetOffset();
475     auto codeId = mda.GetCodeId();
476     if (!codeId) {
477         return std::nullopt;
478     }
479     panda_file::CodeDataAccessor cda(mda.GetPandaFile(), codeId.value());
480     uint32_t codeSize = cda.GetCodeSize();
481     uintptr_t codeBegin = reinterpret_cast<uintptr_t>(cda.GetInstructions());
482     return std::make_optional<MethodInfo>(methodId, codeBegin, codeSize);
483 }
484 
ReadAllMethodInfos(std::shared_ptr<JSPandaFile> jsPandaFile)485 CVector<MethodInfo> JSStackTrace::ReadAllMethodInfos(std::shared_ptr<JSPandaFile> jsPandaFile)
486 {
487     CVector<MethodInfo> result;
488     if (jsPandaFile == nullptr) {
489         LOG_ECMA(ERROR) << "Failed to read all methods info.";
490         return result;
491     }
492     const panda_file::File *pf = jsPandaFile->GetPandaFile();
493     Span<const uint32_t> classIndexes = jsPandaFile->GetClasses();
494     for (const uint32_t index : classIndexes) {
495         panda_file::File::EntityId classId(index);
496         if (jsPandaFile->IsExternal(classId)) {
497             continue;
498         }
499         panda_file::ClassDataAccessor cda(*pf, classId);
500         cda.EnumerateMethods([&result, jsPandaFile](panda_file::MethodDataAccessor &mda) {
501             auto info = JSStackTrace::ReadMethodInfo(mda);
502             if (!info) {
503                 return;
504             }
505             result.push_back(info.value());
506         });
507     }
508 
509     std::sort(result.begin(), result.end());
510     return result;
511 }
512 
TranslateByteCodePc(uintptr_t realPc,const CVector<MethodInfo> & vec)513 std::optional<CodeInfo> JSStackTrace::TranslateByteCodePc(uintptr_t realPc, const CVector<MethodInfo> &vec)
514 {
515     if (vec.size() == 0) {
516         LOG_ECMA(ERROR) << "Translate bytecode pc failed, vec is empty.";
517         return std::nullopt;
518     }
519     int32_t left = 0;
520     int32_t right = static_cast<int32_t>(vec.size()) - 1;
521     for (; left <= right;) {
522         int32_t mid = (left + right) / 2;
523         bool isRight = realPc >= (vec[mid].codeBegin + vec[mid].codeSize);
524         bool isLeft = realPc < vec[mid].codeBegin;
525         // codeBegin <= realPc < codeBegin + codeSize
526         if (!isRight && !isLeft) {
527             return std::make_optional<CodeInfo>(realPc - vec[mid].codeBegin, vec[mid].methodId, vec[mid].codeSize);
528         } else if (isRight) {
529             left = mid + 1;
530         } else {
531             right = mid -1;
532         }
533     }
534     LOG_ECMA(ERROR) << "Translate bytecode pc failed, pc: " << std::hex << realPc;
535     return std::nullopt;
536 }
537 
SaveFuncName(EntityId entityId,const std::string & name)538 void SaveFuncName(EntityId entityId, const std::string &name)
539 {
540     size_t length = 256; // maximum stack length
541     if (JsStackInfo::nameMap.size() > length) {
542         auto it = JsStackInfo::nameMap.begin();
543         JsStackInfo::nameMap.erase(it);
544     }
545     JsStackInfo::nameMap.emplace(entityId, name);
546 }
547 
548 template<typename T>
ParseJsFrameInfo(JSPandaFile * jsPandaFile,DebugInfoExtractor * debugExtractor,EntityId methodId,uintptr_t offset,T & jsFrame,SourceMap * sourceMap)549 void ParseJsFrameInfo(JSPandaFile *jsPandaFile, DebugInfoExtractor *debugExtractor,
550                       EntityId methodId, uintptr_t offset, T &jsFrame, SourceMap *sourceMap)
551 {
552     if (jsPandaFile == nullptr) {
553         LOG_ECMA(ERROR) << "Parse jsFrame info failed, jsPandaFile is nullptr.";
554         return;
555     }
556     std::string name = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
557     name = name.empty() ? "anonymous" : name;
558     SaveFuncName(methodId, name);
559     std::string url = debugExtractor->GetSourceFile(methodId);
560 
561     // line number and column number
562     int lineNumber = 0;
563     int columnNumber = 0;
564     auto callbackLineFunc = [&lineNumber](int32_t line) -> bool {
565         lineNumber = line + 1;
566         return true;
567     };
568     auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool {
569         columnNumber = column + 1;
570         return true;
571     };
572 
573     if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
574         !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
575         lineNumber = 0;
576         columnNumber = 0;
577     }
578 
579     std::string packageName;
580     if (sourceMap != nullptr) {
581         sourceMap->TranslateUrlPositionBySourceMap(url, lineNumber, columnNumber, packageName);
582     }
583 
584     size_t urlSize = url.size() + 1;
585     size_t nameSize = name.size() + 1;
586     size_t packageNameSize = packageName.size() + 1;
587     if (strcpy_s(jsFrame.url, urlSize, url.c_str()) != EOK ||
588         strcpy_s(jsFrame.functionName, nameSize, name.c_str()) != EOK ||
589         strcpy_s(jsFrame.packageName, packageNameSize, packageName.c_str()) != EOK) {
590         LOG_ECMA(FATAL) << "jsFrame strcpy_s failed";
591         UNREACHABLE();
592     }
593     jsFrame.line = lineNumber;
594     jsFrame.column = columnNumber;
595 }
596 
ArkParseJsFrameInfo(uintptr_t byteCodePc,uintptr_t mapBase,uintptr_t loadOffset,uint8_t * data,uint64_t dataSize,uintptr_t extractorptr,JsFunction * jsFunction)597 bool ArkParseJsFrameInfo(uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset,
598                          uint8_t *data, uint64_t dataSize, uintptr_t extractorptr, JsFunction *jsFunction)
599 {
600     if (data == nullptr) {
601         LOG_ECMA(ERROR) << "Parse JSframe info failed, buffer is nullptr.";
602         return false;
603     }
604     loadOffset = loadOffset % PageSize();
605     auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
606     if (extractor == nullptr) {
607         LOG_ECMA(ERROR) << "Parse JSframe info failed, extractor is nullptr.";
608         return false;
609     }
610     auto jsPandaFile = extractor->GetJSPandaFile(data, dataSize);
611     if (jsPandaFile == nullptr) {
612         LOG_ECMA(ERROR) << "Parse JSframe info failed, panda file is nullptr.";
613         return false;
614     }
615     auto debugExtractor = extractor->GetDebugExtractor();
616     auto methodInfos = extractor->GetMethodInfos();
617     if (methodInfos.empty()) {
618         LOG_ECMA(ERROR) << "Read all method info from JSPandaFile failed, methodInfos is empty.";
619         return false;
620     }
621     uintptr_t realOffset = byteCodePc - mapBase - loadOffset;
622     uintptr_t pfBasePtr = reinterpret_cast<uintptr_t>(jsPandaFile->GetBase());
623     auto codeInfo = JSStackTrace::TranslateByteCodePc(realOffset + pfBasePtr, methodInfos);
624     if (!codeInfo) {
625         LOG_ECMA(ERROR) << std::hex << "Failed to get methodId, pc: " << byteCodePc;
626         return false;
627     }
628     auto offset = codeInfo->offset;
629     ParseJsFrameInfo(jsPandaFile, debugExtractor, EntityId(codeInfo->methodId), offset,
630                      *jsFunction, extractor->GetSourceMap());
631 
632     jsFunction->codeBegin = byteCodePc - offset;
633     jsFunction->codeSize = codeInfo->codeSize;
634     return true;
635 }
636 
GetBytecodeOffset(void * ctx,ReadMemFunc readMem,uintptr_t frameType,uintptr_t currentPtr)637 uintptr_t GetBytecodeOffset(void *ctx, ReadMemFunc readMem, uintptr_t frameType, uintptr_t currentPtr)
638 {
639     // currentPtr points to the frametype.
640     uintptr_t bytecodePc = 0;
641     FrameType type = static_cast<FrameType>(frameType);
642     switch (type) {
643         // return bytecode pc
644         case FrameType::ASM_INTERPRETER_FRAME:
645         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
646             currentPtr -= AsmInterpretedFrame::GetTypeOffset();
647             currentPtr += AsmInterpretedFrame::GetPcOffset(false);
648             readMem(ctx, currentPtr, &bytecodePc);
649             return bytecodePc;
650         }
651         case FrameType::INTERPRETER_FRAME:
652         case FrameType::INTERPRETER_FAST_NEW_FRAME: {
653             currentPtr -= InterpretedFrame::GetTypeOffset();
654             currentPtr += InterpretedFrame::GetPcOffset(false);
655             readMem(ctx, currentPtr, &bytecodePc);
656             return bytecodePc;
657         }
658         case FrameType::FASTJIT_FUNCTION_FRAME:
659         case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
660             currentPtr -= FASTJITFunctionFrame::GetTypeOffset();
661             readMem(ctx, currentPtr, &bytecodePc);
662             return bytecodePc;
663         }
664         // return returnaddr
665         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
666         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
667         case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME: {
668             currentPtr -= OptimizedJSFunctionFrame::GetTypeOffset();
669             currentPtr += OptimizedJSFunctionFrame::GetReturnAddrOffset();
670             readMem(ctx, currentPtr, &bytecodePc);
671             return bytecodePc;
672         }
673         case FrameType::BUILTIN_FRAME:
674         case FrameType::BUILTIN_ENTRY_FRAME: {
675             currentPtr -= BuiltinFrame::GetTypeOffset();
676             currentPtr += BuiltinFrame::GetReturnAddrOffset();
677             readMem(ctx, currentPtr, &bytecodePc);
678             return bytecodePc;
679         }
680         case FrameType::BUILTIN_FRAME_WITH_ARGV:
681         case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME: {
682             currentPtr -= BuiltinWithArgvFrame::GetTypeOffset();
683             currentPtr += BuiltinWithArgvFrame::GetReturnAddrOffset();
684             readMem(ctx, currentPtr, &bytecodePc);
685             return bytecodePc;
686         }
687         case FrameType::BASELINE_BUILTIN_FRAME: {
688             currentPtr -= BaselineBuiltinFrame::GetTypeOffset();
689             currentPtr += BaselineBuiltinFrame::GetReturnAddrOffset();
690             readMem(ctx, currentPtr, &bytecodePc);
691             return bytecodePc;
692         }
693         case FrameType::ASM_BRIDGE_FRAME: {
694             currentPtr -= AsmBridgeFrame::GetTypeOffset();
695             currentPtr += AsmBridgeFrame::GetReturnAddrOffset();
696             readMem(ctx, currentPtr, &bytecodePc);
697             return bytecodePc;
698         }
699         case FrameType::LEAVE_FRAME: {
700             currentPtr -= OptimizedLeaveFrame::GetTypeOffset();
701             currentPtr += OptimizedLeaveFrame::GetReturnAddrOffset();
702             readMem(ctx, currentPtr, &bytecodePc);
703             return bytecodePc;
704         }
705         case FrameType::LEAVE_FRAME_WITH_ARGV: {
706             currentPtr -= OptimizedWithArgvLeaveFrame::GetTypeOffset();
707             currentPtr += OptimizedWithArgvLeaveFrame::GetReturnAddrOffset();
708             readMem(ctx, currentPtr, &bytecodePc);
709             return bytecodePc;
710         }
711         case FrameType::BUILTIN_CALL_LEAVE_FRAME: {
712             currentPtr -= OptimizedBuiltinLeaveFrame::GetTypeOffset();
713             currentPtr += OptimizedBuiltinLeaveFrame::GetReturnAddrOffset();
714             readMem(ctx, currentPtr, &bytecodePc);
715             return bytecodePc;
716         }
717         case FrameType::OPTIMIZED_FRAME: {
718             currentPtr -= OptimizedFrame::GetTypeOffset();
719             currentPtr += OptimizedFrame::GetReturnAddrOffset();
720             readMem(ctx, currentPtr, &bytecodePc);
721             return bytecodePc;
722         }
723         case FrameType::ASM_INTERPRETER_BRIDGE_FRAME: {
724             currentPtr -= AsmInterpretedBridgeFrame::GetTypeOffset();
725             currentPtr += AsmInterpretedBridgeFrame::GetReturnAddrOffset(false);
726             readMem(ctx, currentPtr, &bytecodePc);
727             return bytecodePc;
728         }
729         case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
730             currentPtr -= OptimizedJSFunctionUnfoldArgVFrame::GetTypeOffset();
731             currentPtr += OptimizedJSFunctionUnfoldArgVFrame::GetReturnAddrOffset();
732             readMem(ctx, currentPtr, &bytecodePc);
733             return bytecodePc;
734         }
735         default: {
736             break;
737         }
738     }
739     return 0;
740 }
741 
ArkGetFunction(void * ctx,ReadMemFunc readMem,uintptr_t currentPtr)742 uintptr_t ArkGetFunction(void *ctx, ReadMemFunc readMem, uintptr_t currentPtr)
743 {
744     // only for jit frame
745     uintptr_t funcAddr = currentPtr;
746     funcAddr -= FASTJITFunctionFrame::GetTypeOffset();
747     funcAddr += FASTJITFunctionFrame::GetFunctionOffset();
748     uintptr_t function = 0;
749     if (!readMem(ctx, funcAddr, &function)) {
750         return 0;
751     }
752     return function;
753 }
754 
ArkGetNextFrame(void * ctx,ReadMemFunc readMem,uintptr_t & currentPtr,uintptr_t & frameType,uintptr_t & pc)755 bool ArkGetNextFrame(void *ctx, ReadMemFunc readMem, uintptr_t &currentPtr,
756                      uintptr_t &frameType, uintptr_t &pc)
757 {
758     currentPtr -= sizeof(FrameType);
759     if (!readMem(ctx, currentPtr, &frameType)) {
760         return false;
761     }
762     if (ArkFrameCheck(frameType)) {
763         return true;
764     }
765     bool ret = false;
766     if (IsJsFunctionFrame(frameType) || IsNativeFunctionFrame(frameType)) {
767         pc = GetBytecodeOffset(ctx, readMem, frameType, currentPtr);
768         ret = true;
769     }
770 
771     uintptr_t typeOffset = 0;
772     uintptr_t prevOffset = 0;
773     if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
774         return false;
775     }
776     currentPtr -= typeOffset;
777     currentPtr += prevOffset;
778     if (!readMem(ctx, currentPtr, &currentPtr)) {
779         return false;
780     }
781 
782     if (ret) {
783         return true;
784     }
785     return ArkGetNextFrame(ctx, readMem, currentPtr, frameType, pc);
786 }
787 
ArkGetMethodIdWithJit(ArkUnwindParam * arkUnwindParam,uintptr_t currentPtr)788 bool ArkGetMethodIdWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t currentPtr)
789 {
790     // only for jit frame
791     uintptr_t function = ArkGetFunction(arkUnwindParam->ctx, arkUnwindParam->readMem, currentPtr);
792     if (!function) {
793         LOG_ECMA(DEBUG) << "Failed to get function";
794         return false;
795     }
796     uintptr_t machineCode = 0;
797     uintptr_t functionAddr = function + JSFunction::MACHINECODE_OFFSET;
798     arkUnwindParam->readMem(arkUnwindParam->ctx, functionAddr, &machineCode);
799     uintptr_t size = 0;
800     uintptr_t funcAddr = 0;
801     if (machineCode) {
802         arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::INSTRSIZ_OFFSET, &size);
803         arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::FUNCADDR_OFFSET, &funcAddr);
804     }
805     if (size && funcAddr) {
806         // take the lower four bytes
807         size &= 0xFFFFFFFF;
808         std::vector<uint8> codeVec;
809         for (size_t l = 0; l < size; l++) {
810             uintptr_t tmp = 0;
811             arkUnwindParam->readMem(arkUnwindParam->ctx, funcAddr + l, &tmp);
812             codeVec.push_back(tmp);
813         }
814         arkUnwindParam->jitCache.push_back(*arkUnwindParam->methodId);
815         JsStackInfo::machineCodeMap[EntityId(*arkUnwindParam->methodId)] = codeVec;
816     }
817     return true;
818 }
819 
ArkGetNextFrameWithJit(ArkUnwindParam * arkUnwindParam,uintptr_t & currentPtr,uintptr_t & frameType)820 bool ArkGetNextFrameWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t &currentPtr, uintptr_t &frameType)
821 {
822     currentPtr -= sizeof(FrameType);
823     if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, &frameType)) {
824         return false;
825     }
826     if (ArkFrameCheck(frameType)) {
827         return true;
828     }
829     bool ret = false;
830     if (IsJsFunctionFrame(frameType) ||
831         IsNativeFunctionFrame(frameType)) {
832         *arkUnwindParam->pc = GetBytecodeOffset(arkUnwindParam->ctx, arkUnwindParam->readMem, frameType, currentPtr);
833         ret = true;
834     } else if (IsFastJitFunctionFrame(frameType)) {
835         *arkUnwindParam->pc = GetBytecodeOffset(arkUnwindParam->ctx, arkUnwindParam->readMem, frameType, currentPtr);
836         ret = ArkGetMethodIdWithJit(arkUnwindParam, currentPtr);
837     }
838 
839     uintptr_t typeOffset = 0;
840     uintptr_t prevOffset = 0;
841     if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
842         return false;
843     }
844     currentPtr -= typeOffset;
845     currentPtr += prevOffset;
846     if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, &currentPtr)) {
847         return false;
848     }
849 
850     if (ret) {
851         return true;
852     }
853     return ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType);
854 }
855 
ArkWriteJitCode(void * ctx,ReadMemFunc readMem,int fd,const uintptr_t * const jitCodeArray,const size_t jitSize)856 bool ArkWriteJitCode([[maybe_unused]] void *ctx, [[maybe_unused]] ReadMemFunc readMem,
857                      int fd, const uintptr_t *const jitCodeArray, const size_t jitSize)
858 {
859     JsJitDumpElf jitDumpElf;
860     jitDumpElf.Init();
861     std::set<uintptr_t> memos;
862     int64 idx = 0;
863     size_t offset = 0;
864     for (size_t i = 0; i < jitSize; i++) {
865         uintptr_t methodId = jitCodeArray[i];
866         auto res = memos.insert(methodId);
867         if (res.second) {
868             std::vector<uint8> codeVec = JsStackInfo::machineCodeMap[EntityId(methodId)];
869             std::string name = JsStackInfo::nameMap[EntityId(methodId)];
870             size_t len = codeVec.size();
871             jitDumpElf.AppendData(codeVec);
872             jitDumpElf.AppendSymbolToSymTab(idx++, offset, len, name);
873             offset += len;
874         }
875     }
876     jitDumpElf.WriteJitElfFile(fd);
877     JsStackInfo::nameMap.clear();
878     JsStackInfo::machineCodeMap.clear();
879     return true;
880 }
881 
StepArkWithRecordJit(ArkUnwindParam * arkUnwindParam)882 bool StepArkWithRecordJit(ArkUnwindParam *arkUnwindParam)
883 {
884     constexpr size_t FP_SIZE = sizeof(uintptr_t);
885     uintptr_t currentPtr = *arkUnwindParam->fp;
886     if (currentPtr == 0) {
887         LOG_ECMA(ERROR) << "fp is nullptr in StepArkWithRecordJit()!";
888         return false;
889     }
890 
891     uintptr_t frameType = 0;
892     if (ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType)) {
893         if (ArkFrameCheck(frameType)) {
894             currentPtr += sizeof(FrameType);
895             *arkUnwindParam->sp = currentPtr;
896             bool ret = arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->fp);
897             currentPtr += FP_SIZE;
898             ret &= arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->pc);
899             *arkUnwindParam->isJsFrame = false;
900             return ret;
901         } else {
902             *arkUnwindParam->fp = currentPtr;
903             *arkUnwindParam->sp = currentPtr;
904             // js && jit -> true, native -> false
905             *arkUnwindParam->isJsFrame = IsJsFunctionFrame(frameType) ||
906                 IsFastJitFunctionFrame(frameType);
907         }
908     } else {
909         LOG_ECMA(ERROR) << "ArkGetNextFrameWithJit failed, currentPtr: " << currentPtr << ", frameType: " << frameType;
910         return false;
911     }
912     return true;
913 }
914 
StepArk(void * ctx,ReadMemFunc readMem,ArkStepParam * arkStepParam)915 bool StepArk(void *ctx, ReadMemFunc readMem, ArkStepParam *arkStepParam)
916 {
917     constexpr size_t FP_SIZE = sizeof(uintptr_t);
918     uintptr_t currentPtr = *arkStepParam->fp;
919     if (currentPtr == 0) {
920         LOG_ECMA(ERROR) << "fp is nullptr in StepArk()!";
921         return false;
922     }
923 
924     uintptr_t frameType = 0;
925     if (ArkGetNextFrame(ctx, readMem, currentPtr, frameType, *arkStepParam->pc)) {
926         if (ArkFrameCheck(frameType)) {
927             currentPtr += sizeof(FrameType);
928             *arkStepParam->sp = currentPtr;
929             bool ret = readMem(ctx, currentPtr, arkStepParam->fp);
930             currentPtr += FP_SIZE;
931             ret &= readMem(ctx, currentPtr, arkStepParam->pc);
932             *arkStepParam->isJsFrame = false;
933             return ret;
934         } else {
935             *arkStepParam->fp = currentPtr;
936             *arkStepParam->sp = currentPtr;
937             // js -> true, native -> false
938             *arkStepParam->isJsFrame = IsJsFunctionFrame(frameType);
939         }
940     } else {
941         LOG_ECMA(ERROR) << std::hex << "ArkGetNextFrame failed, addr: " << currentPtr;
942         return false;
943     }
944 
945     return true;
946 }
947 
GetData()948 uint8_t* JSSymbolExtractor::GetData()
949 {
950     return data_;
951 }
952 
GetLoadOffset()953 uintptr_t JSSymbolExtractor::GetLoadOffset()
954 {
955     return loadOffset_;
956 }
957 
GetDataSize()958 uintptr_t JSSymbolExtractor::GetDataSize()
959 {
960     return dataSize_;
961 }
962 
ParseHapFileData(std::string & hapName)963 bool JSSymbolExtractor::ParseHapFileData([[maybe_unused]] std::string& hapName)
964 {
965     bool ret = false;
966 #if defined(PANDA_TARGET_OHOS)
967     if (hapName.empty()) {
968         LOG_ECMA(ERROR) << "Get file data failed, path empty.";
969         return false;
970     }
971     bool newCreate = false;
972     std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(hapName, newCreate);
973     if (extractor == nullptr) {
974         LOG_ECMA(ERROR) << "GetExtractor failed, hap path: " << hapName;
975         return false;
976     }
977 
978     std::string pandaFilePath = "ets/modules.abc";
979     auto data = extractor->GetSafeData(pandaFilePath);
980     if (!data) {
981         LOG_ECMA(ERROR) << "GetSafeData failed, hap path: " << hapName;
982         return false;
983     }
984 
985     data_ = data->GetDataPtr();
986     dataSize_ = data->GetDataLen();
987     loadOffset_ = static_cast<uintptr_t>(data->GetOffset());
988     ret = true;
989     auto zipFile = std::make_unique<ZipFile>(hapName);
990     if (zipFile == nullptr || !zipFile->Open()) {
991         return false;
992     }
993     auto &entrys = zipFile->GetAllEntries();
994     if (ret) {
995         std::string filePath = "ets/sourceMaps.map";
996         if (entrys.find(filePath) == entrys.end()) {
997             LOG_ECMA(INFO) << "Can't find sourceMaps.map in hap/hsp";
998             return ret;
999         }
1000         CreateSourceMap(hapName);
1001     }
1002 #endif
1003     return ret;
1004 }
1005 
ArkParseJSFileInfo(uintptr_t byteCodePc,uintptr_t mapBase,const char * filePath,uintptr_t extractorptr,JsFunction * jsFunction)1006 bool ArkParseJSFileInfo([[maybe_unused]] uintptr_t byteCodePc, [[maybe_unused]] uintptr_t mapBase,
1007                         [[maybe_unused]] const char* filePath, [[maybe_unused]] uintptr_t extractorptr,
1008                         [[maybe_unused]] JsFunction *jsFunction)
1009 {
1010     bool ret = false;
1011 #if defined(PANDA_TARGET_OHOS)
1012     if (filePath == nullptr) {
1013         LOG_ECMA(ERROR) << "FilePath from dfx is nullptr.";
1014         return false;
1015     }
1016     auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
1017     if (extractor == nullptr) {
1018         LOG_ECMA(ERROR) << "Parse JSframe info failed, extractor is nullptr.";
1019         return false;
1020     }
1021     if (extractor->GetJSPandaFile() == nullptr) {
1022         std::string hapName = std::string(filePath);
1023         extractor->ParseHapFileData(hapName);
1024         extractor->CreateJSPandaFile();
1025     }
1026     ret = ArkParseJsFrameInfo(byteCodePc, mapBase, extractor->GetLoadOffset(),
1027             extractor->GetData(), extractor->GetDataSize(), extractorptr, jsFunction);
1028 #endif
1029     return ret;
1030 }
1031 
~JSSymbolExtractor()1032 JSSymbolExtractor::~JSSymbolExtractor()
1033 {
1034     if (sourceMap_ != nullptr) {
1035         sourceMap_.reset();
1036     }
1037     if (debugExtractor_ != nullptr) {
1038         debugExtractor_.reset();
1039     }
1040     if (jsPandaFile_ != nullptr) {
1041         jsPandaFile_.reset();
1042     }
1043     methodInfo_.clear();
1044 }
1045 
Create()1046 JSSymbolExtractor* JSSymbolExtractor::Create()
1047 {
1048     auto extractor = new JSSymbolExtractor();
1049     return extractor;
1050 }
1051 
Destory(JSSymbolExtractor * extractor)1052 bool JSSymbolExtractor::Destory(JSSymbolExtractor *extractor)
1053 {
1054     if (extractor == nullptr) {
1055         LOG_ECMA(ERROR) << "Destory ark symbol extractor failed, extractor is nullptr.";
1056         return false;
1057     }
1058     delete extractor;
1059     extractor = nullptr;
1060     return true;
1061 }
1062 
GetMethodInfos()1063 CVector<MethodInfo> JSSymbolExtractor::GetMethodInfos()
1064 {
1065     if (methodInfo_.empty()) {
1066         methodInfo_ = JSStackTrace::ReadAllMethodInfos(jsPandaFile_);
1067     }
1068 
1069     return methodInfo_;
1070 }
1071 
GetJSPandaFile(uint8_t * data,size_t dataSize)1072 JSPandaFile* JSSymbolExtractor::GetJSPandaFile(uint8_t *data, size_t dataSize)
1073 {
1074     if (jsPandaFile_ == nullptr && data != nullptr) {
1075         CreateJSPandaFile(data, dataSize);
1076     }
1077     return jsPandaFile_.get();
1078 }
1079 
CreateJSPandaFile()1080 void JSSymbolExtractor::CreateJSPandaFile()
1081 {
1082     auto pf = panda_file::OpenPandaFileFromSecureMemory(data_, dataSize_);
1083     if (pf == nullptr) {
1084         LOG_ECMA(ERROR) << "Failed to open panda file.";
1085         return;
1086     }
1087     jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "", CreateMode::DFX);
1088 }
1089 
CreateJSPandaFile(uint8_t * data,size_t dataSize)1090 void JSSymbolExtractor::CreateJSPandaFile(uint8_t *data, size_t dataSize)
1091 {
1092     auto pf = panda_file::OpenPandaFileFromSecureMemory(data, dataSize);
1093     if (pf == nullptr) {
1094         LOG_ECMA(ERROR) << "Failed to open panda file.";
1095         return;
1096     }
1097     jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "", CreateMode::DFX);
1098 }
1099 
GetSourceMap(uint8_t * data,size_t dataSize)1100 SourceMap* JSSymbolExtractor::GetSourceMap(uint8_t *data, size_t dataSize)
1101 {
1102     if (sourceMap_ == nullptr && data != nullptr) {
1103         JSSymbolExtractor::CreateSourceMap(data, dataSize);
1104     }
1105     return sourceMap_.get();
1106 }
1107 
CreateSourceMap(const std::string & hapPath)1108 void JSSymbolExtractor::CreateSourceMap([[maybe_unused]] const std::string &hapPath)
1109 {
1110 #if defined(PANDA_TARGET_OHOS)
1111     if (sourceMap_ == nullptr) {
1112         sourceMap_ = std::make_shared<SourceMap>();
1113         sourceMap_->Init(hapPath);
1114     }
1115 #endif
1116 }
1117 
CreateSourceMap(uint8_t * data,size_t dataSize)1118 void JSSymbolExtractor::CreateSourceMap(uint8_t *data, size_t dataSize)
1119 {
1120     sourceMap_ = std::make_shared<SourceMap>();
1121     sourceMap_->Init(data, dataSize);
1122 }
1123 
GetDebugExtractor()1124 DebugInfoExtractor* JSSymbolExtractor::GetDebugExtractor()
1125 {
1126     if (debugExtractor_ == nullptr) {
1127         JSSymbolExtractor::CreateDebugExtractor();
1128     }
1129     return debugExtractor_.get();
1130 }
1131 
CreateDebugExtractor()1132 void JSSymbolExtractor::CreateDebugExtractor()
1133 {
1134     debugExtractor_ = std::make_unique<DebugInfoExtractor>(jsPandaFile_.get());
1135 }
1136 
ArkCreateJSSymbolExtractor()1137 uintptr_t ArkCreateJSSymbolExtractor()
1138 {
1139     auto extractor = JSSymbolExtractor::Create();
1140     auto extractorptr = reinterpret_cast<uintptr_t>(extractor);
1141     return extractorptr;
1142 }
1143 
ArkDestoryJSSymbolExtractor(uintptr_t extractorptr)1144 bool ArkDestoryJSSymbolExtractor(uintptr_t extractorptr)
1145 {
1146     auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
1147     return JSSymbolExtractor::Destory(extractor);
1148 }
1149 
AddReference()1150 void JSStackTrace::AddReference()
1151 {
1152     std::unique_lock<std::mutex> lock(mutex_);
1153     if (count_ == 0) {
1154         trace_ = new JSStackTrace();
1155     }
1156     ++count_;
1157     LOG_ECMA(INFO) << "Add reference, count: " << count_;
1158 }
1159 
ReleaseReference()1160 void JSStackTrace::ReleaseReference()
1161 {
1162     std::unique_lock<std::mutex> lock(mutex_);
1163     if (trace_ == nullptr) {
1164         return ;
1165     }
1166     --count_;
1167     LOG_ECMA(INFO) << "Release reference, count: " << count_;
1168     if (count_ == 0) {
1169         delete trace_;
1170         trace_ = nullptr;
1171     }
1172 }
1173 
~JSStackTrace()1174 JSStackTrace::~JSStackTrace()
1175 {
1176     {
1177         std::unique_lock<std::shared_mutex> lock(infosMutex_);
1178         methodInfos_.clear();
1179     }
1180     {
1181         std::unique_lock<std::shared_mutex> lock(pfMutex_);
1182         jsPandaFiles_.clear();
1183     }
1184 }
1185 
InitializeMethodInfo(uintptr_t mapBase)1186 bool JSStackTrace::InitializeMethodInfo(uintptr_t mapBase)
1187 {
1188     auto pandafile = FindJSpandaFile(mapBase);
1189     if (pandafile != nullptr) {
1190         return true;
1191     }
1192     pandafile =
1193         JSPandaFileManager::GetInstance()->FindJSPandaFileByMapBase(mapBase);
1194     if (pandafile == nullptr) {
1195         LOG_ECMA(ERROR) << "Find pandafile failed, mapBase: " << std::hex << mapBase;
1196         return false;
1197     }
1198     auto methodInfos = ReadAllMethodInfos(pandafile);
1199     SetMethodInfos(mapBase, methodInfos);
1200     SetJSpandaFile(mapBase, pandafile);
1201     return true;
1202 }
1203 
FindJSpandaFile(uintptr_t mapBase)1204 std::shared_ptr<JSPandaFile> JSStackTrace::FindJSpandaFile(uintptr_t mapBase)
1205 {
1206     std::shared_lock<std::shared_mutex> lock(pfMutex_);
1207     auto iter = jsPandaFiles_.find(mapBase);
1208     if (iter == jsPandaFiles_.end()) {
1209         return nullptr;
1210     }
1211     return iter->second;
1212 }
1213 
SetJSpandaFile(uintptr_t mapBase,std::shared_ptr<JSPandaFile> pandafile)1214 void JSStackTrace::SetJSpandaFile(uintptr_t mapBase, std::shared_ptr<JSPandaFile> pandafile)
1215 {
1216     std::unique_lock<std::shared_mutex> lock(pfMutex_);
1217     jsPandaFiles_.emplace(mapBase, pandafile);
1218 }
1219 
FindMethodInfos(uintptr_t mapBase)1220 const CVector<MethodInfo> &JSStackTrace::FindMethodInfos(uintptr_t mapBase)
1221 {
1222     std::shared_lock<std::shared_mutex> lock(infosMutex_);
1223     auto iter = methodInfos_.find(mapBase);
1224     if (iter == methodInfos_.end()) {
1225         return methodInfo_;
1226     }
1227     return iter->second;
1228 }
1229 
SetMethodInfos(uintptr_t mapBase,CVector<MethodInfo> & infos)1230 void JSStackTrace::SetMethodInfos(uintptr_t mapBase, CVector<MethodInfo> &infos)
1231 {
1232     std::unique_lock<std::shared_mutex> lock(infosMutex_);
1233     methodInfos_.emplace(mapBase, std::move(infos));
1234 }
1235 
GetJsFrameInfo(uintptr_t byteCodePc,uintptr_t mapBase,uintptr_t loadOffset,JsFunction * jsFunction)1236 bool JSStackTrace::GetJsFrameInfo(uintptr_t byteCodePc, uintptr_t mapBase,
1237                                   uintptr_t loadOffset, JsFunction *jsFunction)
1238 {
1239     if (!InitializeMethodInfo(mapBase)) {
1240         return false;
1241     }
1242     loadOffset = loadOffset % PageSize();
1243     byteCodePc = byteCodePc - loadOffset;
1244     auto infos = FindMethodInfos(mapBase);
1245     auto codeInfo = TranslateByteCodePc(byteCodePc, infos);
1246     if (!codeInfo) {
1247         LOG_ECMA(ERROR) << std::hex << "Failed to get methodId, pc: " << byteCodePc;
1248         return false;
1249     }
1250     auto offset = codeInfo->offset;
1251     auto pandafile = FindJSpandaFile(mapBase);
1252     auto debugInfoExtractor =
1253         JSPandaFileManager::GetInstance()->GetJSPtExtractor(pandafile.get());
1254     ParseJsFrameInfo(pandafile.get(), debugInfoExtractor, EntityId(codeInfo->methodId), offset, *jsFunction);
1255     jsFunction->codeBegin = byteCodePc - offset;
1256     jsFunction->codeSize = codeInfo->codeSize;
1257     return true;
1258 }
1259 
ArkCreateLocal()1260 void ArkCreateLocal()
1261 {
1262     JSStackTrace::AddReference();
1263 }
1264 
ArkParseJsFrameInfoLocal(uintptr_t byteCodePc,uintptr_t mapBase,uintptr_t loadOffset,JsFunction * jsFunction)1265 bool ArkParseJsFrameInfoLocal(uintptr_t byteCodePc, uintptr_t mapBase,
1266                               uintptr_t loadOffset, JsFunction *jsFunction)
1267 {
1268     auto trace = JSStackTrace::GetInstance();
1269     if (trace == nullptr) {
1270         LOG_ECMA(ERROR) << "singleton is null, need create first.";
1271         return false;
1272     }
1273     return trace->GetJsFrameInfo(byteCodePc, mapBase, loadOffset, jsFunction);
1274 }
1275 
ArkDestoryLocal()1276 void ArkDestoryLocal()
1277 {
1278     JSStackTrace::ReleaseReference();
1279 }
1280 
1281 } // namespace panda::ecmascript
1282 
ark_create_js_symbol_extractor(uintptr_t * extractorptr)1283 __attribute__((visibility("default"))) int ark_create_js_symbol_extractor(uintptr_t *extractorptr)
1284 {
1285     *extractorptr = panda::ecmascript::ArkCreateJSSymbolExtractor();
1286     return 1;
1287 }
1288 
ark_destory_js_symbol_extractor(uintptr_t extractorptr)1289 __attribute__((visibility("default"))) int ark_destory_js_symbol_extractor(uintptr_t extractorptr)
1290 {
1291     if (panda::ecmascript::ArkDestoryJSSymbolExtractor(extractorptr)) {
1292         return 1;
1293     }
1294     return -1;
1295 }
1296 
ark_destroy_local()1297 __attribute__((visibility("default"))) int ark_destroy_local()
1298 {
1299     panda::ecmascript::ArkDestoryLocal();
1300     return 1;
1301 }
1302 
ark_create_local()1303 __attribute__((visibility("default"))) int ark_create_local()
1304 {
1305     panda::ecmascript::ArkCreateLocal();
1306     return 1;
1307 }
1308 
step_ark_with_record_jit(panda::ecmascript::ArkUnwindParam * arkUnwindParam)1309 __attribute__((visibility("default"))) int step_ark_with_record_jit(panda::ecmascript::ArkUnwindParam *arkUnwindParam)
1310 {
1311     if (panda::ecmascript::StepArkWithRecordJit(arkUnwindParam)) {
1312         return 1;
1313     }
1314     return -1;
1315 }
1316 
ark_write_jit_code(void * ctx,panda::ecmascript::ReadMemFunc readMem,int fd,const uintptr_t * const jitCodeArray,const size_t jitSize)1317 __attribute__((visibility("default"))) int ark_write_jit_code(
1318     void *ctx, panda::ecmascript::ReadMemFunc readMem, int fd, const uintptr_t *const jitCodeArray,
1319     const size_t jitSize)
1320 {
1321     if (panda::ecmascript::ArkWriteJitCode(ctx, readMem, fd, jitCodeArray, jitSize)) {
1322         return 1;
1323     }
1324     return -1;
1325 }
1326 
step_ark(void * ctx,panda::ecmascript::ReadMemFunc readMem,panda::ecmascript::ArkStepParam * arkStepParam)1327 __attribute__((visibility("default"))) int step_ark(
1328     void *ctx, panda::ecmascript::ReadMemFunc readMem, panda::ecmascript::ArkStepParam *arkStepParam)
1329 {
1330     if (panda::ecmascript::StepArk(ctx, readMem, arkStepParam)) {
1331         return 1;
1332     }
1333     return -1;
1334 }
1335 
ark_parse_js_frame_info(uintptr_t byteCodePc,uintptr_t mapBase,uintptr_t loadOffset,uint8_t * data,uint64_t dataSize,uintptr_t extractorptr,panda::ecmascript::JsFunction * jsFunction)1336 __attribute__((visibility("default"))) int ark_parse_js_frame_info(
1337     uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset, uint8_t *data,
1338     uint64_t dataSize, uintptr_t extractorptr, panda::ecmascript::JsFunction *jsFunction)
1339 {
1340     if (panda::ecmascript::ArkParseJsFrameInfo(byteCodePc, mapBase, loadOffset, data,
1341                                                dataSize, extractorptr, jsFunction)) {
1342         return 1;
1343     }
1344     return -1;
1345 }
1346 
ark_parse_js_file_info(uintptr_t byteCodePc,uintptr_t mapBase,const char * filePath,uintptr_t extractorptr,panda::ecmascript::JsFunction * jsFunction)1347 __attribute__((visibility("default"))) int ark_parse_js_file_info(
1348     uintptr_t byteCodePc, uintptr_t mapBase, const char* filePath, uintptr_t extractorptr,
1349     panda::ecmascript::JsFunction *jsFunction)
1350 {
1351     if (panda::ecmascript::ArkParseJSFileInfo(byteCodePc, mapBase, filePath, extractorptr, jsFunction)) {
1352         return 1;
1353     }
1354     return -1;
1355 }
1356 
ark_parse_js_frame_info_local(uintptr_t byteCodePc,uintptr_t mapBase,uintptr_t loadOffset,panda::ecmascript::JsFunction * jsFunction)1357 __attribute__((visibility("default"))) int ark_parse_js_frame_info_local(
1358     uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset, panda::ecmascript::JsFunction *jsFunction)
1359 {
1360     if (panda::ecmascript::ArkParseJsFrameInfoLocal(byteCodePc, mapBase, loadOffset, jsFunction)) {
1361         return 1;
1362     }
1363     return -1;
1364 }