1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "watchdog_task.h"
17
18#include <fstream>
19#include <unistd.h>
20
21#include "backtrace_local.h"
22#include "hisysevent.h"
23#include "mmi_log.h"
24#include "parameter.h"
25
26#undef MMI_LOG_DOMAIN
27#define MMI_LOG_DOMAIN MMI_LOG_SERVER
28#undef MMI_LOG_TAG
29#define MMI_LOG_TAG "WatchdogTask"
30
31namespace OHOS {
32namespace MMI {
33namespace {
34const std::string THREAD_NAME { "mmi_service" };
35} // namespace
36
37WatchdogTask::WatchdogTask() {}
38
39WatchdogTask::~WatchdogTask() {}
40
41std::string WatchdogTask::GetFirstLine(const std::string& path)
42{
43    char checkPath[PATH_MAX] = { 0 };
44    if (realpath(path.c_str(), checkPath) == nullptr) {
45        MMI_HILOGE("Canonicalize failed. path:%{private}s", path.c_str());
46        return "";
47    }
48    std::ifstream inFile(checkPath);
49    if (!inFile.is_open()) {
50        MMI_HILOGE("inFile.is_open() false");
51        return "";
52    }
53    std::string firstLine;
54    getline(inFile, firstLine);
55    inFile.close();
56    return firstLine;
57}
58
59std::string WatchdogTask::GetProcessNameFromProcCmdline(int32_t pid)
60{
61    std::string procCmdlinePath = "/proc/" + std::to_string(pid) + "/cmdline";
62    std::string procCmdlineContent = GetFirstLine(procCmdlinePath);
63    if (procCmdlineContent.empty()) {
64        return "";
65    }
66    auto pos = procCmdlineContent.find('\0');
67    if (pos != std::string::npos) {
68        procCmdlineContent = procCmdlineContent.substr(0, pos);
69    }
70    pos = procCmdlineContent.rfind('/');
71    if (pos != std::string::npos) {
72        return procCmdlineContent.substr(pos + 1);
73    }
74    return procCmdlineContent;
75}
76
77bool WatchdogTask::IsNumberic(const std::string &str)
78{
79    return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
80}
81
82bool WatchdogTask::IsProcessDebug(int32_t pid)
83{
84    const int32_t buffSize = 128;
85    char param[buffSize] = { 0 };
86    std::string filter = "hiviewdfx.freeze.filter." + GetProcessNameFromProcCmdline(pid);
87    GetParameter(filter.c_str(), "", param, buffSize - 1);
88    if (!IsNumberic(param)) {
89        MMI_HILOGE("Parameter:%{public}s is error", param);
90        return false;
91    }
92    int32_t debugPid = atoi(param);
93    if (debugPid == pid) {
94        return true;
95    }
96    return false;
97}
98
99std::string WatchdogTask::GetBlockDescription(uint64_t interval)
100{
101    std::string desc = "Watchdog: thread(";
102    desc += THREAD_NAME;
103    desc += ") blocked " + std::to_string(interval) + "s";
104    return desc;
105}
106
107std::string WatchdogTask::GetSelfProcName()
108{
109    constexpr uint16_t READ_SIZE = 128;
110    std::ifstream fin;
111    fin.open("/proc/self/comm", std::ifstream::in);
112    if (!fin.is_open()) {
113        MMI_HILOGE("fin.is_open() false");
114        return "";
115    }
116    char readStr[READ_SIZE] = {'\0'};
117    fin.getline(readStr, READ_SIZE - 1);
118    fin.close();
119
120    std::string ret = std::string(readStr);
121    auto comparisonFun = [](unsigned char c) {
122        if (c >= '0' && c <= '9') {
123            return false;
124        }
125        if (c >= 'a' && c <= 'z') {
126            return false;
127        }
128        if (c >= 'A' && c <= 'Z') {
129            return false;
130        }
131        if (c == '.' || c == '-' || c == '_') {
132            return false;
133        }
134        return true;
135    };
136    ret.erase(std::remove_if(ret.begin(), ret.end(), comparisonFun), ret.end());
137    return ret;
138}
139
140void WatchdogTask::SendEvent(const std::string &msg, const std::string &eventName)
141{
142    int32_t pid = getpid();
143    if (IsProcessDebug(pid)) {
144        MMI_HILOGI("Heap dump for %{public}d, don't report", pid);
145        return;
146    }
147    uint32_t gid = getgid();
148    uint32_t uid = getuid();
149    time_t curTime = time(nullptr);
150    std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) +
151        "\n" + msg + "\n";
152    HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::FRAMEWORK, eventName,
153        OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
154        "PID", pid,
155        "TGID", gid,
156        "UID", uid,
157        "MODULE_NAME", THREAD_NAME,
158        "PROCESS_NAME", GetSelfProcName(),
159        "MSG", sendMsg,
160        "STACK", OHOS::HiviewDFX::GetProcessStacktrace());
161    MMI_HILOGI("Send event, eventName:%{public}s, msg:%{public}s", eventName.c_str(), msg.c_str());
162}
163} // namespace MMI
164} // namespace OHOS
165