1 /*
2 * Copyright (c) 2021-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_inner.h"
17
18 #include <climits>
19 #include <mutex>
20
21 #include <pthread.h>
22 #include <unistd.h>
23
24 #include "hisysevent.h"
25 #include "xcollie_utils.h"
26
27 namespace OHOS {
28 namespace HiviewDFX {
29 constexpr uint64_t DEFAULT_TIMEOUT = 60 * 1000;
WatchdogInner()30 WatchdogInner::WatchdogInner()
31 {
32 }
33
~WatchdogInner()34 WatchdogInner::~WatchdogInner()
35 {
36 Stop();
37 }
38
IsInAppspwan()39 static bool IsInAppspwan()
40 {
41 if (getuid() == 0 && GetSelfProcName().find("appspawn") != std::string::npos) {
42 return true;
43 }
44 return false;
45 }
46
AddThread(const std::string & name,std::shared_ptr<AppExecFwk::EventHandler> handler,TimeOutCallback timeOutCallback,uint64_t interval)47 int WatchdogInner::AddThread(const std::string &name,
48 std::shared_ptr<AppExecFwk::EventHandler> handler, TimeOutCallback timeOutCallback, uint64_t interval)
49 {
50 if (name.empty() || handler == nullptr) {
51 XCOLLIE_LOGE("Add thread fail, invalid args!");
52 return -1;
53 }
54
55 if (IsInAppspwan()) {
56 return -1;
57 }
58
59 XCOLLIE_LOGI("Add thread %{public}s to watchdog.", name.c_str());
60 std::unique_lock<std::mutex> lock(lock_);
61 if (!InsertWatchdogTaskLocked(name, WatchdogTask(name, handler, timeOutCallback, interval))) {
62 return -1;
63 }
64 return 0;
65 }
66
RunOneShotTask(const std::string & name,Task && task,uint64_t delay)67 void WatchdogInner::RunOneShotTask(const std::string& name, Task&& task, uint64_t delay)
68 {
69 if (name.empty() || task == nullptr) {
70 XCOLLIE_LOGE("Add task fail, invalid args!");
71 return;
72 }
73
74 if (IsInAppspwan()) {
75 return;
76 }
77
78 std::unique_lock<std::mutex> lock(lock_);
79 InsertWatchdogTaskLocked(name, WatchdogTask(name, std::move(task), delay, 0, true));
80 }
81
RunPeriodicalTask(const std::string & name,Task && task,uint64_t interval,uint64_t delay)82 void WatchdogInner::RunPeriodicalTask(const std::string& name, Task&& task, uint64_t interval, uint64_t delay)
83 {
84 if (name.empty() || task == nullptr) {
85 XCOLLIE_LOGE("Add task fail, invalid args!");
86 return;
87 }
88
89 if (IsInAppspwan()) {
90 return;
91 }
92
93 XCOLLIE_LOGI("Add periodical task %{public}s to watchdog.", name.c_str());
94 std::unique_lock<std::mutex> lock(lock_);
95 InsertWatchdogTaskLocked(name, WatchdogTask(name, std::move(task), delay, interval, false));
96 }
97
IsTaskExistLocked(const std::string & name)98 bool WatchdogInner::IsTaskExistLocked(const std::string& name)
99 {
100 if (taskNameSet_.find(name) != taskNameSet_.end()) {
101 return true;
102 }
103
104 return false;
105 }
106
IsExceedMaxTaskLocked()107 bool WatchdogInner::IsExceedMaxTaskLocked()
108 {
109 if (checkerQueue_.size() >= MAX_WATCH_NUM) {
110 XCOLLIE_LOGE("Exceed max watchdog task!");
111 return true;
112 }
113
114 return false;
115 }
116
InsertWatchdogTaskLocked(const std::string & name,WatchdogTask && task)117 bool WatchdogInner::InsertWatchdogTaskLocked(const std::string& name, WatchdogTask&& task)
118 {
119 if (!task.isOneshotTask && IsTaskExistLocked(name)) {
120 XCOLLIE_LOGI("Task with %{public}s already exist, failed to insert.", name.c_str());
121 return false;
122 }
123
124 if (IsExceedMaxTaskLocked()) {
125 XCOLLIE_LOGE("Exceed max watchdog task, failed to insert.");
126 return false;
127 }
128
129 checkerQueue_.push(std::move(task));
130 if (!task.isOneshotTask) {
131 taskNameSet_.insert(name);
132 }
133 CreateWatchdogThreadIfNeed();
134 condition_.notify_all();
135 return true;
136 }
137
StopWatchdog()138 void WatchdogInner::StopWatchdog()
139 {
140 Stop();
141 }
142
CreateWatchdogThreadIfNeed()143 void WatchdogInner::CreateWatchdogThreadIfNeed()
144 {
145 std::call_once(flag_, [this] {
146 if (threadLoop_ == nullptr) {
147 threadLoop_ = std::make_unique<std::thread>(&WatchdogInner::Start, this);
148 XCOLLIE_LOGI("Watchdog is running!");
149 }
150 });
151 }
152
FetchNextTask(uint64_t now,WatchdogTask & task)153 uint64_t WatchdogInner::FetchNextTask(uint64_t now, WatchdogTask& task)
154 {
155 std::unique_lock<std::mutex> lock(lock_);
156 if (isNeedStop_) {
157 while (!checkerQueue_.empty()) {
158 checkerQueue_.pop();
159 }
160 return DEFAULT_TIMEOUT;
161 }
162
163 if (checkerQueue_.empty()) {
164 return DEFAULT_TIMEOUT;
165 }
166
167 const WatchdogTask& queuedTask = checkerQueue_.top();
168 if (queuedTask.nextTickTime > now) {
169 return queuedTask.nextTickTime - now;
170 }
171
172 task = queuedTask;
173 checkerQueue_.pop();
174 return 0;
175 }
176
ReInsertTaskIfNeed(WatchdogTask & task)177 void WatchdogInner::ReInsertTaskIfNeed(WatchdogTask& task)
178 {
179 if (task.checkInterval == 0) {
180 return;
181 }
182
183 std::unique_lock<std::mutex> lock(lock_);
184 task.nextTickTime = task.nextTickTime + task.checkInterval;
185 checkerQueue_.push(task);
186 }
187
Start()188 bool WatchdogInner::Start()
189 {
190 (void)pthread_setname_np(pthread_self(), "DfxWatchdog");
191 XCOLLIE_LOGI("Watchdog is running in thread(%{public}d)!", gettid());
192 while (!isNeedStop_) {
193 uint64_t now = GetCurrentTickMillseconds();
194 WatchdogTask task;
195 uint64_t leftTimeMill = FetchNextTask(now, task);
196 if (leftTimeMill == 0) {
197 task.Run(now);
198 ReInsertTaskIfNeed(task);
199 continue;
200 } else if (isNeedStop_) {
201 break;
202 } else {
203 std::unique_lock<std::mutex> lock(lock_);
204 condition_.wait_for(lock, std::chrono::milliseconds(leftTimeMill));
205 }
206 }
207 return true;
208 }
209
Stop()210 bool WatchdogInner::Stop()
211 {
212 isNeedStop_.store(true);
213 condition_.notify_all();
214 if (threadLoop_ != nullptr && threadLoop_->joinable()) {
215 threadLoop_->join();
216 threadLoop_ = nullptr;
217 }
218 return true;
219 }
220 } // end of namespace HiviewDFX
221 } // end of namespace OHOS
222