1# Using HiCollie to Detect Service Thread Stuck and Jank Events (C/C++) 2 3## Overview 4 5HiCollie provides APIs for detecting service thread stuck and jank events and reporting stuck events. 6 7## Availability APIs 8 9| API | Description | 10| ------------------------------- | --------------------------------- | 11| OH_HiCollie_Init_StuckDetection | Registers a callback to periodically detect service thread stuck events. | 12| OH_HiCollie_Init_StuckDetectionWithTimeout | Registers a callback to periodically detect service thread stuck events. You can set the detection time.| 13| OH_HiCollie_Init_JankDetection | Registers a callback to detect service thread jank events. To monitor service thread jank events, you can implement two callbacks as instrumentation functions, placing them before and after the service thread event. | 14| OH_HiCollie_Report | Reports service thread stuck events and generates timeout logs to help locate application timeout events. This API is used together with **OH_HiCollie_Init_StuckDetection()**, which initializes the stuck event detection at first, and then **OH_HiCollie_Report()** reports the stuck event when it occurs.| 15 16The usage of HiCollie: 17 18- For details (such as parameter usage and value ranges), see [HiCollie](../reference/apis-performance-analysis-kit/_hi_collie.md). 19- The service thread stuck faultlog starts with **appfreeze-** and is generated in **Device/data/log/faultlog/faultlogger/**. The log files are named in the format of **appfreeze-application bundle name-application UID-time (seconds)**. For details, see [appfreeze Log Analysis](./appfreeze-guidelines.md#appfreeze-log-analysis). 20- For details about the specifications of service thread jank event logs, see [Main Thread Jank Event Specifications](./hiappevent-watcher-mainthreadjank-events.md#main-thread-jank-event-specifications). 21 22## How to Develop 23 24The following describes how to add a button in the application and click the button to call the HiCollie APIs. 25 261. Create a native C++ project. The directory structure is as follows: 27 28 ```yml 29 entry: 30 src: 31 main: 32 cpp: 33 - types: 34 libentry: 35 - index.d.ts 36 - CMakeLists.txt 37 - napi_init.cpp 38 ets: 39 - entryability: 40 - EntryAbility.ts 41 - pages: 42 - Index.ets 43 ``` 44 452. In the **CMakeLists.txt** file, add the source file and dynamic libraries. 46 47 ```cmake 48 # Add libhilog_ndk.z.so (log output). 49 target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhicollie.so) 50 ``` 51 523. Import the dependencies to the **napi_init.cpp** file, and define **LOG_TAG**. The following code steps are used to simulate the jank and stuck scenarios. Use the code based on service requirements. The sample code is as follows: 53 54 (1) **OH_HiCollie_Init_JankDetection**: 55 56 ```c++ 57 #include <thread> 58 #include <string> 59 #include <unistd.h> 60 #include <atomic> 61 #include "napi/native_api.h" 62 #include "hilog/log.h" 63 #include "hicollie/hicollie.h" 64 65 #undef LOG_TAG 66 #define LOG_TAG "JankTest" 67 68 //Define two callback objects. 69 static OH_HiCollie_BeginFunc beginFunc_; 70 static OH_HiCollie_EndFunc endFunc_; 71 72 //Define the callbacks for monitoring application display start and end. 73 void InitBeginFunc(const char* eventName) 74 { 75 std::string str(eventName); 76 OH_LOG_INFO(LogType::LOG_APP, "InitBeginFunc eventName: %{public}s", str.c_str()); 77 } 78 void InitEndFunc(const char* eventName) 79 { 80 std::string str(eventName); 81 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_EndFunc eventName: %{public}s", str.c_str()); 82 } 83 84 void StartDelayTimer() 85 { 86 //Wait for 10s. 87 std::chrono::seconds delay(10); 88 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay before"); 89 std::this_thread::sleep_for(delay); 90 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay after"); 91 } 92 93 // Define the callback of the subthread. 94 void TestJankDetection() 95 { 96 // Initialize the callback parameters. 97 beginFunc_ = InitBeginFunc; 98 endFunc_ = InitEndFunc; 99 HiCollie_DetectionParam param {0}; 100 // Initialize the thread jank detection function. 101 int initResult = OH_HiCollie_Init_JankDetection(&beginFunc_, &endFunc_, param); 102 // Set the thread to be not detected within 10s after being started. 103 StartDelayTimer(); 104 // Success result: 0 105 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection: %{public}d", initResult); 106 int count = 0; 107 while (count < 3) { 108 // Set the callback used to record the start time of the processing event. 109 beginFunc_("TestBegin"); 110 // Simulate a thread jank event by putting the thread to sleep for 350 ms. 111 usleep(350 * 1000); 112 // Set the callback used to record the end time of the processing event. 113 endFunc_("TestEnd"); 114 count++; 115 } 116 } 117 118 static napi_value TestHiCollieJankNdk(napi_env env, napi_callback_info info) 119 { 120 // Create a subthread. 121 std::thread threadObj(TestJankDetection); 122 // Execute the TestJankDetection task. 123 threadObj.detach(); 124 return 0; 125 } 126 127 EXTERN_C_START 128 static napi_value Init(napi_env env, napi_value exports) 129 { 130 napi_property_descriptor desc[] = { 131 { "testHiCollieJankNdk", nullptr, TestHiCollieJankNdk, nullptr, nullptr, nullptr, napi_default, nullptr }, 132 }; 133 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 134 return exports; 135 } 136 EXTERN_C_END 137 138 static napi_module demoModule = { 139 .nm_version = 1, 140 .nm_flags = 0, 141 .nm_filename = nullptr, 142 .nm_register_func = Init, 143 .nm_modname = "entry", 144 .nm_priv = ((void*)0), 145 .reserved = { 0 }, 146 }; 147 148 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 149 { 150 napi_module_register(&demoModule); 151 } 152 ``` 153 154 (2) **OH_HiCollie_Init_StuckDetection**: 155 156 ```c++ 157 #include "napi/native_api.h" 158 #include "hilog/log.h" 159 #include "hicollie/hicollie.h" 160 #include <thread> 161 #include <string> 162 #include <unistd.h> 163 164 #undef LOG_TAG 165 #define LOG_TAG "StruckTest" 166 167 // Check whether the thread is running properly. The value 1 indicates that the thread is running properly, and the value 0 indicates that the thread is stuck. 168 const int64_t CHECK_BUSSINESS_THREAD_IS_ALIVE = 1; 169 // Simulate a thread stuck event by putting the thread to sleep for a custom time. 170 const int64_t BLOCK_TIME = 3; 171 // Set the task execution status flag of the application thread. The value true indicates the thread is normal and the value false indicates the thread is stuck. 172 std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true); 173 // Set the flag for reporting the application thread stuck event. 174 std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false); 175 176 void ReportEvent() { 177 bool temp = isSixSecondEvent_->load(); 178 int reportResult = OH_HiCollie_Report(&temp); 179 // Success result: 0 180 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load()); 181 isSixSecondEvent_->store(temp); 182 } 183 184 void SetTimeout() 185 { 186 int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono:: 187 system_clock::now().time_since_epoch()).count(); 188 sleep(BLOCK_TIME); 189 int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono:: 190 system_clock::now().time_since_epoch()).count(); 191 if (currentTime - now < BLOCK_TIME) { 192 appThreadIsAlive_->store(true); 193 return; 194 } 195 appThreadIsAlive_->store(false); 196 } 197 198 // You can customize periodic detection tasks. 199 void Timer() 200 { 201 // Check whether the application thread runs properly every 3 seconds. 202 if (appThreadIsAlive_->load()) { 203 OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive."); 204 // Update appThreadIsAlive_. The value true indicates that the application thread runs properly. 205 appThreadIsAlive_->store(false); 206 // Simulate a timeout scenario. 207 SetTimeout(); 208 return; 209 } 210 ReportEvent(); 211 } 212 213 // Define the callback of the subthread. 214 void InitStuckDetection() 215 { 216 // Initialize the thread stuck detection function. 217 int initResult = OH_HiCollie_Init_StuckDetection(Timer); 218 // Success result: 0 219 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult); 220 } 221 222 static napi_value TestHiCollieStuckNdk(napi_env env, napi_callback_info info) 223 { 224 // Create a subthread. 225 std::thread threadObj(InitStuckDetection); 226 // Execute a task. 227 threadObj.join(); 228 return 0; 229 } 230 231 EXTERN_C_START 232 static napi_value Init(napi_env env, napi_value exports) 233 { 234 napi_property_descriptor desc[] = { 235 { "testHiCollieStuckNdk", nullptr, TestHiCollieStuckNdk, nullptr, nullptr, nullptr, napi_default, nullptr }, 236 }; 237 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 238 return exports; 239 } 240 EXTERN_C_END 241 242 static napi_module demoModule = { 243 .nm_version = 1, 244 .nm_flags = 0, 245 .nm_filename = nullptr, 246 .nm_register_func = Init, 247 .nm_modname = "entry", 248 .nm_priv = ((void*)0), 249 .reserved = { 0 }, 250 }; 251 252 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 253 { 254 napi_module_register(&demoModule); 255 } 256 ``` 257 258 (3) **OH_HiCollie_Init_StuckDetectionWithTimeout**: 259 260 ```c++ 261 #include "napi/native_api.h" 262 #include "hilog/log.h" 263 #include "hicollie/hicollie.h" 264 #include <thread> 265 #include <string> 266 #include <unistd.h> 267 268 #undef LOG_TAG 269 #define LOG_TAG "StruckTest" 270 271 // Check whether the thread is running properly. The value 1 indicates that the thread is running properly, and the value 0 indicates that the thread is stuck. 272 const int64_t CHECK_BUSSINESS_THREAD_IS_ALIVE = 1; 273 // Simulate a thread stuck event by putting the thread to sleep for a custom time. 274 const int64_t BLOCK_TIME = 5; 275 // Set the task execution status flag of the application thread. The value true indicates the thread is normal and the value false indicates the thread is stuck. 276 std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true); 277 // Set the flag for reporting the application thread stuck event. 278 std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false); 279 280 void ReportEvent() { 281 bool temp = isSixSecondEvent_->load(); 282 int reportResult = OH_HiCollie_Report(&temp); 283 // Success result: 0 284 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load()); 285 isSixSecondEvent_->store(temp); 286 } 287 288 void SetTimeout() 289 { 290 int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono:: 291 system_clock::now().time_since_epoch()).count(); 292 sleep(BLOCK_TIME); 293 int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono:: 294 system_clock::now().time_since_epoch()).count(); 295 if (currentTime - now < BLOCK_TIME) { 296 appThreadIsAlive_->store(true); 297 return; 298 } 299 appThreadIsAlive_->store(false); 300 } 301 302 // You can customize periodic detection tasks. 303 void Timer() 304 { 305 // Check whether the application thread runs properly every 5 seconds. 306 if (appThreadIsAlive_->load()) { 307 OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive."); 308 // Update appThreadIsAlive_. The value true indicates that the application thread runs properly. 309 appThreadIsAlive_->store(false); 310 // Simulate a timeout scenario. 311 SetTimeout(); 312 return; 313 } 314 ReportEvent(); 315 } 316 317 // Define the callback of the subthread. 318 void InitStuckDetectionWithTimeout() 319 { 320 // Initialize the thread stuck detection function. 321 int initResult = OH_HiCollie_Init_StuckDetectionWithTimeout(Timer, BLOCK_TIME); 322 // Success result: 0 323 OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult); 324 } 325 326 static napi_value TestHiCollieStuckWithTimeoutNdk(napi_env env, napi_callback_info info) 327 { 328 // Create a subthread. 329 std::thread threadObj(InitStuckDetectionWithTimeout); 330 // Execute a task. 331 threadObj.join(); 332 return 0; 333 } 334 335 EXTERN_C_START 336 static napi_value Init(napi_env env, napi_value exports) 337 { 338 napi_property_descriptor desc[] = { 339 { "testHiCollieStuckWithTimeoutNdk", nullptr, TestHiCollieStuckWithTimeoutNdk, nullptr, nullptr, nullptr, napi_default, nullptr }, 340 }; 341 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 342 return exports; 343 } 344 EXTERN_C_END 345 346 static napi_module demoModule = { 347 .nm_version = 1, 348 .nm_flags = 0, 349 .nm_filename = nullptr, 350 .nm_register_func = Init, 351 .nm_modname = "entry", 352 .nm_priv = ((void*)0), 353 .reserved = { 0 }, 354 }; 355 356 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 357 { 358 napi_module_register(&demoModule); 359 } 360 ``` 361 3624. Register **TestHiCollieNdk** as an ArkTS API. 363 364 (1) **OH_HiCollie_Init_JankDetection**: 365 366 ```typescript 367 export const testHiCollieJankNdk: () => void; 368 ``` 369 370 (2) **OH_HiCollie_Init_StuckDetection**: 371 372 ```typescript 373 export const testHiCollieStuckNdk: () => void; 374 ``` 375 376 (3) **OH_HiCollie_Init_StuckDetectionWithTimeout**: 377 378 ```typescript 379 export const testHiCollieStuckWithTimeoutNdk: () => void; 380 ``` 381 3825. Edit the **Index.ets** file. 383 384 ```ts 385 import testNapi from 'libentry.so' 386 387 @Entry 388 @Component 389 struct Index { 390 build() { 391 RelativeContainer() { 392 Column() { 393 //Add the click event corresponding to the detection. 394 395 }.width('100%') 396 } 397 .height('100%') 398 .width('100%') 399 } 400 } 401 ``` 402 403 (1) Add a click event to trigger **OH_HiCollie_Init_JankDetection()**. 404 405 ```ts 406 Button("testHiCollieJankNdk", { stateEffect:true, type: ButtonType.Capsule}) 407 .width('75%') 408 .height(50) 409 .margin(15) 410 .fontSize(20) 411 .fontWeight(FontWeight.Bold) 412 .onClick(testNapi.testHiCollieJankNdk); 413 ``` 414 415 (2) Add a click event to trigger **OH_HiCollie_Init_StuckDetection()**. 416 417 ```ts 418 Button("testHiCollieStuckNdk", { stateEffect:true, type: ButtonType.Capsule}) 419 .width('75%') 420 .height(50) 421 .margin(15) 422 .fontSize(20) 423 .fontWeight(FontWeight.Bold) 424 .onClick(testNapi.testHiCollieStuckNdk); 425 ``` 426 427 (3) Add a click event to trigger **OH_HiCollie_Init_StuckDetectionWithTimeout()**. 428 429 ```ts 430 Button("testHiCollieStuckWithTimeoutNdk", { stateEffect:true, type: ButtonType.Capsule}) 431 .width('75%') 432 .height(50) 433 .margin(15) 434 .fontSize(20) 435 .fontWeight(FontWeight.Bold) 436 .onClick(testNapi.testHiCollieStuckWithTimeoutNdk); 437 ``` 438 4396. Click the **Run** button in DevEco Studio to run the project. 440 4417. At the bottom of DevEco Studio, switch to the **Log** tab and filter the custom **LOG_TAG**. 442 443 (1) Wait for 10s and click the **testHiCollieJankNdk** button. (The jank event detection is not performed within 10s after the thread starts.) 444 The thread timeout information of the sampling stack obtained through **OH_HiCollie_Init_JankDetection()** is displayed. You can obtain the corresponding event by [subscribing to the main thread timeout event](./hiappevent-watcher-mainthreadjank-events-arkts.md). 445 446 (2) Click the **testHiCollieStuckNdk** button. 447 The callback used for detecting stuck events is initialized through **OH_HiCollie_Init_StuckDetection()**. You can define the detection function for stuck events as required. 448 449 (3) Click the **testHiCollieStuckWithTimeoutNdk** button. 450 The callback used for detecting stuck events is initialized through **OH_HiCollie_Init_StuckDetectionWithTimeout**. You can define the detection function and time for stuck events as required. 451