/*
 * 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 "hitrace_dump.h"

#include <map>
#include <atomic>
#include <cinttypes>
#include <csignal>
#include <ctime>
#include <dirent.h>
#include <fcntl.h>
#include <filesystem>
#include <fstream>
#include <set>
#include <sys/epoll.h>
#include <sys/file.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/wait.h>
#include <thread>
#include <unistd.h>

#include "common_utils.h"
#include "dynamic_buffer.h"
#include "hilog/log.h"
#include "hitrace_osal.h"
#include "parameters.h"
#include "securec.h"

using namespace std;
using namespace OHOS::HiviewDFX::HitraceOsal;
using OHOS::HiviewDFX::HiLog;

namespace OHOS {
namespace HiviewDFX {
namespace Hitrace {

namespace {

struct TraceParams {
    std::vector<std::string> tags;
    std::vector<std::string> tagGroups;
    std::string bufferSize;
    std::string clockType;
    std::string isOverWrite;
    std::string outputFile;
    std::string fileSize;
    std::string fileLimit;
    std::string appPid;
};

constexpr uint16_t MAGIC_NUMBER = 57161;
constexpr uint16_t VERSION_NUMBER = 1;
constexpr uint8_t FILE_RAW_TRACE = 0;
constexpr uint8_t HM_FILE_RAW_TRACE = 1;
constexpr int UNIT_TIME = 100000;
constexpr int ALIGNMENT_COEFFICIENT = 4;

const int DEFAULT_BUFFER_SIZE = 12 * 1024;
const int DEFAULT_FILE_SIZE = 100 * 1024;
#ifdef DOUBLE_TRACEBUFFER_ENABLE
const int HM_DEFAULT_BUFFER_SIZE = 288 * 1024;
#else
const int HM_DEFAULT_BUFFER_SIZE = 144 * 1024;
#endif
const int SAVED_CMDLINES_SIZE = 3072; // 3M
const int KB_PER_MB = 1024;
const int S_TO_NS = 1000000000;
const int MAX_NEW_TRACE_FILE_LIMIT = 5;
const int JUDGE_FILE_EXIST = 10;  // Check whether the trace file exists every 10 times.
const int SNAPSHOT_FILE_MAX_COUNT = 20;

const std::string DEFAULT_OUTPUT_DIR = "/data/log/hitrace/";
const std::string SAVED_EVENTS_FORMAT = "saved_events_format";
const std::string TRACE_DEFAULT_DIR = "/data/log/hitrace/";
const std::string TRACE_EVENT_FORMT = "saved_events_format";
const std::string TRACE_SNAPSHOT_PREFIX = "trace_";
const std::string TRACE_RECORDING_PREFIX = "record_trace_";
const size_t DEFAULT_TRACE_FILE_LIMIT = 15;

struct alignas(ALIGNMENT_COEFFICIENT) TraceFileHeader {
    uint16_t magicNumber {MAGIC_NUMBER};
    uint8_t fileType {FILE_RAW_TRACE};
    uint16_t versionNumber {VERSION_NUMBER};
    uint32_t reserved {0};
};

enum ContentType : uint8_t {
    CONTENT_TYPE_DEFAULT = 0,
    CONTENT_TYPE_EVENTS_FORMAT = 1,
    CONTENT_TYPE_CMDLINES  = 2,
    CONTENT_TYPE_TGIDS = 3,
    CONTENT_TYPE_CPU_RAW = 4,
    CONTENT_TYPE_HEADER_PAGE = 30,
    CONTENT_TYPE_PRINTK_FORMATS = 31,
    CONTENT_TYPE_KALLSYMS = 32
};

struct alignas(ALIGNMENT_COEFFICIENT) TraceFileContentHeader {
    uint8_t type = CONTENT_TYPE_DEFAULT;
    uint32_t length = 0;
};

struct PageHeader {
    uint64_t timestamp = 0;
    uint64_t size = 0;
    uint8_t overwrite = 0;
    uint8_t *startPos = nullptr;
    uint8_t *endPos = nullptr;
};

#ifndef PAGE_SIZE
constexpr size_t PAGE_SIZE = 4096;
#endif

const int BUFFER_SIZE = 256 * PAGE_SIZE; // 1M

std::atomic<bool> g_dumpFlag(false);
std::atomic<bool> g_dumpEnd(true);
std::mutex g_traceMutex;

bool g_serviceThreadIsStart = false;
uint64_t g_sysInitParamTags = 0;
TraceMode g_traceMode = TraceMode::CLOSE;
std::string g_traceRootPath;
uint8_t g_buffer[BUFFER_SIZE] = {0};
std::vector<std::pair<std::string, int>> g_traceFilesTable;
std::vector<std::string> g_outputFilesForCmd;
int g_outputFileSize = 0;
int g_timeLimit = 0;
int g_newTraceFileLimit = 0;
int g_writeFileLimit = 0;
bool g_needGenerateNewTraceFile = false;

TraceParams g_currentTraceParams = {};

struct FileWithTime {
    std::string filename;
    time_t ctime;
};

void RemoveFile(const std::string& fileName)
{
    int fd = open(fileName.c_str(), O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        HILOG_WARN(LOG_CORE, "RemoveFile :: open file failed: %{public}s", fileName.c_str());
        return;
    }
    if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
        HILOG_WARN(LOG_CORE, "RemoveFile :: get file lock failed, skip remove: %{public}s", fileName.c_str());
        return;
    }
    if (remove(fileName.c_str()) == 0) {
        HILOG_INFO(LOG_CORE, "RemoveFile :: Delete %{public}s success.", fileName.c_str());
    } else {
        HILOG_WARN(LOG_CORE, "RemoveFile :: Delete %{public}s failed.", fileName.c_str());
    }
    flock(fd, LOCK_UN);
    close(fd);
}

void GetRecordTraceFilesInDir(std::vector<FileWithTime>& fileList)
{
    struct stat fileStat;
    for (const auto &entry : std::filesystem::directory_iterator(TRACE_DEFAULT_DIR)) {
        if (!entry.is_regular_file()) {
            continue;
        }
        std::string fileName = entry.path().filename().string();
        if (fileName.substr(0, TRACE_RECORDING_PREFIX.size()) == TRACE_RECORDING_PREFIX) {
            if (stat((TRACE_DEFAULT_DIR + fileName).c_str(), &fileStat) == 0) {
                fileList.push_back({fileName, fileStat.st_ctime});
            }
        }
    }
    std::sort(fileList.begin(), fileList.end(), [](const FileWithTime& a, const FileWithTime& b) {
        return a.ctime < b.ctime;
    });
}

std::string GenerateTraceFileName(bool isSnapshot = true)
{
    // eg: /data/log/hitrace/trace_localtime@boottime.sys
    std::string name = TRACE_DEFAULT_DIR;

    if (isSnapshot) {
        name += TRACE_SNAPSHOT_PREFIX;
    } else {
        name += TRACE_RECORDING_PREFIX;
    }

    // get localtime
    time_t currentTime = time(nullptr);
    struct tm timeInfo = {};
    const int bufferSize = 16;
    char timeStr[bufferSize] = {0};
    if (localtime_r(&currentTime, &timeInfo) == nullptr) {
        HILOG_ERROR(LOG_CORE, "Failed to get localtime.");
        return "";
    }
    (void)strftime(timeStr, bufferSize, "%Y%m%d%H%M%S", &timeInfo);
    name += std::string(timeStr);
    // get boottime
    struct timespec bts = {0, 0};
    clock_gettime(CLOCK_BOOTTIME, &bts);
    name += "@" + std::to_string(bts.tv_sec) + "-" + std::to_string(bts.tv_nsec) + ".sys";

    struct timespec mts = {0, 0};
    clock_gettime(CLOCK_MONOTONIC, &mts);
    HILOG_INFO(LOG_CORE, "output trace: %{public}s, boot_time(%{public}" PRId64 "), mono_time(%{public}" PRId64 ").",
        name.c_str(), static_cast<int64_t>(bts.tv_sec), static_cast<int64_t>(mts.tv_sec));
    return name;
}

/**
 * When the SERVICE_MODE is started, clear the remaining trace files in the folder.
*/
void DelSnapshotTraceFile(const bool deleteSavedFmt = true, const int keepFileCount = 0)
{
    if (access(TRACE_DEFAULT_DIR.c_str(), F_OK) != 0) {
        return;
    }
    DIR* dirPtr = opendir(TRACE_DEFAULT_DIR.c_str());
    if (dirPtr == nullptr) {
        HILOG_ERROR(LOG_CORE, "Failed to opendir %{public}s.", TRACE_DEFAULT_DIR.c_str());
        return;
    }
    std::vector<FileWithTime> snapshotTraceFiles;
    struct dirent* ptr = nullptr;
    struct stat fileStat;
    while ((ptr = readdir(dirPtr)) != nullptr) {
        if (ptr->d_type == DT_REG) {
            std::string name = std::string(ptr->d_name);
            if (deleteSavedFmt && name.compare(0, TRACE_EVENT_FORMT.size(), TRACE_EVENT_FORMT) == 0) {
                RemoveFile(TRACE_DEFAULT_DIR + name);
                continue;
            }
            if (name.compare(0, TRACE_SNAPSHOT_PREFIX.size(), TRACE_SNAPSHOT_PREFIX) != 0) {
                continue;
            }
            if (stat((TRACE_DEFAULT_DIR + name).c_str(), &fileStat) == 0) {
                snapshotTraceFiles.push_back({name, fileStat.st_ctime});
            }
        }
    }
    closedir(dirPtr);

    std::sort(snapshotTraceFiles.begin(), snapshotTraceFiles.end(), [](const FileWithTime& a, const FileWithTime& b) {
        return a.ctime < b.ctime;
    });

    int deleteFileCnt = snapshotTraceFiles.size() - keepFileCount;
    for (int i = 0; i < deleteFileCnt && i < snapshotTraceFiles.size(); i++) {
        RemoveFile(TRACE_DEFAULT_DIR + snapshotTraceFiles[i].filename);
    }
}

