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