1 /*
2 * Copyright (c) 2021 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 "xcollie_inner.h"
17
18 #include <future>
19 #include <pthread.h>
20 #include <unistd.h>
21
22 #include "hisysevent.h"
23
24 #include "xcollie_define.h"
25 #include "xcollie_utils.h"
26
27 namespace OHOS {
28 namespace HiviewDFX {
XCollieInner()29 XCollieInner::XCollieInner()
30 : timerRing_(nullptr), threadChecker_(nullptr), lockChecker_(nullptr),
31 threadLoop_(nullptr), checkStatus_(CheckStatus::COMPLETED), startTime_(0),
32 lockCheckResult_(false), thread_(nullptr), exitThread_(0),
33 checkerInterval_(STACK_INTERVAL), recovery_(true), cntCallback_(0), timeCallback_(0)
34 {
35 }
36
~XCollieInner()37 XCollieInner::~XCollieInner()
38 {
39 Stop();
40 lockChecker_ = nullptr;
41 threadChecker_ = nullptr;
42 }
43
StopChecker()44 void XCollieInner::StopChecker()
45 {
46 if (thread_ != nullptr) {
47 exitThread_++;
48 }
49 if (threadLoop_ != nullptr) {
50 exitThread_++;
51 }
52 if (exitThread_ == 0) {
53 return;
54 }
55 while (exitThread_ > 0) {
56 {
57 std::unique_lock<std::mutex> lock(lock_);
58 xcollieCheckers_.clear();
59 condition_.notify_all();
60 }
61 std::this_thread::sleep_for(std::chrono::milliseconds(200)); /* 200: wait thread exit */
62 }
63 if (thread_ != nullptr && thread_->joinable()) {
64 thread_->join();
65 thread_ = nullptr;
66 }
67 if (threadLoop_ != nullptr && threadLoop_->joinable()) {
68 threadLoop_->join();
69 threadLoop_ = nullptr;
70 }
71 }
72
Stop()73 void XCollieInner::Stop()
74 {
75 /* when thread exit, timerRing_ exit safely. this scene used in test case. */
76 if (timerRing_ != nullptr) {
77 while (timerRing_->IsRunning()) {
78 timerRing_->TryNotify();
79 timerRing_->NotifyExitAsync();
80 std::this_thread::sleep_for(std::chrono::milliseconds(200)); /* 200: wait thread exit */
81 }
82 timerRing_ = nullptr;
83 }
84 StopChecker();
85 XCOLLIE_LOGE("XCollieInner::~XCollieInner exit...");
86 }
87
Start(bool isWatchdog)88 bool XCollieInner::Start(bool isWatchdog)
89 {
90 /* if watchdog, then start assist thread */
91 if (isWatchdog) {
92 if (exitThread_ > 0) {
93 XCOLLIE_LOGE("watchdog is stopping");
94 return false;
95 }
96
97 if (thread_ == nullptr) {
98 thread_ = std::make_unique<std::thread>(&XCollieInner::RunChecker, this);
99 (void)pthread_setname_np(thread_->native_handle(), XCOLLIE_CHECKER_NAME.c_str());
100 }
101 if (threadLoop_ == nullptr) {
102 checkStatus_ = CheckStatus::COMPLETED;
103 threadLoop_ = std::make_unique<std::thread>(&XCollieInner::CheckResult, this);
104 (void)pthread_setname_np(threadLoop_->native_handle(), XCOLLIE_LOOP_NAME.c_str());
105 }
106 if ((thread_ == nullptr) || (threadLoop_ == nullptr)) {
107 XCOLLIE_LOGE("XCollieInner: start thread failed");
108 return false;
109 }
110 } else {
111 /* init timer */
112 if (timerRing_ == nullptr) {
113 timerRing_ = std::make_unique<TimerRing>();
114 if (timerRing_ == nullptr) {
115 XCOLLIE_LOGE("XCollieInner:new TimerRing failed");
116 return false;
117 }
118 if (timerRing_->Start(timerRing_->GetName()) != ThreadStatus::OK) {
119 XCOLLIE_LOGE("XCollieInner:TimerRing thread failed");
120 return false;
121 }
122 }
123 }
124 return true;
125 }
126
IsCallbackLimit(unsigned int flag)127 bool XCollieInner::IsCallbackLimit(unsigned int flag)
128 {
129 bool ret = false;
130 time_t startTime = time(nullptr);
131 if (!(flag & XCOLLIE_FLAG_LOG)) {
132 return ret;
133 }
134 if (timeCallback_ + XCOLLIE_CALLBACK_TIMEWIN_MAX < startTime) {
135 timeCallback_ = startTime;
136 } else {
137 if (++cntCallback_ > XCOLLIE_CALLBACK_HISTORY_MAX) {
138 ret = true;
139 }
140 }
141 return ret;
142 }
143
SendEvent(int tid,const std::string & timerName,const std::string & keyMsg) const144 void XCollieInner::SendEvent(int tid, const std::string &timerName, const std::string &keyMsg) const
145 {
146 int32_t pid = getpid();
147 int32_t gid = getgid();
148 int32_t uid = getuid();
149 time_t curTime = time(nullptr);
150 std::string sendMsg = std::string((ctime(&curTime) == nullptr) ? "" : ctime(&curTime)) + "\n" +
151 "timeout timer: " + timerName + "\n" + keyMsg;
152 HiSysEvent::Write("FRAMEWORK", "SERVICE_TIMEOUT", HiSysEvent::EventType::FAULT,
153 "PID", pid,
154 "TGID", gid,
155 "UID", uid,
156 "MODULE_NAME", timerName,
157 "PROCESS_NAME", GetSelfProcName(),
158 "MSG", sendMsg);
159 XCOLLIE_LOGI("send event [FRAMEWORK,SERVICE_TIMEOUT], msg=%s", keyMsg.c_str());
160 }
161
DoTimerCallback(struct InputTimerPara * task)162 void XCollieInner::DoTimerCallback(struct InputTimerPara *task)
163 {
164 if (task == nullptr) {
165 XCOLLIE_LOGE("XCollieInner::DoTimerCallback data is null... ");
166 return;
167 }
168 if (task->func) {
169 XCOLLIE_LOGE("XCollieInner::DoTimerCallback %s callback", task->name.c_str());
170 task->func(task->arg);
171 }
172
173 if (IsCallbackLimit(task->flag)) {
174 XCOLLIE_LOGE("Too many callback triggered in a short time, %s skip", task->name.c_str());
175 delete task;
176 return;
177 }
178 if (task->flag & XCOLLIE_FLAG_LOG) {
179 /* send to freezedetector */
180 std::string msg = "timeout: " + task->name + " start at" +
181 std::to_string(task->startTime) + " to check " + std::to_string(task->timeout) + "s ago";
182 SendEvent(task->tid, task->name, msg);
183 }
184 if ((task->flag & XCOLLIE_FLAG_RECOVERY)) {
185 XCOLLIE_LOGE("%s blocked, after timeout %u , will be exit", task->name.c_str(), task->timeout);
186 if (recovery_) {
187 std::thread exitFunc ([](unsigned int time) {
188 std::this_thread::sleep_for(std::chrono::seconds(time));
189 _exit(1);
190 }, task->timeout);
191 if (exitFunc.joinable()) {
192 exitFunc.detach();
193 }
194 }
195 XCOLLIE_LOGE("%s timeout, exit...", task->name.c_str());
196 }
197 delete task;
198 }
199
SetTimer(const std::string & name,unsigned int timeout,std::function<void (void *)> func,void * arg,unsigned int flag)200 int XCollieInner::SetTimer(const std::string &name, unsigned int timeout,
201 std::function<void(void *)> func, void *arg, unsigned int flag)
202 {
203 std::lock_guard<std::mutex> lock(lock_);
204 int id = INVALID_ID;
205 /* 1. start timer ring */
206 if (Start(false) == false) {
207 return id;
208 }
209
210 /* 2. add timer ring task */
211 auto *newTask = new InputTimerPara();
212 if (newTask == nullptr) {
213 return id;
214 }
215 newTask->name = name;
216 newTask->flag = flag;
217 newTask->timeout = timeout;
218 newTask->tid = gettid();
219 newTask->func = func;
220 newTask->arg = arg;
221 newTask->startTime = time(nullptr);
222
223 struct TimerTask task = {
224 .name = name,
225 .timeout = timeout,
226 .loop = false,
227 .inputTimerPara = newTask,
228 };
229 task.func = [&](struct InputTimerPara *inputPara) {
230 DoTimerCallback(inputPara);
231 };
232 id = timerRing_->AddTask(task);
233 if (id == INVALID_ID) {
234 delete newTask;
235 }
236 return id;
237 }
238
UpdateTimer(int id,unsigned int timeout)239 bool XCollieInner::UpdateTimer(int id, unsigned int timeout)
240 {
241 std::lock_guard<std::mutex> lock(lock_);
242 /* 1. start timer ring */
243 if (Start(false) == false) {
244 return false;
245 }
246 return timerRing_->UpdateTask(id, timeout);
247 }
248
DestroyTimer(const int id)249 void XCollieInner::DestroyTimer(const int id)
250 {
251 auto *inputTimer = timerRing_->CancelTask(id);
252 if (inputTimer == nullptr) {
253 return;
254 }
255 delete inputTimer;
256 }
257
CancelTimer(int id)258 void XCollieInner::CancelTimer(int id)
259 {
260 std::lock_guard<std::mutex> lock(lock_);
261 /* 1. start timer ring */
262 if (Start(false) == false) {
263 return;
264 }
265 DestroyTimer(id);
266 }
267
RegisterXCollieChecker(const sptr<XCollieChecker> & checker,unsigned int type)268 void XCollieInner::RegisterXCollieChecker(const sptr<XCollieChecker> &checker, unsigned int type)
269 {
270 /* param check */
271 if (checker == nullptr) {
272 XCOLLIE_LOGE("register failed, checker is null");
273 return;
274 }
275 if (!(type & XCOLLIE_LOCK) && !(type & XCOLLIE_THREAD)) {
276 XCOLLIE_LOGE("register failed, type %u invalid", type);
277 return;
278 }
279
280 std::lock_guard<std::mutex> lock(lock_);
281 /* 1. start timer ring */
282 if (Start(true) == false) {
283 return;
284 }
285
286 /* 2. add timeout, check */
287 xcollieCheckers_[checker] = type;
288 }
289
UnRegisterXCollieChecker(const sptr<XCollieChecker> & checker)290 void XCollieInner::UnRegisterXCollieChecker(const sptr<XCollieChecker> &checker)
291 {
292 {
293 std::lock_guard<std::mutex> lock(lock_);
294 auto search = xcollieCheckers_.find(checker);
295 if (search == xcollieCheckers_.end()) {
296 return;
297 }
298 xcollieCheckers_.erase(search);
299 if (threadChecker_ == checker) {
300 threadChecker_ = nullptr;
301 }
302 if (lockChecker_ == checker) {
303 lockChecker_ = nullptr;
304 }
305 }
306 if (xcollieCheckers_.size() == 0) {
307 StopChecker();
308 }
309 }
310
StartCheckService()311 int XCollieInner::StartCheckService()
312 {
313 lockChecker_ = nullptr;
314 threadChecker_ = nullptr;
315 std::map<sptr<XCollieChecker>, unsigned int> checkers;
316 {
317 std::lock_guard<std::mutex> lock(lock_);
318 checkers = xcollieCheckers_;
319 lockCheckResult_ = false;
320 }
321
322 /* start check thread block */
323 for (const auto &it : checkers) {
324 unsigned int type = it.second;
325 if (type & XCOLLIE_THREAD) {
326 sptr<XCollieChecker> checker = it.first;
327 checker->SetThreadBlockResult(false);
328 checker->CheckThreadBlock();
329 }
330 }
331
332 /* start check lock */
333 for (const auto &it : checkers) {
334 unsigned int type = it.second;
335 if (type & XCOLLIE_LOCK) {
336 lockChecker_ = it.first;
337 lockChecker_->CheckLock();
338 }
339 }
340
341 lockChecker_ = nullptr;
342 lockCheckResult_ = true;
343 return 0;
344 }
345
RunChecker()346 void XCollieInner::RunChecker()
347 {
348 for (;;) {
349 {
350 std::unique_lock<std::mutex> lock(lock_);
351 condition_.wait(lock);
352 if (exitThread_ > 0) {
353 exitThread_--;
354 break;
355 }
356 }
357 StartCheckService();
358 }
359 XCOLLIE_LOGE("XCollieInner::RunChecker thread exit...");
360 }
361
CheckResult()362 void XCollieInner::CheckResult()
363 {
364 for (;;) {
365 /* per interval check services */
366 std::unique_lock<std::mutex> lock(lock_);
367 (void)condition_.wait_for(lock, std::chrono::seconds(checkerInterval_), [&]() {
368 return exitThread_ > 0;
369 });
370 if (exitThread_ > 0) {
371 exitThread_--;
372 break;
373 }
374 if ((checkStatus_ == CheckStatus::COMPLETED) || (GetThreadBlockResult() && lockCheckResult_)) {
375 startTime_ = time(nullptr);
376 checkStatus_ = CheckStatus::WAITING;
377 condition_.notify_all();
378 continue;
379 }
380 /* check result */
381 std::string name;
382 std::string msg = GetBlockServiceMsg(name);
383 if (checkStatus_ == CheckStatus::WAITING) {
384 checkStatus_ = CheckStatus::WAITED_HALF;
385 double duration = difftime(time(nullptr), startTime_);
386 XCOLLIE_LOGW("%s, duration %{public}.0lf", msg.c_str(), duration);
387 /* send to freezedetector start dump */
388 SendEvent(getpid(), name, "watchdog: " + msg + " duration " + std::to_string(duration));
389 continue;
390 }
391 if (checkStatus_ == CheckStatus::WAITED_HALF) {
392 XCOLLIE_LOGE("%s, duration %{public}.01f, will restart", msg.c_str(), difftime(time(nullptr), startTime_));
393 if (recovery_) {
394 std::thread exitFunc([]() {
395 _exit(1);
396 });
397 if (exitFunc.joinable()) {
398 exitFunc.detach();
399 }
400 }
401 }
402 }
403 }
404
GetBlockServiceMsg(std::string & name) const405 std::string XCollieInner::GetBlockServiceMsg(std::string &name) const
406 {
407 std::string msg;
408 if (threadChecker_ != nullptr) {
409 name = threadChecker_->GetCheckerName();
410 msg = "Blocked in main thread on service " + name;
411 } else if (lockChecker_ != nullptr) {
412 name = lockChecker_->GetCheckerName();
413 msg = "Blocked in lock on service " + name;
414 } else {
415 name = "unknown";
416 msg = "Blocked in lock on unknown service";
417 }
418 return msg;
419 }
420
GetThreadBlockResult()421 bool XCollieInner::GetThreadBlockResult()
422 {
423 for (const auto &it : xcollieCheckers_) {
424 unsigned int type = it.second;
425 sptr<XCollieChecker> checker = it.first;
426 if ((type & XCOLLIE_THREAD) && (checker->GetThreadBlockResult() == false)) {
427 threadChecker_ = checker;
428 return false;
429 }
430 }
431 return true;
432 }
433
GetBlockdServiceName()434 std::string XCollieInner::GetBlockdServiceName()
435 {
436 std::lock_guard<std::mutex> lock(lock_);
437
438 if (threadChecker_ != nullptr) {
439 return threadChecker_->GetCheckerName();
440 } else if (lockChecker_ != nullptr) {
441 return lockChecker_->GetCheckerName();
442 } else {
443 return std::string("");
444 }
445 }
446 } // end of namespace HiviewDFX
447 } // end of namespace OHOS
448