• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Using HiCollie to Detect Service Thread Stuck and Jank Events (C/C++)
2<!--Kit: Performance Analysis Kit-->
3<!--Subsystem: HiviewDFX-->
4<!--Owner: @rr_cn-->
5<!--Designer: @peterhuangyu-->
6<!--Tester: @gcw_KuLfPSbe-->
7<!--Adviser: @foryourself-->
8
9## Overview
10
11[Application freeze (AppFreeze)](appfreeze-guidelines.md) means that an application does not respond to user operations (for example, clicking) for a specified period of time. This topic describes the capabilities provided by the HiCollie module for detecting service thread stuck and jank events and reporting stuck events.
12
13## Availability APIs
14
15| API| Description|
16| -------- | -------- |
17| OH_HiCollie_Init_StuckDetection | Registers a callback to periodically detect service thread stuck events.  <br>By default, the **BUSSINESS_THREAD_BLOCK_3S** event is reported when the thread is blocked for 3s and the **BUSSINESS_THREAD_BLOCK_6S** event is reported when the thread is blocked for 6s.|
18| OH_HiCollie_Init_StuckDetectionWithTimeout | Registers a callback to periodically detect service thread stuck events.  <br>You can set the interval for the stuck event detection. The value range is [3, 15], in seconds.<br>Note: This API is supported since API version 18.|
19| OH_HiCollie_Init_JankDetection | Registers a callback to detect service thread jank events.<br>To monitor service thread jank events, you can implement two callbacks as instrumentation functions, placing them before and after the service thread event.  |
20| OH_HiCollie_Report | Reports a service thread stuck event and generates logs to help locate application stuck issues.<br>Call **OH_HiCollie_Init_StuckDetection()** or **OH_HiCollie_Init_StuckDetectionWithTimeout()** to initialize the detection task.<br>If the task times out, call **OH_HiCollie_Report()** to report the stuck event based on the service logic.|
21
22For details about how to use the APIs (such as parameter usage restrictions and value ranges), see [HiCollie](../reference/apis-performance-analysis-kit/capi-hicollie-h.md).
23
24## Detection Principles
25
261. For details about the fault specifications of service thread jank events detected by **OH_HiCollie_Init_JankDetection**, see [Main Thread Jank Event Detection Principles](hiappevent-watcher-mainthreadjank-events.md)
27
282. Service thread stuck events:
29   (1) Principles of **OH_HiCollie_Init_StuckDetection**: The watchdog thread of the application periodically performs activation detection on service threads. If the activation detection is not executed within 3 seconds, a **BUSSINESS_THREAD_BLOCK_3S** warning event is reported. If the activation detection is not executed within 6 seconds, a **BUSSINESS_THREAD_BLOCK_6S** stuck event is reported. The two events constitute AppFreeze fault logs based on system matching rules.
30
31   (2) Principles of **OH_HiCollie_Init_StuckDetectionWithTimeout**: The watchdog thread of the application periodically performs activation detection on service threads. If the activation detection is not executed within the time specified by **stuckTimeout**, the **BUSSINESS_THREAD_BLOCK_3S** warning event is reported. If the activation detection is not executed within the time specified by **stuckTimeout** × 2, the **BUSSINESS_THREAD_BLOCK_6S** stuck event is reported. The two events constitute AppFreeze fault logs.
32
33## Log Specifications
34
351. The fault log file of the service thread stuck event starts with **appfreeze-** and is generated in **Device/data/log/faultlog/faultlogger/**. The log files are named in the format of **appfreeze-application bundle name-application UID-time (seconds)**. For details, see [AppFreeze Log Specifications](appfreeze-guidelines.md#log-specifications).
36
372. For details about the **OH_HiCollie_Init_StuckDetection** log specifications, see [Main Thread Jank Event Log Specifications](hiappevent-watcher-mainthreadjank-events.md).
38
39## How to Develop
40
41The following describes how to add a button in the application and click the button to call the HiCollie APIs.
42
431. Create a native C++ project. The directory structure is as follows:
44
45   ```yml
46   entry:
47     src:
48       main:
49         cpp:
50           types:
51             libentry:
52               - index.d.ts
53           - CMakeLists.txt
54           - napi_init.cpp
55         ets:
56           entryability:
57             - EntryAbility.ts
58           pages:
59             - Index.ets
60   ```
61
622. In the **CMakeLists.txt** file, add the source file and dynamic libraries.
63
64   ```cmake
65   # Add libhilog_ndk.z.so (log output).
66   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhicollie.so)
67   ```
68
693. Import the dependencies to the **napi_init.cpp** file, and define **LOG_TAG**. The following code steps are used to simulate the jank and stuck scenarios. Use the code based on service requirements. The sample code is as follows:
70
71   (1) **OH_HiCollie_Init_JankDetection**:
72
73   ```c++
74   #include <thread>
75   #include <string>
76   #include <unistd.h>
77   #include <atomic>
78   #include "napi/native_api.h"
79   #include "hilog/log.h"
80   #include "hicollie/hicollie.h"
81
82   #undef LOG_TAG
83   #define LOG_TAG "JankTest"
84
85   //Define two callback objects.
86   static OH_HiCollie_BeginFunc beginFunc_;
87   static OH_HiCollie_EndFunc endFunc_;
88
89   //Define the callbacks for monitoring application display start and end.
90   void InitBeginFunc(const char* eventName)
91   {
92       std::string str(eventName);
93       OH_LOG_INFO(LogType::LOG_APP, "InitBeginFunc eventName: %{public}s", str.c_str());
94   }
95   void InitEndFunc(const char* eventName)
96   {
97       std::string str(eventName);
98       OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_EndFunc eventName: %{public}s", str.c_str());
99   }
100
101   void StartDelayTimer()
102   {
103     // Wait for 1s.
104     std::chrono::seconds delay(1);
105     OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay before");
106     std::this_thread::sleep_for(delay);
107     OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay after");
108   }
109
110   // Define the callback of the subthread.
111   void TestJankDetection()
112   {
113       // Initialize the callback parameters.
114       beginFunc_ = InitBeginFunc;
115       endFunc_ = InitEndFunc;
116       HiCollie_DetectionParam param {0};
117       // Initialize the thread jank detection function.
118       int initResult = OH_HiCollie_Init_JankDetection(&beginFunc_, &endFunc_, param);
119       // Set the thread to be not detected within 1s after being started.
120       StartDelayTimer();
121       // Success result: 0
122       OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection: %{public}d", initResult);
123       int count = 0;
124       while (count < 3) {
125           // Set the callback used to record the start time of the processing event.
126           beginFunc_("TestBegin");
127           // Simulate a thread jank event by putting the thread to sleep for 350 ms.
128           usleep(350 * 1000);
129           // Set the callback used to record the end time of the processing event.
130           endFunc_("TestEnd");
131           count++;
132       }
133   }
134
135   static napi_value TestHiCollieJankNdk(napi_env env, napi_callback_info info)
136   {
137       // Create a subthread.
138       std::thread threadObj(TestJankDetection);
139       // Execute the TestJankDetection task.
140       threadObj.join();
141       return 0;
142   }
143
144   EXTERN_C_START
145   static napi_value Init(napi_env env, napi_value exports)
146   {
147       napi_property_descriptor desc[] = {
148           { "testHiCollieJankNdk", nullptr, TestHiCollieJankNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
149       };
150       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
151       return exports;
152   }
153   EXTERN_C_END
154
155   static napi_module demoModule = {
156       .nm_version = 1,
157       .nm_flags = 0,
158       .nm_filename = nullptr,
159       .nm_register_func = Init,
160       .nm_modname = "entry",
161       .nm_priv = ((void*)0),
162       .reserved = { 0 },
163   };
164
165   extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
166   {
167       napi_module_register(&demoModule);
168   }
169   ```
170
171   (2) **OH_HiCollie_Init_StuckDetection**:
172
173   ```c++
174   #include "napi/native_api.h"
175   #include "hilog/log.h"
176   #include "hicollie/hicollie.h"
177   #include <thread>
178   #include <string>
179   #include <unistd.h>
180
181   #undef LOG_TAG
182   #define LOG_TAG "StruckTest"
183
184   // Simulate a thread stuck event by putting the thread to sleep for a custom time.
185   const int64_t BLOCK_TIME = 3;
186   // Set the task execution status flag of the application thread. The value true indicates the thread is normal and the value false indicates the thread is stuck.
187   std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true);
188   // Set the flag for reporting the application thread stuck event.
189   std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false);
190
191   void ReportEvent() {
192       bool temp = isSixSecondEvent_->load();
193       int reportResult = OH_HiCollie_Report(&temp);
194       // Success result: 0
195       OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load());
196       isSixSecondEvent_->store(temp);
197   }
198
199   void SetTimeout()
200   {
201     int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
202       system_clock::now().time_since_epoch()).count();
203     sleep(BLOCK_TIME);
204     int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
205       system_clock::now().time_since_epoch()).count();
206     if (currentTime - now < BLOCK_TIME) {
207       appThreadIsAlive_->store(true);
208       return;
209     }
210     appThreadIsAlive_->store(false);
211   }
212
213   // You can customize periodic detection tasks.
214   void Timer()
215   {
216     // Check whether the application thread runs properly every 3 seconds.
217     if (appThreadIsAlive_->load()) {
218       OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive.");
219       // Update appThreadIsAlive_. The value true indicates that the application thread runs properly.
220       appThreadIsAlive_->store(false);
221       // Simulate a timeout scenario.
222       SetTimeout();
223       return;
224     }
225     ReportEvent();
226   }
227
228   // Define the callback of the subthread.
229   void InitStuckDetection()
230   {
231     // Initialize the thread stuck detection function.
232     int initResult = OH_HiCollie_Init_StuckDetection(Timer);
233     // Success result: 0
234     OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult);
235   }
236
237   static napi_value TestHiCollieStuckNdk(napi_env env, napi_callback_info info)
238   {
239     // Create a subthread.
240     std::thread threadObj(InitStuckDetection);
241     // Execute a task.
242     threadObj.join();
243     return 0;
244   }
245
246   EXTERN_C_START
247   static napi_value Init(napi_env env, napi_value exports)
248   {
249       napi_property_descriptor desc[] = {
250           { "testHiCollieStuckNdk", nullptr, TestHiCollieStuckNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
251       };
252       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
253       return exports;
254   }
255   EXTERN_C_END
256
257   static napi_module demoModule = {
258       .nm_version = 1,
259       .nm_flags = 0,
260       .nm_filename = nullptr,
261       .nm_register_func = Init,
262       .nm_modname = "entry",
263       .nm_priv = ((void*)0),
264       .reserved = { 0 },
265   };
266
267   extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
268   {
269       napi_module_register(&demoModule);
270   }
271   ```
272
273   (3) **OH_HiCollie_Init_StuckDetectionWithTimeout**:
274
275   ```c++
276   #include "napi/native_api.h"
277   #include "hilog/log.h"
278   #include "hicollie/hicollie.h"
279   #include <thread>
280   #include <string>
281   #include <unistd.h>
282
283   #undef LOG_TAG
284   #define LOG_TAG "StruckTest"
285
286   // Simulate a thread stuck event by putting the thread to sleep for a custom time.
287   const int64_t BLOCK_TIME = 5;
288   // Set the task execution status flag of the application thread. The value true indicates the thread is normal and the value false indicates the thread is stuck.
289   std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true);
290   // Set the flag for reporting the application thread stuck event.
291   std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false);
292
293   void ReportEvent() {
294       bool temp = isSixSecondEvent_->load();
295       int reportResult = OH_HiCollie_Report(&temp);
296       // Success result: 0
297       OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load());
298       isSixSecondEvent_->store(temp);
299   }
300
301   void SetTimeout()
302   {
303     int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
304       system_clock::now().time_since_epoch()).count();
305     sleep(BLOCK_TIME);
306     int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
307       system_clock::now().time_since_epoch()).count();
308     if (currentTime - now < BLOCK_TIME) {
309       appThreadIsAlive_->store(true);
310       return;
311     }
312     appThreadIsAlive_->store(false);
313   }
314
315   // You can customize periodic detection tasks.
316   void Timer()
317   {
318     // Check whether the application thread runs properly every 5 seconds.
319     if (appThreadIsAlive_->load()) {
320       OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive.");
321       // Update appThreadIsAlive_. The value true indicates that the application thread runs properly.
322       appThreadIsAlive_->store(false);
323       // Simulate a timeout scenario.
324       SetTimeout();
325       return;
326     }
327     ReportEvent();
328   }
329
330   // Define the callback of the subthread.
331   void InitStuckDetectionWithTimeout()
332   {
333     // Initialize the thread stuck detection function.
334     int initResult = OH_HiCollie_Init_StuckDetectionWithTimeout(Timer, BLOCK_TIME);
335     // Success result: 0
336     OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult);
337   }
338
339   static napi_value TestHiCollieStuckWithTimeoutNdk(napi_env env, napi_callback_info info)
340   {
341     // Create a subthread.
342     std::thread threadObj(InitStuckDetectionWithTimeout);
343     // Execute a task.
344     threadObj.join();
345     return 0;
346   }
347
348   EXTERN_C_START
349   static napi_value Init(napi_env env, napi_value exports)
350   {
351       napi_property_descriptor desc[] = {
352           { "testHiCollieStuckWithTimeoutNdk", nullptr, TestHiCollieStuckWithTimeoutNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
353       };
354       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
355       return exports;
356   }
357   EXTERN_C_END
358
359   static napi_module demoModule = {
360       .nm_version = 1,
361       .nm_flags = 0,
362       .nm_filename = nullptr,
363       .nm_register_func = Init,
364       .nm_modname = "entry",
365       .nm_priv = ((void*)0),
366       .reserved = { 0 },
367   };
368
369   extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
370   {
371       napi_module_register(&demoModule);
372   }
373   ```
374
3754. In the **index.d.ts** file, register **TestHiCollieNdk** as an ArkTS API.
376   (1) **OH_HiCollie_Init_JankDetection**:
377
378   ```typescript
379   export const testHiCollieJankNdk: () => void;
380   ```
381
382   (2) **OH_HiCollie_Init_StuckDetection**:
383
384   ```typescript
385   export const testHiCollieStuckNdk: () => void;
386   ```
387
388   (3) **OH_HiCollie_Init_StuckDetectionWithTimeout**:
389
390   ```typescript
391   export const testHiCollieStuckWithTimeoutNdk: () => void;
392   ```
393
3945. Edit the **Index.ets** file.
395
396   ```ts
397   import testNapi from 'libentry.so'
398
399   @Entry
400   @Component
401   struct Index {
402     build() {
403       RelativeContainer() {
404         Column() {
405           // Add the click event corresponding to the detection.
406
407         }
408         .width('100%')
409       }
410       .height('100%')
411       .width('100%')
412     }
413   }
414   ```
415
416   (1) Add a click event to trigger **OH_HiCollie_Init_JankDetection()**.
417
418   ```ts
419   Column() {
420     Button("testHiCollieJankNdk", { stateEffect:true, type: ButtonType.Capsule})
421       .width('75%')
422       .height(50)
423       .margin(15)
424       .fontSize(20)
425       .fontWeight(FontWeight.Bold)
426       .onClick(testNapi.testHiCollieJankNdk);
427   }
428   ```
429
430   (2) Add a click event to trigger **OH_HiCollie_Init_StuckDetection()**.
431
432   ```ts
433   Column() {
434     Button("testHiCollieStuckNdk", { stateEffect:true, type: ButtonType.Capsule})
435       .width('75%')
436       .height(50)
437       .margin(15)
438       .fontSize(20)
439       .fontWeight(FontWeight.Bold)
440       .onClick(testNapi.testHiCollieStuckNdk);
441   }
442   ```
443
444   (3) Add a click event to trigger **OH_HiCollie_Init_StuckDetectionWithTimeout()**.
445
446   ```ts
447   Column() {
448     Button("testHiCollieStuckWithTimeoutNdk", { stateEffect:true, type: ButtonType.Capsule})
449       .width('75%')
450       .height(50)
451       .margin(15)
452       .fontSize(20)
453       .fontWeight(FontWeight.Bold)
454       .onClick(testNapi.testHiCollieStuckWithTimeoutNdk);
455   }
456   ```
457
4586. Click the **Run** button in DevEco Studio to run the project.
459
4607. At the bottom of DevEco Studio, switch to the **Log** tab and filter the custom **LOG_TAG**.
461   (1) Click the **testHiCollieJankNdk** button.
462
463   The thread timeout information of the sampling stack obtained through **OH_HiCollie_Init_JankDetection()** is displayed. You can obtain the corresponding event by [subscribing to the main thread jank event](hiappevent-watcher-mainthreadjank-events-arkts.md).
464
465   (2) Click the **testHiCollieStuckNdk** button.
466
467   The callback used for detecting stuck events is initialized through **OH_HiCollie_Init_StuckDetection()**. You can define the detection function for stuck events as required.
468
469   (3) Click the **testHiCollieStuckWithTimeoutNdk** button.
470
471   The callback used for detecting stuck events is initialized through **OH_HiCollie_Init_StuckDetectionWithTimeout**. You can define the detection function and time for stuck events as required.
472