/**
 * open trace file aging mechanism
 */
void DelOldRecordTraceFile(const std::string& fileLimit)
{
    size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
    if (!fileLimit.empty()) {
        traceFileLimit = static_cast<size_t>(std::stoi(fileLimit));
    }
    HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);

    std::vector<FileWithTime> fileList;
    GetRecordTraceFilesInDir(fileList);

    if (fileList.size() <= traceFileLimit) {
        HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: no record trace file need be deleted.");
        return;
    }

    size_t deleteNum = fileList.size() - traceFileLimit;
    for (int i = 0; i < deleteNum; ++i) {
        if (remove((TRACE_DEFAULT_DIR + fileList[i].filename).c_str()) == 0) {
            HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s success.",
                fileList[i].filename.c_str());
        } else {
            HILOG_ERROR(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s failed, errno: %{public}d.",
                fileList[i].filename.c_str(), errno);
        }
    }
}

void ClearOldTraceFile(std::vector<std::string>& fileLists, const std::string& fileLimit)
{
    if (fileLists.size() <= 0) {
        return;
    }

    size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
    if (!fileLimit.empty()) {
        traceFileLimit = static_cast<size_t>(std::stoi(fileLimit));
    }
    HILOG_INFO(LOG_CORE, "ClearOldTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);

    if (fileLists.size() > traceFileLimit && access(fileLists[0].c_str(), F_OK) == 0) {
        if (remove(fileLists[0].c_str()) == 0) {
            fileLists.erase(fileLists.begin());
            HILOG_INFO(LOG_CORE, "ClearOldTraceFile: delete first success.");
        } else {
            HILOG_ERROR(LOG_CORE, "ClearOldTraceFile: delete first failed, errno: %{public}d.", errno);
        }
    }
}

/**
 * When the raw trace is started, clear the saved_events_format files in the folder.
 */
void DelSavedEventsFormat()
{
    const std::string savedEventsFormatPath = TRACE_DEFAULT_DIR + TRACE_EVENT_FORMT;
    if (access(savedEventsFormatPath.c_str(), F_OK) != 0) {
        // saved_events_format not exit
        return;
    }
    // saved_events_format exit
    if (remove(savedEventsFormatPath.c_str()) == 0) {
        HILOG_INFO(LOG_CORE, "Delete saved_events_format success.");
    } else {
        HILOG_ERROR(LOG_CORE, "Delete saved_events_format failed.");
    }
}

std::string GetFilePath(const std::string &fileName)
{
    return g_traceRootPath + fileName;
}

std::vector<std::string> Split(const std::string &str, char delimiter)
{
    std::vector<std::string> res;
    size_t startPos = 0;
    for (size_t i = 0; i < str.size(); i++) {
        if (str[i] == delimiter) {
            res.push_back(str.substr(startPos, i - startPos));
            startPos = i + 1;
        }
    }
    if (startPos < str.size()) {
        res.push_back(str.substr(startPos));
    }
    return res;
}

bool IsTraceMounted()
{
    const std::string debugfsPath = "/sys/kernel/debug/tracing/";
    const std::string tracefsPath = "/sys/kernel/tracing/";
    if (access((debugfsPath + "trace_marker").c_str(), F_OK) != -1) {
        g_traceRootPath = debugfsPath;
        return true;
    }
    if (access((tracefsPath + "trace_marker").c_str(), F_OK) != -1) {
        g_traceRootPath = tracefsPath;
        return true;
    }
    HILOG_ERROR(LOG_CORE, "IsTraceMounted: Did not find trace folder");
    return false;
}

// Arch is 64bit when reserved = 0; Arch is 32bit when reserved = 1.
void GetArchWordSize(TraceFileHeader& header)
{
    if (sizeof(void*) == sizeof(uint64_t)) {
        header.reserved |= 0;
    } else if (sizeof(void*) == sizeof(uint32_t)) {
        header.reserved |= 1;
    }
    HILOG_INFO(LOG_CORE, "reserved with arch word info is %{public}d.", header.reserved);
}


int GetCpuProcessors()
{
    int processors = 0;
    processors = sysconf(_SC_NPROCESSORS_CONF);
    return (processors == 0) ? 1 : processors;
}

void GetCpuNums(TraceFileHeader& header)
{
    const int maxCpuNums = 24;
    int cpuNums = GetCpuProcessors();
    if (cpuNums > maxCpuNums || cpuNums <= 0) {
        HILOG_ERROR(LOG_CORE, "error: cpu_number is %{public}d.", cpuNums);
        return;
    }
    header.reserved |= (static_cast<uint64_t>(cpuNums) << 1);
    HILOG_INFO(LOG_CORE, "reserved with cpu number info is %{public}d.", header.reserved);
}

bool CheckTags(const std::vector<std::string> &tags, const std::map<std::string, TagCategory> &allTags)
{
    for (const auto &tag : tags) {
        if (allTags.find(tag) == allTags.end()) {
            HILOG_ERROR(LOG_CORE, "CheckTags: %{public}s is not provided.", tag.c_str());
            return false;
        }
    }
    return true;
}

bool CheckTagGroup(const std::vector<std::string> &tagGroups,
                   const std::map<std::string, std::vector<std::string>> &tagGroupTable)
{
    for (auto groupName : tagGroups) {
        if (tagGroupTable.find(groupName) == tagGroupTable.end()) {
            HILOG_ERROR(LOG_CORE, "CheckTagGroup: %{public}s is not provided.", groupName.c_str());
            return false;
        }
    }
    return true;
}

bool WriteStrToFileInner(const std::string& filename, const std::string& str)
{
    std::ofstream out;
    out.open(filename, std::ios::out);
    if (out.fail()) {
        HILOG_ERROR(LOG_CORE, "WriteStrToFile: %{public}s open failed.", filename.c_str());
        return false;
    }
    out << str;
    if (out.bad()) {
        HILOG_ERROR(LOG_CORE, "WriteStrToFile: %{public}s write failed.", filename.c_str());
        out.close();
        return false;
    }
    out.flush();
    out.close();
    return true;
}

bool WriteStrToFile(const std::string& filename, const std::string& str)
{
    if (access((g_traceRootPath + filename).c_str(), W_OK) < 0) {
        HILOG_ERROR(LOG_CORE, "WriteStrToFile: Failed to access %{public}s, errno(%{public}d).",
            (g_traceRootPath + filename).c_str(), errno);
        return false;
    }
    return WriteStrToFileInner(g_traceRootPath + filename, str);
}

void SetTraceNodeStatus(const std::string &path, bool enabled)
{
    WriteStrToFile(path, enabled ? "1" : "0");
}

void TruncateFile()
{
    int fd = creat((g_traceRootPath + "trace").c_str(), 0);
    if (fd == -1) {
        HILOG_ERROR(LOG_CORE, "TruncateFile: clear old trace failed.");
        return;
    }
    close(fd);
    return;
}

bool SetProperty(const std::string& property, const std::string& value)
{
    bool result = OHOS::system::SetParameter(property, value);
    if (!result) {
        HILOG_ERROR(LOG_CORE, "SetProperty: set %{public}s failed.", value.c_str());
    } else {
        HILOG_INFO(LOG_CORE, "SetProperty: set %{public}s success.", value.c_str());
    }
    return result;
}

// close all trace node
void TraceInit(const std::map<std::string, TagCategory> &allTags)
{
    // close all ftrace events
    for (auto it = allTags.begin(); it != allTags.end(); it++) {
        if (it->second.type != 1) {
            continue;
        }
        for (size_t i = 0; i < it->second.sysFiles.size(); i++) {
            SetTraceNodeStatus(it->second.sysFiles[i], false);
        }
    }
    // close all user tags
    SetProperty("debug.hitrace.tags.enableflags", std::to_string(0));

    // set buffer_size_kb 1
    WriteStrToFile("buffer_size_kb", "1");

    // close tracing_on
    SetTraceNodeStatus("tracing_on", false);
}

// Open specific trace node
void SetAllTags(const TraceParams &traceParams, const std::map<std::string, TagCategory> &allTags,
                const std::map<std::string, std::vector<std::string>> &tagGroupTable)
{
    std::set<std::string> readyEnableTagList;
    for (std::string tagName : traceParams.tags) {
        readyEnableTagList.insert(tagName);
    }

    // if set tagGroup, need to append default group
    if (traceParams.tagGroups.size() > 0) {
        auto iter = tagGroupTable.find("default");
        if (iter == tagGroupTable.end()) {
            HILOG_ERROR(LOG_CORE, "SetAllTags: default group is wrong.");
        } else {
            for (auto defaultTag : iter->second) {
                readyEnableTagList.insert(defaultTag);
            }
        }
    }

    for (std::string groupName : traceParams.tagGroups) {
        auto iter = tagGroupTable.find(groupName);
        if (iter == tagGroupTable.end()) {
            continue;
        }
        for (std::string tag : iter->second) {
            readyEnableTagList.insert(tag);
        }
    }

    uint64_t enabledUserTags = 0;
    for (std::string tagName : readyEnableTagList) {
        auto iter = allTags.find(tagName);
        if (iter == allTags.end()) {
            HILOG_ERROR(LOG_CORE, "tag<%{public}s> is invalid.", tagName.c_str());
            continue;
        }

        if (iter->second.type == 0) {
            enabledUserTags |= iter->second.tag;
        }

        if (iter->second.type == 1) {
            for (const auto& path : iter->second.sysFiles) {
                SetTraceNodeStatus(path, true);
            }
        }
    }
    SetProperty("debug.hitrace.tags.enableflags", std::to_string(enabledUserTags));
}

std::string ReadFileInner(const std::string& filename)
{
    std::string resolvedPath = CanonicalizeSpecPath(filename.c_str());
    std::ifstream fileIn(resolvedPath.c_str());
    if (!fileIn.is_open()) {
        HILOG_ERROR(LOG_CORE, "ReadFile: %{public}s open failed.", filename.c_str());
        return "";
    }

    std::string str((std::istreambuf_iterator<char>(fileIn)), std::istreambuf_iterator<char>());
    fileIn.close();
    return str;
}

std::string ReadFile(const std::string& filename)
{
    std::string filePath = GetFilePath(filename);
    return ReadFileInner(filePath);
}

void SetClock(const std::string& clockType)
{
    const std::string traceClockPath = "trace_clock";
    if (clockType.size() == 0) {
        WriteStrToFile(traceClockPath, "boot"); //set default: boot
        return;
    }
    std::string allClocks = ReadFile(traceClockPath);
    if (allClocks.find(clockType) == std::string::npos) {
        HILOG_ERROR(LOG_CORE, "SetClock: %{public}s is non-existent, set to boot", clockType.c_str());
        WriteStrToFile(traceClockPath, "boot"); // set default: boot
        return;
    }

    allClocks.erase(allClocks.find_last_not_of(" \n") + 1);
    allClocks.push_back(' ');

    std::set<std::string> allClockTypes;
    size_t curPos = 0;
    for (size_t i = 0; i < allClocks.size(); i++) {
        if (allClocks[i] == ' ') {
            allClockTypes.insert(allClocks.substr(curPos, i - curPos));
            curPos = i + 1;
        }
    }

    std::string currentClockType;
    for (auto i : allClockTypes) {
        if (clockType.compare(i) == 0) {
            HILOG_INFO(LOG_CORE, "SetClock: set clock %{public}s success.", clockType.c_str());
            WriteStrToFile(traceClockPath, clockType);
            return;
        }
        if (i[0] == '[') {
            currentClockType = i;
        }
    }

    const int marks = 2;
    if (clockType.compare(currentClockType.substr(1, currentClockType.size() - marks)) == 0) {
        HILOG_INFO(LOG_CORE, "SetClock: set clock %{public}s success.", clockType.c_str());
        return;
    }

    HILOG_INFO(LOG_CORE, "SetClock: unknown %{public}s, change to default clock_type: boot.", clockType.c_str());
    WriteStrToFile(traceClockPath, "boot"); // set default: boot
    return;
}

bool SetTraceSetting(const TraceParams &traceParams, const std::map<std::string, TagCategory> &allTags,
                     const std::map<std::string, std::vector<std::string>> &tagGroupTable)
{
    TraceInit(allTags);

    TruncateFile();

    SetAllTags(traceParams, allTags, tagGroupTable);

    WriteStrToFile("current_tracer", "nop");
    WriteStrToFile("buffer_size_kb", traceParams.bufferSize);

    SetClock(traceParams.clockType);

    if (traceParams.isOverWrite == "1") {
        WriteStrToFile("options/overwrite", "1");
    } else {
        WriteStrToFile("options/overwrite", "0");
    }

    WriteStrToFile("saved_cmdlines_size", std::to_string(SAVED_CMDLINES_SIZE));
    WriteStrToFile("options/record-tgid", "1");
    WriteStrToFile("options/record-cmd", "1");
    return true;
}

bool CheckPage(uint8_t contentType, uint8_t *page)
{
    const int pageThreshold = PAGE_SIZE / 2;

    // Check raw_trace page size.
    if (contentType >= CONTENT_TYPE_CPU_RAW && !IsHmKernel()) {
        PageHeader *pageHeader = reinterpret_cast<PageHeader*>(&page);
        if (pageHeader->size < static_cast<uint64_t>(pageThreshold)) {
            return false;
        }
    }

    return true;
}

bool CheckFileExist(const std::string &outputFile)
{
    g_writeFileLimit++;
    if (g_writeFileLimit > JUDGE_FILE_EXIST) {
        g_writeFileLimit = 0;
        if (access(outputFile.c_str(), F_OK) != 0) {
            g_needGenerateNewTraceFile = true;
            HILOG_INFO(LOG_CORE, "CheckFileExist access file:%{public}s failed, errno: %{public}d.",
                outputFile.c_str(), errno);
            return false;
        }
    }
    return true;
}

bool IsWriteFileOverflow(const int &outputFileSize, const ssize_t &writeLen, const int &fileSizeThreshold)
{
    // attention: we only check file size threshold in CMD_MODE
    if (g_traceMode != TraceMode::CMD_MODE) {
        return false;
    }
    if (outputFileSize + writeLen + static_cast<int>(sizeof(TraceFileContentHeader)) >= fileSizeThreshold) {
        HILOG_ERROR(LOG_CORE, "Failed to write, current round write file size exceeds the file size limit.");
        return true;
    }
    if (writeLen > INT_MAX - BUFFER_SIZE) {
        HILOG_ERROR(LOG_CORE, "Failed to write, write file length is nearly overflow.");
        return true;
    }
    return false;
}

bool WriteFile(uint8_t contentType, const std::string &src, int outFd, const std::string &outputFile)
{
    std::string srcPath = CanonicalizeSpecPath(src.c_str());
    int srcFd = open(srcPath.c_str(), O_RDONLY | O_NONBLOCK);
    if (srcFd < 0) {
        HILOG_ERROR(LOG_CORE, "WriteFile: open %{public}s failed.", src.c_str());
        return false;
    }
    if (!CheckFileExist(outputFile)) {
        HILOG_ERROR(LOG_CORE, "need generate new trace file, old file:%{public}s.", outputFile.c_str());
        return false;
    }
    struct TraceFileContentHeader contentHeader;
    contentHeader.type = contentType;
    write(outFd, reinterpret_cast<char *>(&contentHeader), sizeof(contentHeader));
    ssize_t writeLen = 0;
    int count = 0;
    const int maxCount = 2;
    struct timespec bts = {0, 0};
    int64_t traceStartTime = 0;

    if ((contentType == CONTENT_TYPE_CPU_RAW) && (g_timeLimit > 0)) {
        clock_gettime(CLOCK_BOOTTIME, &bts);
        traceStartTime = bts.tv_sec * S_TO_NS + bts.tv_nsec - g_timeLimit * S_TO_NS;
    }
    int fileSizeThreshold = DEFAULT_FILE_SIZE * KB_PER_MB;
    if (!g_currentTraceParams.fileSize.empty()) {
        fileSizeThreshold = std::stoi(g_currentTraceParams.fileSize) * KB_PER_MB;
    }

    while (true) {
        int bytes = 0;
        bool endFlag = false;
        /* Write 1M at a time */
        while (bytes < BUFFER_SIZE) {
            ssize_t readBytes = TEMP_FAILURE_RETRY(read(srcFd, g_buffer + bytes, PAGE_SIZE));
            if (readBytes == 0) {
                endFlag = true;
                HILOG_DEBUG(LOG_CORE, "WriteFile: read %{public}s end.", src.c_str());
                break;
            }

            if (readBytes < 0) {
                endFlag = true;
                HILOG_ERROR(LOG_CORE, "WriteFile: read %{public}s, data size: %{public}zd failed, errno: %{public}d.",
                    src.c_str(), readBytes, errno);
                break;
            }

            if (traceStartTime > 0) {
                uint64_t traceTime = *(reinterpret_cast<uint64_t *>(g_buffer));
                if (traceTime < static_cast<uint64_t>(traceStartTime)) {
                    continue;
                }
            }
            if (CheckPage(contentType, g_buffer + bytes) == false) {
                count++;
            }
            bytes += readBytes;
            if (count >= maxCount) {
                endFlag = true;
                break;
            }
        }

        ssize_t writeRet = TEMP_FAILURE_RETRY(write(outFd, g_buffer, bytes));
        if (writeRet < 0) {
            HILOG_WARN(LOG_CORE, "WriteFile Fail, errno: %{public}d.", errno);
        } else {
            if (writeRet != static_cast<ssize_t>(bytes)) {
                HILOG_WARN(LOG_CORE, "Failed to write full info, writeLen: %{public}zd, FullLen: %{public}d.",
                    writeRet, bytes);
            }
            writeLen += writeRet;
        }

        if (contentType == CONTENT_TYPE_CPU_RAW && IsWriteFileOverflow(g_outputFileSize, writeLen, fileSizeThreshold)) {
            break;
        }

        if (endFlag == true) {
            break;
        }
    }
    contentHeader.length = static_cast<uint32_t>(writeLen);
    uint32_t offset = contentHeader.length + sizeof(contentHeader);
    off_t pos = lseek(outFd, 0, SEEK_CUR);
    lseek(outFd, pos - offset, SEEK_SET);
    write(outFd, reinterpret_cast<char *>(&contentHeader), sizeof(contentHeader));
    lseek(outFd, pos, SEEK_SET);
    close(srcFd);
    g_outputFileSize += static_cast<int>(offset);
    g_needGenerateNewTraceFile = false;
    HILOG_INFO(LOG_CORE, "WriteFile end, path: %{public}s, byte: %{public}zd. g_writeFileLimit: %{public}d",
        src.c_str(), writeLen, g_writeFileLimit);
    return true;
}

void WriteEventFile(std::string &srcPath, int outFd)
{
    uint8_t buffer[PAGE_SIZE] = {0};
    std::string srcSpecPath = CanonicalizeSpecPath(srcPath.c_str());
    int srcFd = open(srcSpecPath.c_str(), O_RDONLY);
    if (srcFd < 0) {
        HILOG_ERROR(LOG_CORE, "WriteEventFile: open %{public}s failed.", srcPath.c_str());
        return;
    }
    ssize_t readLen = 0;
    do {
        ssize_t len = read(srcFd, buffer, PAGE_SIZE);
        if (len <= 0) {
            break;
        }
        write(outFd, buffer, len);
        readLen += len;
    } while (true);
    close(srcFd);
    HILOG_INFO(LOG_CORE, "WriteEventFile end, path: %{public}s, data size: %{public}zd.", srcPath.c_str(), readLen);
}

bool WriteEventsFormat(int outFd, const std::string &outputFile)
{
    const std::string savedEventsFormatPath = DEFAULT_OUTPUT_DIR + SAVED_EVENTS_FORMAT;
    if (access(savedEventsFormatPath.c_str(), F_OK) != -1) {
        return WriteFile(CONTENT_TYPE_EVENTS_FORMAT, savedEventsFormatPath, outFd, outputFile);
    }
    std::string filePath = CanonicalizeSpecPath(savedEventsFormatPath.c_str());
    int fd = open(filePath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); // 0644:-rw-r--r--
    if (fd < 0) {
        HILOG_ERROR(LOG_CORE, "WriteEventsFormat: open %{public}s failed.", savedEventsFormatPath.c_str());
        return false;
    }
    const std::vector<std::string> priorityTracingCategory = {
        "events/sched/sched_wakeup/format",
        "events/sched/sched_switch/format",
        "events/sched/sched_blocked_reason/format",
        "events/power/cpu_frequency/format",
        "events/power/clock_set_rate/format",
        "events/power/cpu_frequency_limits/format",
        "events/f2fs/f2fs_sync_file_enter/format",
        "events/f2fs/f2fs_sync_file_exit/format",
        "events/f2fs/f2fs_readpage/format",
        "events/f2fs/f2fs_readpages/format",
        "events/f2fs/f2fs_sync_fs/format",
        "events/hmdfs/hmdfs_syncfs_enter/format",
        "events/hmdfs/hmdfs_syncfs_exit/format",
        "events/erofs/erofs_readpage/format",
        "events/erofs/erofs_readpages/format",
        "events/ext4/ext4_da_write_begin/format",
        "events/ext4/ext4_da_write_end/format",
        "events/ext4/ext4_sync_file_enter/format",
        "events/ext4/ext4_sync_file_exit/format",
        "events/block/block_bio_remap/format",
        "events/block/block_rq_issue/format",
        "events/block/block_rq_complete/format",
        "events/block/block_rq_insert/format",
        "events/dma_fence/dma_fence_emit/format",
        "events/dma_fence/dma_fence_destroy/format",
        "events/dma_fence/dma_fence_enable_signal/format",
        "events/dma_fence/dma_fence_signaled/format",
        "events/dma_fence/dma_fence_wait_end/format",
        "events/dma_fence/dma_fence_wait_start/format",
        "events/dma_fence/dma_fence_init/format",
        "events/binder/binder_transaction/format",
        "events/binder/binder_transaction_received/format",
        "events/mmc/mmc_request_start/format",
        "events/mmc/mmc_request_done/format",
        "events/memory_bus/format",
        "events/cpufreq_interactive/format",
        "events/filemap/file_check_and_advance_wb_err/format",
        "events/filemap/filemap_set_wb_err/format",
        "events/filemap/mm_filemap_add_to_page_cache/format",
        "events/filemap/mm_filemap_delete_from_page_cache/format",
        "events/workqueue/workqueue_execute_end/format",
        "events/workqueue/workqueue_execute_start/format",
        "events/thermal_power_allocator/thermal_power_allocator/format",
        "events/thermal_power_allocator/thermal_power_allocator_pid/format",
        "events/ftrace/print/format",
        "events/tracing_mark_write/tracing_mark_write/format",
        "events/power/cpu_idle/format",
        "events/power_kernel/cpu_idle/format",
        "events/xacct/tracing_mark_write/format",
        "events/ufs/ufshcd_command/format",
        "events/irq/irq_handler_entry/format"
    };
    for (size_t i = 0; i < priorityTracingCategory.size(); i++) {
        std::string srcPath = g_traceRootPath + priorityTracingCategory[i];
        if (access(srcPath.c_str(), R_OK) != -1) {
            WriteEventFile(srcPath, fd);
        }
    }
    close(fd);
    HILOG_INFO(LOG_CORE, "WriteEventsFormat end. path: %{public}s.", filePath.c_str());
    return WriteFile(CONTENT_TYPE_EVENTS_FORMAT, filePath, outFd, outputFile);
}

bool WriteHeaderPage(int outFd, const std::string &outputFile)
{
    if (IsHmKernel()) {
        return true;
    }
    std::string headerPagePath = GetFilePath("events/header_page");
    return WriteFile(CONTENT_TYPE_HEADER_PAGE, headerPagePath, outFd, outputFile);
}

bool WritePrintkFormats(int outFd, const std::string &outputFile)
{
    if (IsHmKernel()) {
        return true;
    }
    std::string printkFormatPath = GetFilePath("printk_formats");
    return WriteFile(CONTENT_TYPE_PRINTK_FORMATS, printkFormatPath, outFd, outputFile);
}

bool WriteKallsyms(int outFd)
{
    /* not implement in hmkernel */
    if (IsHmKernel()) {
        return true;
    }
    /* not implement in linux */
    return true;
}

bool HmWriteCpuRawInner(int outFd, const std::string &outputFile)
{
    uint8_t type = CONTENT_TYPE_CPU_RAW;
    std::string src = g_traceRootPath + "/trace_pipe_raw";

    return WriteFile(type, src, outFd, outputFile);
}

bool WriteCpuRawInner(int outFd, const std::string &outputFile)
{
    int cpuNums = GetCpuProcessors();
    int ret = true;
    uint8_t type = CONTENT_TYPE_CPU_RAW;
    for (int i = 0; i < cpuNums; i++) {
        std::string src = g_traceRootPath + "per_cpu/cpu" + std::to_string(i) + "/trace_pipe_raw";
        if (!WriteFile(static_cast<uint8_t>(type + i), src, outFd, outputFile)) {
            ret = false;
            break;
        }
    }
    return ret;
}

bool WriteCpuRaw(int outFd, const std::string &outputFile)
{
    if (!IsHmKernel()) {
        return WriteCpuRawInner(outFd, outputFile);
    } else {
        return HmWriteCpuRawInner(outFd, outputFile);
    }
}

bool WriteCmdlines(int outFd, const std::string &outputFile)
{
    std::string cmdlinesPath = GetFilePath("saved_cmdlines");
    return WriteFile(CONTENT_TYPE_CMDLINES, cmdlinesPath, outFd, outputFile);
}

bool WriteTgids(int outFd, const std::string &outputFile)
{
    std::string tgidsPath = GetFilePath("saved_tgids");
    return WriteFile(CONTENT_TYPE_TGIDS, tgidsPath, outFd, outputFile);
}

bool GenerateNewFile(int &outFd, std::string &outPath)
{
    if (access(outPath.c_str(), F_OK) == 0) {
        return true;
    }
    std::string outputFileName = GenerateTraceFileName(false);
    outPath = CanonicalizeSpecPath(outputFileName.c_str());
    outFd = open(outPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); // 0644:-rw-r--r--
    if (outFd < 0) {
        g_newTraceFileLimit++;
        HILOG_ERROR(LOG_CORE, "open %{public}s failed, errno: %{public}d.", outPath.c_str(), errno);
    }
    if (g_newTraceFileLimit > MAX_NEW_TRACE_FILE_LIMIT) {
        HILOG_ERROR(LOG_CORE, "create new trace file %{public}s limited.", outPath.c_str());
        return false;
    }
    g_needGenerateNewTraceFile = true;
    return true;
}

bool DumpTraceLoop(const std::string &outputFileName, bool isLimited)
{
    const int sleepTime = 1;
    int fileSizeThreshold = DEFAULT_FILE_SIZE * KB_PER_MB;
    if (!g_currentTraceParams.fileSize.empty()) {
        fileSizeThreshold = std::stoi(g_currentTraceParams.fileSize) * KB_PER_MB;
    }
    g_outputFileSize = 0;
    std::string outPath = CanonicalizeSpecPath(outputFileName.c_str());
    int outFd = open(outPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); // 0644:-rw-r--r--
    if (outFd < 0) {
        HILOG_ERROR(LOG_CORE, "open %{public}s failed, errno: %{public}d.", outPath.c_str(), errno);
        return false;
    }
    MarkClockSync(g_traceRootPath);
    struct TraceFileHeader header;
    GetArchWordSize(header);
    GetCpuNums(header);
    if (IsHmKernel()) {
        header.fileType = HM_FILE_RAW_TRACE;
    }
    do {
        g_needGenerateNewTraceFile = false;
        write(outFd, reinterpret_cast<char *>(&header), sizeof(header));
        WriteEventsFormat(outFd, outPath);
        while (g_dumpFlag) {
            if (isLimited && g_outputFileSize > fileSizeThreshold) {
                break;
            }
            sleep(sleepTime);
            if (!WriteCpuRaw(outFd, outPath)) {
                break;
            }
        }
        WriteCmdlines(outFd, outPath);
        WriteTgids(outFd, outPath);
        WriteHeaderPage(outFd, outPath);
        WritePrintkFormats(outFd, outPath);
        WriteKallsyms(outFd);
        if (!GenerateNewFile(outFd, outPath)) {
            HILOG_INFO(LOG_CORE, "DumpTraceLoop access file:%{public}s failed, errno: %{public}d.",
                outPath.c_str(), errno);
            return false;
        }
    } while (g_needGenerateNewTraceFile);
    close(outFd);
    return true;
}

/**
 * read trace data loop
 * g_dumpFlag: true = open,false = close
 * g_dumpEnd: true = end,false = not end
 * if user has own output file, Output all data to the file specified by the user;
 * if not, Then place all the result files in /data/log/hitrace/ and package them once in 96M.
*/
void ProcessDumpTask()
{
    g_dumpFlag = true;
    g_dumpEnd = false;
    g_outputFilesForCmd = {};
    const std::string threadName = "TraceDumpTask";
    prctl(PR_SET_NAME, threadName.c_str());
    HILOG_INFO(LOG_CORE, "ProcessDumpTask: trace dump thread start.");

    // clear old record file before record tracing start.
    DelSavedEventsFormat();
    if (!IsRootVersion()) {
        DelOldRecordTraceFile(g_currentTraceParams.fileLimit);
    }

    if (g_currentTraceParams.fileSize.empty()) {
        std::string outputFileName = g_currentTraceParams.outputFile.empty() ?
                                     GenerateTraceFileName(false) : g_currentTraceParams.outputFile;
        if (DumpTraceLoop(outputFileName, false)) {
            g_outputFilesForCmd.push_back(outputFileName);
        }
        g_dumpEnd = true;
        return;
    }

    while (g_dumpFlag) {
        if (!IsRootVersion()) {
            ClearOldTraceFile(g_outputFilesForCmd, g_currentTraceParams.fileLimit);
        }
        // Generate file name
        std::string outputFileName = GenerateTraceFileName(false);
        if (DumpTraceLoop(outputFileName, true)) {
            g_outputFilesForCmd.push_back(outputFileName);
        } else {
            break;
        }
    }
    HILOG_INFO(LOG_CORE, "ProcessDumpTask: trace dump thread exit.");
    g_dumpEnd = true;
}

void SearchFromTable(std::vector<std::string> &outputFiles, int nowSec)
{
    const int maxInterval = 30;
    const int agingTime = 30 * 60;

    for (auto iter = g_traceFilesTable.begin(); iter != g_traceFilesTable.end();) {
        if (nowSec - iter->second >= agingTime) {
            // delete outdated trace file
            if (access(iter->first.c_str(), F_OK) == 0) {
                remove(iter->first.c_str());
                HILOG_INFO(LOG_CORE, "delete old %{public}s file success.", iter->first.c_str());
            }
            iter = g_traceFilesTable.erase(iter);
            continue;
        }

        if (nowSec - iter->second <= maxInterval) {
            outputFiles.push_back(iter->first);
        }
        iter++;
    }
}

bool ReadRawTrace(std::string &outputFileName)
{
    // read trace data from /per_cpu/cpux/trace_pipe_raw
    std::string outPath = CanonicalizeSpecPath(outputFileName.c_str());
    int outFd = open(outPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); // 0644:-rw-r--r--
    if (outFd < 0) {
        return false;
    }
    struct TraceFileHeader header;
    GetArchWordSize(header);
    GetCpuNums(header);
    if (IsHmKernel()) {
        header.fileType = HM_FILE_RAW_TRACE;
    }
    write(outFd, reinterpret_cast<char*>(&header), sizeof(header));

    if (WriteEventsFormat(outFd, outPath) && WriteCpuRaw(outFd, outPath) &&
        WriteCmdlines(outFd, outPath) && WriteTgids(outFd, outPath) &&
        WriteHeaderPage(outFd, outPath) && WritePrintkFormats(outFd, outPath) &&
        WriteKallsyms(outFd)) {
        fsync(outFd);
        close(outFd);
        return true;
    }
    HILOG_ERROR(LOG_CORE, "ReadRawTrace failed.");
    fsync(outFd);
    close(outFd);
    return false;
}

bool WaitPidTimeout(pid_t pid, const int timeoutUsec)
{
    int delayTime = timeoutUsec;
    while (delayTime > 0) {
        usleep(UNIT_TIME);
        delayTime -= UNIT_TIME;
        int status = 0;
        int ret = waitpid(pid, &status, WNOHANG);
        if (ret == pid) {
            HILOG_INFO(LOG_CORE, "wait pid(%{public}d) exit success.", pid);
            return true;
        } else if (ret < 0) {
            HILOG_ERROR(LOG_CORE, "wait pid(%{public}d) exit failed, status: %{public}d.", pid, status);
            return false;
        }
        HILOG_DEBUG(LOG_CORE, "grasping trace, pid(%{public}d), ret(%{public}d).", pid, ret);
    }
    HILOG_ERROR(LOG_CORE, "wait pid(%{public}d) %{public}d us timeout.", pid, timeoutUsec);
    return false;
}

void SetProcessName(std::string& processName)
{
    if (processName.size() <= 0) {
        return;
    }

    const int maxNameLen = 16;
    std::string setName;
    if (processName.size() > maxNameLen) {
        setName = processName.substr(0, maxNameLen);
    } else {
        setName = processName;
    }

    prctl(PR_SET_NAME, setName.c_str(), nullptr, nullptr, nullptr);
    HILOG_INFO(LOG_CORE, "New process: %{public}s.", setName.c_str());
}

TraceErrorCode DumpTraceInner(std::vector<std::string> &outputFiles)
{
    g_dumpEnd = false;
    std::string outputFileName = GenerateTraceFileName();
    std::string reOutPath = CanonicalizeSpecPath(outputFileName.c_str());

    /*Child process handles task, Father process wait.*/
    pid_t pid = fork();
    if (pid < 0) {
        HILOG_ERROR(LOG_CORE, "fork error.");
        g_dumpEnd = true;
        return TraceErrorCode::WRITE_TRACE_INFO_ERROR;
    }
    bool ret = false;
    if (pid == 0) {
        std::string processName = "HitraceDump";
        SetProcessName(processName);
        MarkClockSync(g_traceRootPath);
        const int waitTime = 10000; // 10ms
        usleep(waitTime);
        ReadRawTrace(reOutPath);
        if (!IsRootVersion()) {
            DelSnapshotTraceFile(false, SNAPSHOT_FILE_MAX_COUNT);
        }
        HILOG_DEBUG(LOG_CORE, "%{public}s exit.", processName.c_str());
        _exit(EXIT_SUCCESS);
    } else {
        const int timeoutUsec = 10000000; // 10s
        bool isTrue = WaitPidTimeout(pid, timeoutUsec);
        if (isTrue && access(reOutPath.c_str(), F_OK) == 0) {
            HILOG_INFO(LOG_CORE, "Output: %{public}s.", reOutPath.c_str());
            ret = true;
        } else {
            HILOG_ERROR(LOG_CORE, "Output error: %{public}s.", reOutPath.c_str());
        }
    }

    struct timeval now = {0, 0};
    gettimeofday(&now, nullptr);
    int nowSec = now.tv_sec;
    SearchFromTable(outputFiles, nowSec);
    if (ret) {
        outputFiles.push_back(outputFileName);
        g_traceFilesTable.push_back({outputFileName, nowSec});
    } else {
        HILOG_ERROR(LOG_CORE, "DumpTraceInner: write %{public}s failed.", outputFileName.c_str());
        g_dumpEnd = true;
        return TraceErrorCode::WRITE_TRACE_INFO_ERROR;
    }
    g_dumpEnd = true;
    return TraceErrorCode::SUCCESS;
}

uint64_t GetSysParamTags()
{
    return OHOS::system::GetUintParameter<uint64_t>("debug.hitrace.tags.enableflags", 0);
}

void RestartService()
{
    CloseTrace();
    const std::vector<std::string> tagGroups = {"scene_performance"};
    OpenTrace(tagGroups);
}

bool CheckParam()
{
    uint64_t currentTags = GetSysParamTags();
    if (currentTags == g_sysInitParamTags) {
        return true;
    }

    if (currentTags == 0) {
        HILOG_ERROR(LOG_CORE, "tag is 0, restart it.");
        RestartService();
        return false;
    }
    HILOG_ERROR(LOG_CORE, "trace is being used, restart later.");
    return false;
}

bool CheckTraceFile()
{
    const std::string enable = "1";
    if (ReadFile("tracing_on").substr(0, enable.size()) == enable) {
        return true;
    }
    HILOG_ERROR(LOG_CORE, "tracing_on is 0, restart it.");
    RestartService();
    return false;
}

/**
 * SERVICE_MODE is running, check param and tracing_on.
*/
bool CheckServiceRunning()
{
    if (CheckParam() && CheckTraceFile()) {
        return true;
    }
    return false;
}

void MonitorServiceTask()
{
    g_serviceThreadIsStart = true;
    const std::string threadName = "TraceMonitor";
    prctl(PR_SET_NAME, threadName.c_str());
    HILOG_INFO(LOG_CORE, "MonitorServiceTask: monitor thread start.");
    const int intervalTime = 15;
    while (true) {
        sleep(intervalTime);
        if (g_traceMode != TraceMode::SERVICE_MODE) {
            break;
        }

        if (!CheckServiceRunning()) {
            continue;
        }

        const int cpuNums = GetCpuProcessors();
        std::vector<int> result;
        std::unique_ptr<DynamicBuffer> dynamicBuffer = std::make_unique<DynamicBuffer>(g_traceRootPath, cpuNums);
        dynamicBuffer->CalculateBufferSize(result);

        if (static_cast<int>(result.size()) != cpuNums) {
            HILOG_ERROR(LOG_CORE, "CalculateAllNewBufferSize failed.");
            break;
        }

        for (size_t i = 0; i < result.size(); i++) {
            HILOG_DEBUG(LOG_CORE, "cpu%{public}zu set size %{public}d.", i, result[i]);
            std::string path = "per_cpu/cpu" + std::to_string(i) + "/buffer_size_kb";
            WriteStrToFile(path, std::to_string(result[i]));
        }
    }
    HILOG_INFO(LOG_CORE, "MonitorServiceTask: monitor thread exit.");
    g_serviceThreadIsStart = false;
}

TraceErrorCode HandleTraceOpen(const TraceParams &traceParams,
                               const std::map<std::string, TagCategory> &allTags,
                               const std::map<std::string, std::vector<std::string>> &tagGroupTable)
{
    if (!SetTraceSetting(traceParams, allTags, tagGroupTable)) {
        return TraceErrorCode::FILE_ERROR;
    }
    SetTraceNodeStatus("tracing_on", true);
    g_currentTraceParams = traceParams;
    return TraceErrorCode::SUCCESS;
}

TraceErrorCode HandleServiceTraceOpen(const std::vector<std::string> &tagGroups,
                                      const std::map<std::string, TagCategory> &allTags,
                                      const std::map<std::string, std::vector<std::string>> &tagGroupTable)
{
    TraceParams serviceTraceParams;
    serviceTraceParams.tagGroups = tagGroups;
    serviceTraceParams.bufferSize = std::to_string(DEFAULT_BUFFER_SIZE);
    if (IsHmKernel()) {
        serviceTraceParams.bufferSize = std::to_string(HM_DEFAULT_BUFFER_SIZE);
    }
    serviceTraceParams.clockType = "boot";
    serviceTraceParams.isOverWrite = "1";
    serviceTraceParams.fileSize = std::to_string(DEFAULT_FILE_SIZE);
    return HandleTraceOpen(serviceTraceParams, allTags, tagGroupTable);
}

void RemoveUnSpace(std::string str, std::string& args)
{
    int maxCircleTimes = 30;
    int curTimes = 0;
    const size_t symbolAndSpaceLen = 2;
    std::string strSpace = str + " ";
    while (curTimes < maxCircleTimes) {
        curTimes++;
        std::string::size_type index = args.find(strSpace);
        if (index != std::string::npos) {
            args.replace(index, symbolAndSpaceLen, str);
        } else {
            break;
        }
    }
}

/**
 * args: tags:tag1,tags2... tagGroups:group1,group2... clockType:boot bufferSize:1024 overwrite:1 output:filename
 * cmdTraceParams:  Save the above parameters
*/
bool ParseArgs(const std::string &args, TraceParams &cmdTraceParams, const std::map<std::string, TagCategory> &allTags,
               const std::map<std::string, std::vector<std::string>> &tagGroupTable)
{
    std::string userArgs = args;
    std::string str = ":";
    RemoveUnSpace(str, userArgs);
    str = ",";
    RemoveUnSpace(str, userArgs);
    std::vector<std::string> argList = Split(userArgs, ' ');
    for (std::string item : argList) {
        size_t pos = item.find(":");
        if (pos == std::string::npos) {
            HILOG_ERROR(LOG_CORE, "trace command line without colon appears: %{public}s, continue.", item.c_str());
            continue;
        }
        std::string itemName = item.substr(0, pos);
        if (itemName == "tags") {
            cmdTraceParams.tags = Split(item.substr(pos + 1), ',');
        } else if (itemName == "tagGroups") {
            cmdTraceParams.tagGroups = Split(item.substr(pos + 1), ',');
        } else if (itemName == "clockType") {
            cmdTraceParams.clockType = item.substr(pos + 1);
        } else if (itemName == "bufferSize") {
            cmdTraceParams.bufferSize = item.substr(pos + 1);
        } else if (itemName == "overwrite") {
            cmdTraceParams.isOverWrite = item.substr(pos + 1);
        } else if (itemName == "output") {
            cmdTraceParams.outputFile = item.substr(pos + 1);
        } else if (itemName == "fileSize") {
            cmdTraceParams.fileSize = item.substr(pos + 1);
        } else if (itemName == "fileLimit") {
            cmdTraceParams.fileLimit = item.substr(pos + 1);
        } else if (itemName == "appPid") {
            std::string pidStr = item.substr(pos + 1);
            if (!IsNumber(pidStr)) {
                HILOG_ERROR(LOG_CORE, "Illegal input, appPid(%{public}s) must be number.", pidStr.c_str());
                return false;
            }
            int appPid = std::stoi(pidStr);
            if (appPid <= 0) {
                HILOG_ERROR(LOG_CORE, "Illegal input, appPid(%{public}d) must be greater than 0.", appPid);
                return false;
            }
            OHOS::system::SetParameter("debug.hitrace.app_pid", pidStr);
        } else {
            HILOG_ERROR(LOG_CORE, "Extra trace command line options appear when ParseArgs: %{public}s, return false.",
                itemName.c_str());
            return false;
        }
    }
    if (CheckTags(cmdTraceParams.tags, allTags) && CheckTagGroup(cmdTraceParams.tagGroups, tagGroupTable)) {
        return true;
    }
    return false;
}
} // namespace

