1 /*
2 * Copyright (c) 2024 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 "hicollie.h"
17
18 #include <atomic>
19 #include <unistd.h>
20 #include <string>
21 #include <sys/syscall.h>
22 #include "watchdog.h"
23 #include "report_data.h"
24 #include "xcollie.h"
25 #include "xcollie_define.h"
26 #include "xcollie_utils.h"
27 #include "iservice_registry.h"
28 #include "iremote_object.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace {
33 DECLARE_INTERFACE_DESCRIPTOR(u"ohos.appexecfwk.AppMgr");
34 #ifdef SUPPORT_ASAN
35 constexpr uint32_t CHECK_INTERVAL_TIME = 45000;
36 #else
37 constexpr uint32_t CHECK_INTERVAL_TIME = 3000;
38 constexpr uint32_t TIME_S_TO_MS = 1000;
39 constexpr uint32_t MAX_TIMEOUT = 15000;
40 #endif
41 constexpr uint32_t INI_TIMER_FIRST_SECOND = 10000;
42 constexpr uint32_t NOTIFY_APP_FAULT = 38;
43 constexpr uint32_t APP_MGR_SERVICE_ID = 501;
44 constexpr uint32_t RATIO = 2;
45 constexpr int32_t BACKGROUND_REPORT_COUNT_MAX = 5;
46 }
47
48 static int32_t g_bussinessTid = 0;
49 static uint32_t g_stuckTimeout = 0;
50 static int64_t g_lastWatchTime = 0;
51 static std::atomic_int g_backgroundReportCount = 0;
52 static int g_pid = getpid();
53
IsAppMainThread()54 bool IsAppMainThread()
55 {
56 static uint64_t uid = getuid();
57 if (g_pid == gettid() && uid >= MIN_APP_UID) {
58 return true;
59 }
60 return false;
61 }
62
NotifyAppFault(const OHOS::HiviewDFX::ReportData & reportData)63 int32_t NotifyAppFault(const OHOS::HiviewDFX::ReportData &reportData)
64 {
65 XCOLLIE_LOGD("called.");
66 auto systemAbilityMgr = OHOS::SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
67 if (systemAbilityMgr == nullptr) {
68 XCOLLIE_LOGE("ReportData failed to get system ability manager.");
69 return -1;
70 }
71 auto appMgrService = systemAbilityMgr->GetSystemAbility(APP_MGR_SERVICE_ID);
72 if (appMgrService == nullptr) {
73 XCOLLIE_LOGE("ReportData failed to find APP_MGR_SERVICE_ID.");
74 return -1;
75 }
76 OHOS::MessageParcel data;
77 OHOS::MessageParcel reply;
78 OHOS::MessageOption option;
79 if (!data.WriteInterfaceToken(GetDescriptor())) {
80 XCOLLIE_LOGE("ReportData failed to WriteInterfaceToken.");
81 return -1;
82 }
83 if (!data.WriteParcelable(&reportData)) {
84 XCOLLIE_LOGE("ReportData write reportData failed.");
85 return -1;
86 }
87 auto ret = appMgrService->SendRequest(NOTIFY_APP_FAULT, data, reply, option);
88 if (ret != OHOS::NO_ERROR) {
89 XCOLLIE_LOGE("ReportData Send request failed with error code: %{public}d", ret);
90 return -1;
91 }
92 return reply.ReadInt32();
93 }
94
CheckInBackGround(bool * isSixSecond)95 bool CheckInBackGround(bool* isSixSecond)
96 {
97 int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
98 system_clock::now().time_since_epoch()).count();
99 if ((now - g_lastWatchTime) > (RATIO * g_stuckTimeout)) {
100 XCOLLIE_LOGI("Update backgroundCount, currentTime: %{public}llu, lastTime: %{public}llu",
101 static_cast<unsigned long long>(now), static_cast<unsigned long long>(g_lastWatchTime));
102 g_backgroundReportCount.store(0);
103 }
104 bool inBackground = !OHOS::HiviewDFX::Watchdog::GetInstance().GetForeground();
105 XCOLLIE_LOGD("In Background, thread is background: %{public}d", inBackground);
106 if (inBackground && g_backgroundReportCount.load() < BACKGROUND_REPORT_COUNT_MAX) {
107 XCOLLIE_LOGI("In Background, not report event: g_backgroundReportCount: %{public}d, "
108 "currentTime: %{public}llu, lastTime: %{public}llu", g_backgroundReportCount.load(),
109 static_cast<unsigned long long>(now), static_cast<unsigned long long>(g_lastWatchTime));
110 g_backgroundReportCount++;
111 g_lastWatchTime = now;
112 return true;
113 }
114 return false;
115 }
116
Report(bool * isSixSecond)117 int Report(bool* isSixSecond)
118 {
119 if (CheckInBackGround(isSixSecond)) {
120 return 0;
121 }
122 g_backgroundReportCount++;
123
124 OHOS::HiviewDFX::ReportData reportData;
125 reportData.faultType = OHOS::HiviewDFX::FaultDataType::APP_FREEZE;
126 int stuckTimeout = g_stuckTimeout;
127 if (*isSixSecond) {
128 reportData.errorObject.name = "BUSSINESS_THREAD_BLOCK_6S";
129 reportData.forceExit = true;
130 *isSixSecond = false;
131 stuckTimeout = g_stuckTimeout * RATIO;
132 std::ifstream statmStream("/proc/" + std::to_string(g_pid) + "/statm");
133 if (statmStream) {
134 std::string procStatm;
135 std::getline(statmStream, procStatm);
136 statmStream.close();
137 reportData.procStatm = procStatm;
138 }
139 } else {
140 reportData.errorObject.name = "BUSSINESS_THREAD_BLOCK_3S";
141 reportData.forceExit = false;
142 *isSixSecond = true;
143 }
144 std::string timeStamp = "\nFaultTime:" + FormatTime("%Y-%m-%d %H:%M:%S") + "\n";
145 reportData.errorObject.message = timeStamp +
146 "Bussiness thread is not response, timeout " + std::to_string(stuckTimeout) + "ms.\n";
147 reportData.timeoutMarkers = "";
148 reportData.errorObject.stack = "";
149 reportData.notifyApp = false;
150 reportData.waitSaveState = false;
151 reportData.tid = g_bussinessTid > 0 ? g_bussinessTid : g_pid;
152 reportData.stuckTimeout = g_stuckTimeout;
153 auto result = NotifyAppFault(reportData);
154 XCOLLIE_LOGI("OH_HiCollie_Report result: %{public}d, current tid: %{public}d, timeout: %{public}u",
155 result, reportData.tid, reportData.stuckTimeout);
156 int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
157 system_clock::now().time_since_epoch()).count();
158 if ((now - g_lastWatchTime) < 0 || (now - g_lastWatchTime) >= (g_stuckTimeout / RATIO)) {
159 XCOLLIE_LOGI("Update backgroundCount, currentTime: %{public}llu, lastTime: %{public}llu",
160 static_cast<unsigned long long>(now), static_cast<unsigned long long>(g_lastWatchTime));
161 g_lastWatchTime = now;
162 }
163 return result;
164 }
165 } // end of namespace HiviewDFX
166 } // end of namespace OHOS
167
InitStuckDetection(OH_HiCollie_Task task,uint32_t timeout)168 inline HiCollie_ErrorCode InitStuckDetection(OH_HiCollie_Task task, uint32_t timeout)
169 {
170 if (OHOS::HiviewDFX::IsAppMainThread()) {
171 return HICOLLIE_WRONG_THREAD_CONTEXT;
172 }
173 if (!task) {
174 OHOS::HiviewDFX::g_stuckTimeout = 0;
175 OHOS::HiviewDFX::g_bussinessTid = 0;
176 OHOS::HiviewDFX::Watchdog::GetInstance().RemovePeriodicalTask("BussinessWatchdog");
177 } else {
178 if (OHOS::HiviewDFX::g_stuckTimeout != 0) {
179 OHOS::HiviewDFX::Watchdog::GetInstance().RemovePeriodicalTask("BussinessWatchdog");
180 }
181 OHOS::HiviewDFX::g_stuckTimeout = timeout;
182 OHOS::HiviewDFX::g_lastWatchTime = 0;
183 OHOS::HiviewDFX::g_backgroundReportCount.store(0);
184 OHOS::HiviewDFX::g_bussinessTid = syscall(SYS_gettid);
185 OHOS::HiviewDFX::Watchdog::GetInstance().RunPeriodicalTask("BussinessWatchdog", task,
186 timeout, OHOS::HiviewDFX::INI_TIMER_FIRST_SECOND);
187 OHOS::HiviewDFX::Watchdog::GetInstance().RemovePeriodicalTask("AppkitWatchdog");
188 }
189 return HICOLLIE_SUCCESS;
190 }
191
OH_HiCollie_Init_StuckDetection(OH_HiCollie_Task task)192 HiCollie_ErrorCode OH_HiCollie_Init_StuckDetection(OH_HiCollie_Task task)
193 {
194 return InitStuckDetection(task, OHOS::HiviewDFX::CHECK_INTERVAL_TIME);
195 }
196
OH_HiCollie_Init_StuckDetectionWithTimeout(OH_HiCollie_Task task,uint32_t timeout)197 HiCollie_ErrorCode OH_HiCollie_Init_StuckDetectionWithTimeout(OH_HiCollie_Task task, uint32_t timeout)
198 {
199 #ifdef SUPPORT_ASAN
200 timeout = OHOS::HiviewDFX::CHECK_INTERVAL_TIME;
201 #else
202 timeout = timeout * OHOS::HiviewDFX::TIME_S_TO_MS;
203 if (timeout < OHOS::HiviewDFX::CHECK_INTERVAL_TIME || timeout > OHOS::HiviewDFX::MAX_TIMEOUT) {
204 return HICOLLIE_INVALID_ARGUMENT;
205 }
206 #endif
207 return InitStuckDetection(task, timeout);
208 }
209
OH_HiCollie_Init_JankDetection(OH_HiCollie_BeginFunc * beginFunc,OH_HiCollie_EndFunc * endFunc,HiCollie_DetectionParam param)210 HiCollie_ErrorCode OH_HiCollie_Init_JankDetection(OH_HiCollie_BeginFunc* beginFunc,
211 OH_HiCollie_EndFunc* endFunc, HiCollie_DetectionParam param)
212 {
213 if (OHOS::HiviewDFX::IsAppMainThread()) {
214 return HICOLLIE_WRONG_THREAD_CONTEXT;
215 }
216 if ((!beginFunc && endFunc) || (beginFunc && !endFunc)) {
217 return HICOLLIE_INVALID_ARGUMENT;
218 }
219 OHOS::HiviewDFX::Watchdog::GetInstance().InitMainLooperWatcher(beginFunc, endFunc);
220 return HICOLLIE_SUCCESS;
221 }
222
OH_HiCollie_Report(bool * isSixSecond)223 HiCollie_ErrorCode OH_HiCollie_Report(bool* isSixSecond)
224 {
225 if (OHOS::HiviewDFX::IsAppMainThread()) {
226 return HICOLLIE_WRONG_THREAD_CONTEXT;
227 }
228 if (isSixSecond == nullptr) {
229 return HICOLLIE_INVALID_ARGUMENT;
230 }
231 if (OHOS::HiviewDFX::Watchdog::GetInstance().GetAppDebug()) {
232 XCOLLIE_LOGD("Bussiness: Get appDebug state is true");
233 return HICOLLIE_SUCCESS;
234 }
235 if (OHOS::HiviewDFX::Report(isSixSecond) != 0) {
236 return HICOLLIE_REMOTE_FAILED;
237 }
238 return HICOLLIE_SUCCESS;
239 }
240
OH_HiCollie_SetTimer(HiCollie_SetTimerParam param,int * id)241 HiCollie_ErrorCode OH_HiCollie_SetTimer(HiCollie_SetTimerParam param, int *id)
242 {
243 if (param.name == nullptr) {
244 XCOLLIE_LOGE("timer name is nullptr");
245 return HICOLLIE_INVALID_TIMER_NAME;
246 }
247 std::string timerName = param.name;
248 if (timerName.empty()) {
249 XCOLLIE_LOGE("timer name is empty");
250 return HICOLLIE_INVALID_TIMER_NAME;
251 }
252 if (param.timeout == 0) {
253 XCOLLIE_LOGE("invalid timeout value");
254 return HICOLLIE_INVALID_TIMEOUT_VALUE;
255 }
256 if (id == nullptr) {
257 XCOLLIE_LOGE("wrong timer id output param");
258 return HICOLLIE_WRONG_TIMER_ID_OUTPUT_PARAM;
259 }
260
261 int timerId = OHOS::HiviewDFX::XCollie::GetInstance().SetTimer(timerName, param.timeout, param.func, param.arg,
262 param.flag);
263 if (timerId == OHOS::HiviewDFX::INVALID_ID) {
264 XCOLLIE_LOGE("wrong process context, process is in appspawn or nativespawn");
265 return HICOLLIE_WRONG_PROCESS_CONTEXT;
266 }
267 if (timerId == 0) {
268 XCOLLIE_LOGE("task quque size exceed max");
269 return HICOLLIE_WRONG_TIMER_ID_OUTPUT_PARAM;
270 }
271
272 *id = timerId;
273 return HICOLLIE_SUCCESS;
274 }
275
OH_HiCollie_CancelTimer(int id)276 void OH_HiCollie_CancelTimer(int id)
277 {
278 if (id <= 0) {
279 XCOLLIE_LOGE("invalid timer id, cancel timer failed");
280 return;
281 }
282 OHOS::HiviewDFX::XCollie::GetInstance().CancelTimer(id);
283 }