/*
 * Copyright (c) 2024-2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ecmascript/dfx/stackinfo/js_stackinfo.h"

#include "ecmascript/base/error_helper.h"
#include "ecmascript/base/string_helper.h"
#include "ecmascript/module/module_path_helper.h"
#include "ecmascript/platform/aot_crash_info.h"
#include "ecmascript/platform/file.h"
#include "ecmascript/platform/os.h"
#include "ecmascript/snapshot/common/modules_snapshot_helper.h"
#include "ecmascript/stubs/runtime_stubs-inl.h"
#include "ecmascript/jit/jit.h"
#include "ecmascript/ohos/aot_runtime_info.h"
#if defined(PANDA_TARGET_OHOS)
#include "ecmascript/extractortool/src/extractor.h"
#endif
#if defined(ENABLE_EXCEPTION_BACKTRACE)
#include "ecmascript/platform/backtrace.h"
#endif
#if defined(ENABLE_STATIC_BACKTRACE)
#include "tooling/backtrace/backtrace.h"
#endif

namespace panda::ecmascript {
std::unordered_map<EntityId, std::string> JsStackInfo::nameMap;
std::unordered_map<EntityId, std::vector<uint8>> JsStackInfo::machineCodeMap;
JSStackTrace *JSStackTrace::trace_ = nullptr;
std::mutex JSStackTrace::mutex_;
size_t JSStackTrace::count_ = 0;
#if defined(ENABLE_STATIC_BACKTRACE)
void *JSStackTrace::arkStaticHandle_ = nullptr;
ParseStaticArkLocalFunc JSStackTrace::parseStaticArkLocalFunc_ = nullptr;
CreateStaticArkLocalFunc JSStackTrace::createStaticArkLocalFunc_ = nullptr;
DestroyStaticArkLocalFunc JSStackTrace::destroyStaticArkLocalFunc_ = nullptr;
static_local_trace JSStackTrace::arkStaticLocalTracePtr_ = nullptr;
#endif
static std::mutex nameMapMutex;

bool IsFastJitFunctionFrame(const FrameType frameType)
{
    return frameType == FrameType::FASTJIT_FUNCTION_FRAME || frameType == FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME;
}

bool IsFastJitFunctionFrame(uintptr_t frameType)
{
    return static_cast<FrameType>(frameType) == FrameType::FASTJIT_FUNCTION_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME;
}

bool IsSteedFunctionFrame(const FrameType frameType)
{
    return frameType == FrameType::STEED_FUNCTION_FRAME;
}

bool IsSteedFunctionFrame(uintptr_t frameType)
{
    return static_cast<FrameType>(frameType) == FrameType::STEED_FUNCTION_FRAME;
}

void JsStackInfo::AppendMethodTrace(std::string &data, const JSThread *thread, Method *method, uint32_t pcOffset,
                                    LastBuilderCache &lastCache, bool enableStackSourceFile)
{
    data.append("    at ");
    std::string name = method->ParseFunctionName(thread);
    if (name.empty()) {
        data.append("anonymous (");
    } else {
        data.append(name).append(" (");
    }
    // source file
    DebugInfoExtractor *debugExtractor = nullptr;
    const JSPandaFile *pandaFile = method->GetJSPandaFile(thread);
    if (pandaFile == lastCache.pf) {
        debugExtractor = lastCache.extractor;
    } else {
        debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(pandaFile);
        lastCache.pf = pandaFile;
        lastCache.extractor = debugExtractor;
    }
    if (enableStackSourceFile) {
        const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
        if (sourceFile.empty()) {
            data.push_back('?');
        } else {
            data += sourceFile;
        }
    } else {
        data.append("hidden");
    }

    data.push_back(':');
    // line number and column number
    auto callbackLineFunc = [&data](int32_t line) -> bool {
        data += std::to_string(line + 1);
        data.push_back(':');
        return true;
    };
    auto callbackColumnFunc = [&data](int32_t column) -> bool {
        data += std::to_string(column + 1);
        return true;
    };
    panda_file::File::EntityId methodId = method->GetMethodId();
    if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, pcOffset) ||
        !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, pcOffset)) {
        data.push_back('?');
    }
    data.append(")\n");
}

void JsStackInfo::DumpJitCode(JSThread *thread)
{
    JSTaggedType exception = thread->GetException().GetRawData();
    auto &jitCodeMaps = thread->GetJitCodeMaps();
    auto jitCode = jitCodeMaps.find(exception);
    if (jitCode == jitCodeMaps.end()) {
        return;
    }
    std::set<MachineCode*> memos;
    JsJitDumpElf jitDumpElf;
    jitDumpElf.Init();
    int64 idx = 0;
    size_t offset = 0;
    auto jitCodeVec = jitCodeMaps[exception];
    for (size_t i = 0; i < jitCodeVec->size(); i++) {
        auto item = (*jitCodeVec)[i];
        auto machineCode = std::get<0>(item);
        std::string methodName = std::get<1>(item);
        uintptr_t pcOffset = std::get<2>(item);
        auto res = memos.insert(machineCode);
        if (res.second) {
            LOG_ECMA(ERROR) << "jit : js crash at method : " << methodName << ", offset :" << pcOffset;
            char *funcAddr = reinterpret_cast<char *>(machineCode->GetFuncAddr());
            size_t len = machineCode->GetTextSize();
            std::vector<uint8> vec(len);
            if (memmove_s(vec.data(), len, funcAddr, len) != EOK) {
                LOG_ECMA(ERROR) << "Fail to get machineCode on function addr: " << funcAddr;
            }
            jitDumpElf.AppendData(vec);
            jitDumpElf.AppendSymbolToSymTab(idx++, offset, len, methodName);
            offset += len;
        }
    }
    std::string fileName = "jitCode-" + std::to_string(getpid());
    std::string realOutPath;
    std::string sanboxPath = panda::os::file::File::GetExtendedFilePath(AotCrashInfo::GetSandBoxPath());
    if (!ecmascript::RealPath(sanboxPath, realOutPath, false)) {
        return;
    }
    std::string outFile = realOutPath + "/" + fileName;
    int fd = open(outFile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
    FdsanExchangeOwnerTag(reinterpret_cast<fd_t>(fd));
    jitDumpElf.WriteJitElfFile(fd);
    Close(reinterpret_cast<fd_t>(fd));
}

void AssembleJitCodeMap(JSThread *thread, const JSHandle<JSObject> &jsErrorObj, JSFunction *func, Method *method,
                        uintptr_t offset)
{
    ASSERT(!jsErrorObj.GetTaggedValue().IsUndefined());
    JSTaggedValue machineCodeTagVal = func->GetMachineCode(thread);
    MachineCode *machineCode = MachineCode::Cast(machineCodeTagVal.GetTaggedObject());
    std::string methodName = method->ParseFunctionName(thread);
    if (methodName.empty()) {
        methodName = "anonymous";
    }
    thread->SetJitCodeMap(jsErrorObj.GetTaggedValue().GetRawData(), machineCode, methodName, offset);
}

std::string JsStackInfo::BuildJsStackTrace(JSThread *thread, bool needNative, const JSHandle<JSObject> &jsErrorObj,
                                           bool needNativeStack, uint32_t depth)
{
    std::string data;
    // disallow cross thread execution
    if (thread->IsCrossThreadExecutionEnable()) {
        return data;
    }
    data.reserve(InitialDeeps * InitialLength);
    JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
    FrameIterator it(current, thread);
    uintptr_t baselineNativePc = 0;

    LastBuilderCache lastCache;
    for (; !it.Done() && depth > 0; it.Advance<GCVisitedFlag::HYBRID_STACK>()) {
        if (it.GetFrameType() == FrameType::BASELINE_BUILTIN_FRAME) {
            auto *frame = it.GetFrame<BaselineBuiltinFrame>();
            baselineNativePc = frame->GetReturnAddr();
            continue;
        }
        if (!it.IsJSFrame()) {
            continue;
        }
        auto method = it.CheckAndGetMethod();
        if (method == nullptr) {
            continue;
        }
        if (!method->IsNativeWithCallField()) {
            uint32_t pcOffset = 0;
            bool needBaselineSpecialHandling =
                (it.GetFrameType() == FrameType::ASM_INTERPRETER_FRAME && baselineNativePc != 0);
            if (needBaselineSpecialHandling) {
                // the pcOffste in baseline frame slot is always uint64::max(), so pcOffset should be computed
                JSHandle<JSFunction> function(thread, it.GetFunction());
                pcOffset = RuntimeStubs::RuntimeGetBytecodePcOfstForBaseline(thread, function, baselineNativePc);
                baselineNativePc = 0;
            }
            AppendJsStackTraceInfo(data, thread, method, it, jsErrorObj, lastCache,
                                   needBaselineSpecialHandling, pcOffset);
            --depth;
        } else if (needNative) {
            auto addr = JSFunction::Cast(it.GetFunction().GetTaggedObject())->GetNativePointer();
            std::stringstream strm;
            strm << addr;
            data.append("    at native method (").append(strm.str()).append(")\n");
            --depth;
        }
    }
#if defined(ENABLE_EXCEPTION_BACKTRACE)
    if (!needNativeStack) {
        return data;
    }
    if (data.empty()) {
        std::ostringstream stack;
        Backtrace(stack, true);
        data = stack.str();
    } else if (thread->IsMainThread()) {
        ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "BacktraceFromFp", "");
        auto vm = thread->GetEcmaVM();
        int size = BacktraceHybrid(vm->GetPcVectorData());
        vm->SetPcVectorSize(size);
    }
#endif
    return data;
}

void JsStackInfo::AppendJsStackTraceInfo(std::string &data, JSThread *thread, Method *method, FrameIterator &it,
                                         const JSHandle<JSObject> &jsErrorObj,
                                         LastBuilderCache &lastCache,
                                         bool needBaselineSpecialHandling, uint32_t pcOffset)
{
    FrameType frameType = it.GetFrameType();
    if (IsFastJitFunctionFrame(frameType) || IsSteedFunctionFrame(frameType)) {
        JSFunction *func = static_cast<JSFunction*>(it.GetFunction().GetTaggedObject());
        if (!jsErrorObj.GetTaggedValue().IsUndefined()) {
            AssembleJitCodeMap(thread, jsErrorObj, func, method, it.GetOptimizedReturnAddr());
        }
    }
    std::vector<std::pair<JSTaggedType, uint32_t>> stackTraceInfos;
    it.GetStackTraceInfos(stackTraceInfos, needBaselineSpecialHandling, pcOffset);
    for (auto &info : stackTraceInfos) {
        Method *methodInline = ECMAObject::Cast(reinterpret_cast<TaggedObject *>(info.first))->GetCallTarget(thread);
        uint32_t pcOffsetInline = info.second;
        AppendMethodTrace(data, thread, methodInline, pcOffsetInline, lastCache,
                          thread->GetEnableStackSourceFile());
    }
}

void JsStackInfo::BuildCrashInfo(bool isJsCrash, uintptr_t pc, JSThread *thread)
{
    if (isJsCrash) {
        ModulesSnapshotHelper::TryDisableSnapshot(thread);
    }

    if (JsStackInfo::loader == nullptr) {
        return;
    }
    if (!JsStackInfo::loader->IsEnableAOT() && !Jit::GetInstance()->IsEnableFastJit() &&
        !pgo::PGOProfilerManager::GetInstance()->IsEnable()) {
        return;
    }
    ohos::RuntimeInfoType type;
    if (isJsCrash) {
        type = ohos::RuntimeInfoType::JS;
    } else if (pc != 0 && JsStackInfo::loader != nullptr && JsStackInfo::loader->InsideAOT(pc)) {
        type = ohos::RuntimeInfoType::AOT_CRASH;
    } else {
        type = ohos::RuntimeInfoType::OTHERS;
    }
#if !ENABLE_MEMORY_OPTIMIZATION
    ohos::AotRuntimeInfo::GetInstance().BuildCrashRuntimeInfo(type);
#else
    ohos::AotRuntimeInfo::GetInstance().BuildCrashInfo();
#endif
    if (isJsCrash && thread != nullptr) {
        DumpJitCode(thread);
    }
}

std::vector<struct JsFrameInfo> JsStackInfo::BuildJsStackInfo(JSThread *thread, bool currentStack)
{
    std::vector<struct JsFrameInfo> jsFrame;
    JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
    FrameIterator it(current, thread);
    for (; !it.Done(); it.Advance<GCVisitedFlag::HYBRID_STACK>()) {
        if (!it.IsJSFrame()) {
            continue;
        }
        auto method = it.CheckAndGetMethod();
        if (method == nullptr) {
            continue;
        }
        struct JsFrameInfo frameInfo;
        if (!method->IsNativeWithCallField()) {
            std::string name = method->ParseFunctionName(thread);
            if (name.empty()) {
                frameInfo.functionName = "anonymous";
            } else {
                frameInfo.functionName = name;
            }
            // source file
            DebugInfoExtractor *debugExtractor =
                JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile(thread));
            const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
            if (sourceFile.empty()) {
                frameInfo.fileName = "?";
            } else {
                frameInfo.fileName = sourceFile;
            }
            // line number and column number
            int lineNumber = 0;
            auto callbackLineFunc = [&frameInfo, &lineNumber](int32_t line) -> bool {
                lineNumber = line + 1;
                frameInfo.pos = std::to_string(lineNumber) + ":";
                return true;
            };
            auto callbackColumnFunc = [&frameInfo](int32_t column) -> bool {
                frameInfo.pos += std::to_string(column + 1);
                return true;
            };
            panda_file::File::EntityId methodId = method->GetMethodId();
            uint32_t offset = it.GetBytecodeOffset();
            if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
                !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
                frameInfo.pos = "?";
            }
            jsFrame.push_back(std::move(frameInfo));
            if (currentStack) {
                return jsFrame;
            }
        }
    }
    return jsFrame;
}

bool GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType, uintptr_t &typeOffset, uintptr_t &prevOffset)
{
    FrameType type = static_cast<FrameType>(frameType);
    switch (type) {
        case FrameType::OPTIMIZED_FRAME:
            typeOffset = OptimizedFrame::GetTypeOffset();
            prevOffset = OptimizedFrame::GetPrevOffset();
            break;
        case FrameType::OPTIMIZED_ENTRY_FRAME:
            typeOffset = OptimizedEntryFrame::GetTypeOffset();
            prevOffset = OptimizedEntryFrame::GetLeaveFrameFpOffset();
            break;
        case FrameType::BASELINE_BUILTIN_FRAME:
            typeOffset = BaselineBuiltinFrame::GetTypeOffset();
            prevOffset = BaselineBuiltinFrame::GetPrevOffset();
            break;
        case FrameType::ASM_BRIDGE_FRAME:
            typeOffset = AsmBridgeFrame::GetTypeOffset();
            prevOffset = AsmBridgeFrame::GetPrevOffset();
            break;
        case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME:
            typeOffset = OptimizedJSFunctionUnfoldArgVFrame::GetTypeOffset();
            prevOffset = OptimizedJSFunctionUnfoldArgVFrame::GetPrevOffset();
            break;
        case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME:
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
            typeOffset = OptimizedJSFunctionFrame::GetTypeOffset();
            prevOffset = OptimizedJSFunctionFrame::GetPrevOffset();
            break;
        case FrameType::LEAVE_FRAME:
            typeOffset = OptimizedLeaveFrame::GetTypeOffset();
            prevOffset = OptimizedLeaveFrame::GetPrevOffset();
            break;
        case FrameType::LEAVE_FRAME_WITH_ARGV:
            typeOffset = OptimizedWithArgvLeaveFrame::GetTypeOffset();
            prevOffset = OptimizedWithArgvLeaveFrame::GetPrevOffset();
            break;
        case FrameType::BUILTIN_CALL_LEAVE_FRAME:
            typeOffset = OptimizedBuiltinLeaveFrame::GetTypeOffset();
            prevOffset = OptimizedBuiltinLeaveFrame::GetPrevOffset();
            break;
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME:
            typeOffset = InterpretedFrame::GetTypeOffset();
            prevOffset = InterpretedFrame::GetPrevOffset();
            break;
        case FrameType::INTERPRETER_BUILTIN_FRAME:
            typeOffset = InterpretedBuiltinFrame::GetTypeOffset();
            prevOffset = InterpretedBuiltinFrame::GetPrevOffset();
            break;
        case FrameType::INTERPRETER_CONSTRUCTOR_FRAME:
        case FrameType::ASM_INTERPRETER_FRAME:
            typeOffset = AsmInterpretedFrame::GetTypeOffset();
            prevOffset = AsmInterpretedFrame::GetPrevOffset();
            break;
        case FrameType::BUILTIN_FRAME:
        case FrameType::BUILTIN_ENTRY_FRAME:
            typeOffset = BuiltinFrame::GetTypeOffset();
            prevOffset = BuiltinFrame::GetPrevOffset();
            break;
        case FrameType::BUILTIN_FRAME_WITH_ARGV:
        case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME:
            typeOffset = BuiltinWithArgvFrame::GetTypeOffset();
            prevOffset = BuiltinWithArgvFrame::GetPrevOffset();
            break;
        case FrameType::INTERPRETER_ENTRY_FRAME:
            typeOffset = InterpretedEntryFrame::GetTypeOffset();
            prevOffset = InterpretedEntryFrame::GetPrevOffset();
            break;
        case FrameType::ASM_INTERPRETER_ENTRY_FRAME:
            typeOffset = AsmInterpretedEntryFrame::GetTypeOffset();
            prevOffset = AsmInterpretedEntryFrame::GetPrevOffset();
            break;
        case FrameType::ASM_INTERPRETER_BRIDGE_FRAME:
            typeOffset = AsmInterpretedBridgeFrame::GetTypeOffset();
            prevOffset = AsmInterpretedBridgeFrame::GetPrevOffset();
            break;
        case FrameType::FASTJIT_FUNCTION_FRAME:
        case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME:
            typeOffset = FASTJITFunctionFrame::GetTypeOffset();
            prevOffset = FASTJITFunctionFrame::GetPrevOffset();
            break;
        default:
            return false;
    }
    return true;
}

bool ArkFrameCheck(uintptr_t frameType)
{
    return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_ENTRY_FRAME;
}

bool IsJsFunctionFrame(uintptr_t frameType)
{
    return static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::INTERPRETER_CONSTRUCTOR_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FAST_NEW_FRAME;
}

bool IsNativeFunctionFrame(uintptr_t frameType)
{
    return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::BASELINE_BUILTIN_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::ASM_BRIDGE_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::LEAVE_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::LEAVE_FRAME_WITH_ARGV ||
           static_cast<FrameType>(frameType) == FrameType::BUILTIN_CALL_LEAVE_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::BUILTIN_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::BUILTIN_ENTRY_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::BUILTIN_FRAME_WITH_ARGV ||
           static_cast<FrameType>(frameType) == FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::ASM_INTERPRETER_BRIDGE_FRAME;
}

bool IsAotFunctionFrame(uintptr_t frameType)
{
    return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FUNCTION_FRAME ||
           static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME;
}

std::optional<MethodInfo> JSStackTrace::ReadMethodInfo(panda_file::MethodDataAccessor &mda)
{
    uintptr_t methodId = mda.GetMethodId().GetOffset();
    auto codeId = mda.GetCodeId();
    if (!codeId) {
        return std::nullopt;
    }
    panda_file::CodeDataAccessor cda(mda.GetPandaFile(), codeId.value());
    uint32_t codeSize = cda.GetCodeSize();
    uintptr_t codeBegin = reinterpret_cast<uintptr_t>(cda.GetInstructions());
    return std::make_optional<MethodInfo>(methodId, codeBegin, codeSize);
}

CVector<MethodInfo> JSStackTrace::ReadAllMethodInfos(std::shared_ptr<JSPandaFile> jsPandaFile)
{
    CVector<MethodInfo> result;
    if (jsPandaFile == nullptr) {
        LOG_ECMA(ERROR) << "Failed to read all methods info.";
        return result;
    }
    const panda_file::File *pf = jsPandaFile->GetPandaFile();
    Span<const uint32_t> classIndexes = jsPandaFile->GetClasses();
    for (const uint32_t index : classIndexes) {
        panda_file::File::EntityId classId(index);
        if (jsPandaFile->IsExternal(classId)) {
            continue;
        }
        panda_file::ClassDataAccessor cda(*pf, classId);
        cda.EnumerateMethods([&result, jsPandaFile](panda_file::MethodDataAccessor &mda) {
            auto info = JSStackTrace::ReadMethodInfo(mda);
            if (!info) {
                return;
            }
            result.push_back(info.value());
        });
    }

    std::sort(result.begin(), result.end());
    return result;
}

std::optional<CodeInfo> JSStackTrace::TranslateByteCodePc(uintptr_t realPc, const CVector<MethodInfo> &vec)
{
    if (vec.size() == 0) {
        LOG_ECMA(ERROR) << "Translate bytecode pc failed, vec is empty.";
        return std::nullopt;
    }
    int32_t left = 0;
    int32_t right = static_cast<int32_t>(vec.size()) - 1;
    for (; left <= right;) {
        int32_t mid = (left + right) / 2;
        bool isRight = realPc >= (vec[mid].codeBegin + vec[mid].codeSize);
        bool isLeft = realPc < vec[mid].codeBegin;
        // codeBegin <= realPc < codeBegin + codeSize
        if (!isRight && !isLeft) {
            return std::make_optional<CodeInfo>(realPc - vec[mid].codeBegin, vec[mid].methodId, vec[mid].codeSize);
        } else if (isRight) {
            left = mid + 1;
        } else {
            right = mid -1;
        }
    }
    LOG_ECMA(ERROR) << "Translate bytecode pc failed, pc: " << std::hex << realPc;
    return std::nullopt;
}

void SaveFuncName(EntityId entityId, const std::string &name)
{
    std::unique_lock<std::mutex> lock(nameMapMutex);
    size_t length = 256; // maximum stack length
    if (JsStackInfo::nameMap.size() > length) {
        auto it = JsStackInfo::nameMap.begin();
        JsStackInfo::nameMap.erase(it);
    }
    JsStackInfo::nameMap.emplace(entityId, name);
}

template<typename T>
void ParseJsFrameInfo(JSPandaFile *jsPandaFile, DebugInfoExtractor *debugExtractor,
                      EntityId methodId, uintptr_t offset, T &jsFrame, SourceMap *sourceMap)
{
    if (jsPandaFile == nullptr) {
        LOG_ECMA(ERROR) << "Parse jsFrame info failed, jsPandaFile is nullptr.";
        return;
    }
    std::string name = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
    name = name.empty() ? "anonymous" : name;
    std::string url = debugExtractor->GetSourceFile(methodId);

    // line number and column number
    int lineNumber = 0;
    int columnNumber = 0;
    auto callbackLineFunc = [&lineNumber](int32_t line) -> bool {
        lineNumber = line + 1;
        return true;
    };
    auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool {
        columnNumber = column + 1;
        return true;
    };

    if (!debugExtractor->MatchLineAndRevisedOffset(callbackLineFunc, methodId, offset) ||
        !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
        lineNumber = 0;
        columnNumber = 0;
    }

    std::string packageName;
    if (sourceMap != nullptr) {
        sourceMap->TranslateUrlPositionBySourceMap(url, lineNumber, columnNumber, packageName);
    }

    size_t urlSize = url.size() + 1;
    size_t nameSize = name.size() + 1;
    size_t packageNameSize = packageName.size() + 1;
    if (strcpy_s(jsFrame.url, urlSize, url.c_str()) != EOK ||
        strcpy_s(jsFrame.functionName, nameSize, name.c_str()) != EOK ||
        strcpy_s(jsFrame.packageName, packageNameSize, packageName.c_str()) != EOK) {
        LOG_ECMA(FATAL) << "jsFrame strcpy_s failed";
        UNREACHABLE();
    }
    jsFrame.line = lineNumber;
    jsFrame.column = columnNumber;
}

#if defined(ENABLE_STATIC_BACKTRACE)
bool StepStaticArk(void *ctx, ReadMemFunc readMem, ark::tooling::ArkStepParam *arkStepParam)
{
    if (!ark::tooling::Backtrace::StepArkByNativeFrame(ctx, readMem, arkStepParam)) {
        return false;
    }
    return true;
}

bool SymbolizeStaticArk(uintptr_t pc, uintptr_t mapBase, uintptr_t loadOffset, uint8_t *abcData,
                        uint64_t abcSize, JsFunction *jsFunction, uintptr_t extractor)
{
    if (!ark::tooling::Backtrace::SymbolizeByNativeFrame(pc, mapBase, loadOffset, abcData, abcSize,
        reinterpret_cast<ark::tooling::Function *>(jsFunction), extractor)) {
        LOG_ECMA(ERROR) << "symbolizeStaticArkFunc failed";
        return false;
    }
    return true;
}

panda_file::PandaFileType JSSymbolExtractor::GetPandaFileType(const uint8_t *data)
{
    if (type_ != panda_file::PandaFileType::FILE_FORMAT_INVALID) {
        return type_;
    }

    auto header = reinterpret_cast<const panda_file::File::Header *>(data);
    if (header->version == panda_file::File::STATIC_VERSION) {
        type_ = panda_file::PandaFileType::FILE_STATIC;
    } else {
        type_ = panda_file::PandaFileType::FILE_DYNAMIC;
    }

    return type_;
}
#endif

bool ArkParseJsFrameInfo(uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset,
                         uint8_t *data, uint64_t dataSize, uintptr_t extractorptr, JsFunction *jsFunction)
{
    if (data == nullptr) {
        LOG_ECMA(ERROR) << "Parse JSframe info failed, buffer is nullptr.";
        return false;
    }
    loadOffset = loadOffset % PageSize();
    auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
    if (extractor == nullptr) {
        LOG_ECMA(ERROR) << "Parse JSframe info failed, extractor is nullptr.";
        return false;
    }

#if defined(ENABLE_STATIC_BACKTRACE)
    auto fileType = extractor->GetPandaFileType(data);
    if (fileType == panda_file::PandaFileType::FILE_STATIC) {
        auto staticExtractor = extractor->GetStaticSymbolExtractor();
        return SymbolizeStaticArk(byteCodePc, mapBase, loadOffset, data, dataSize, jsFunction, staticExtractor);
    }
#endif

    auto jsPandaFile = extractor->GetJSPandaFile(data, dataSize);
    if (jsPandaFile == nullptr) {
        LOG_ECMA(ERROR) << "Parse JSframe info failed, panda file is nullptr.";
        return false;
    }
    auto debugExtractor = extractor->GetDebugExtractor();
    const auto &methodInfos = extractor->GetMethodInfos();
    if (methodInfos.empty()) {
        LOG_ECMA(ERROR) << "Read all method info from JSPandaFile failed, methodInfos is empty.";
        return false;
    }
    uintptr_t realOffset = byteCodePc - mapBase - loadOffset;
    uintptr_t pfBasePtr = reinterpret_cast<uintptr_t>(jsPandaFile->GetBase());
    auto codeInfo = JSStackTrace::TranslateByteCodePc(realOffset + pfBasePtr, methodInfos);
    if (!codeInfo) {
        LOG_ECMA(ERROR) << std::hex << "Failed to get methodId, pc: " << byteCodePc;
        return false;
    }
    auto offset = codeInfo->offset;
    ParseJsFrameInfo(jsPandaFile, debugExtractor, EntityId(codeInfo->methodId), offset,
                     *jsFunction, extractor->GetSourceMap());
    SaveFuncName(EntityId(codeInfo->methodId), jsFunction->functionName);
    jsFunction->codeBegin = byteCodePc - offset;
    jsFunction->codeSize = codeInfo->codeSize;
    return true;
}

uintptr_t GetBytecodeOffset(void *ctx, ReadMemFunc readMem, uintptr_t frameType, uintptr_t currentPtr)
{
    // currentPtr points to the frametype.
    uintptr_t bytecodePc = 0;
    FrameType type = static_cast<FrameType>(frameType);
    switch (type) {
        // return bytecode pc
        case FrameType::ASM_INTERPRETER_FRAME:
        case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: {
            currentPtr -= AsmInterpretedFrame::GetTypeOffset();
            currentPtr += AsmInterpretedFrame::GetPcOffset(false);
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::INTERPRETER_FRAME:
        case FrameType::INTERPRETER_FAST_NEW_FRAME: {
            currentPtr -= InterpretedFrame::GetTypeOffset();
            currentPtr += InterpretedFrame::GetPcOffset(false);
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::FASTJIT_FUNCTION_FRAME:
        case FrameType::FASTJIT_FAST_CALL_FUNCTION_FRAME: {
            currentPtr -= FASTJITFunctionFrame::GetTypeOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        // return returnaddr
        case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_FRAME:
        case FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME: {
            currentPtr -= OptimizedJSFunctionFrame::GetTypeOffset();
            currentPtr += OptimizedJSFunctionFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::BUILTIN_FRAME:
        case FrameType::BUILTIN_ENTRY_FRAME: {
            currentPtr -= BuiltinFrame::GetTypeOffset();
            currentPtr += BuiltinFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::BUILTIN_FRAME_WITH_ARGV:
        case FrameType::BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME: {
            currentPtr -= BuiltinWithArgvFrame::GetTypeOffset();
            currentPtr += BuiltinWithArgvFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::BASELINE_BUILTIN_FRAME: {
            currentPtr -= BaselineBuiltinFrame::GetTypeOffset();
            currentPtr += BaselineBuiltinFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::ASM_BRIDGE_FRAME: {
            currentPtr -= AsmBridgeFrame::GetTypeOffset();
            currentPtr += AsmBridgeFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::LEAVE_FRAME: {
            currentPtr -= OptimizedLeaveFrame::GetTypeOffset();
            currentPtr += OptimizedLeaveFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::LEAVE_FRAME_WITH_ARGV: {
            currentPtr -= OptimizedWithArgvLeaveFrame::GetTypeOffset();
            currentPtr += OptimizedWithArgvLeaveFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::BUILTIN_CALL_LEAVE_FRAME: {
            currentPtr -= OptimizedBuiltinLeaveFrame::GetTypeOffset();
            currentPtr += OptimizedBuiltinLeaveFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::OPTIMIZED_FRAME: {
            currentPtr -= OptimizedFrame::GetTypeOffset();
            currentPtr += OptimizedFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::ASM_INTERPRETER_BRIDGE_FRAME: {
            currentPtr -= AsmInterpretedBridgeFrame::GetTypeOffset();
            currentPtr += AsmInterpretedBridgeFrame::GetReturnAddrOffset(false);
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        case FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME: {
            currentPtr -= OptimizedJSFunctionUnfoldArgVFrame::GetTypeOffset();
            currentPtr += OptimizedJSFunctionUnfoldArgVFrame::GetReturnAddrOffset();
            readMem(ctx, currentPtr, &bytecodePc);
            return bytecodePc;
        }
        default: {
            break;
        }
    }
    return 0;
}

uintptr_t ArkGetFunction(void *ctx, ReadMemFunc readMem, uintptr_t currentPtr)
{
    // only for jit frame
    uintptr_t funcAddr = currentPtr;
    funcAddr -= FASTJITFunctionFrame::GetTypeOffset();
    funcAddr += FASTJITFunctionFrame::GetFunctionOffset();
    uintptr_t function = 0;
    if (!readMem(ctx, funcAddr, &function)) {
        return 0;
    }
    return function;
}

bool ArkGetNextFrame(void *ctx, ReadMemFunc readMem, uintptr_t &currentPtr,
                     uintptr_t &frameType, uintptr_t &pc)
{
    currentPtr -= sizeof(FrameType);
    if (!readMem(ctx, currentPtr, &frameType)) {
        return false;
    }
    FrameIterator::TryRemoveLazyDeoptFlag(frameType);
    if (ArkFrameCheck(frameType)) {
        return true;
    }
    bool ret = false;
    if (IsJsFunctionFrame(frameType) || IsNativeFunctionFrame(frameType)) {
        pc = GetBytecodeOffset(ctx, readMem, frameType, currentPtr);
        ret = true;
    }

    uintptr_t typeOffset = 0;
    uintptr_t prevOffset = 0;
    if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
        return false;
    }
    currentPtr -= typeOffset;
    currentPtr += prevOffset;
    if (!readMem(ctx, currentPtr, &currentPtr)) {
        return false;
    }

    if (ret) {
        return true;
    }
    return ArkGetNextFrame(ctx, readMem, currentPtr, frameType, pc);
}

bool ArkGetMethodIdWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t currentPtr)
{
    // only for jit frame
    uintptr_t function = ArkGetFunction(arkUnwindParam->ctx, arkUnwindParam->readMem, currentPtr);
    if (!function) {
        LOG_ECMA(DEBUG) << "Failed to get function";
        return false;
    }
    uintptr_t machineCode = 0;
    uintptr_t functionAddr = function + JSFunction::MACHINECODE_OFFSET;
    arkUnwindParam->readMem(arkUnwindParam->ctx, functionAddr, &machineCode);
    uintptr_t size = 0;
    uintptr_t funcAddr = 0;
    if (machineCode) {
        arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::INSTRSIZ_OFFSET, &size);
        arkUnwindParam->readMem(arkUnwindParam->ctx, machineCode + MachineCode::FUNCADDR_OFFSET, &funcAddr);
    }
    if (size && funcAddr) {
        // take the lower four bytes
        size &= 0xFFFFFFFF;
        std::vector<uint8> codeVec;
        for (size_t l = 0; l < size; l++) {
            uintptr_t tmp = 0;
            arkUnwindParam->readMem(arkUnwindParam->ctx, funcAddr + l, &tmp);
            codeVec.push_back(tmp);
        }
        arkUnwindParam->jitCache.push_back(*arkUnwindParam->methodId);
        JsStackInfo::machineCodeMap[EntityId(*arkUnwindParam->methodId)] = codeVec;
    }
    return true;
}

bool ArkGetNextFrameWithJit(ArkUnwindParam *arkUnwindParam, uintptr_t &currentPtr, uintptr_t &frameType)
{
    currentPtr -= sizeof(FrameType);
    if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, &frameType)) {
        return false;
    }
    FrameIterator::TryRemoveLazyDeoptFlag(frameType);
    if (ArkFrameCheck(frameType)) {
        return true;
    }
    bool ret = false;
    if (IsJsFunctionFrame(frameType) ||
        IsNativeFunctionFrame(frameType)) {
        *arkUnwindParam->pc = GetBytecodeOffset(arkUnwindParam->ctx, arkUnwindParam->readMem, frameType, currentPtr);
        ret = true;
    } else if (IsFastJitFunctionFrame(frameType) || IsSteedFunctionFrame(frameType)) {
        *arkUnwindParam->pc = GetBytecodeOffset(arkUnwindParam->ctx, arkUnwindParam->readMem, frameType, currentPtr);
        ret = ArkGetMethodIdWithJit(arkUnwindParam, currentPtr);
    }

    uintptr_t typeOffset = 0;
    uintptr_t prevOffset = 0;
    if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) {
        return false;
    }
    currentPtr -= typeOffset;
    currentPtr += prevOffset;
    if (!arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, &currentPtr)) {
        return false;
    }

    if (ret) {
        return true;
    }
    return ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType);
}

bool ArkWriteJitCode([[maybe_unused]] void *ctx, [[maybe_unused]] ReadMemFunc readMem,
                     int fd, const uintptr_t *const jitCodeArray, const size_t jitSize)
{
    JsJitDumpElf jitDumpElf;
    jitDumpElf.Init();
    std::set<uintptr_t> memos;
    int64 idx = 0;
    size_t offset = 0;
    for (size_t i = 0; i < jitSize; i++) {
        uintptr_t methodId = jitCodeArray[i];
        auto res = memos.insert(methodId);
        if (res.second) {
            std::vector<uint8> codeVec = JsStackInfo::machineCodeMap[EntityId(methodId)];
            std::string name = JsStackInfo::nameMap[EntityId(methodId)];
            size_t len = codeVec.size();
            jitDumpElf.AppendData(codeVec);
            jitDumpElf.AppendSymbolToSymTab(idx++, offset, len, name);
            offset += len;
        }
    }
    jitDumpElf.WriteJitElfFile(fd);
    JsStackInfo::nameMap.clear();
    JsStackInfo::machineCodeMap.clear();
    return true;
}

bool StepArkWithRecordJit(ArkUnwindParam *arkUnwindParam)
{
    constexpr size_t FP_SIZE = sizeof(uintptr_t);
    uintptr_t currentPtr = *arkUnwindParam->fp;
    if (currentPtr == 0) {
        LOG_ECMA(ERROR) << "fp is nullptr in StepArkWithRecordJit()!";
        return false;
    }

#if defined(ENABLE_STATIC_BACKTRACE)
    if (*arkUnwindParam->frameType == StepFrameType::STATIC_JS_FRAME) {
        ark::tooling::ArkStepParam staticParam;
        staticParam.fp = arkUnwindParam->fp;
        staticParam.sp = arkUnwindParam->sp;
        staticParam.pc = arkUnwindParam->pc;
        staticParam.isArkFrame = arkUnwindParam->isJsFrame;
        staticParam.frameType = reinterpret_cast<ark::tooling::StepFrameType*>(arkUnwindParam->frameType);
        staticParam.frameIndex = arkUnwindParam->frameIndex;
        return StepStaticArk(arkUnwindParam->ctx, arkUnwindParam->readMem, &staticParam);
    }
#endif

    uintptr_t frameType = 0;
    if (ArkGetNextFrameWithJit(arkUnwindParam, currentPtr, frameType)) {
        if (ArkFrameCheck(frameType)) {
            currentPtr += sizeof(FrameType);
            *arkUnwindParam->sp = currentPtr;
            bool ret = arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->fp);
            currentPtr += FP_SIZE;
            ret &= arkUnwindParam->readMem(arkUnwindParam->ctx, currentPtr, arkUnwindParam->pc);
            *arkUnwindParam->isJsFrame = false;
            *arkUnwindParam->frameType = StepFrameType::NATIVE_FRAME;
            return ret;
        } else {
            *arkUnwindParam->fp = currentPtr;
            *arkUnwindParam->sp = currentPtr;
            // js && jit -> true, native -> false
            *arkUnwindParam->isJsFrame = IsJsFunctionFrame(frameType) ||
                IsFastJitFunctionFrame(frameType) ||
                IsSteedFunctionFrame(frameType);
            if (*arkUnwindParam->isJsFrame) {
                *arkUnwindParam->frameType = StepFrameType::JS_FRAME;
            } else {
                *arkUnwindParam->frameType = StepFrameType::NATIVE_FRAME;
            }
        }
    } else {
        LOG_ECMA(ERROR) << "ArkGetNextFrameWithJit failed, currentPtr: " << currentPtr << ", frameType: " << frameType;
        return false;
    }
    return true;
}

bool StepArk(void *ctx, ReadMemFunc readMem, ArkStepParam *arkStepParam)
{
    constexpr size_t FP_SIZE = sizeof(uintptr_t);
    uintptr_t currentPtr = *arkStepParam->fp;
    if (currentPtr == 0) {
        return false;
    }

#if defined(ENABLE_STATIC_BACKTRACE)
    if (*arkStepParam->frameType == StepFrameType::STATIC_JS_FRAME) {
        ark::tooling::ArkStepParam staticParam;
        staticParam.fp = arkStepParam->fp;
        staticParam.sp = arkStepParam->sp;
        staticParam.pc = arkStepParam->pc;
        staticParam.isArkFrame = arkStepParam->isJsFrame;
        staticParam.frameType = reinterpret_cast<ark::tooling::StepFrameType*>(arkStepParam->frameType);
        staticParam.frameIndex = arkStepParam->frameIndex;
        return StepStaticArk(ctx, readMem, &staticParam);
    }
#endif

    uintptr_t frameType = 0;
    if (ArkGetNextFrame(ctx, readMem, currentPtr, frameType, *arkStepParam->pc)) {
        if (ArkFrameCheck(frameType)) {
            currentPtr += sizeof(FrameType);
            *arkStepParam->sp = currentPtr;
            bool ret = readMem(ctx, currentPtr, arkStepParam->fp);
            currentPtr += FP_SIZE;
            ret &= readMem(ctx, currentPtr, arkStepParam->pc);
            *arkStepParam->isJsFrame = false;
            *arkStepParam->frameType = StepFrameType::NATIVE_FRAME;
            return ret;
        } else {
            *arkStepParam->fp = currentPtr;
            *arkStepParam->sp = currentPtr;
            // js -> true, native -> false
            *arkStepParam->isJsFrame = IsJsFunctionFrame(frameType);
            if (*arkStepParam->isJsFrame) {
                *arkStepParam->frameType = StepFrameType::JS_FRAME;
            } else {
                *arkStepParam->frameType = StepFrameType::NATIVE_FRAME;
            }
        }
    } else {
        return false;
    }

    return true;
}

uint8_t* JSSymbolExtractor::GetData()
{
    return data_;
}

uintptr_t JSSymbolExtractor::GetLoadOffset()
{
    return loadOffset_;
}

uintptr_t JSSymbolExtractor::GetDataSize()
{
    return dataSize_;
}

bool JSSymbolExtractor::InitializeHapFileInfo([[maybe_unused]]uintptr_t offset,
                                              [[maybe_unused]]bool needTranslate,
                                              [[maybe_unused]]const char* filePath)
{
#if defined(PANDA_TARGET_OHOS)
    if (isInitialized_) {
        return isInitialized_;
    }
    bool newCreate = false;
    std::string hapPath(filePath);
    std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(hapPath, newCreate);
    if (extractor == nullptr) {
        LOG_ECMA(ERROR) << "GetExtractor failed, hap path: " << hapPath;
        return isInitialized_;
    }

    const std::string &pandaFilePath = extractor->GetFilePathByOffset(offset);
    fileMapper_ = extractor->GetSafeData(pandaFilePath);
    if (!fileMapper_) {
        LOG_ECMA(ERROR) << "GetSafeData failed, hap path: " << hapPath;
        return isInitialized_;
    }

    data_ = fileMapper_->GetDataPtr();
    dataSize_ = fileMapper_->GetDataLen();
    loadOffset_ = static_cast<uintptr_t>(fileMapper_->GetOffset());
    isInitialized_ = true;
#if defined(ENABLE_STATIC_BACKTRACE)
    type_ = GetPandaFileType(data_);
    if (type_ == panda_file::PandaFileType::FILE_STATIC) {
        ark::tooling::Backtrace::SetExtractorData(data_, dataSize_, 0, staticSymbolExtractor_);
        return isInitialized_;
    }
#endif
    CreateJSPandaFile();
    auto zipFile = std::make_unique<ZipFile>(hapPath);
    if (zipFile == nullptr || !zipFile->Open()) {
        return false;
    }
    auto &entrys = zipFile->GetAllEntries();
    if (isInitialized_ && needTranslate) {
        std::string filePath = "ets/sourceMaps.map";
        if (entrys.find(filePath) == entrys.end()) {
            LOG_ECMA(INFO) << "Can't find sourceMaps.map in hap/hsp";
            return isInitialized_;
        }
        CreateSourceMap(hapPath);
    }
#endif
    return isInitialized_;
}

bool JSSymbolExtractor::InitializeAbcFileInfo([[maybe_unused]]const char* filePath)
{
#if defined(PANDA_TARGET_OHOS)
    if (isInitialized_) {
        return isInitialized_;
    }
    fileMapMem_ = FileMap(filePath, O_RDONLY, PROT_READ, 0);
    if (fileMapMem_.GetMem() == nullptr) {
        return isInitialized_;
    }
    data_ = reinterpret_cast<uint8_t *>(fileMapMem_.GetMem());
    dataSize_ = reinterpret_cast<uintptr_t>(fileMapMem_.GetSize());
    isInitialized_ = true;
#if defined(ENABLE_STATIC_BACKTRACE)
    type_ = panda_file::GetFileType(data_, dataSize_);
    if (type_ == panda_file::PandaFileType::FILE_STATIC) {
        ark::tooling::Backtrace::SetExtractorData(data_, dataSize_, 0, staticSymbolExtractor_);
        return isInitialized_;
    }
#endif
    CreateJSPandaFile();
#endif
    return isInitialized_;
}

bool JSSymbolExtractor::Initialize(uintptr_t offset, bool needTranslate, const char* filePath)
{
    if (base::StringHelper::StringEndWith(filePath, ModulePathHelper::EXT_NAME_ABC)) {
        return InitializeAbcFileInfo(filePath);
    }
    return InitializeHapFileInfo(offset, needTranslate, filePath);
}

bool ArkParseJSFileInfo([[maybe_unused]] uintptr_t byteCodePc, [[maybe_unused]] uintptr_t mapBase,
                        [[maybe_unused]] uintptr_t offset, [[maybe_unused]] const char* filePath,
                        [[maybe_unused]] uintptr_t extractorptr, [[maybe_unused]] bool needTranslate,
                        [[maybe_unused]] JsFunction *jsFunction)
{
    bool ret = false;
#if defined(PANDA_TARGET_OHOS)
    if (filePath == nullptr) {
        LOG_ECMA(ERROR) << "FilePath from dfx is nullptr.";
        return false;
    }
    auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
    if (extractor == nullptr) {
        LOG_ECMA(ERROR) << "Parse JSframe info failed, extractor is nullptr.";
        return false;
    }
    if (!extractor->Initialize(offset, needTranslate, filePath)) {
        return false;
    }
    ret = ArkParseJsFrameInfo(byteCodePc, mapBase, extractor->GetLoadOffset(),
            extractor->GetData(), extractor->GetDataSize(), extractorptr, jsFunction);
#endif
    return ret;
}

JSSymbolExtractor::~JSSymbolExtractor()
{
    if (sourceMap_ != nullptr) {
        sourceMap_.reset();
    }
    if (debugExtractor_ != nullptr) {
        debugExtractor_.reset();
    }
    if (jsPandaFile_ != nullptr) {
        jsPandaFile_.reset();
    }
    methodInfo_.clear();
}

JSSymbolExtractor* JSSymbolExtractor::Create()
{
    auto extractor = new JSSymbolExtractor();
    return extractor;
}

bool JSSymbolExtractor::Destroy(JSSymbolExtractor *extractor)
{
    if (extractor == nullptr) {
        LOG_ECMA(ERROR) << "Destroy ark symbol extractor failed, extractor is nullptr.";
        return false;
    }
    delete extractor;
    extractor = nullptr;
    return true;
}

void JSSymbolExtractor::SetStaticSymbolExtractor(uintptr_t extractorptr)
{
    if (!extractorptr) {
        LOG_ECMA(ERROR) << "Set static ark symbol extractor failed, extractor is nullptr.";
        return;
    }
    staticSymbolExtractor_ = extractorptr;
}

uintptr_t JSSymbolExtractor::GetStaticSymbolExtractor()
{
    return staticSymbolExtractor_;
}

const CVector<MethodInfo> &JSSymbolExtractor::GetMethodInfos() const
{
    if (methodInfo_.empty()) {
        methodInfo_ = JSStackTrace::ReadAllMethodInfos(jsPandaFile_);
    }

    return methodInfo_;
}

JSPandaFile* JSSymbolExtractor::GetJSPandaFile(uint8_t *data, size_t dataSize)
{
    if (jsPandaFile_ == nullptr && data != nullptr) {
        CreateJSPandaFile(data, dataSize);
    }
    return jsPandaFile_.get();
}

void JSSymbolExtractor::CreateJSPandaFile()
{
    auto pf = panda_file::OpenPandaFileFromSecureMemory(data_, dataSize_);
    if (pf == nullptr) {
        LOG_ECMA(ERROR) << "Failed to open panda file.";
        return;
    }
    jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "", CreateMode::DFX);
}

void JSSymbolExtractor::CreateJSPandaFile(uint8_t *data, size_t dataSize)
{
    auto pf = panda_file::OpenPandaFileFromSecureMemory(data, dataSize);
    if (pf == nullptr) {
        LOG_ECMA(ERROR) << "Failed to open panda file.";
        return;
    }
    jsPandaFile_ = std::make_shared<JSPandaFile>(pf.release(), "", CreateMode::DFX);
}

SourceMap* JSSymbolExtractor::GetSourceMap()
{
    return sourceMap_.get();
}

void JSSymbolExtractor::CreateSourceMap([[maybe_unused]] const std::string &hapPath)
{
#if defined(PANDA_TARGET_OHOS)
    if (sourceMap_ == nullptr) {
        sourceMap_ = std::make_shared<SourceMap>();
        sourceMap_->Init(hapPath);
    }
#endif
}

DebugInfoExtractor* JSSymbolExtractor::GetDebugExtractor()
{
    if (debugExtractor_ == nullptr) {
        JSSymbolExtractor::CreateDebugExtractor();
    }
    return debugExtractor_.get();
}

void JSSymbolExtractor::CreateDebugExtractor()
{
    debugExtractor_ = std::make_unique<DebugInfoExtractor>(jsPandaFile_.get());
}

uintptr_t ArkCreateJSSymbolExtractor()
{
    auto extractor = JSSymbolExtractor::Create();
    auto extractorptr = reinterpret_cast<uintptr_t>(extractor);
#if defined(ENABLE_STATIC_BACKTRACE)
    auto staticSymbolExtractor = ark::tooling::Backtrace::CreateArkSymbolExtractor();
    extractor->SetStaticSymbolExtractor(staticSymbolExtractor);
#endif
    return extractorptr;
}

bool ArkDestroyJSSymbolExtractor(uintptr_t extractorptr)
{
    auto extractor = reinterpret_cast<JSSymbolExtractor*>(extractorptr);
#if defined(ENABLE_STATIC_BACKTRACE)
    auto staticSymbolExtractor = extractor->GetStaticSymbolExtractor();
    ark::tooling::Backtrace::DestroyArkSymbolExtractor(staticSymbolExtractor);
#endif
    return JSSymbolExtractor::Destroy(extractor);
}

void JSStackTrace::AddReference()
{
    std::unique_lock<std::mutex> lock(mutex_);
    if (count_ == 0) {
        trace_ = new JSStackTrace();
#if defined(ENABLE_STATIC_BACKTRACE)
        arkStaticHandle_ = dlopen("libarkruntime.so", RTLD_LAZY | RTLD_NOLOAD | RTLD_NODELETE);
        if (arkStaticHandle_) {
            parseStaticArkLocalFunc_ = reinterpret_cast<ParseStaticArkLocalFunc>(dlsym(arkStaticHandle_,
                "ArkParseArkFrameInfoLocal"));
            createStaticArkLocalFunc_ = reinterpret_cast<CreateStaticArkLocalFunc>(dlsym(arkStaticHandle_,
                "ArkCreateLocalStackTrace"));
            destroyStaticArkLocalFunc_ = reinterpret_cast<DestroyStaticArkLocalFunc>(dlsym(arkStaticHandle_,
                "ArkDestroyLocalStackTrace"));
            if (parseStaticArkLocalFunc_ && createStaticArkLocalFunc_ && destroyStaticArkLocalFunc_) {
                createStaticArkLocalFunc_(&arkStaticLocalTracePtr_);
            }
        }
#endif
    }
    ++count_;
    LOG_ECMA(INFO) << "Add reference, count: " << count_;
}

void JSStackTrace::ReleaseReference()
{
    std::unique_lock<std::mutex> lock(mutex_);
    if (trace_ == nullptr) {
        return ;
    }
    --count_;
    LOG_ECMA(INFO) << "Release reference, count: " << count_;
    if (count_ == 0) {
        delete trace_;
        trace_ = nullptr;
#if defined(ENABLE_STATIC_BACKTRACE)
        if (arkStaticHandle_) {
            if (parseStaticArkLocalFunc_ && createStaticArkLocalFunc_ && destroyStaticArkLocalFunc_) {
                destroyStaticArkLocalFunc_(arkStaticLocalTracePtr_);
                arkStaticLocalTracePtr_ = nullptr;
            }
            dlclose(arkStaticHandle_);
            arkStaticHandle_ = nullptr;
            parseStaticArkLocalFunc_ = nullptr;
            createStaticArkLocalFunc_ = nullptr;
            destroyStaticArkLocalFunc_ = nullptr;
        }
#endif
    }
}

#if defined(ENABLE_STATIC_BACKTRACE)
bool JSStackTrace::GetArkStaticFrameInfo(uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset,
                                         JsFunction *jsFunction)
{
    if (!parseStaticArkLocalFunc_) {
        LOG_ECMA(ERROR) << "Can not call ParseStaticArkLocalFunc";
        return false;
    }
    return parseStaticArkLocalFunc_(arkStaticLocalTracePtr_, byteCodePc, mapBase, loadOffset, jsFunction);
}
#endif

JSStackTrace::~JSStackTrace()
{
    {
        std::unique_lock<std::shared_mutex> lock(infosMutex_);
        methodInfos_.clear();
    }
    {
        std::unique_lock<std::shared_mutex> lock(pfMutex_);
        jsPandaFiles_.clear();
    }
}

bool JSStackTrace::InitializeMethodInfo(uintptr_t mapBase)
{
    auto pandafile = FindJSpandaFile(mapBase);
    if (pandafile != nullptr) {
        return true;
    }
    pandafile =
        JSPandaFileManager::GetInstance()->FindJSPandaFileByMapBase(mapBase);
    if (pandafile == nullptr) {
        LOG_ECMA(ERROR) << "Find pandafile failed, mapBase: " << std::hex << mapBase;
        return false;
    }
    auto methodInfos = ReadAllMethodInfos(pandafile);
    SetMethodInfos(mapBase, methodInfos);
    SetJSpandaFile(mapBase, pandafile);
    return true;
}

std::shared_ptr<JSPandaFile> JSStackTrace::FindJSpandaFile(uintptr_t mapBase)
{
    std::shared_lock<std::shared_mutex> lock(pfMutex_);
    auto iter = jsPandaFiles_.find(mapBase);
    if (iter == jsPandaFiles_.end()) {
        return nullptr;
    }
    return iter->second;
}

void JSStackTrace::SetJSpandaFile(uintptr_t mapBase, std::shared_ptr<JSPandaFile> pandafile)
{
    std::unique_lock<std::shared_mutex> lock(pfMutex_);
    jsPandaFiles_.emplace(mapBase, pandafile);
}

const CVector<MethodInfo> &JSStackTrace::FindMethodInfos(uintptr_t mapBase)
{
    std::shared_lock<std::shared_mutex> lock(infosMutex_);
    auto iter = methodInfos_.find(mapBase);
    if (iter == methodInfos_.end()) {
        return methodInfo_;
    }
    return iter->second;
}

void JSStackTrace::SetMethodInfos(uintptr_t mapBase, CVector<MethodInfo> &infos)
{
    std::unique_lock<std::shared_mutex> lock(infosMutex_);
    methodInfos_.emplace(mapBase, std::move(infos));
}

bool JSStackTrace::GetJsFrameInfo(uintptr_t byteCodePc, uintptr_t mapBase,
                                  uintptr_t loadOffset, JsFunction *jsFunction)
{
    if (!InitializeMethodInfo(mapBase)) {
        return false;
    }
    loadOffset = loadOffset % PageSize();
    byteCodePc = byteCodePc - loadOffset;
    const auto &infos = FindMethodInfos(mapBase);
    auto codeInfo = TranslateByteCodePc(byteCodePc, infos);
    if (!codeInfo) {
        LOG_ECMA(ERROR) << std::hex << "Failed to get methodId, pc: " << byteCodePc;
        return false;
    }
    auto offset = codeInfo->offset;
    auto pandafile = FindJSpandaFile(mapBase);
    auto debugInfoExtractor =
        JSPandaFileManager::GetInstance()->GetJSPtExtractor(pandafile.get());
    ParseJsFrameInfo(pandafile.get(), debugInfoExtractor, EntityId(codeInfo->methodId), offset, *jsFunction);
    jsFunction->codeBegin = byteCodePc - offset;
    jsFunction->codeSize = codeInfo->codeSize;
    return true;
}

void ArkCreateLocal()
{
    JSStackTrace::AddReference();
}

bool ArkParseJsFrameInfoLocal(uintptr_t byteCodePc, uintptr_t mapBase,
                              uintptr_t loadOffset, JsFunction *jsFunction)
{
    auto trace = JSStackTrace::GetInstance();
    if (trace == nullptr) {
        LOG_ECMA(ERROR) << "singleton is null, need create first.";
        return false;
    }
    bool result = trace->GetJsFrameInfo(byteCodePc, mapBase, loadOffset, jsFunction);
#if defined(ENABLE_STATIC_BACKTRACE)
    if (!result) {
        result = trace->GetArkStaticFrameInfo(byteCodePc, mapBase, loadOffset, jsFunction);
    }
#endif
    return result;
}

void ArkDestroyLocal()
{
    JSStackTrace::ReleaseReference();
}

} // namespace panda::ecmascript

__attribute__((visibility("default"))) int ark_create_js_symbol_extractor(uintptr_t *extractorptr)
{
    *extractorptr = panda::ecmascript::ArkCreateJSSymbolExtractor();
    return 1;
}

__attribute__((visibility("default"))) int ark_destory_js_symbol_extractor(uintptr_t extractorptr)
{
    if (panda::ecmascript::ArkDestroyJSSymbolExtractor(extractorptr)) {
        return 1;
    }
    return -1;
}

__attribute__((visibility("default"))) int ark_destroy_local()
{
    panda::ecmascript::ArkDestroyLocal();
    return 1;
}

__attribute__((visibility("default"))) int ark_create_local()
{
    panda::ecmascript::ArkCreateLocal();
    return 1;
}

__attribute__((visibility("default"))) int step_ark_with_record_jit(panda::ecmascript::ArkUnwindParam *arkUnwindParam)
{
    if (panda::ecmascript::StepArkWithRecordJit(arkUnwindParam)) {
        return 1;
    }
    return -1;
}

__attribute__((visibility("default"))) int ark_write_jit_code(
    void *ctx, panda::ecmascript::ReadMemFunc readMem, int fd, const uintptr_t *const jitCodeArray,
    const size_t jitSize)
{
    if (panda::ecmascript::ArkWriteJitCode(ctx, readMem, fd, jitCodeArray, jitSize)) {
        return 1;
    }
    return -1;
}

__attribute__((visibility("default"))) int step_ark(
    void *ctx, panda::ecmascript::ReadMemFunc readMem, panda::ecmascript::ArkStepParam *arkStepParam)
{
    if (panda::ecmascript::StepArk(ctx, readMem, arkStepParam)) {
        return 1;
    }
    return -1;
}

__attribute__((visibility("default"))) int 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)
{
    if (panda::ecmascript::ArkParseJsFrameInfo(byteCodePc, mapBase, loadOffset, data,
                                               dataSize, extractorptr, jsFunction)) {
        return 1;
    }
    return -1;
}

__attribute__((visibility("default"))) int ark_parse_js_file_info(
    uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t offset, const char* filePath,
    uintptr_t extractorptr, bool needTranslate, panda::ecmascript::JsFunction *jsFunction)
{
    if (panda::ecmascript::ArkParseJSFileInfo(byteCodePc, mapBase, offset, filePath,
        extractorptr, needTranslate, jsFunction)) {
        return 1;
    }
    return -1;
}

__attribute__((visibility("default"))) int ark_parse_js_frame_info_local(
    uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset, panda::ecmascript::JsFunction *jsFunction)
{
    if (panda::ecmascript::ArkParseJsFrameInfoLocal(byteCodePc, mapBase, loadOffset, jsFunction)) {
        return 1;
    }
    return -1;
}