• 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 <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