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