14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/platform/backtrace.h"
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ci#include <cstring>
194514f5e3Sopenharmony_ci#include <cinttypes>
204514f5e3Sopenharmony_ci#include <dlfcn.h>
214514f5e3Sopenharmony_ci#include <iomanip>
224514f5e3Sopenharmony_ci#include <ios>
234514f5e3Sopenharmony_ci#include <map>
244514f5e3Sopenharmony_ci#include <unwind.h>
254514f5e3Sopenharmony_ci#include "securec.h"
264514f5e3Sopenharmony_ci
274514f5e3Sopenharmony_ci#include "ecmascript/mem/mem.h"
284514f5e3Sopenharmony_ci#if defined(ENABLE_UNWINDER) && defined(__aarch64__)
294514f5e3Sopenharmony_ci#if defined(__clang__)
304514f5e3Sopenharmony_ci#pragma clang diagnostic push
314514f5e3Sopenharmony_ci#pragma clang diagnostic ignored "-Wshadow"
324514f5e3Sopenharmony_ci#pragma clang diagnostic ignored "-Wunused-parameter"
334514f5e3Sopenharmony_ci#endif
344514f5e3Sopenharmony_ci#include "fp_unwinder.h"
354514f5e3Sopenharmony_ci#if defined(__clang__)
364514f5e3Sopenharmony_ci#pragma clang diagnostic pop
374514f5e3Sopenharmony_ci#endif
384514f5e3Sopenharmony_ci#endif
394514f5e3Sopenharmony_ci
404514f5e3Sopenharmony_cinamespace panda::ecmascript {
414514f5e3Sopenharmony_cistatic const std::string LIB_UNWIND_SO_NAME = "libunwind.so";
424514f5e3Sopenharmony_cistatic const std::string LIB_UNWIND_Z_SO_NAME = "libunwind.z.so";
434514f5e3Sopenharmony_cistatic const int MAX_STACK_SIZE = 16;
444514f5e3Sopenharmony_cistatic const int LOG_BUF_LEN = 1024;
454514f5e3Sopenharmony_ci
464514f5e3Sopenharmony_ciusing UnwBackTraceFunc = int (*)(void**, int);
474514f5e3Sopenharmony_ci
484514f5e3Sopenharmony_cistatic std::map<void *, Dl_info> stackInfoCache;
494514f5e3Sopenharmony_ci
504514f5e3Sopenharmony_ci#if defined(ENABLE_UNWINDER) && defined(__aarch64__)
514514f5e3Sopenharmony_cistatic inline ARK_INLINE void GetPcFpRegs([[maybe_unused]] void *regs)
524514f5e3Sopenharmony_ci{
534514f5e3Sopenharmony_ci    asm volatile(
544514f5e3Sopenharmony_ci    "1:\n"
554514f5e3Sopenharmony_ci    "adr x12, 1b\n"
564514f5e3Sopenharmony_ci    "stp x12, x29, [%[base], #0]\n"
574514f5e3Sopenharmony_ci    : [base] "+r"(regs)
584514f5e3Sopenharmony_ci    :
594514f5e3Sopenharmony_ci    : "x12", "memory");
604514f5e3Sopenharmony_ci}
614514f5e3Sopenharmony_ci#endif
624514f5e3Sopenharmony_ci
634514f5e3Sopenharmony_cibool GetPcs(size_t &size, uintptr_t* pcs)
644514f5e3Sopenharmony_ci{
654514f5e3Sopenharmony_ci#if defined(ENABLE_UNWINDER) && defined(__aarch64__)
664514f5e3Sopenharmony_ci    uintptr_t regs[2] = {0}; // 2: pc and fp reg
674514f5e3Sopenharmony_ci    GetPcFpRegs(regs);
684514f5e3Sopenharmony_ci    uintptr_t pc = regs[0];
694514f5e3Sopenharmony_ci    uintptr_t fp = regs[1];
704514f5e3Sopenharmony_ci    size = OHOS::HiviewDFX::FpUnwinder::GetPtr()->Unwind(pc, fp, pcs, MAX_STACK_SIZE);
714514f5e3Sopenharmony_ci    if (size <= 1) {
724514f5e3Sopenharmony_ci        size = OHOS::HiviewDFX::FpUnwinder::GetPtr()->UnwindSafe(pc, fp, pcs, MAX_STACK_SIZE);
734514f5e3Sopenharmony_ci    }
744514f5e3Sopenharmony_ci#else
754514f5e3Sopenharmony_ci    static UnwBackTraceFunc unwBackTrace = nullptr;
764514f5e3Sopenharmony_ci    if (!unwBackTrace) {
774514f5e3Sopenharmony_ci        void *handle = dlopen(LIB_UNWIND_SO_NAME.c_str(), RTLD_NOW);
784514f5e3Sopenharmony_ci        if (handle == nullptr) {
794514f5e3Sopenharmony_ci            handle = dlopen(LIB_UNWIND_Z_SO_NAME.c_str(), RTLD_NOW);
804514f5e3Sopenharmony_ci            if (handle == nullptr) {
814514f5e3Sopenharmony_ci                LOG_ECMA(INFO) << "dlopen libunwind.so failed";
824514f5e3Sopenharmony_ci                return false;
834514f5e3Sopenharmony_ci            }
844514f5e3Sopenharmony_ci        }
854514f5e3Sopenharmony_ci        unwBackTrace = reinterpret_cast<UnwBackTraceFunc>(dlsym(handle, "unw_backtrace"));
864514f5e3Sopenharmony_ci        if (unwBackTrace == nullptr) {
874514f5e3Sopenharmony_ci            LOG_ECMA(INFO) << "dlsym unw_backtrace failed";
884514f5e3Sopenharmony_ci            return false;
894514f5e3Sopenharmony_ci        }
904514f5e3Sopenharmony_ci    }
914514f5e3Sopenharmony_ci    size = unwBackTrace(reinterpret_cast<void**>(pcs), MAX_STACK_SIZE);
924514f5e3Sopenharmony_ci#endif
934514f5e3Sopenharmony_ci    return true;
944514f5e3Sopenharmony_ci}
954514f5e3Sopenharmony_ci
964514f5e3Sopenharmony_civoid Backtrace(std::ostringstream &stack, bool enableCache)
974514f5e3Sopenharmony_ci{
984514f5e3Sopenharmony_ci    uintptr_t pcs[MAX_STACK_SIZE] = {0};
994514f5e3Sopenharmony_ci    size_t unwSz = 0;
1004514f5e3Sopenharmony_ci    if (!GetPcs(unwSz, pcs)) {
1014514f5e3Sopenharmony_ci        return;
1024514f5e3Sopenharmony_ci    }
1034514f5e3Sopenharmony_ci    stack << "=====================Backtrace========================";
1044514f5e3Sopenharmony_ci#if defined(ENABLE_UNWINDER) && defined(__aarch64__)
1054514f5e3Sopenharmony_ci    size_t i = 0;
1064514f5e3Sopenharmony_ci#else
1074514f5e3Sopenharmony_ci    size_t i = 1;
1084514f5e3Sopenharmony_ci#endif
1094514f5e3Sopenharmony_ci    for (; i < unwSz; i++) {
1104514f5e3Sopenharmony_ci        Dl_info info;
1114514f5e3Sopenharmony_ci        auto iter = stackInfoCache.find(reinterpret_cast<void *>(pcs[i]));
1124514f5e3Sopenharmony_ci        if (enableCache && iter != stackInfoCache.end()) {
1134514f5e3Sopenharmony_ci            info = iter->second;
1144514f5e3Sopenharmony_ci        } else {
1154514f5e3Sopenharmony_ci            if (!dladdr(reinterpret_cast<void *>(pcs[i]), &info)) {
1164514f5e3Sopenharmony_ci                break;
1174514f5e3Sopenharmony_ci            }
1184514f5e3Sopenharmony_ci            if (enableCache) {
1194514f5e3Sopenharmony_ci                stackInfoCache.emplace(reinterpret_cast<void *>(pcs[i]), info);
1204514f5e3Sopenharmony_ci            }
1214514f5e3Sopenharmony_ci        }
1224514f5e3Sopenharmony_ci        const char *file = info.dli_fname ? info.dli_fname : "";
1234514f5e3Sopenharmony_ci        uint64_t offset = info.dli_fbase ? pcs[i] - ToUintPtr(info.dli_fbase) : 0;
1244514f5e3Sopenharmony_ci        char buf[LOG_BUF_LEN] = {0};
1254514f5e3Sopenharmony_ci        char frameFormatWithMapName[] = "#%02zu pc %016" PRIx64 " %s";
1264514f5e3Sopenharmony_ci        int ret = 0;
1274514f5e3Sopenharmony_ci        ret = static_cast<int>(snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, frameFormatWithMapName, \
1284514f5e3Sopenharmony_ci            i, offset, file));
1294514f5e3Sopenharmony_ci        if (ret <= 0) {
1304514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "Backtrace snprintf_s failed";
1314514f5e3Sopenharmony_ci            return;
1324514f5e3Sopenharmony_ci        }
1334514f5e3Sopenharmony_ci        stack << std::endl;
1344514f5e3Sopenharmony_ci        stack << buf;
1354514f5e3Sopenharmony_ci    }
1364514f5e3Sopenharmony_ci}
1374514f5e3Sopenharmony_ci} // namespace panda::ecmascript
138