/*
 * Copyright (c) 2023-2024 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 "printer.h"

#include <cinttypes>
#include <dlfcn.h>
#include <map>
#include <string>
#include <fcntl.h>
#include <unistd.h>

#include "dfx_config.h"
#include "dfx_frame_formatter.h"
#include "dfx_logger.h"
#include "dfx_ring_buffer_wrapper.h"
#include "dfx_signal.h"
#include "dfx_util.h"
#include "string_util.h"
#ifndef is_ohos_lite
#include "parameter.h"
#include "parameters.h"
#endif

#ifndef PAGE_SIZE
constexpr size_t PAGE_SIZE = 4096;
#endif

namespace OHOS {
namespace HiviewDFX {
void Printer::PrintDumpHeader(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process,
                              std::shared_ptr<Unwinder> unwinder)
{
    if (process == nullptr || request == nullptr) {
        return;
    }
    std::stringstream headerInfo;
    bool isCrash = (request->siginfo.si_signo != SIGDUMP);
    if (isCrash) {
#ifndef is_ohos_lite
        std::string buildInfo = OHOS::system::GetParameter("const.product.software.version", "Unknown");
        headerInfo << "Build info:" << buildInfo << "\n";
        DfxRingBufferWrapper::GetInstance().AppendMsg("Build info:" + buildInfo + "\n");
#endif
        headerInfo << "Timestamp:" << GetCurrentTimeStr(request->timeStamp);
        DfxRingBufferWrapper::GetInstance().AppendMsg("Timestamp:" + GetCurrentTimeStr(request->timeStamp));
    } else {
        headerInfo << "Timestamp:" << GetCurrentTimeStr();
        DfxRingBufferWrapper::GetInstance().AppendMsg("Timestamp:" + GetCurrentTimeStr());
    }
    headerInfo << "Pid:" << process->processInfo_.pid << "\n" <<
                  "Uid:" << process->processInfo_.uid << "\n" <<
                  "Process name:" << process->processInfo_.processName << "\n";
    DfxRingBufferWrapper::GetInstance().AppendBuf("Pid:%d\n", process->processInfo_.pid);
    DfxRingBufferWrapper::GetInstance().AppendBuf("Uid:%d\n", process->processInfo_.uid);
    DfxRingBufferWrapper::GetInstance().AppendBuf("Process name:%s\n", process->processInfo_.processName.c_str());
    if (isCrash) {
        DfxRingBufferWrapper::GetInstance().AppendBuf("Process life time:%s\n",
            DfxProcess::GetProcessLifeCycle(process->processInfo_.pid).c_str());

        std::string reasonInfo;
        PrintReason(request, process, unwinder, reasonInfo);
        headerInfo << reasonInfo << "\n";
        auto msg = process->GetFatalMessage();
        if (!msg.empty()) {
            headerInfo << "LastFatalMessage:" << msg.c_str() << "\n";
            DfxRingBufferWrapper::GetInstance().AppendBuf("LastFatalMessage:%s\n", msg.c_str());
        }

        auto traceId = request->traceInfo;
        if (traceId.chainId != 0) {
            headerInfo << "TraceId:" << std::hex << std::uppercase <<
                          static_cast<unsigned long long>(traceId.chainId) << "\n";
            DfxRingBufferWrapper::GetInstance().AppendBuf("TraceId:%llx\n",
                static_cast<unsigned long long>(traceId.chainId));
        }

        headerInfo << "Fault thread info:\n";
        DfxRingBufferWrapper::GetInstance().AppendMsg("Fault thread info:\n");
    }
    DfxRingBufferWrapper::GetInstance().AppendBaseInfo(headerInfo.str());
}

void Printer::PrintReason(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process,
                          std::shared_ptr<Unwinder> unwinder, std::string& reasonInfo)
{
    reasonInfo += "Reason:";
    DfxRingBufferWrapper::GetInstance().AppendMsg("Reason:");
    if (process == nullptr) {
        DFXLOG_WARN("%s", "process is nullptr");
        return;
    }
    process->reason += DfxSignal::PrintSignal(request->siginfo);
    uint64_t addr = (uint64_t)(request->siginfo.si_addr);
    if (request->siginfo.si_signo == SIGSEGV &&
        (request->siginfo.si_code == SEGV_MAPERR || request->siginfo.si_code == SEGV_ACCERR)) {
        if (addr < PAGE_SIZE) {
            process->reason += " probably caused by NULL pointer dereference\n";
            DfxRingBufferWrapper::GetInstance().AppendMsg(process->reason);
            reasonInfo += process->reason;
            return;
        }
        if (unwinder == nullptr) {
            DFXLOG_WARN("%s", "unwinder is nullptr");
            return;
        }
        std::shared_ptr<DfxMaps> maps = unwinder->GetMaps();
        std::vector<std::shared_ptr<DfxMap>> map;
        if ((request->dumpMode == SPLIT_MODE && process->vmThread_ == nullptr) || process->keyThread_ == nullptr) {
            DFXLOG_WARN("%s", "Thread_ is nullptr");
            return;
        }
        auto regs = DfxRegs::CreateFromUcontext(request->context);
        if (regs == nullptr) {
            DFXLOG_WARN("%s", "regs is nullptr");
            return;
        }
        std::string elfName = StringPrintf("[anon:stack:%d]", process->keyThread_->threadInfo_.tid);
        if (maps != nullptr && maps->FindMapsByName(elfName, map)) {
            if (map[0] != nullptr && (addr < map[0]->begin && map[0]->begin - addr <= PAGE_SIZE)) {
                process->reason += StringPrintf(
#if defined(__LP64__)
                    " current thread stack low address = %#018lx, probably caused by stack-buffer-overflow",
#else
                    " current thread stack low address = %#010llx, probably caused by stack-buffer-overflow",
#endif
                    map[0]->begin);
            }
        }
    }
    process->reason += "\n";
    DfxRingBufferWrapper::GetInstance().AppendMsg(process->reason);
    reasonInfo += process->reason;
}

void Printer::PrintProcessMapsByConfig(std::shared_ptr<DfxMaps> maps)
{
    if (DfxConfig::GetConfig().displayMaps) {
        if (maps == nullptr) {
            return;
        }
        auto mapsVec = maps->GetMaps();
        DfxRingBufferWrapper::GetInstance().AppendMsg("\nMaps:\n");
        for (auto iter = mapsVec.begin(); iter != mapsVec.end() && (*iter) != nullptr; iter++) {
            DfxRingBufferWrapper::GetInstance().AppendMsg((*iter)->ToString());
        }
    }
}

void Printer::PrintOtherThreadHeaderByConfig()
{
    if (DfxConfig::GetConfig().displayBacktrace) {
        DfxRingBufferWrapper::GetInstance().AppendMsg("Other thread info:\n");
    }
}

void Printer::PrintThreadHeaderByConfig(std::shared_ptr<DfxThread> thread, bool isKeyThread)
{
    std::stringstream headerInfo;
    if (DfxConfig::GetConfig().displayBacktrace && thread != nullptr) {
        DfxRingBufferWrapper::GetInstance().AppendBuf("Tid:%d, Name:%s\n",\
            thread->threadInfo_.tid, thread->threadInfo_.threadName.c_str());
        headerInfo << "Tid:" << thread->threadInfo_.tid << ", Name:" << thread->threadInfo_.threadName << "\n";
    }
    if (isKeyThread) {
        DfxRingBufferWrapper::GetInstance().AppendBaseInfo(headerInfo.str());
    }
}

bool Printer::IsLastValidFrame(const DfxFrame& frame)
{
    static uintptr_t libcStartPc = 0;
    static uintptr_t libffrtStartEntry = 0;
    if (((libcStartPc != 0) && (frame.pc == libcStartPc)) ||
        ((libffrtStartEntry != 0) && (frame.pc == libffrtStartEntry))) {
        return true;
    }

    if (frame.mapName.find("ld-musl-aarch64.so.1") != std::string::npos &&
        frame.funcName.find("start") != std::string::npos) {
        libcStartPc = frame.pc;
        return true;
    }

    if (frame.mapName.find("libffrt") != std::string::npos &&
        frame.funcName.find("CoStartEntry") != std::string::npos) {
        libffrtStartEntry = frame.pc;
        return true;
    }

    return false;
}

void Printer::PrintThreadBacktraceByConfig(std::shared_ptr<DfxThread> thread, bool isKeyThread)
{
    if (DfxConfig::GetConfig().displayBacktrace && thread != nullptr) {
        const auto& frames = thread->GetFrames();
        if (frames.size() == 0) {
            return;
        }
        bool needSkip = false;
        bool isSubmitter = true;
        for (const auto& frame : frames) {
            if (frame.index == 0) {
                isSubmitter = !isSubmitter;
            }
            if (isSubmitter) {
                DfxRingBufferWrapper::GetInstance().AppendMsg("========SubmitterStacktrace========\n");
                DfxRingBufferWrapper::GetInstance().AppendBaseInfo("========SubmitterStacktrace========\n");
                isSubmitter = false;
                needSkip = false;
            }
            if (needSkip) {
                continue;
            }
            DfxRingBufferWrapper::GetInstance().AppendMsg(DfxFrameFormatter::GetFrameStr(frame));
            if (isKeyThread) {
                DfxRingBufferWrapper::GetInstance().AppendBaseInfo(DfxFrameFormatter::GetFrameStr(frame));
            }
#if defined(__aarch64__)
            if (IsLastValidFrame(frame)) {
                needSkip = true;
            }
#endif
        }
    }
}

void Printer::PrintThreadRegsByConfig(std::shared_ptr<DfxThread> thread)
{
    if (thread == nullptr) {
        return;
    }
    if (DfxConfig::GetConfig().displayRegister) {
        auto regs = thread->GetThreadRegs();
        if (regs != nullptr) {
            DfxRingBufferWrapper::GetInstance().AppendMsg(regs->PrintRegs());
        }
    }
}

void Printer::PrintRegsByConfig(std::shared_ptr<DfxRegs> regs)
{
    if (regs == nullptr) {
        return;
    }
    if (DfxConfig::GetConfig().displayRegister) {
        DfxRingBufferWrapper::GetInstance().AppendMsg(regs->PrintRegs());
        DfxRingBufferWrapper::GetInstance().AppendBaseInfo(regs->PrintRegs());
    }
}

void Printer::PrintThreadFaultStackByConfig(std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread,
                                            std::shared_ptr<Unwinder> unwinder)
{
    if (DfxConfig::GetConfig().displayFaultStack) {
        if (process == nullptr || thread == nullptr) {
            return;
        }
        thread->InitFaultStack();
        auto faultStack = thread->GetFaultStack();
        if (faultStack == nullptr) {
            return;
        }
        if (process->regs_ == nullptr) {
            DFXLOG_ERROR("%s", "process regs is nullptr");
            return;
        }
        faultStack->CollectRegistersBlock(process->regs_, unwinder->GetMaps());
        faultStack->Print();
    }
}

void Printer::PrintThreadOpenFiles(std::shared_ptr<DfxProcess> process)
{
    if (process == nullptr) {
        return;
    }

    DfxRingBufferWrapper::GetInstance().AppendMsg("OpenFiles:\n");
    std::string infos = process->openFiles;
    constexpr size_t step = 1024;
    for (size_t i = 0; i < infos.size(); i += step) {
        DfxRingBufferWrapper::GetInstance().AppendMsg(infos.substr(i, step));
    }
}
} // namespace HiviewDFX
} // namespace OHOS