#ifdef HITRACE_UNITTEST
void SetSysInitParamTags(uint64_t sysInitParamTags)
{
    g_sysInitParamTags = sysInitParamTags;
}

bool SetCheckParam()
{
    int ret = CheckParam();
    return ret;
}
#endif

TraceMode GetTraceMode()
{
    return g_traceMode;
}

TraceErrorCode OpenTrace(const std::vector<std::string> &tagGroups)
{
    if (g_traceMode != CLOSE) {
        HILOG_ERROR(LOG_CORE, "OpenTrace: CALL_ERROR, g_traceMode:%{public}d.", static_cast<int>(g_traceMode));
        return CALL_ERROR;
    }
    std::lock_guard<std::mutex> lock(g_traceMutex);
    if (!IsTraceMounted()) {
        HILOG_ERROR(LOG_CORE, "OpenTrace: TRACE_NOT_SUPPORTED.");
        return TRACE_NOT_SUPPORTED;
    }

    std::map<std::string, TagCategory> allTags;
    std::map<std::string, std::vector<std::string>> tagGroupTable;
    if (!ParseTagInfo(allTags, tagGroupTable)) {
        HILOG_ERROR(LOG_CORE, "OpenTrace: ParseTagInfo TAG_ERROR.");
        return TAG_ERROR;
    }

    if (tagGroups.size() == 0 || !CheckTagGroup(tagGroups, tagGroupTable)) {
        HILOG_ERROR(LOG_CORE, "OpenTrace: TAG_ERROR.");
        return TAG_ERROR;
    }

    TraceErrorCode ret = HandleServiceTraceOpen(tagGroups, allTags, tagGroupTable);
    if (ret != SUCCESS) {
        HILOG_ERROR(LOG_CORE, "OpenTrace: open fail.");
        return ret;
    }
    g_traceMode = SERVICE_MODE;

    DelSnapshotTraceFile();
    if (!IsHmKernel() && !g_serviceThreadIsStart) {
        // open SERVICE_MODE monitor thread
        auto it = []() {
            MonitorServiceTask();
        };
        std::thread auxiliaryTask(it);
        auxiliaryTask.detach();
    }
    g_sysInitParamTags = GetSysParamTags();
    HILOG_INFO(LOG_CORE, "OpenTrace: SERVICE_MODE open success.");
    return ret;
}

