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