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 ¤tPtr,
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, ¤tPtr)) {
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 ¤tPtr, 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, ¤tPtr)) {
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