TraceErrorCode OpenTrace(const std::string &args)
{
    std::lock_guard<std::mutex> lock(g_traceMutex);
    if (g_traceMode != CLOSE) {
        HILOG_ERROR(LOG_CORE, "OpenTrace: CALL_ERROR, g_traceMode:%{public}d.", static_cast<int>(g_traceMode));
        return CALL_ERROR;
    }

    if (!IsTraceMounted()) {
        HILOG_ERROR(LOG_CORE, "Hitrace OpenTrace: TRACE_NOT_SUPPORTED.");
        return TRACE_NOT_SUPPORTED;
    }

    std::map<std::string, TagCategory> allTags;
    std::map<std::string, std::vector<std::string>> tagGroupTable;
    if (!ParseTagInfo(allTags, tagGroupTable) || allTags.size() == 0 || tagGroupTable.size() == 0) {
        HILOG_ERROR(LOG_CORE, "Hitrace OpenTrace: ParseTagInfo TAG_ERROR.");
        return TAG_ERROR;
    }
    // parse args
    TraceParams cmdTraceParams;
    if (!ParseArgs(args, cmdTraceParams, allTags, tagGroupTable)) {
        HILOG_ERROR(LOG_CORE, "Hitrace OpenTrace: TAG_ERROR.");
        return TAG_ERROR;
    }

    TraceErrorCode ret = HandleTraceOpen(cmdTraceParams, allTags, tagGroupTable);
    if (ret != SUCCESS) {
        HILOG_ERROR(LOG_CORE, "Hitrace OpenTrace: CMD_MODE open failed.");
        return FILE_ERROR;
    }
    g_traceMode = CMD_MODE;
    HILOG_INFO(LOG_CORE, "Hitrace OpenTrace: CMD_MODE open success, args:%{public}s.", args.c_str());
    return ret;
}

