• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #include "ecmascript/dfx/stackinfo/js_stackinfo.h"
17 #include "ecmascript/base/builtins_base.h"
18 #include "ecmascript/ecma_vm.h"
19 #include "ecmascript/interpreter/frame_handler.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/jspandafile/js_pandafile_manager.h"
22 #include "ecmascript/message_string.h"
23 #include "ecmascript/platform/os.h"
24 #if defined(ENABLE_EXCEPTION_BACKTRACE)
25 #include "ecmascript/platform/backtrace.h"
26 #endif
27 
28 namespace panda::ecmascript {
BuildMethodTrace(Method * method,uint32_t pcOffset)29 std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset)
30 {
31     std::string data;
32     data.append("    at ");
33     std::string name = method->ParseFunctionName();
34     if (name.empty()) {
35         name = "anonymous";
36     }
37     data += name;
38     data.append(" (");
39     // source file
40     DebugInfoExtractor *debugExtractor =
41         JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
42     const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
43     if (sourceFile.empty()) {
44         data.push_back('?');
45     } else {
46         data += sourceFile;
47     }
48     data.push_back(':');
49     // line number and column number
50     auto callbackLineFunc = [&data](int32_t line) -> bool {
51         data += std::to_string(line + 1);
52         data.push_back(':');
53         return true;
54     };
55     auto callbackColumnFunc = [&data](int32_t column) -> bool {
56         data += std::to_string(column + 1);
57         return true;
58     };
59     panda_file::File::EntityId methodId = method->GetMethodId();
60     if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, pcOffset) ||
61         !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, pcOffset)) {
62         data.push_back('?');
63     }
64     data.push_back(')');
65     data.push_back('\n');
66     return data;
67 }
68 
BuildJsStackTrace(JSThread * thread,bool needNative)69 std::string JsStackInfo::BuildJsStackTrace(JSThread *thread, bool needNative)
70 {
71     std::string data;
72     JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
73     FrameIterator it(current, thread);
74     for (; !it.Done(); it.Advance<GCVisitedFlag::VISITED>()) {
75         if (!it.IsJSFrame()) {
76             continue;
77         }
78         auto method = it.CheckAndGetMethod();
79         if (method == nullptr) {
80             continue;
81         }
82         if (!method->IsNativeWithCallField()) {
83             auto pcOffset = it.GetBytecodeOffset();
84             data += BuildMethodTrace(method, pcOffset);
85         } else if (needNative) {
86             auto addr = method->GetNativePointer();
87             std::stringstream strm;
88             strm << addr;
89             data.append("    at native method (").append(strm.str()).append(")\n");
90         }
91     }
92     if (data.empty()) {
93 #if defined(ENABLE_EXCEPTION_BACKTRACE)
94         std::ostringstream stack;
95         Backtrace(stack);
96         data = stack.str();
97 #endif
98     }
99     return data;
100 }
101 
BuildJsStackInfo(JSThread * thread)102 std::vector<struct JsFrameInfo> JsStackInfo::BuildJsStackInfo(JSThread *thread)
103 {
104     std::vector<struct JsFrameInfo> jsFrame;
105     uintptr_t *native = nullptr;
106     JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
107     FrameIterator it(current, thread);
108     for (; !it.Done(); it.Advance<GCVisitedFlag::HYBRID_STACK>()) {
109         if (!it.IsJSFrame()) {
110             continue;
111         }
112         auto method = it.CheckAndGetMethod();
113         if (method == nullptr) {
114             continue;
115         }
116         struct JsFrameInfo frameInfo;
117         if (native != nullptr) {
118             frameInfo.nativePointer = native;
119             native = nullptr;
120         }
121         if (!method->IsNativeWithCallField()) {
122             std::string name = method->ParseFunctionName();
123             if (name.empty()) {
124                 frameInfo.functionName = "anonymous";
125             } else {
126                 frameInfo.functionName = name;
127             }
128             // source file
129             DebugInfoExtractor *debugExtractor =
130                 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
131             const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
132             if (sourceFile.empty()) {
133                 frameInfo.fileName = "?";
134             } else {
135                 frameInfo.fileName = sourceFile;
136             }
137             // line number and column number
138             int lineNumber = 0;
139             auto callbackLineFunc = [&frameInfo, &lineNumber](int32_t line) -> bool {
140                 lineNumber = line + 1;
141                 frameInfo.pos = std::to_string(lineNumber) + ":";
142                 return true;
143             };
144             auto callbackColumnFunc = [&frameInfo](int32_t column) -> bool {
145                 frameInfo.pos += std::to_string(column + 1);
146                 return true;
147             };
148             panda_file::File::EntityId methodId = method->GetMethodId();
149             uint32_t offset = it.GetBytecodeOffset();
150             if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
151                 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
152                 frameInfo.pos = "?";
153             }
154             jsFrame.push_back(frameInfo);
155         } else {
156             JSTaggedValue function = it.GetFunction();
157             JSHandle<JSTaggedValue> extraInfoValue(
158                 thread, JSFunction::Cast(function.GetTaggedObject())->GetFunctionExtraInfo());
159             if (extraInfoValue->IsJSNativePointer()) {
160                 JSHandle<JSNativePointer> extraInfo(extraInfoValue);
161                 native = reinterpret_cast<uintptr_t *>(extraInfo->GetExternalPointer());
162             }
163         }
164     }
165     return jsFrame;
166 }
167 
CrashCallback(char * buf,size_t len,void * ucontext)168 void CrashCallback(char *buf __attribute__((unused)), size_t len __attribute__((unused)),
169                    void *ucontext __attribute__((unused)))
170 {
171 #if defined(__aarch64__) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
172     if (ucontext == nullptr) {
173         // should not happen
174         return;
175     }
176     auto uctx = static_cast<ucontext_t *>(ucontext);
177     uintptr_t pc = uctx->uc_mcontext.pc;
178     uintptr_t fp = uctx->uc_mcontext.regs[29];  // 29: fp
179     // 1. check pc is between ark code heap
180     // 2. assemble crash info for ark code with signal-safe code
181     // 3. do not do much things inside callback, stack size is limited
182     // 4. do not use normal log
183     if (JsStackInfo::loader == nullptr) {
184         return;
185     }
186     if (!JsStackInfo::loader->InsideStub(pc) && !JsStackInfo::loader->InsideAOT(pc)) {
187         return;
188     }
189     LOG_ECMA(ERROR) << std::hex << "CrashCallback pc:" << pc << " fp:" << fp;
190     FrameIterator frame(reinterpret_cast<JSTaggedType *>(fp));
191     bool isBuiltinStub = (frame.GetFrameType() == FrameType::OPTIMIZED_FRAME);
192     Method *method = frame.CheckAndGetMethod();
193     while (method == nullptr) {
194         frame.Advance();
195         if (frame.Done()) {
196             break;
197         }
198         method = frame.CheckAndGetMethod();
199     }
200     std::string faultInfo;
201     if (method != nullptr) {
202         std::string methodName = method->GetMethodName();
203         std::string recordName = method->GetRecordName().c_str();
204         faultInfo = "Method Name:" + methodName + " Record Name:" + recordName;
205     } else {
206         faultInfo = "method is nullptr!";
207     }
208     if (isBuiltinStub) {
209         uintptr_t func = uctx->uc_mcontext.regs[2];  // 2: func
210         JSTaggedValue builtinMethod = JSFunction::Cast(reinterpret_cast<TaggedObject *>(func))->GetMethod();
211         uint8_t builtinId = Method::Cast(builtinMethod.GetTaggedObject())->GetBuiltinId();
212         size_t builtinStart = static_cast<size_t>(GET_MESSAGE_STRING_ID(CharCodeAt) - 1);  // 1: offset NONE
213         std::string builtinStr = MessageString::GetMessageString(builtinStart + builtinId);
214         faultInfo += " " + builtinStr;
215     }
216     if (memcpy_s(buf, len, faultInfo.c_str(), faultInfo.length()) != EOK) {
217         LOG_ECMA(ERROR) << "memcpy_s fail in CrashCallback()!";  // not FATAL to avoid further crash
218     }
219 #endif
220 }
221 
ReadUintptrFromAddr(int pid,uintptr_t addr,uintptr_t & value,bool needCheckRegion)222 bool ReadUintptrFromAddr(int pid, uintptr_t addr, uintptr_t &value, bool needCheckRegion)
223 {
224     if (pid == getpid()) {
225         if (needCheckRegion) {
226             bool flag = false;
227             auto callback = [addr, &flag](Region *region) {
228                 uintptr_t regionBegin = region->GetBegin();
229                 uintptr_t regionEnd = region->GetEnd();
230                 if (regionBegin <= addr && addr <= regionEnd) {
231                     flag = true;
232                 }
233             };
234             if (JsStackInfo::loader != nullptr) {
235                 const Heap *heap = JsStackInfo::loader->GetHeap();
236                 if (heap != nullptr) {
237                     heap->EnumerateRegions(callback);
238                 }
239             }
240             if (!flag) {
241                 LOG_ECMA(ERROR) << "addr not in Region, addr: " << addr;
242                 return false;
243             }
244         }
245         value = *(reinterpret_cast<uintptr_t *>(addr));
246         return true;
247     }
248     long *retAddr = reinterpret_cast<long *>(&value);
249     // note: big endian
250     for (size_t i = 0; i < sizeof(uintptr_t) / sizeof(long); i++) {
251         *retAddr = PtracePeektext(pid, addr);
252         if (*retAddr == -1) {
253             LOG_ECMA(ERROR) << "ReadFromAddr ERROR, addr: " << addr;
254             return false;
255         }
256         addr += sizeof(long);
257         retAddr++;
258     }
259     return true;
260 }
261 
GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType,uintptr_t & typeOffset,uintptr_t & prevOffset)262 bool GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType, uintptr_t &typeOffset, uintptr_t &prevOffset)
263 {
264     FrameType type = static_cast<FrameType>(frameType);
265     switch (type) {
266         case FrameType::OPTIMIZED_FRAME:
267             typeOffset = OptimizedFrame::GetTypeOffset();
268             prevOffset = OptimizedFrame::GetPrevOffset();
269             break;
270         case FrameType::OPTIMIZED_ENTRY_FRAME:
271             typeOffset = OptimizedEntryFrame::GetTypeOffset();
272             prevOffset = OptimizedEntryFrame::GetLeaveFrameFpOffset();
273             break;
274         case FrameType::ASM_BRIDGE_FRAME:
275             typeOffset = AsmBridgeFrame::GetTypeOffset();
276             prevOffset = AsmBridgeFrame::GetPrevOffset();
277             break;
278         case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME:
279             typeOffset = OptimizedJSFunctionUnfoldArgVFrame::GetTypeOffset();
280             prevOffset = OptimizedJSFunctionUnfoldArgVFrame::GetPrevOffset();
281             break;
282         case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
283         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
284         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
285             typeOffset = OptimizedJSFunctionFrame::GetTypeOffset();
286             prevOffset = OptimizedJSFunctionFrame::GetPrevOffset();
287             break;
288         case FrameType::LEAVE_FRAME:
289             typeOffset = OptimizedLeaveFrame::GetTypeOffset();
290             prevOffset = OptimizedLeaveFrame::GetPrevOffset();
291             break;
292         case FrameType::LEAVE_FRAME_WITH_ARGV:
293             typeOffset = OptimizedWithArgvLeaveFrame::GetTypeOffset();
294             prevOffset = OptimizedWithArgvLeaveFrame::GetPrevOffset();
295             break;
296         case FrameType::BUILTIN_CALL_LEAVE_FRAME:
297             typeOffset = OptimizedBuiltinLeaveFrame::GetTypeOffset();
298             prevOffset = OptimizedBuiltinLeaveFrame::GetPrevOffset();
299             break;
300         case FrameType::INTERPRETER_FRAME:
301         case FrameType::INTERPRETER_FAST_NEW_FRAME:
302             typeOffset = InterpretedFrame::GetTypeOffset();
303             prevOffset = InterpretedFrame::GetPrevOffset();
304             break;
305         case FrameType::INTERPRETER_BUILTIN_FRAME:
306             typeOffset = InterpretedBuiltinFrame::GetTypeOffset();
307             prevOffset = InterpretedBuiltinFrame::GetPrevOffset();
308             break;
309         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME:
310         case FrameType::ASM_INTERPRETER_FRAME:
311             typeOffset = AsmInterpretedFrame::GetTypeOffset();
312             prevOffset = AsmInterpretedFrame::GetPrevOffset();
313             break;
314         case FrameType::BUILTIN_FRAME:
315         case FrameType::BUILTIN_ENTRY_FRAME:
316             typeOffset = BuiltinFrame::GetTypeOffset();
317             prevOffset = BuiltinFrame::GetPrevOffset();
318             break;
319         case FrameType::BUILTIN_FRAME_WITH_ARGV:
320         case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME:
321             typeOffset = BuiltinWithArgvFrame::GetTypeOffset();
322             prevOffset = BuiltinWithArgvFrame::GetPrevOffset();
323             break;
324         case FrameType::INTERPRETER_ENTRY_FRAME:
325             typeOffset = InterpretedEntryFrame::GetTypeOffset();
326             prevOffset = InterpretedEntryFrame::GetPrevOffset();
327             break;
328         case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
329             typeOffset = AsmInterpretedEntryFrame::GetTypeOffset();
330             prevOffset = AsmInterpretedEntryFrame::GetPrevOffset();
331             break;
332         case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
333             typeOffset = AsmInterpretedBridgeFrame::GetTypeOffset();
334             prevOffset = AsmInterpretedBridgeFrame::GetPrevOffset();
335             break;
336         default:
337             return false;
338     }
339     return true;
340 }
341 
StepArkManagedNativeFrame(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,char * buf,size_t bufSize)342 bool StepArkManagedNativeFrame(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp,
343                                [[maybe_unused]] char *buf, [[maybe_unused]] size_t bufSize)
344 {
345     constexpr size_t FP_SIZE = 8;
346     constexpr size_t LR_SIZE = 8;
347     uintptr_t currentPtr = *fp;
348     if (currentPtr == 0) {
349         LOG_ECMA(ERROR) << "fp is nullptr in StepArkManagedNativeFrame()!";
350         return false;
351     }
352     if (pid == getpid() && JsStackInfo::loader != nullptr &&
353         !JsStackInfo::loader->InsideStub(*pc) && !JsStackInfo::loader->InsideAOT(*pc)) {
354         LOG_ECMA(ERROR) << "invalid pc in StepArkManagedNativeFrame()!";
355         return false;
356     }
357     while (true) {
358         currentPtr -= sizeof(FrameType);
359         uintptr_t frameType = 0;
360         if (!ReadUintptrFromAddr(pid, currentPtr, frameType, true)) {
361             return false;
362         }
363         uintptr_t typeOffset = 0;
364         uintptr_t prevOffset = 0;
365         if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
366             LOG_ECMA(ERROR) << "FrameType ERROR, addr: " << currentPtr << ", frameType: " << frameType;
367             return false;
368         }
369         if (static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME ||
370             static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_ENTRY_FRAME ||
371             static_cast<FrameType>(frameType) == FrameType::BUILTIN_ENTRY_FRAME) {
372             break;
373         }
374         currentPtr -= typeOffset;
375         currentPtr += prevOffset;
376         if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr, true)) {
377             return false;
378         }
379         if (currentPtr == 0) {
380             LOG_ECMA(ERROR) << "currentPtr is nullptr in StepArkManagedNativeFrame()!";
381             return false;
382         }
383     }
384     currentPtr += sizeof(FrameType);
385     *fp = currentPtr;
386     currentPtr += FP_SIZE;
387     if (!ReadUintptrFromAddr(pid, currentPtr, *pc, true)) {
388         return false;
389     }
390     currentPtr += LR_SIZE;
391     *sp = currentPtr;
392     return true;
393 }
394 
CopyBytecodeInfoToBuffer(const char * prefix,uintptr_t fullBytecode,size_t & strIdx,char * outStr,size_t strLen)395 void CopyBytecodeInfoToBuffer(const char *prefix, uintptr_t fullBytecode, size_t &strIdx, char *outStr, size_t strLen)
396 {
397     // note: big endian
398     for (size_t i = 0; prefix[i] != '\0' && strIdx < strLen - 1; i++) {  // 1: last '\0'
399         outStr[strIdx++] = prefix[i];
400     }
401     size_t start = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleLdundefined));
402     size_t bytecode = fullBytecode & 0xff;  // 0xff: last byte
403     const char *bytecodeName = MessageString::GetMessageString(start + bytecode).c_str();
404     for (size_t i = 0; bytecodeName[i] != '\0' && strIdx < strLen - 1; i++) {  // 1: last '\0'
405         outStr[strIdx++] = bytecodeName[i];
406     }
407     if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleDeprecated)) ||
408         start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleWide)) ||
409         start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleThrow)) ||
410         start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleCallRuntime))) {
411         size_t startSecond = start;
412         if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleDeprecated))) {
413             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleDeprecatedLdlexenvPrefNone));
414         } else if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleWide))) {
415             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(
416                 HandleWideCreateobjectwithexcludedkeysPrefImm16V8V8));
417         } else if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleThrow))) {
418             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleThrowPrefNone));
419         } else if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleCallRuntime))) {
420             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleCallRuntimeNotifyConcurrentResultPrefNone));
421         }
422         size_t bytecodeSecond = (fullBytecode >> 8) & 0xff;  // 8, 0xff: second last byte
423         const char *bytecodeNameSecond = MessageString::GetMessageString(startSecond + bytecodeSecond).c_str();
424         if (strIdx < strLen - 1) {  // 1: last '\0'
425             outStr[strIdx++] = '/';
426         }
427         for (size_t i = 0; bytecodeNameSecond[i] != '\0' && strIdx < strLen - 1; i++) {  // 1: last '\0'
428             outStr[strIdx++] = bytecodeNameSecond[i];
429         }
430     }
431     outStr[strIdx] = '\0';
432 }
433 
GetArkJSHeapCrashInfo(int pid,uintptr_t * bytecodePc,uintptr_t * fp,bool outJSInfo,char * outStr,size_t strLen)434 bool GetArkJSHeapCrashInfo(int pid, uintptr_t *bytecodePc, uintptr_t *fp, bool outJSInfo, char *outStr, size_t strLen)
435 {
436     // bytecodePc: X20 in ARM
437     // fp: X29 in ARM
438     // outJSInfo: not async-safe, more info
439     uintptr_t currentPtr = *fp;
440     if (currentPtr == 0) {
441         LOG_ECMA(ERROR) << "fp is nullptr in GetArkJSHeapCrashInfo()!";
442         return false;
443     }
444     currentPtr -= sizeof(FrameType);
445     uintptr_t frameType = 0;
446     if (!ReadUintptrFromAddr(pid, currentPtr, frameType, false)) {
447         return false;
448     }
449     if (static_cast<FrameType>(frameType) != FrameType::ASM_INTERPRETER_FRAME) {
450         return false;
451     }
452     size_t strIndex = 0;
453     uintptr_t registerBytecode = 0;
454     if (!ReadUintptrFromAddr(pid, *bytecodePc, registerBytecode, false)) {
455         return false;
456     }
457     CopyBytecodeInfoToBuffer("RegisterBytecode:", registerBytecode, strIndex, outStr, strLen);
458     uintptr_t typeOffset = MEMBER_OFFSET(AsmInterpretedFrame, base) + MEMBER_OFFSET(InterpretedFrameBase, type);
459     uintptr_t pcOffset = MEMBER_OFFSET(AsmInterpretedFrame, pc);
460     currentPtr -= typeOffset;
461     currentPtr += pcOffset;
462     uintptr_t framePc = 0;
463     uintptr_t frameBytecode = 0;
464     if (!ReadUintptrFromAddr(pid, currentPtr, framePc, false)) {
465         return false;
466     }
467     if (!ReadUintptrFromAddr(pid, framePc, frameBytecode, false)) {
468         return false;
469     }
470     CopyBytecodeInfoToBuffer(" FrameBytecode:", frameBytecode, strIndex, outStr, strLen);
471     if (outJSInfo) {
472         uintptr_t functionOffset = MEMBER_OFFSET(AsmInterpretedFrame, function);
473         currentPtr -= pcOffset;
474         currentPtr += functionOffset;
475         uintptr_t functionAddress = 0;
476         if (!ReadUintptrFromAddr(pid, currentPtr, functionAddress, false)) {
477             return false;
478         }
479         JSTaggedValue functionValue(static_cast<JSTaggedType>(functionAddress));
480         Method *method = ECMAObject::Cast(functionValue.GetTaggedObject())->GetCallTarget();
481         auto bytecodeOffset = static_cast<uint32_t>(reinterpret_cast<uint8_t *>(*bytecodePc) -
482                                                     method->GetBytecodeArray());
483         std::string info = JsStackInfo::BuildMethodTrace(method, bytecodeOffset);
484         const char *infoChar = info.c_str();
485         if (strIndex < strLen - 1) {  // 1: last '\0'
486             outStr[strIndex++] = ' ';
487         }
488         for (size_t i = 0; infoChar[i] != '\0' && strIndex < strLen - 1; i++) {  // 1: last '\0'
489             outStr[strIndex++] = infoChar[i];
490         }
491         outStr[strIndex] = '\0';
492     }
493     return true;
494 }
495 } // namespace panda::ecmascript
496 
step_ark_managed_native_frame(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,char * buf,size_t buf_sz)497 __attribute__((visibility("default"))) int step_ark_managed_native_frame(
498     int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, char *buf, size_t buf_sz)
499 {
500     if (panda::ecmascript::StepArkManagedNativeFrame(pid, pc, fp, sp, buf, buf_sz)) {
501         return 1;
502     }
503     return -1;
504 }
505 
get_ark_js_heap_crash_info(int pid,uintptr_t * x20,uintptr_t * fp,int outJsInfo,char * buf,size_t buf_sz)506 __attribute__((visibility("default"))) int get_ark_js_heap_crash_info(
507     int pid, uintptr_t *x20, uintptr_t *fp, int outJsInfo, char *buf, size_t buf_sz)
508 {
509     if (panda::ecmascript::GetArkJSHeapCrashInfo(pid, x20, fp, outJsInfo != 0, buf, buf_sz)) {
510         return 1;
511     }
512     return -1;
513 }