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