• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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