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.txt。 213 214 (2)点击"testHiCollieStuckNdk"按钮。 215 此时窗口将显示通过OH_HiCollie_Init_StuckDetection接口,初始化卡死检测回调函数。可以根据实际业务场景,自行定义卡死检测函数。 216