TraceRetInfo DumpTrace()
{
    TraceRetInfo ret;
    HILOG_INFO(LOG_CORE, "DumpTrace start.");
    if (g_traceMode != SERVICE_MODE) {
        HILOG_ERROR(LOG_CORE, "DumpTrace: CALL_ERROR, g_traceMode:%{public}d.", static_cast<int>(g_traceMode));
        ret.errorCode = CALL_ERROR;
        return ret;
    }

    if (!CheckServiceRunning()) {
        HILOG_ERROR(LOG_CORE, "DumpTrace: TRACE_IS_OCCUPIED.");
        ret.errorCode = TRACE_IS_OCCUPIED;
        return ret;
    }
    std::lock_guard<std::mutex> lock(g_traceMutex);
    ret.errorCode = DumpTraceInner(ret.outputFiles);
    HILOG_INFO(LOG_CORE, "DumpTrace done.");
    return ret;
}

TraceRetInfo DumpTrace(int timeLimit)
{
    HILOG_INFO(LOG_CORE, "DumpTrace with time limit start, time limit is %{public}d.", timeLimit);
    TraceRetInfo ret;
    if (timeLimit <= 0) {
        HILOG_ERROR(LOG_CORE, "DumpTrace: Illegal input.");
        ret.errorCode = CALL_ERROR;
        return ret;
    }
    {
        std::lock_guard<std::mutex> lock(g_traceMutex);
        g_timeLimit = timeLimit;
    }
    ret = DumpTrace();
    {
        std::lock_guard<std::mutex> lock(g_traceMutex);
        g_timeLimit = 0;
    }
    HILOG_INFO(LOG_CORE, "DumpTrace with time limit done.");
    return ret;
}

