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