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