• 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 "hisysevent.h"
30 #include "watchdog_inner.h"
31 #include "xcollie_define.h"
32 #include "xcollie_utils.h"
33 
34 namespace OHOS {
35 namespace HiviewDFX {
36 int64_t WatchdogTask::curId = 0;
37 const char* g_bboxPath = "/dev/bbox";
38 struct HstackVal {
39     uint32_t magic;
40     pid_t tid;
41     char hstackLogBuff[BUFF_STACK_SIZE];
42 };
WatchdogTask(std::string name,std::shared_ptr<AppExecFwk::EventHandler> handler,TimeOutCallback timeOutCallback,uint64_t interval)43 WatchdogTask::WatchdogTask(std::string name, std::shared_ptr<AppExecFwk::EventHandler> handler,
44     TimeOutCallback timeOutCallback, uint64_t interval)
45     : name(name), task(nullptr), timeOutCallback(timeOutCallback), timeout(0), func(nullptr),
46       arg(nullptr), flag(0)
47 {
48     id = ++curId;
49     checker = std::make_shared<HandlerChecker>(name, handler);
50     checkInterval = interval;
51     nextTickTime = GetCurrentTickMillseconds();
52     isTaskScheduled = false;
53     isOneshotTask = false;
54 }
55 
WatchdogTask(std::string name,Task && task,uint64_t delay,uint64_t interval,bool isOneshot)56 WatchdogTask::WatchdogTask(std::string name, Task&& task, uint64_t delay, uint64_t interval,  bool isOneshot)
57     : name(name), task(std::move(task)), timeOutCallback(nullptr), checker(nullptr), timeout(0), func(nullptr),
58       arg(nullptr), flag(0)
59 {
60     id = ++curId;
61     checkInterval = interval;
62     nextTickTime = GetCurrentTickMillseconds() + delay;
63     isTaskScheduled = false;
64     isOneshotTask = isOneshot;
65 }
66 
WatchdogTask(std::string name,unsigned int timeout,XCollieCallback func,void * arg,unsigned int flag)67 WatchdogTask::WatchdogTask(std::string name, unsigned int timeout, XCollieCallback func, void *arg, unsigned int flag)
68     : name(name), task(nullptr), timeOutCallback(nullptr), checker(nullptr), timeout(timeout), func(std::move(func)),
69       arg(arg), flag(flag)
70 {
71     id = ++curId;
72     checkInterval = 0;
73     nextTickTime = GetCurrentTickMillseconds() + timeout;
74     isTaskScheduled = false;
75     isOneshotTask = true;
76     watchdogTid = gettid();
77 }
78 
DoCallback()79 void WatchdogTask::DoCallback()
80 {
81     if (func) {
82         XCOLLIE_LOGE("XCollieInner::DoTimerCallback %{public}s callback", name.c_str());
83         func(arg);
84     }
85     if (WatchdogInner::GetInstance().IsCallbackLimit(flag)) {
86         XCOLLIE_LOGE("Too many callback triggered in a short time, %{public}s skip", name.c_str());
87         return;
88     }
89     if (flag & XCOLLIE_FLAG_LOG) {
90         /* send to freezedetetor */
91         std::string msg = "timeout: " + name + " to check " + std::to_string(timeout) + "ms ago";
92         SendXCollieEvent(name, msg);
93     }
94     if (flag & XCOLLIE_FLAG_RECOVERY) {
95         XCOLLIE_LOGE("%{public}s blocked, after timeout %{public}llu ,process will exit", name.c_str(),
96             static_cast<long long>(timeout));
97         std::thread exitFunc([]() {
98             std::string description = "timeout, exit...";
99             WatchdogInner::LeftTimeExitProcess(description);
100         });
101         if (exitFunc.joinable()) {
102             exitFunc.detach();
103         }
104     }
105 }
106 
Run(uint64_t now)107 void WatchdogTask::Run(uint64_t now)
108 {
109     constexpr int resetRatio = 2;
110     if ((checkInterval != 0) && (now - nextTickTime > (resetRatio * checkInterval))) {
111         XCOLLIE_LOGI("checker thread may be blocked, reset next tick time."
112             "now:%{public}" PRIu64 " expect:%{public}" PRIu64 " interval:%{public}" PRIu64 "",
113             now, nextTickTime, checkInterval);
114         nextTickTime = now;
115         isTaskScheduled = false;
116         return;
117     }
118 
119     if (timeout != 0) {
120         DoCallback();
121     } else if (task != nullptr) {
122         task();
123     } else {
124         RunHandlerCheckerTask();
125     }
126 }
127 
RunHandlerCheckerTask()128 void WatchdogTask::RunHandlerCheckerTask()
129 {
130     if (checker == nullptr) {
131         return;
132     }
133 
134     if (!isTaskScheduled) {
135         checker->ScheduleCheck();
136         isTaskScheduled = true;
137     } else {
138         if (EvaluateCheckerState() == CheckStatus::COMPLETED) {
139             // allow next check
140             isTaskScheduled = false;
141         }
142     }
143 }
144 
SendEvent(const std::string & msg,const std::string & eventName) const145 void WatchdogTask::SendEvent(const std::string &msg, const std::string &eventName) const
146 {
147     int32_t pid = getpid();
148     if (IsProcessDebug(pid)) {
149         XCOLLIE_LOGI("heap dump for %{public}d, don't report.", pid);
150         return;
151     }
152     uint32_t gid = getgid();
153     uint32_t uid = getuid();
154     time_t curTime = time(nullptr);
155     std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) +
156         "\n" + msg + "\n";
157     sendMsg += checker->GetDumpInfo();
158     int ret = HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, eventName, HiSysEvent::EventType::FAULT,
159         "PID", pid,
160         "TGID", gid,
161         "UID", uid,
162         "MODULE_NAME", name,
163         "PROCESS_NAME", GetSelfProcName(),
164         "MSG", sendMsg,
165         "STACK", GetProcessStacktrace());
166     XCOLLIE_LOGI("hisysevent write result=%{public}d, send event [FRAMEWORK,%{public}s], msg=%{public}s",
167         ret, eventName.c_str(), msg.c_str());
168 }
169 
SendXCollieEvent(const std::string & timerName,const std::string & keyMsg) const170 void WatchdogTask::SendXCollieEvent(const std::string &timerName, const std::string &keyMsg) const
171 {
172     int32_t pid = getpid();
173     uint32_t gid = getgid();
174     uint32_t uid = getuid();
175     time_t curTime = time(nullptr);
176     std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) + "\n"+
177         "timeout timer: " + timerName + "\n" +keyMsg;
178 
179     struct HstackVal val;
180     if (memset_s(&val, sizeof(val), 0, sizeof(val)) != 0) {
181         XCOLLIE_LOGE("memset val failed\n");
182         return;
183     }
184     val.tid = watchdogTid;
185     val.magic = MAGIC_NUM;
186     int fd = open(g_bboxPath, O_WRONLY | O_CLOEXEC);
187     if (fd < 0) {
188         XCOLLIE_LOGE("open %s failed", name.c_str());
189         return;
190     }
191     int ret = ioctl(fd, LOGGER_GET_STACK, &val);
192     close(fd);
193     if (ret != 0) {
194         XCOLLIE_LOGE("XCollieDumpKernel getStack failed");
195     } else {
196         XCOLLIE_LOGI("XCollieDumpKernel buff is %{public}s", val.hstackLogBuff);
197     }
198 
199     int result = HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, "SERVICE_TIMEOUT",
200         HiSysEvent::EventType::FAULT,
201         "PID", pid,
202         "TGID", gid,
203         "UID", uid,
204         "MODULE_NAME", timerName,
205         "PROCESS_NAME", GetSelfProcName(),
206         "MSG", sendMsg,
207         "STACK", GetProcessStacktrace()+ "\n"+ (ret != 0 ? "" : val.hstackLogBuff));
208     XCOLLIE_LOGI("hisysevent write result=%{public}d, send event [FRAMEWORK,SERVICE_TIMEOUT], "
209         "msg=%{public}s", result, keyMsg.c_str());
210 }
211 
EvaluateCheckerState()212 int WatchdogTask::EvaluateCheckerState()
213 {
214     int waitState = checker->GetCheckState();
215     if (waitState == CheckStatus::COMPLETED) {
216         return waitState;
217     } else if (waitState == CheckStatus::WAITED_HALF) {
218         XCOLLIE_LOGI("Watchdog half-block happened, send event");
219         std::string description = GetBlockDescription(checkInterval / 1000); // 1s = 1000ms
220         if (timeOutCallback != nullptr) {
221             timeOutCallback(name, waitState);
222         } else {
223             if (name.compare(IPC_FULL) != 0) {
224                 SendEvent(description, "SERVICE_WARNING");
225             }
226         }
227     } else {
228         XCOLLIE_LOGI("Watchdog happened, send event twice.");
229         std::string description = GetBlockDescription(checkInterval / 1000) +
230             ", report twice instead of exiting process."; // 1s = 1000ms
231         if (timeOutCallback != nullptr) {
232             timeOutCallback(name, waitState);
233         } else {
234             if (name.compare(IPC_FULL) == 0) {
235                 SendEvent(description, IPC_FULL);
236             } else {
237                 SendEvent(description, "SERVICE_BLOCK");
238             }
239             // peer binder log is collected in hiview asynchronously
240             // if blocked process exit early, binder blocked state will change
241             // thus delay exit and let hiview have time to collect log.
242             WatchdogInner::LeftTimeExitProcess(description);
243         }
244     }
245     return waitState;
246 }
247 
GetBlockDescription(uint64_t interval)248 std::string WatchdogTask::GetBlockDescription(uint64_t interval)
249 {
250     std::string desc = "Watchdog: thread(";
251     desc += name;
252     desc += ") blocked " + std::to_string(interval) + "s";
253     return desc;
254 }
255 } // end of namespace HiviewDFX
256 } // end of namespace OHOS
257