• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用HiCollie检测业务线程卡死卡顿问题(C/C++)
2
3## 简介
4
5HiCollie模块对外提供检测业务线程卡死、卡顿,以及上报卡死事件的能力。
6
7## 接口说明
8
9| 接口名                          | 描述                              |
10| ------------------------------- | --------------------------------- |
11| OH_HiCollie_Init_StuckDetection | 注册应用业务线程卡死的周期性检测任务。用户实现回调函数, 用于定时检测业务线程卡死情况。<br/>默认检测时间:3s上报BUSSINESS_THREAD_BLOCK_3S告警事件,6s上报BUSSINESS_THREAD_BLOCK_6S卡死事件。|
12| OH_HiCollie_Init_StuckDetectionWithTimeout | 注册应用业务线程卡死的周期性检测任务。用户实现回调函数, 用于定时检测业务线程卡死情况。<br/>开发者可以设置卡死检测时间,可设置的时间范围:[3, 15],单位:秒。|
13| OH_HiCollie_Init_JankDetection | 注册应用业务线程卡顿检测的回调函数。<br/>线程卡顿监控功能需要开发者实现两个卡顿检测回调函数,分别放在业务线程处理事件的前后。作为插桩函数,监控业务线程处理事件执行情况。 |
14| OH_HiCollie_Report | 上报应用业务线程卡死事件,生成卡死故障日志,辅助定位应用卡死问题。<br/>先调用OH_HiCollie_Init_StuckDetection或OH_HiCollie_Init_StuckDetectionWithTimeout接口,初始化检测的task;<br/>如果task任务超时,结合业务逻辑,调用OH_HiCollie_Report接口上报卡死事件。|
15
16API接口的具体使用说明(参数使用限制、具体取值范围等)请参考[HiCollie](../reference/apis-performance-analysis-kit/_hi_collie.md)。
17
18### 检测原理
19
201. 业务线程卡顿OH_HiCollie_Init_StuckDetection故障规格,请参考[主线程超时事件默认时间规格](./hiappevent-watcher-mainthreadjank-events.md#主线程超时事件默认时间规格)。
21
222. 业务线程卡死故障:
23
24- OH_HiCollie_Init_StuckDetection检测原理:应用的watchdog线程会周期性进行业务线程判活检测。当判活检测超过3s没有被执行,上报BUSSINESS_THREAD_BLOCK_3S线程告警事件;超过6s依然没有被执行,会上报BUSSINESS_THREAD_BLOCK_6S线程卡死事件。两个事件匹配生成appfreeze故障日志。
25
26- OH_HiCollie_Init_StuckDetectionWithTimeout检测原理:应用的watchdog线程会周期性进行业务线程判活检测。当判活检测超过stuckTimeout时间没有被执行,上报BUSSINESS_THREAD_BLOCK_3S告警事件;超过stuckTimeout * 2时间,依然没有被执行,会上报BUSSINESS_THREAD_BLOCK_6S线程卡死事件。两个事件匹配生成appfreeze故障日志。
27
28### 日志规格
29
301. 业务线程卡死故障日志以appfreeze-开头,生成在“设备/data/log/faultlog/faultlogger/”路径下。该日志文件名格式为“appfreeze-应用包名-应用UID-秒级时间”。具体规格可参考:[appfreeze-应用无响应日志分析](./appfreeze-guidelines.md#应用无响应日志分析)。
31
322. OH_HiCollie_Init_StuckDetection日志规格,请参考[主线程超时事件日志规格](./hiappevent-watcher-mainthreadjank-events.md#主线程超时事件日志规格)。
33
34## 开发步骤
35
36下文将展示如何在应用内增加一个按钮,并单击该按钮以调用HiCollie Ndk接口。
37
381. 新建Native C++工程,目录结构如下:
39
40   ```yml
41   entry:
42     src:
43       main:
44         cpp:
45           - types:
46               libentry:
47                 - index.d.ts
48           - CMakeLists.txt
49           - napi_init.cpp
50         ets:
51           - entryability:
52               - EntryAbility.ts
53           - pages:
54               - Index.ets
55   ```
56
572. 编辑"CMakeLists.txt"文件,添加源文件及动态库:
58
59   ```cmake
60   # 新增动态库依赖libhilog_ndk.z.so(日志输出)
61   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhicollie.so)
62   ```
63
643. 编辑"napi_init.cpp"文件,导入依赖的文件,定义LOG_TAG,下述代码步骤用于模拟卡死卡顿场景,具体使用请结合业务需要。示例代码如下:
65
66  (1)**应用线程卡顿检测:** OH_HiCollie_Init_JankDetection,示例代码如下:
67
68  ```c++
69  #include <thread>
70  #include <string>
71  #include <unistd.h>
72  #include <atomic>
73  #include "napi/native_api.h"
74  #include "hilog/log.h"
75  #include "hicollie/hicollie.h"
76
77  #undef LOG_TAG
78  #define LOG_TAG "JankTest"
79
80  //定义两个回调函数对象
81  static OH_HiCollie_BeginFunc beginFunc_;
82  static OH_HiCollie_EndFunc endFunc_;
83
84  //定义监控应用显示开始、结束的回调函数
85  void InitBeginFunc(const char* eventName)
86  {
87      std::string str(eventName);
88      OH_LOG_INFO(LogType::LOG_APP, "InitBeginFunc eventName: %{public}s", str.c_str());
89  }
90  void InitEndFunc(const char* eventName)
91  {
92      std::string str(eventName);
93      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_EndFunc eventName: %{public}s", str.c_str());
94  }
95
96  void StartDelayTimer()
97  {
98    //等待10s
99    std::chrono::seconds delay(10);
100    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay before");
101    std::this_thread::sleep_for(delay);
102    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay after");
103  }
104
105  //定义子线程回调函数
106  void TestJankDetection()
107  {
108      // 初始化回调函数参数
109      beginFunc_ = InitBeginFunc;
110      endFunc_ = InitEndFunc;
111      HiCollie_DetectionParam param {0};
112      // 初始化线程卡顿监控函数
113      int initResult = OH_HiCollie_Init_JankDetection(&beginFunc_, &endFunc_, param);
114      // 线程启动10s内,不进行检测
115      StartDelayTimer();
116      // 成功结果:0
117      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection: %{public}d", initResult);
118      int count = 0;
119      while (count < 3) {
120          // 设置处理开始回调函数,监控线程任务执行开始时长
121          beginFunc_("TestBegin");
122          // 休眠350ms,模拟任务线程处理事件卡顿场景
123          usleep(350 * 1000);
124          // 设置处理结束回调函数,监控线程任务执行结束时长
125          endFunc_("TestEnd");
126          count++;
127      }
128  }
129
130  static napi_value TestHiCollieJankNdk(napi_env env, napi_callback_info info)
131  {
132      // 创建子线程
133      std::thread threadObj(TestJankDetection);
134      // 执行TestJankDetection任务
135      threadObj.detach();
136      return 0;
137  }
138
139  EXTERN_C_START
140  static napi_value Init(napi_env env, napi_value exports)
141  {
142      napi_property_descriptor desc[] = {
143          { "testHiCollieJankNdk", nullptr, TestHiCollieJankNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
144      };
145      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
146      return exports;
147  }
148  EXTERN_C_END
149
150  static napi_module demoModule = {
151      .nm_version = 1,
152      .nm_flags = 0,
153      .nm_filename = nullptr,
154      .nm_register_func = Init,
155      .nm_modname = "entry",
156      .nm_priv = ((void*)0),
157      .reserved = { 0 },
158  };
159
160  extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
161  {
162      napi_module_register(&demoModule);
163  }
164  ```
165
166  (2)**应用线程卡死检测:** OH_HiCollie_Init_StuckDetection, 示例代码如下:
167
168  ```c++
169  #include "napi/native_api.h"
170  #include "hilog/log.h"
171  #include "hicollie/hicollie.h"
172  #include <thread>
173  #include <string>
174  #include <unistd.h>
175
176  #undef LOG_TAG
177  #define LOG_TAG "StruckTest"
178
179  // 检查当前线程是否正常执行:1-正常,0-卡死
180  const int64_t CHECK_BUSSINESS_THREAD_IS_ALIVE = 1;
181  // 自定义休眠时间,模拟卡死场景
182  const int64_t BLOCK_TIME = 3;
183  // 设置应用线程执行任务情况标志位, true-正常,false-卡死
184  std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true);
185  // 设置上报应用线程卡死事件标志位
186  std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false);
187
188  void ReportEvent() {
189      bool temp = isSixSecondEvent_->load();
190      int reportResult = OH_HiCollie_Report(&temp);
191      // 成功:0
192      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load());
193      isSixSecondEvent_->store(temp);
194  }
195
196  void SetTimeout()
197  {
198    int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
199      system_clock::now().time_since_epoch()).count();
200    sleep(BLOCK_TIME);
201    int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
202      system_clock::now().time_since_epoch()).count();
203    if (currentTime - now < BLOCK_TIME) {
204      appThreadIsAlive_->store(true);
205      return;
206    }
207    appThreadIsAlive_->store(false);
208  }
209
210  // 开发者可自定义周期性检测任务
211  void Timer()
212  {
213    // 每隔3s检查应用是否正常执行任务
214    if (appThreadIsAlive_->load()) {
215      OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive.");
216      // 更新appThreadIsAlive_,正常执行下次检测时为true
217      appThreadIsAlive_->store(false);
218      // 模拟超时场景
219      SetTimeout();
220      return;
221    }
222    ReportEvent();
223  }
224
225  //定义子线程回调函数
226  void InitStuckDetection()
227  {
228    // 初始化线程卡死监控函数
229    int initResult = OH_HiCollie_Init_StuckDetection(Timer);
230    // 成功结果:0
231    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult);
232  }
233
234  static napi_value TestHiCollieStuckNdk(napi_env env, napi_callback_info info)
235  {
236    // 创建子线程
237    std::thread threadObj(InitStuckDetection);
238    // 执行任务
239    threadObj.join();
240    return 0;
241  }
242
243  EXTERN_C_START
244  static napi_value Init(napi_env env, napi_value exports)
245  {
246      napi_property_descriptor desc[] = {
247          { "testHiCollieStuckNdk", nullptr, TestHiCollieStuckNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
248      };
249      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
250      return exports;
251  }
252  EXTERN_C_END
253
254  static napi_module demoModule = {
255      .nm_version = 1,
256      .nm_flags = 0,
257      .nm_filename = nullptr,
258      .nm_register_func = Init,
259      .nm_modname = "entry",
260      .nm_priv = ((void*)0),
261      .reserved = { 0 },
262  };
263
264  extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
265  {
266      napi_module_register(&demoModule);
267  }
268  ```
269
270  (3)**应用线程卡死检测,自定义检测时间:** OH_HiCollie_Init_StuckDetectionWithTimeout,示例代码如下:
271
272  ```c++
273  #include "napi/native_api.h"
274  #include "hilog/log.h"
275  #include "hicollie/hicollie.h"
276  #include <thread>
277  #include <string>
278  #include <unistd.h>
279
280  #undef LOG_TAG
281  #define LOG_TAG "StruckTest"
282
283  // 检查当前线程是否正常执行, 1-正常, 0-卡死
284  const int64_t CHECK_BUSSINESS_THREAD_IS_ALIVE = 1;
285  // 自定义休眠时间,模拟卡死场景
286  const int64_t BLOCK_TIME = 5;
287  // 设置应用线程执行任务情况标志位, true-正常, false-卡死
288  std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true);
289  // 设置上报应用线程卡死事件标志位
290  std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false);
291
292  void ReportEvent() {
293      bool temp = isSixSecondEvent_->load();
294      int reportResult = OH_HiCollie_Report(&temp);
295      // 成功:0
296      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load());
297      isSixSecondEvent_->store(temp);
298  }
299
300  void SetTimeout()
301  {
302    int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
303      system_clock::now().time_since_epoch()).count();
304    sleep(BLOCK_TIME);
305    int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
306      system_clock::now().time_since_epoch()).count();
307    if (currentTime - now < BLOCK_TIME) {
308      appThreadIsAlive_->store(true);
309      return;
310    }
311    appThreadIsAlive_->store(false);
312  }
313
314  // 开发者可自定义周期性检测任务
315  void Timer()
316  {
317    // 每隔5s检查应用是否正常执行任务
318    if (appThreadIsAlive_->load()) {
319      OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive.");
320      // 更新appThreadIsAlive_,正常执行下次检测时为true
321      appThreadIsAlive_->store(false);
322      // 模拟超时场景
323      SetTimeout();
324      return;
325    }
326    ReportEvent();
327  }
328
329  //定义子线程回调函数
330  void InitStuckDetectionWithTimeout()
331  {
332    // 初始化线程卡死监控函数
333    int initResult = OH_HiCollie_Init_StuckDetectionWithTimeout(Timer, BLOCK_TIME);
334    // 成功结果:0
335    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult);
336  }
337
338  static napi_value TestHiCollieStuckWithTimeoutNdk(napi_env env, napi_callback_info info)
339  {
340    // 创建子线程
341    std::thread threadObj(InitStuckDetectionWithTimeout);
342    // 执行任务
343    threadObj.join();
344    return 0;
345  }
346
347  EXTERN_C_START
348  static napi_value Init(napi_env env, napi_value exports)
349  {
350      napi_property_descriptor desc[] = {
351          { "testHiCollieStuckWithTimeoutNdk", nullptr, TestHiCollieStuckWithTimeoutNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
352      };
353      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
354      return exports;
355  }
356  EXTERN_C_END
357
358  static napi_module demoModule = {
359      .nm_version = 1,
360      .nm_flags = 0,
361      .nm_filename = nullptr,
362      .nm_register_func = Init,
363      .nm_modname = "entry",
364      .nm_priv = ((void*)0),
365      .reserved = { 0 },
366  };
367
368  extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
369  {
370      napi_module_register(&demoModule);
371  }
372  ```
373
3744. 将TestHiCollieNdk注册为ArkTS接口:
375
376  (1)OH_HiCollie_Init_JankDetection示例,编辑"index.d.ts"文件,定义ArkTS接口:
377
378  ```typescript
379  export const testHiCollieJankNdk: () => void;
380  ```
381
382  (2)OH_HiCollie_Init_StuckDetection示例,编辑"index.d.ts"文件,定义ArkTS接口:
383
384  ```typescript
385  export const testHiCollieStuckNdk: () => void;
386  ```
387
388  (3)OH_HiCollie_Init_StuckDetectionWithTimeout示例,编辑"index.d.ts"文件,定义ArkTS接口:
389
390  ```typescript
391  export const testHiCollieStuckWithTimeoutNdk: () => void;
392  ```
393
3945. 编辑"Index.ets"文件:
395
396  ```ts
397  import testNapi from 'libentry.so'
398
399  @Entry
400  @Component
401  struct Index {
402    build() {
403      RelativeContainer() {
404        Column() {
405          //选择对应的功能,在此处添加不同的点击事件
406
407        }.width('100%')
408      }
409      .height('100%')
410      .width('100%')
411    }
412  }
413  ```
414
415  (1)添加点击事件,触发OH_HiCollie_Init_JankDetection方法。
416
417  ```ts
418  Button("testHiCollieJankNdk", { stateEffect:true, type: ButtonType.Capsule})
419    .width('75%')
420    .height(50)
421    .margin(15)
422    .fontSize(20)
423    .fontWeight(FontWeight.Bold)
424    .onClick(testNapi.testHiCollieJankNdk);
425  ```
426
427  (2)添加点击事件,触发OH_HiCollie_Init_StuckDetection方法。
428
429  ```ts
430  Button("testHiCollieStuckNdk", { stateEffect:true, type: ButtonType.Capsule})
431    .width('75%')
432    .height(50)
433    .margin(15)
434    .fontSize(20)
435    .fontWeight(FontWeight.Bold)
436    .onClick(testNapi.testHiCollieStuckNdk);
437  ```
438
439  (3)添加点击事件,触发OH_HiCollie_Init_StuckDetectionWithTimeout方法。
440
441  ```ts
442  Button("testHiCollieStuckWithTimeoutNdk", { stateEffect:true, type: ButtonType.Capsule})
443    .width('75%')
444    .height(50)
445    .margin(15)
446    .fontSize(20)
447    .fontWeight(FontWeight.Bold)
448    .onClick(testNapi.testHiCollieStuckWithTimeoutNdk);
449  ```
450
4516. 点击DevEco Studio界面中的运行按钮,运行应用工程。
452
4537. 在DevEco Studio的底部,切换到“Log”窗口,过滤自定义的LOG_TAG。
454
455  (1)等待10s,再点击"testHiCollieJankNdk"按钮(线程启动10s内,不进行卡顿检测)。
456    此时窗口将显示通过OH_HiCollie_Init_JankDetection接口获取的应用业务线程采样栈的超时信息。可以通过订阅hiappevent获取对应的事件:[订阅主线程超时事件](./hiappevent-watcher-mainthreadjank-events-arkts.md)。
457
458  (2)点击"testHiCollieStuckNdk"按钮。
459    此时窗口将显示通过OH_HiCollie_Init_StuckDetection接口,初始化卡死检测回调函数。可以根据实际业务场景,自行定义卡死检测函数。
460
461  (3)点击"testHiCollieStuckWithTimeoutNdk"按钮。
462    此时窗口将显示通过OH_HiCollie_Init_StuckDetectionWithTimeout接口,初始化卡死检测回调函数。可以根据实际业务场景,自行定义卡死检测函数,及卡死检测时间。
463