1 /*
2  * Copyright (c) 2022 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 <cinttypes>
19 #include <ctime>
20 #include <cstdio>
21 #include <securec.h>
22 #include <thread>
23 
24 #include <fcntl.h>
25 #include <dlfcn.h>
26 #include <unistd.h>
27 
28 #include "backtrace_local.h"
29 #include "musl_preinit_common.h"
30 #include "watchdog_inner.h"
31 #include "xcollie_define.h"
32 #include "xcollie_utils.h"
33 
34 #ifdef HISYSEVENT_ENABLE
35 #include "hisysevent.h"
36 #endif
37 
38 namespace OHOS {
39 namespace HiviewDFX {
40 namespace {
41 static const int COUNT_LIMIT_NUM_MAX_RATIO = 2;
42 static const int TIME_LIMIT_NUM_MAX_RATIO = 2;
43 static const int UID_TYPE_THRESHOLD = 20000;
44 constexpr int32_t SAMGR_INIT_UID = 5555;
45 constexpr const char* CORE_PROCS[] = {
46     "anco_service_br", "aptouch_daemon", "foundation", "init", "multimodalinput", "ohos.sceneboard", "render_service"
47 };
48 }
49 int64_t WatchdogTask::curId = 0;
WatchdogTask(std::string name, std::shared_ptr<AppExecFwk::EventHandler> handler, TimeOutCallback timeOutCallback, uint64_t interval)50 WatchdogTask::WatchdogTask(std::string name, std::shared_ptr<AppExecFwk::EventHandler> handler,
51     TimeOutCallback timeOutCallback, uint64_t interval)
52     : name(name), task(nullptr), timeOutCallback(timeOutCallback), timeout(0), func(nullptr),
53       arg(nullptr), flag(0), timeLimit(0), countLimit(0)
54 {
55     id = ++curId;
56     checker = std::make_shared<HandlerChecker>(name, handler);
57     checkInterval = interval;
58     nextTickTime = GetCurrentTickMillseconds();
59     isTaskScheduled = false;
60     isOneshotTask = false;
61 }
62 
WatchdogTask(std::string name, Task&& task, uint64_t delay, uint64_t interval, bool isOneshot)63 WatchdogTask::WatchdogTask(std::string name, Task&& task, uint64_t delay, uint64_t interval,  bool isOneshot)
64     : name(name), task(std::move(task)), timeOutCallback(nullptr), checker(nullptr), timeout(0), func(nullptr),
65       arg(nullptr), flag(0), watchdogTid(0), timeLimit(0), countLimit(0)
66 {
67     id = ++curId;
68     checkInterval = interval;
69     nextTickTime = GetCurrentTickMillseconds() + delay;
70     isTaskScheduled = false;
71     isOneshotTask = isOneshot;
72 }
73 
WatchdogTask(std::string name, unsigned int timeout, XCollieCallback func, void *arg, unsigned int flag)74 WatchdogTask::WatchdogTask(std::string name, unsigned int timeout, XCollieCallback func, void *arg, unsigned int flag)
75     : name(name), task(nullptr), timeOutCallback(nullptr), checker(nullptr), timeout(timeout), func(std::move(func)),
76       arg(arg), flag(flag), timeLimit(0), countLimit(0)
77 {
78     id = ++curId;
79     checkInterval = 0;
80     nextTickTime = GetCurrentTickMillseconds() + timeout;
81     isTaskScheduled = false;
82     isOneshotTask = true;
83     watchdogTid = getproctid();
84 }
85 
WatchdogTask(std::string name, unsigned int timeLimit, int countLimit)86 WatchdogTask::WatchdogTask(std::string name, unsigned int timeLimit, int countLimit)
87     : name(name), task(nullptr), timeOutCallback(nullptr), timeout(0), func(nullptr), arg(nullptr), flag(0),
88       isTaskScheduled(false), isOneshotTask(false), watchdogTid(0), timeLimit(timeLimit), countLimit(countLimit)
89 {
90     id = ++curId;
91     checkInterval = timeLimit / TIME_LIMIT_NUM_MAX_RATIO;
92     nextTickTime = GetCurrentTickMillseconds();
93 }
94 
DoCallback()95 void WatchdogTask::DoCallback()
96 {
97     if (func) {
98         XCOLLIE_LOGE("XCollieInner::DoTimerCallback %{public}s callback", name.c_str());
99         func(arg);
100     }
101     if (WatchdogInner::GetInstance().IsCallbackLimit(flag)) {
102         XCOLLIE_LOGE("Too many callback triggered in a short time, %{public}s skip", name.c_str());
103         return;
104     }
105     if (flag & XCOLLIE_FLAG_LOG) {
106         /* send to freezedetetor */
107         std::string msg = "timeout: " + name + " to check " + std::to_string(timeout) + "ms ago";
108         SendXCollieEvent(name, msg);
109     }
110     if (getuid() > UID_TYPE_THRESHOLD) {
111         XCOLLIE_LOGI("check uid is app, do not exit");
112         return;
113     }
114     if (flag & XCOLLIE_FLAG_RECOVERY) {
115         XCOLLIE_LOGE("%{public}s blocked, after timeout %{public}llu ,process will exit", name.c_str(),
116             static_cast<long long>(timeout));
117         std::thread exitFunc([]() {
118             std::string description = "timeout, exit...";
119             WatchdogInner::LeftTimeExitProcess(description);
120         });
121         if (exitFunc.joinable()) {
122             exitFunc.detach();
123         }
124     }
125 }
126 
Run(uint64_t now)127 void WatchdogTask::Run(uint64_t now)
128 {
129     if (countLimit > 0) {
130         TimerCountTask();
131         return;
132     }
133 
134     constexpr int resetRatio = 2;
135     if ((checkInterval != 0) && (now - nextTickTime > (resetRatio * checkInterval))) {
136         XCOLLIE_LOGI("checker thread may be blocked, reset next tick time."
137             "now:%{public}" PRIu64 " expect:%{public}" PRIu64 " interval:%{public}" PRIu64 "",
138             now, nextTickTime, checkInterval);
139         nextTickTime = now;
140         isTaskScheduled = false;
141         return;
142     }
143 
144     if (timeout != 0) {
145         if (IsMemHookOn()) {
146             return;
147         }
148         DoCallback();
149     } else if (task != nullptr) {
150         task();
151     } else {
152         RunHandlerCheckerTask();
153     }
154 }
155 
TimerCountTask()156 void WatchdogTask::TimerCountTask()
157 {
158     int size = static_cast<int>(triggerTimes.size());
159     if (size < countLimit) {
160         return;
161     }
162     XCOLLIE_LOGD("timeLimit : %{public}" PRIu64 ", countLimit : %{public}d, triggerTimes size : %{public}d",
163         timeLimit, countLimit, size);
164 
165     while (size >= countLimit) {
166         uint64_t timeInterval = triggerTimes[size -1] - triggerTimes[size - countLimit];
167         if (timeInterval < timeLimit) {
168             std::string sendMsg = name + " occured " + std::to_string(countLimit) + " times in " +
169                 std::to_string(timeInterval) + " ms, " + message;
170 #ifdef HISYSEVENT_ENABLE
171             HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, name, HiSysEvent::EventType::FAULT,
172                 "PID", getprocpid(), "PROCESS_NAME", GetSelfProcName(), "MSG", sendMsg);
173 #else
174        XCOLLIE_LOGI("hisysevent not exists");
175 #endif
176             triggerTimes.clear();
177             return;
178         }
179         size--;
180     }
181 
182     if (triggerTimes.size() > static_cast<unsigned long>(countLimit * COUNT_LIMIT_NUM_MAX_RATIO)) {
183         triggerTimes.erase(triggerTimes.begin(), triggerTimes.end() - countLimit);
184     }
185 }
186 
RunHandlerCheckerTask()187 void WatchdogTask::RunHandlerCheckerTask()
188 {
189     if (checker == nullptr) {
190         return;
191     }
192 
193     if (!isTaskScheduled) {
194         checker->ScheduleCheck();
195         isTaskScheduled = true;
196     } else {
197         if (EvaluateCheckerState() == CheckStatus::COMPLETED) {
198             // allow next check
199             isTaskScheduled = false;
200         }
201     }
202 }
203 
SendEvent(const std::string &msg, const std::string &eventName)204 void WatchdogTask::SendEvent(const std::string &msg, const std::string &eventName)
205 {
206     int32_t pid = getprocpid();
207     if (IsProcessDebug(pid)) {
208         XCOLLIE_LOGI("heap dump or debug for %{public}d, don't report.", pid);
209         return;
210     }
211     uint32_t gid = getgid();
212     uint32_t uid = getuid();
213     time_t curTime = time(nullptr);
214     std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) +
215         "\n" + msg + "\n";
216     sendMsg += checker->GetDumpInfo();
217 
218     watchdogTid = pid;
219     std::string tidFrontStr = "Thread ID = ";
220     std::string tidRearStr = ") is running";
221     std::size_t frontPos = sendMsg.find(tidFrontStr);
222     std::size_t rearPos = sendMsg.find(tidRearStr);
223     std::size_t startPos = frontPos + tidFrontStr.length();
224     if (frontPos != std::string::npos && rearPos != std::string::npos && rearPos > startPos) {
225         size_t tidLength = rearPos - startPos;
226         if (tidLength < std::to_string(INT32_MAX).length()) {
227             std::string tidStr = sendMsg.substr(startPos, tidLength);
228             if (std::all_of(std::begin(tidStr), std::end(tidStr), [] (const char &c) {
229                 return isdigit(c);
230             })) {
231                 watchdogTid = std::stoi(tidStr);
232             }
233         }
234     }
235 
236 #ifdef HISYSEVENT_ENABLE
237     int ret = HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, eventName, HiSysEvent::EventType::FAULT,
238         "PID", pid, "TID", watchdogTid, "TGID", gid, "UID", uid, "MODULE_NAME", name,
239         "PROCESS_NAME", GetSelfProcName(), "MSG", sendMsg, "STACK", GetProcessStacktrace());
240     if (ret == ERR_OVER_SIZE) {
241         std::string stack = "";
242         GetBacktraceStringByTid(stack, watchdogTid, 0, true);
243         ret = HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, eventName, HiSysEvent::EventType::FAULT,
244             "PID", pid, "TID", watchdogTid, "TGID", gid, "UID", uid, "MODULE_NAME", name,
245             "PROCESS_NAME", GetSelfProcName(), "MSG", sendMsg, "STACK", stack);
246     }
247 
248     XCOLLIE_LOGI("hisysevent write result=%{public}d, send event [FRAMEWORK,%{public}s], msg=%{public}s",
249         ret, eventName.c_str(), msg.c_str());
250 #else
251        XCOLLIE_LOGI("hisysevent not exists");
252 #endif
253 }
254 
SendXCollieEvent(const std::string &timerName, const std::string &keyMsg) const255 void WatchdogTask::SendXCollieEvent(const std::string &timerName, const std::string &keyMsg) const
256 {
257     XCOLLIE_LOGD("SendXCollieEvent start");
258     int32_t pid = getprocpid();
259     if (IsProcessDebug(pid)) {
260         XCOLLIE_LOGI("heap dump or debug for %{public}d, don't report.", pid);
261         return;
262     }
263     uint32_t gid = getgid();
264     uint32_t uid = getuid();
265     time_t curTime = time(nullptr);
266     std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) + "\n"+
267         "timeout timer: " + timerName + "\n" +keyMsg;
268 
269     std::string userStack = "";
270     if (uid == SAMGR_INIT_UID) {
271         XCOLLIE_LOGD("DumpUserStack dump init stack start");
272         if (!GetBacktraceStringByTid(userStack, 1, 0, true)) {
273             XCOLLIE_LOGE("get tid:1 BacktraceString failed");
274         }
275         XCOLLIE_LOGD("DumpUserStack dump init stack end");
276     }
277 
278     std::string eventName = "APP_HICOLLIE";
279     std::string processName = GetSelfProcName();
280     std::string stack = "";
281     if (uid <= UID_TYPE_THRESHOLD) {
282         eventName = std::find(std::begin(CORE_PROCS), std::end(CORE_PROCS), processName) != std::end(CORE_PROCS) ?
283             "SERVICE_TIMEOUT" : "SERVICE_TIMEOUT_WARNING";
284         stack = GetProcessStacktrace();
285     } else if (!GetBacktraceStringByTid(stack, watchdogTid, 0, true)) {
286         XCOLLIE_LOGE("get tid:%{public}d BacktraceString failed", watchdogTid);
287     }
288 #ifdef HISYSEVENT_ENABLE
289     int result = HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, eventName, HiSysEvent::EventType::FAULT, "PID", pid,
290         "TID", watchdogTid, "TGID", gid, "UID", uid, "MODULE_NAME", timerName, "PROCESS_NAME", processName,
291         "MSG", sendMsg, "STACK", stack + "\n"+ userStack);
292     XCOLLIE_LOGI("hisysevent write result=%{public}d, send event [FRAMEWORK,%{public}s], "
293         "msg=%{public}s", result, eventName.c_str(), keyMsg.c_str());
294 #else
295        XCOLLIE_LOGI("hisysevent not exists");
296 #endif
297 }
298 
EvaluateCheckerState()299 int WatchdogTask::EvaluateCheckerState()
300 {
301     int waitState = checker->GetCheckState();
302     if (waitState == CheckStatus::COMPLETED) {
303         return waitState;
304     } else if (waitState == CheckStatus::WAITED_HALF) {
305         XCOLLIE_LOGI("Watchdog half-block happened, send event");
306         std::string description = GetBlockDescription(checkInterval / 1000); // 1s = 1000ms
307         if (timeOutCallback != nullptr) {
308             timeOutCallback(name, waitState);
309         } else {
310             if (IsMemHookOn()) {
311                 return waitState;
312             }
313             if (name.compare(IPC_FULL) != 0) {
314                 SendEvent(description, "SERVICE_WARNING");
315             }
316         }
317     } else {
318         XCOLLIE_LOGI("Watchdog happened, send event twice.");
319         std::string description = GetBlockDescription(checkInterval / 1000) +
320             ", report twice instead of exiting process."; // 1s = 1000ms
321         if (timeOutCallback != nullptr) {
322             timeOutCallback(name, waitState);
323         } else {
324             if (IsMemHookOn()) {
325                 return waitState;
326             }
327             if (name.compare(IPC_FULL) == 0) {
328                 SendEvent(description, IPC_FULL);
329             } else {
330                 SendEvent(description, "SERVICE_BLOCK");
331             }
332             // peer binder log is collected in hiview asynchronously
333             // if blocked process exit early, binder blocked state will change
334             // thus delay exit and let hiview have time to collect log.
335             WatchdogInner::KillPeerBinderProcess(description);
336         }
337     }
338     return waitState;
339 }
340 
GetBlockDescription(uint64_t interval)341 std::string WatchdogTask::GetBlockDescription(uint64_t interval)
342 {
343     std::string desc = "Watchdog: thread(";
344     desc += name;
345     desc += ") blocked " + std::to_string(interval) + "s";
346     return desc;
347 }
348 
IsMemHookOn()349 bool WatchdogTask::IsMemHookOn()
350 {
351     bool hookFlag = __get_global_hook_flag();
352     if (hookFlag) {
353         XCOLLIE_LOGI("memory hook is on, timeout task will skip");
354     }
355     return hookFlag;
356 }
357 } // end of namespace HiviewDFX
358 } // end of namespace OHOS
359