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