TraceErrorCode DumpTraceOn()
{
    std::lock_guard<std::mutex> lock(g_traceMutex);
    // check current trace status
    if (g_traceMode != CMD_MODE) {
        HILOG_ERROR(LOG_CORE, "DumpTraceOn: CALL_ERROR, g_traceMode:%{public}d.", static_cast<int>(g_traceMode));
        return CALL_ERROR;
    }

    if (!g_dumpEnd) {
        HILOG_ERROR(LOG_CORE, "DumpTraceOn: CALL_ERROR, record trace is dumping now.");
        return CALL_ERROR;
    }

    // start task thread
    auto it = []() {
        ProcessDumpTask();
    };
    std::thread task(it);
    task.detach();
    HILOG_INFO(LOG_CORE, "Recording trace on.");
    return SUCCESS;
}

TraceRetInfo DumpTraceOff()
{
    std::lock_guard<std::mutex> lock(g_traceMutex);
    TraceRetInfo ret;
    // check current trace status
    if (g_traceMode != CMD_MODE) {
        HILOG_ERROR(LOG_CORE, "DumpTraceOff: The current state is %{public}d, data exception.",
            static_cast<int>(g_traceMode));
        ret.errorCode = CALL_ERROR;
        ret.outputFiles = g_outputFilesForCmd;
        return ret;
    }

    g_dumpFlag = false;
    while (!g_dumpEnd) {
        usleep(UNIT_TIME);
        g_dumpFlag = false;
    }
    ret.errorCode = SUCCESS;
    ret.outputFiles = g_outputFilesForCmd;
    HILOG_INFO(LOG_CORE, "Recording trace off.");
    return ret;
}

