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