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