• 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)222 bool ReadUintptrFromAddr(int pid, uintptr_t addr, uintptr_t &value)
223 {
224     if (pid == getpid()) {
225         value = *(reinterpret_cast<uintptr_t *>(addr));
226         return true;
227     }
228     long *retAddr = reinterpret_cast<long *>(&value);
229     // note: big endian
230     for (size_t i = 0; i < sizeof(uintptr_t) / sizeof(long); i++) {
231         *retAddr = PtracePeektext(pid, addr);
232         if (*retAddr == -1) {
233             LOG_ECMA(ERROR) << "ReadFromAddr ERROR, addr: " << addr;
234             return false;
235         }
236         addr += sizeof(long);
237         retAddr++;
238     }
239     return true;
240 }
241 
GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType,uintptr_t & typeOffset,uintptr_t & prevOffset)242 bool GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType, uintptr_t &typeOffset, uintptr_t &prevOffset)
243 {
244     FrameType type = static_cast<FrameType>(frameType);
245     switch (type) {
246         case FrameType::OPTIMIZED_FRAME:
247             typeOffset = OptimizedFrame::GetTypeOffset();
248             prevOffset = OptimizedFrame::GetPrevOffset();
249             break;
250         case FrameType::OPTIMIZED_ENTRY_FRAME:
251             typeOffset = OptimizedEntryFrame::GetTypeOffset();
252             prevOffset = OptimizedEntryFrame::GetLeaveFrameFpOffset();
253             break;
254         case FrameType::ASM_BRIDGE_FRAME:
255             typeOffset = AsmBridgeFrame::GetTypeOffset();
256             prevOffset = AsmBridgeFrame::GetPrevOffset();
257             break;
258         case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME:
259             typeOffset = OptimizedJSFunctionUnfoldArgVFrame::GetTypeOffset();
260             prevOffset = OptimizedJSFunctionUnfoldArgVFrame::GetPrevOffset();
261             break;
262         case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
263         case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
264         case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
265             typeOffset = OptimizedJSFunctionFrame::GetTypeOffset();
266             prevOffset = OptimizedJSFunctionFrame::GetPrevOffset();
267             break;
268         case FrameType::LEAVE_FRAME:
269             typeOffset = OptimizedLeaveFrame::GetTypeOffset();
270             prevOffset = OptimizedLeaveFrame::GetPrevOffset();
271             break;
272         case FrameType::LEAVE_FRAME_WITH_ARGV:
273             typeOffset = OptimizedWithArgvLeaveFrame::GetTypeOffset();
274             prevOffset = OptimizedWithArgvLeaveFrame::GetPrevOffset();
275             break;
276         case FrameType::BUILTIN_CALL_LEAVE_FRAME:
277             typeOffset = OptimizedBuiltinLeaveFrame::GetTypeOffset();
278             prevOffset = OptimizedBuiltinLeaveFrame::GetPrevOffset();
279             break;
280         case FrameType::INTERPRETER_FRAME:
281         case FrameType::INTERPRETER_FAST_NEW_FRAME:
282             typeOffset = InterpretedFrame::GetTypeOffset();
283             prevOffset = InterpretedFrame::GetPrevOffset();
284             break;
285         case FrameType::INTERPRETER_BUILTIN_FRAME:
286             typeOffset = InterpretedBuiltinFrame::GetTypeOffset();
287             prevOffset = InterpretedBuiltinFrame::GetPrevOffset();
288             break;
289         case FrameType::INTERPRETER_CONSTRUCTOR_FRAME:
290         case FrameType::ASM_INTERPRETER_FRAME:
291             typeOffset = AsmInterpretedFrame::GetTypeOffset();
292             prevOffset = AsmInterpretedFrame::GetPrevOffset();
293             break;
294         case FrameType::BUILTIN_FRAME:
295         case FrameType::BUILTIN_ENTRY_FRAME:
296             typeOffset = BuiltinFrame::GetTypeOffset();
297             prevOffset = BuiltinFrame::GetPrevOffset();
298             break;
299         case FrameType::BUILTIN_FRAME_WITH_ARGV:
300         case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME:
301             typeOffset = BuiltinWithArgvFrame::GetTypeOffset();
302             prevOffset = BuiltinWithArgvFrame::GetPrevOffset();
303             break;
304         case FrameType::INTERPRETER_ENTRY_FRAME:
305             typeOffset = InterpretedEntryFrame::GetTypeOffset();
306             prevOffset = InterpretedEntryFrame::GetPrevOffset();
307             break;
308         case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
309             typeOffset = AsmInterpretedEntryFrame::GetTypeOffset();
310             prevOffset = AsmInterpretedEntryFrame::GetPrevOffset();
311             break;
312         case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
313             typeOffset = AsmInterpretedBridgeFrame::GetTypeOffset();
314             prevOffset = AsmInterpretedBridgeFrame::GetPrevOffset();
315             break;
316         default:
317             return false;
318     }
319     return true;
320 }
321 
StepArkManagedNativeFrame(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,char * buf,size_t bufSize)322 bool StepArkManagedNativeFrame(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp,
323                                [[maybe_unused]] char *buf, [[maybe_unused]] size_t bufSize)
324 {
325     constexpr size_t FP_SIZE = 8;
326     constexpr size_t LR_SIZE = 8;
327     uintptr_t currentPtr = *fp;
328     if (currentPtr == 0) {
329         LOG_ECMA(ERROR) << "fp is nullptr in StepArkManagedNativeFrame()!";
330         return false;
331     }
332     while (true) {
333         currentPtr -= sizeof(FrameType);
334         uintptr_t frameType = 0;
335         if (!ReadUintptrFromAddr(pid, currentPtr, frameType)) {
336             return false;
337         }
338         uintptr_t typeOffset = 0;
339         uintptr_t prevOffset = 0;
340         if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
341             LOG_ECMA(ERROR) << "FrameType ERROR, addr: " << currentPtr << ", frameType: " << frameType;
342             return false;
343         }
344         if (static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME ||
345             static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_ENTRY_FRAME ||
346             static_cast<FrameType>(frameType) == FrameType::BUILTIN_ENTRY_FRAME) {
347             break;
348         }
349         currentPtr -= typeOffset;
350         currentPtr += prevOffset;
351         if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr)) {
352             return false;
353         }
354         if (currentPtr == 0) {
355             LOG_ECMA(ERROR) << "currentPtr is nullptr in StepArkManagedNativeFrame()!";
356             return false;
357         }
358     }
359     currentPtr += sizeof(FrameType);
360     *fp = currentPtr;
361     currentPtr += FP_SIZE;
362     if (!ReadUintptrFromAddr(pid, currentPtr, *pc)) {
363         return false;
364     }
365     currentPtr += LR_SIZE;
366     *sp = currentPtr;
367     return true;
368 }
369 
CopyBytecodeInfoToBuffer(const char * prefix,uintptr_t fullBytecode,size_t & strIdx,char * outStr,size_t strLen)370 void CopyBytecodeInfoToBuffer(const char *prefix, uintptr_t fullBytecode, size_t &strIdx, char *outStr, size_t strLen)
371 {
372     // note: big endian
373     for (size_t i = 0; prefix[i] != '\0' && strIdx < strLen - 1; i++) {  // 1: last '\0'
374         outStr[strIdx++] = prefix[i];
375     }
376     size_t start = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleLdundefined));
377     size_t bytecode = fullBytecode & 0xff;  // 0xff: last byte
378     const char *bytecodeName = MessageString::GetMessageString(start + bytecode).c_str();
379     for (size_t i = 0; bytecodeName[i] != '\0' && strIdx < strLen - 1; i++) {  // 1: last '\0'
380         outStr[strIdx++] = bytecodeName[i];
381     }
382     if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleDeprecated)) ||
383         start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleWide)) ||
384         start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleThrow)) ||
385         start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleCallRuntime))) {
386         size_t startSecond = start;
387         if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleDeprecated))) {
388             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleDeprecatedLdlexenvPrefNone));
389         } else if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleWide))) {
390             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(
391                 HandleWideCreateobjectwithexcludedkeysPrefImm16V8V8));
392         } else if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleThrow))) {
393             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleThrowPrefNone));
394         } else if (start + bytecode == static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleCallRuntime))) {
395             startSecond = static_cast<size_t>(GET_MESSAGE_STRING_ID(HandleCallRuntimeNotifyConcurrentResultPrefNone));
396         }
397         size_t bytecodeSecond = (fullBytecode >> 8) & 0xff;  // 8, 0xff: second last byte
398         const char *bytecodeNameSecond = MessageString::GetMessageString(startSecond + bytecodeSecond).c_str();
399         if (strIdx < strLen - 1) {  // 1: last '\0'
400             outStr[strIdx++] = '/';
401         }
402         for (size_t i = 0; bytecodeNameSecond[i] != '\0' && strIdx < strLen - 1; i++) {  // 1: last '\0'
403             outStr[strIdx++] = bytecodeNameSecond[i];
404         }
405     }
406     outStr[strIdx] = '\0';
407 }
408 
GetArkJSHeapCrashInfo(int pid,uintptr_t * bytecodePc,uintptr_t * fp,bool outJSInfo,char * outStr,size_t strLen)409 bool GetArkJSHeapCrashInfo(int pid, uintptr_t *bytecodePc, uintptr_t *fp, bool outJSInfo, char *outStr, size_t strLen)
410 {
411     // bytecodePc: X20 in ARM
412     // fp: X29 in ARM
413     // outJSInfo: not async-safe, more info
414     uintptr_t currentPtr = *fp;
415     if (currentPtr == 0) {
416         LOG_ECMA(ERROR) << "fp is nullptr in GetArkJSHeapCrashInfo()!";
417         return false;
418     }
419     currentPtr -= sizeof(FrameType);
420     uintptr_t frameType = 0;
421     if (!ReadUintptrFromAddr(pid, currentPtr, frameType)) {
422         return false;
423     }
424     if (static_cast<FrameType>(frameType) != FrameType::ASM_INTERPRETER_FRAME) {
425         return false;
426     }
427     size_t strIndex = 0;
428     uintptr_t registerBytecode = 0;
429     if (!ReadUintptrFromAddr(pid, *bytecodePc, registerBytecode)) {
430         return false;
431     }
432     CopyBytecodeInfoToBuffer("RegisterBytecode:", registerBytecode, strIndex, outStr, strLen);
433     uintptr_t typeOffset = MEMBER_OFFSET(AsmInterpretedFrame, base) + MEMBER_OFFSET(InterpretedFrameBase, type);
434     uintptr_t pcOffset = MEMBER_OFFSET(AsmInterpretedFrame, pc);
435     currentPtr -= typeOffset;
436     currentPtr += pcOffset;
437     uintptr_t framePc = 0;
438     uintptr_t frameBytecode = 0;
439     if (!ReadUintptrFromAddr(pid, currentPtr, framePc)) {
440         return false;
441     }
442     if (!ReadUintptrFromAddr(pid, framePc, frameBytecode)) {
443         return false;
444     }
445     CopyBytecodeInfoToBuffer(" FrameBytecode:", frameBytecode, strIndex, outStr, strLen);
446     if (outJSInfo) {
447         uintptr_t functionOffset = MEMBER_OFFSET(AsmInterpretedFrame, function);
448         currentPtr -= pcOffset;
449         currentPtr += functionOffset;
450         uintptr_t functionAddress = 0;
451         if (!ReadUintptrFromAddr(pid, currentPtr, functionAddress)) {
452             return false;
453         }
454         JSTaggedValue functionValue(static_cast<JSTaggedType>(functionAddress));
455         Method *method = ECMAObject::Cast(functionValue.GetTaggedObject())->GetCallTarget();
456         auto bytecodeOffset = static_cast<uint32_t>(reinterpret_cast<uint8_t *>(*bytecodePc) -
457                                                     method->GetBytecodeArray());
458         std::string info = JsStackInfo::BuildMethodTrace(method, bytecodeOffset);
459         const char *infoChar = info.c_str();
460         if (strIndex < strLen - 1) {  // 1: last '\0'
461             outStr[strIndex++] = ' ';
462         }
463         for (size_t i = 0; infoChar[i] != '\0' && strIndex < strLen - 1; i++) {  // 1: last '\0'
464             outStr[strIndex++] = infoChar[i];
465         }
466         outStr[strIndex] = '\0';
467     }
468     return true;
469 }
470 } // namespace panda::ecmascript
471 
step_ark_managed_native_frame(int pid,uintptr_t * pc,uintptr_t * fp,uintptr_t * sp,char * buf,size_t buf_sz)472 __attribute__((visibility("default"))) int step_ark_managed_native_frame(
473     int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, char *buf, size_t buf_sz)
474 {
475     if (panda::ecmascript::StepArkManagedNativeFrame(pid, pc, fp, sp, buf, buf_sz)) {
476         return 1;
477     }
478     return -1;
479 }
480 
get_ark_js_heap_crash_info(int pid,uintptr_t * x20,uintptr_t * fp,int outJsInfo,char * buf,size_t buf_sz)481 __attribute__((visibility("default"))) int get_ark_js_heap_crash_info(
482     int pid, uintptr_t *x20, uintptr_t *fp, int outJsInfo, char *buf, size_t buf_sz)
483 {
484     if (panda::ecmascript::GetArkJSHeapCrashInfo(pid, x20, fp, outJsInfo != 0, buf, buf_sz)) {
485         return 1;
486     }
487     return -1;
488 }