TraceErrorCode CloseTrace()
{
    std::lock_guard<std::mutex> lock(g_traceMutex);
    HILOG_INFO(LOG_CORE, "CloseTrace start.");
    if (g_traceMode == CLOSE) {
        HILOG_INFO(LOG_CORE, "Trace already close.");
        return SUCCESS;
    }

    g_traceMode = CLOSE;
    // Waiting for the data drop task to end
    g_dumpFlag = false;
    while (!g_dumpEnd) {
        usleep(UNIT_TIME);
        g_dumpFlag = false;
    }
    OHOS::system::SetParameter("debug.hitrace.app_pid", "-1");
    std::map<std::string, TagCategory> allTags;
    std::map<std::string, std::vector<std::string>> tagGroupTable;
    if (!ParseTagInfo(allTags, tagGroupTable) || allTags.size() == 0 || tagGroupTable.size() == 0) {
        HILOG_ERROR(LOG_CORE, "CloseTrace: ParseTagInfo TAG_ERROR.");
        return TAG_ERROR;
    }
    TraceInit(allTags);
    TruncateFile();
    HILOG_INFO(LOG_CORE, "CloseTrace done.");
    return SUCCESS;
}

std::vector<std::pair<std::string, int>> GetTraceFilesTable()
{
    return g_traceFilesTable;
}

void SetTraceFilesTable(const std::vector<std::pair<std::string, int>>& traceFilesTable)
{
    g_traceFilesTable = traceFilesTable;
}

} // Hitrace
} // HiviewDFX
} // OHOS