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 <thread>
21
22 #include <unistd.h>
23
24 #include "backtrace_local.h"
25 #include "hisysevent.h"
26 #include "watchdog_inner.h"
27 #include "xcollie_define.h"
28 #include "xcollie_utils.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 int64_t WatchdogTask::curId = 0;
WatchdogTask(std::string name,std::shared_ptr<AppExecFwk::EventHandler> handler,TimeOutCallback timeOutCallback,uint64_t interval)33 WatchdogTask::WatchdogTask(std::string name, std::shared_ptr<AppExecFwk::EventHandler> handler,
34 TimeOutCallback timeOutCallback, uint64_t interval)
35 : name(name), task(nullptr), timeOutCallback(timeOutCallback), timeout(0), func(nullptr),
36 arg(nullptr), flag(0)
37 {
38 id = ++curId;
39 checker = std::make_shared<HandlerChecker>(name, handler);
40 checkInterval = interval;
41 nextTickTime = GetCurrentTickMillseconds();
42 isTaskScheduled = false;
43 isOneshotTask = false;
44 }
45
WatchdogTask(std::string name,Task && task,uint64_t delay,uint64_t interval,bool isOneshot)46 WatchdogTask::WatchdogTask(std::string name, Task&& task, uint64_t delay, uint64_t interval, bool isOneshot)
47 : name(name), task(std::move(task)), timeOutCallback(nullptr), checker(nullptr), timeout(0), func(nullptr),
48 arg(nullptr), flag(0)
49 {
50 id = ++curId;
51 checkInterval = interval;
52 nextTickTime = GetCurrentTickMillseconds() + delay;
53 isTaskScheduled = false;
54 isOneshotTask = isOneshot;
55 }
56
WatchdogTask(std::string name,unsigned int timeout,XCollieCallback func,void * arg,unsigned int flag)57 WatchdogTask::WatchdogTask(std::string name, unsigned int timeout, XCollieCallback func, void *arg, unsigned int flag)
58 : name(name), task(nullptr), timeOutCallback(nullptr), checker(nullptr), timeout(timeout), func(std::move(func)),
59 arg(arg), flag(flag)
60 {
61 id = ++curId;
62 checkInterval = 0;
63 nextTickTime = GetCurrentTickMillseconds() + timeout;
64 isTaskScheduled = false;
65 isOneshotTask =true;
66 }
67
DoCallback()68 void WatchdogTask::DoCallback()
69 {
70 if (func) {
71 XCOLLIE_LOGE("XCollieInner::DoTimerCallback %{public}s callback", name.c_str());
72 func(arg);
73 }
74 if (WatchdogInner::GetInstance().IsCallbackLimit(flag)) {
75 XCOLLIE_LOGE("Too many callback triggered in a short time, %{public}s skip", name.c_str());
76 return;
77 }
78 if (flag & XCOLLIE_FLAG_LOG) {
79 /* send to freezedetetor */
80 std::string msg = "timeout: " + name + " to check " + std::to_string(timeout) + "s ago";
81 SendXCollieEvent(name, msg);
82 }
83 if (flag & XCOLLIE_FLAG_RECOVERY) {
84 XCOLLIE_LOGE("%{public}s blocked, after timeout %{public}llu ,process will exit", name.c_str(),
85 static_cast<long long>(timeout));
86 std::thread exitFunc([]() {
87 XCOLLIE_LOGE("timeout, exit...");
88 _exit(1);
89 });
90 if (exitFunc.joinable()) {
91 exitFunc.detach();
92 }
93 }
94 }
95
Run(uint64_t now)96 void WatchdogTask::Run(uint64_t now)
97 {
98 constexpr int resetRatio = 2;
99 if ((checkInterval != 0) && (now - nextTickTime > (resetRatio * checkInterval))) {
100 XCOLLIE_LOGI("checker thread may be blocked, reset next tick time."
101 "now:%{public}" PRIu64 " expect:%{public}" PRIu64 " interval:%{public}" PRIu64 "",
102 now, nextTickTime, checkInterval);
103 nextTickTime = now;
104 isTaskScheduled = false;
105 return;
106 }
107
108 if (timeout != 0) {
109 DoCallback();
110 } else if (task != nullptr) {
111 task();
112 } else {
113 RunHandlerCheckerTask();
114 }
115 }
116
RunHandlerCheckerTask()117 void WatchdogTask::RunHandlerCheckerTask()
118 {
119 if (checker == nullptr) {
120 return;
121 }
122
123 if (!isTaskScheduled) {
124 checker->ScheduleCheck();
125 isTaskScheduled = true;
126 } else {
127 if (EvaluateCheckerState() == CheckStatus::COMPLETED) {
128 // allow next check
129 isTaskScheduled = false;
130 }
131 }
132 }
133
SendEvent(const std::string & msg,const std::string & eventName) const134 void WatchdogTask::SendEvent(const std::string &msg, const std::string &eventName) const
135 {
136 int32_t pid = getpid();
137 uint32_t gid = getgid();
138 uint32_t uid = getuid();
139 time_t curTime = time(nullptr);
140 std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) +
141 "\n" + msg + "\n";
142 sendMsg += checker->GetDumpInfo();
143 HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, eventName, HiSysEvent::EventType::FAULT,
144 "PID", pid,
145 "TGID", gid,
146 "UID", uid,
147 "MODULE_NAME", name,
148 "PROCESS_NAME", GetSelfProcName(),
149 "MSG", sendMsg,
150 "STACK", GetProcessStacktrace());
151 XCOLLIE_LOGI("send event [FRAMEWORK,%{public}s], msg=%{public}s", eventName.c_str(), msg.c_str());
152 }
153
SendXCollieEvent(const std::string & timerName,const std::string & keyMsg) const154 void WatchdogTask::SendXCollieEvent(const std::string &timerName, const std::string &keyMsg) const
155 {
156 int32_t pid = getpid();
157 uint32_t gid = getgid();
158 uint32_t uid = getuid();
159 time_t curTime = time(nullptr);
160 std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) + "\n"+
161 "timeout timer: " + timerName + "\n" +keyMsg;
162 HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, "SERVICE_TIMEOUT", HiSysEvent::EventType::FAULT,
163 "PID", pid,
164 "TGID", gid,
165 "UID", uid,
166 "MODULE_NAME", timerName,
167 "PROCESS_NAME", GetSelfProcName(),
168 "MSG", sendMsg);
169 XCOLLIE_LOGI("send event [FRAMEWORK,SERVICE_TIMEOUT], msg=%{public}s", keyMsg.c_str());
170 }
171
EvaluateCheckerState()172 int WatchdogTask::EvaluateCheckerState()
173 {
174 int waitState = checker->GetCheckState();
175 if (waitState == CheckStatus::COMPLETED) {
176 return waitState;
177 } else if (waitState == CheckStatus::WAITED_HALF) {
178 XCOLLIE_LOGI("Watchdog half-block happened, send event");
179 std::string description = GetBlockDescription(checkInterval / 1000); // 1s = 1000ms
180 if (timeOutCallback != nullptr) {
181 timeOutCallback(name, waitState);
182 } else {
183 if (name.compare(IPC_FULL) != 0) {
184 SendEvent(description, "SERVICE_WARNING");
185 }
186 }
187 } else {
188 XCOLLIE_LOGI("Watchdog happened, send event twice.");
189 std::string description = GetBlockDescription(checkInterval / 1000) +
190 ", report twice instead of exiting process."; // 1s = 1000ms
191 if (timeOutCallback != nullptr) {
192 timeOutCallback(name, waitState);
193 } else {
194 if (name.compare(IPC_FULL) == 0) {
195 SendEvent(description, IPC_FULL);
196 } else {
197 SendEvent(description, "SERVICE_BLOCK");
198 }
199 // peer binder log is collected in hiview asynchronously
200 // if blocked process exit early, binder blocked state will change
201 // thus delay exit and let hiview have time to collect log.
202 unsigned int leftTime = 3;
203 while (leftTime > 0) {
204 leftTime = sleep(leftTime);
205 }
206 XCOLLIE_LOGI("Process is going to exit, reason:%{public}s.", description.c_str());
207 _exit(0);
208 }
209 }
210 return waitState;
211 }
212
GetBlockDescription(uint64_t interval)213 std::string WatchdogTask::GetBlockDescription(uint64_t interval)
214 {
215 std::string desc = "Watchdog: thread(";
216 desc += name;
217 desc += ") blocked " + std::to_string(interval) + "s";
218 return desc;
219 }
220 } // end of namespace HiviewDFX
221 } // end of namespace OHOS
222