• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用HiCollie检测业务线程卡死卡顿问题(C/C++)
2
3HiCollie模块对外提供检测业务线程卡死、卡顿,以及上报卡死事件的能力。
4
5## 接口说明
6
7| 接口名                          | 描述                              |
8| ------------------------------- | --------------------------------- |
9| OH_HiCollie_Init_StuckDetection | 注册应用业务线程卡死的周期性检测任务。用户实现回调函数,用于定时检测业务线程卡死情况。          |
10| OH_HiCollie_Init_JankDetection | 注册应用业务线程卡顿检测的回调函数。线程卡顿监控功能需要开发者实现两个卡顿检测回调函数,分别放在业务线程处理事件的前后。作为插桩函数,监控业务线程处理事件执行情况。                 |
11| OH_HiCollie_Report | 用于上报应用业务线程卡死事件,生成超时故障日志,辅助定位应用超时问题。结合OH_HiCollie_Init_StuckDetection接口配套使用,先初始化卡死检测,出现卡死时,再上报事件。 |
12
13> **说明:**
14>
15> 业务线程卡死故障日志是以appfreeze-开头,生成在”设备/data/log/faultlog/faultlogger/”路径下。该日志文件名格式为“appfreeze-应用包名-应用UID-秒级时间”。具体规格可参考:[appfreeze-应用无响应日志分析](./appfreeze-guidelines.md#应用无响应日志分析)。
16>
17> 业务线程卡顿故障日志规格,可参考:[MAIN_THREAD_JANK-主线程超时事件规格](./hiappevent-watcher-mainthreadjank-events.md#主线程超时事件规格)。
18
19API接口的具体使用说明(参数使用限制、具体取值范围等)请参考[HiCollie](../reference/apis-performance-analysis-kit/_hi_collie.md)。
20
21## 开发步骤
22
23下文将展示如何在应用内增加一个按钮,并单击该按钮以调用HiCollie Ndk接口。
24
251. 新建Native C++工程,并将jsoncpp导入到新建工程内,目录结构如下:
26
27   ```yml
28   entry:
29     src:
30       main:
31         cpp:
32           - types:
33               libentry:
34                 - index.d.ts
35           - CMakeLists.txt
36           - napi_init.cpp
37         ets:
38           - entryability:
39               - EntryAbility.ts
40           - pages:
41               - Index.ets
42   ```
43
442. 编辑"CMakeLists.txt"文件,添加源文件及动态库:
45
46   ```cmake
47   # 新增动态库依赖libhilog_ndk.z.so(日志输出)
48   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhicollie.so)
49   ```
50
513. 编辑"napi_init.cpp"文件,导入依赖的文件,并定义LOG_TAG及测试方法:
52
53   ```c++
54    #include "napi/native_api.h"
55    #include "hilog/log.h"
56    #include "hicollie/hicollie.h"
57    #include <thread>
58    #include <string>
59    #include <unistd.h>
60    #include <atomic>
61
62    #undef LOG_TAG
63    #define LOG_TAG "testTag"
64
65    static OH_HiCollie_BeginFunc beginFunc_; //定义回调函数对象
66    static OH_HiCollie_EndFunc endFunc_; //定义回调函数对象
67    HiCollie_DetectionParam param {.sampleStackTriggerTime = 150,.reserved = 0}; //定义结构体
68    int64_t lastWatchTime = 0; // 记录上次卡死检测时间
69    const int64_t CHECK_INTERNAL_TIME = 3000; // 设置卡死检测间隔
70    std::shared_ptr<std::atomic<bool>> isReport = std::make_shared<std::atomic<bool>>(false); // 设置上报卡死事件标志位
71    int count = 0; // 记录第一次初始化
72    bool needReport = false; // 根据实际场景,设置是否上报标志
73
74    //定义回调函数
75    void InitBeginFunc(const char* eventName)
76    {
77        std::string str(eventName);
78        OH_LOG_INFO(LogType::LOG_APP, "InitBeginFunc eventName: %{public}s", str.c_str());
79    }
80    void InitEndFunc(const char* eventName)
81    {
82        std::string str(eventName);
83        OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_EndFunc eventName: %{public}s", str.c_str());
84    }
85    //定义子线程回调函数
86    void TestJankDetection()
87    {
88        beginFunc_ = InitBeginFunc; // 初始化回调函数
89        endFunc_ = InitEndFunc;
90        int initResult = OH_HiCollie_Init_JankDetection(&beginFunc_, &endFunc_, param); // 初始化线程卡顿监控函数
91        OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection: %{public}d", initResult); // 成功结果:0
92        int count = 0;
93        while (count < 2) {
94            beginFunc_("TestBegin"); // 设置处理开始回调函数,监控线程任务执行开始时长
95            usleep(350 * 1000); // 睡眠350ms,模拟任务线程处理事件卡顿场景
96            endFunc_("TestEnd"); // 设置处理结束回调函数,监控线程任务执行结束时长
97            count++;
98        }
99    }
100
101    static napi_value TestHiCollieJankNdk(napi_env env, napi_callback_info info)
102    {
103        std::thread threadObj(TestJankDetection); // 创建子线程
104        threadObj.join(); // 执行回调函数
105        return 0;
106    }
107
108    int64_t GetCurrentTime()
109    {
110      return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
111          system_clock::now().time_since_epoch()).count();
112    }
113
114    bool ReportEvent()
115    {
116      if ((GetCurrentTime() - lastWatchTime) > CHECK_INTERNAL_TIME) {
117          return true;
118      }
119      return false;
120    }
121
122    void TestTask()
123    {
124        if (needReport && ReportEvent()) {
125          bool temp = isReport->load();
126          int reportResult = OH_HiCollie_Report(&temp);
127          OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d", reportResult); // 成功结果:0
128          OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report isReport: %{public}d", temp);
129          needReport = false;
130        }
131        int64_t now = GetCurrentTime();
132        if ((now - lastWatchTime) >= (CHECK_INTERNAL_TIME / 2)) {
133            lastWatchTime = now;
134        }
135    }
136
137    //定义子线程回调函数
138    void TestStuckDetection()
139    {
140        int initResult = -1;
141        if(count == 0) {
142          initResult = OH_HiCollie_Init_StuckDetection(TestTask); // 初始化线程卡死监控函数
143          OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult); // 成功结果:0
144          count++;
145        }
146    }
147    static napi_value TestHiCollieStuckNdk(napi_env env, napi_callback_info info)
148    {
149      std::thread threadObj(TestStuckDetection); // 创建子线程
150      threadObj.join(); // 执行回调函数
151      return 0;
152    }
153   ```
154
1554. 将TestHiCollieNdk注册为ArkTS接口:
156
157   编辑"napi_init.cpp"文件,将TestHiCollieNdk注册为ArkTS接口:
158
159   ```c++
160    static napi_value Init(napi_env env, napi_value exports)
161    {
162        napi_property_descriptor desc[] = {
163            { "testHiCollieJankNdk", nullptr, TestHiCollieJankNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
164            { "testHiCollieStuckNdk", nullptr, TestHiCollieStuckNdk, nullptr, nullptr, nullptr, napi_default, nullptr }};
165        };
166        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
167        return exports;
168    }
169   ```
170
171   编辑"index.d.ts"文件,定义ArkTS接口:
172
173   ```typescript
174   export const testHiCollieJankNdk: () => void;
175   export const testHiCollieStuckNdk: () => void;
176   ```
177
1785. 编辑"Index.ets"文件:
179
180   ```ts
181   import testNapi from 'libentry.so'
182
183   @Entry
184   @Component
185   struct Index {
186     @State message: string = 'Hello World'
187
188     build() {
189       Row() {
190         Column() {
191           Button("testHiCollieJankNdk")
192             .fontSize(50)
193             .fontWeight(FontWeight.Bold)
194             .onClick(testNapi.testHiCollieJankNdk);//添加点击事件,触发testHiCollieJankNdk方法。
195           Button("testHiCollieStuckNdk")
196             .fontSize(50)
197             .fontWeight(FontWeight.Bold)
198             .onClick(testNapi.testHiCollieStuckNdk);//添加点击事件,触发testHiCollieStuckNdk方法。
199         }
200         .width('100%')
201       }
202       .height('100%')
203     }
204   }
205   ```
206
2076. 点击DevEco Studio界面中的运行按钮,运行应用工程。
208
2097. 在DevEco Studio的底部,切换到“Log”窗口,设置日志的过滤条件为“testTag”。
210
211    (1)等待10s,再点击"testHiCollieJankNdk"按钮(线程启动10s内,不进行卡顿检测)。
212      此时窗口将显示通过OH_HiCollie_Init_JankDetection接口获取的应用业务线程采样栈的超时信息。
213      生成栈文件目录地址:/data/app/el2/100/log/应用bundle name/watchdog/BUSSINESS_THREAD_JANK_XXX.txt214
215    (2)点击"testHiCollieStuckNdk"按钮。
216      此时窗口将显示通过OH_HiCollie_Init_StuckDetection接口,初始化卡死检测回调函数。可以根据实际业务场景,自行定义卡死检测函数。
217