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 
31 namespace OHOS {
32 namespace MMI {
33 namespace {
34 const std::string THREAD_NAME { "mmi_service" };
35 } // namespace
36 
WatchdogTask()37 WatchdogTask::WatchdogTask() {}
38 
~WatchdogTask()39 WatchdogTask::~WatchdogTask() {}
40 
GetFirstLine(const std::string & path)41 std::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 
GetProcessNameFromProcCmdline(int32_t pid)59 std::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 
IsNumberic(const std::string & str)77 bool WatchdogTask::IsNumberic(const std::string &str)
78 {
79     return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
80 }
81 
IsProcessDebug(int32_t pid)82 bool 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 
GetBlockDescription(uint64_t interval)99 std::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 
GetSelfProcName()107 std::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 
SendEvent(const std::string & msg,const std::string & eventName)140 void 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