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