harmony 鸿蒙使用HiCollie检测业务线程卡死卡顿问题(C/C++)

  • 2025-06-12
  • 浏览 (6)

使用HiCollie检测业务线程卡死卡顿问题(C/C++)

简介

HiCollie模块对外提供检测业务线程卡死、卡顿,以及上报卡死事件的能力。

接口说明

接口名 描述
OH_HiCollie_Init_StuckDetection 注册应用业务线程卡死的周期性检测任务。用户实现回调函数, 用于定时检测业务线程卡死情况。
默认检测时间:3s上报BUSSINESS_THREAD_BLOCK_3S告警事件,6s上报BUSSINESS_THREAD_BLOCK_6S卡死事件。
OH_HiCollie_Init_StuckDetectionWithTimeout 注册应用业务线程卡死的周期性检测任务。用户实现回调函数, 用于定时检测业务线程卡死情况。
开发者可以设置卡死检测时间,可设置的时间范围:[3, 15],单位:秒。
OH_HiCollie_Init_JankDetection 注册应用业务线程卡顿检测的回调函数。
线程卡顿监控功能需要开发者实现两个卡顿检测回调函数,分别放在业务线程处理事件的前后。作为插桩函数,监控业务线程处理事件执行情况。
OH_HiCollie_Report 上报应用业务线程卡死事件,生成卡死故障日志,辅助定位应用卡死问题。
先调用OH_HiCollie_Init_StuckDetection或OH_HiCollie_Init_StuckDetectionWithTimeout接口,初始化检测的task;
如果task任务超时,结合业务逻辑,调用OH_HiCollie_Report接口上报卡死事件。

API接口的具体使用说明(参数使用限制、具体取值范围等)请参考HiCollie

检测原理

  1. 业务线程卡顿OH_HiCollie_Init_JankDetection故障规格,请参考主线程超时事件默认时间规格

  2. 业务线程卡死故障:

  3. OH_HiCollie_Init_StuckDetection检测原理:应用的watchdog线程会周期性进行业务线程判活检测。当判活检测超过3s没有被执行,上报BUSSINESS_THREAD_BLOCK_3S线程告警事件;超过6s依然没有被执行,会上报BUSSINESS_THREAD_BLOCK_6S线程卡死事件。两个事件匹配生成appfreeze故障日志。

  4. OH_HiCollie_Init_StuckDetectionWithTimeout检测原理:应用的watchdog线程会周期性进行业务线程判活检测。当判活检测超过stuckTimeout时间没有被执行,上报BUSSINESS_THREAD_BLOCK_3S告警事件;超过stuckTimeout * 2时间,依然没有被执行,会上报BUSSINESS_THREAD_BLOCK_6S线程卡死事件。两个事件匹配生成appfreeze故障日志。

日志规格

  1. 业务线程卡死故障日志以appfreeze-开头,生成在“设备/data/log/faultlog/faultlogger/”路径下。该日志文件名格式为“appfreeze-应用包名-应用UID-秒级时间”。具体规格可参考:appfreeze-应用无响应日志分析

  2. OH_HiCollie_Init_StuckDetection日志规格,请参考主线程超时事件日志规格

开发步骤

下文将展示如何在应用内增加一个按钮,并单击该按钮以调用HiCollie Ndk接口。

  1. 新建Native C++工程,目录结构如下:
   entry:
     src:
       main:
         cpp:
           - types:
               libentry:
                 - index.d.ts
           - CMakeLists.txt
           - napi_init.cpp
         ets:
           - entryability:
               - EntryAbility.ts
           - pages:
               - Index.ets
  1. 编辑”CMakeLists.txt”文件,添加源文件及动态库:
   # 新增动态库依赖libhilog_ndk.z.so(日志输出)
   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libohhicollie.so)
  1. 编辑”napi_init.cpp”文件,导入依赖的文件,定义LOG_TAG,下述代码步骤用于模拟卡死卡顿场景,具体使用请结合业务需要。示例代码如下:

(1)应用线程卡顿检测: OH_HiCollie_Init_JankDetection,示例代码如下:

  #include <thread>
  #include <string>
  #include <unistd.h>
  #include <atomic>
  #include "napi/native_api.h"
  #include "hilog/log.h"
  #include "hicollie/hicollie.h"

  #undef LOG_TAG
  #define LOG_TAG "JankTest"

  //定义两个回调函数对象
  static OH_HiCollie_BeginFunc beginFunc_;
  static OH_HiCollie_EndFunc endFunc_;

  //定义监控应用显示开始、结束的回调函数
  void InitBeginFunc(const char* eventName)
  {
      std::string str(eventName);
      OH_LOG_INFO(LogType::LOG_APP, "InitBeginFunc eventName: %{public}s", str.c_str());
  }
  void InitEndFunc(const char* eventName)
  {
      std::string str(eventName);
      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_EndFunc eventName: %{public}s", str.c_str());
  }

  void StartDelayTimer()
  {
    //等待10s
    std::chrono::seconds delay(10);
    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay before");
    std::this_thread::sleep_for(delay);
    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection delay after");
  }

  //定义子线程回调函数
  void TestJankDetection()
  {
      // 初始化回调函数参数
      beginFunc_ = InitBeginFunc;
      endFunc_ = InitEndFunc;
      HiCollie_DetectionParam param {0};
      // 初始化线程卡顿监控函数
      int initResult = OH_HiCollie_Init_JankDetection(&beginFunc_, &endFunc_, param);
      // 线程启动10s内,不进行检测
      StartDelayTimer();
      // 成功结果:0
      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_JankDetection: %{public}d", initResult);
      int count = 0;
      while (count < 3) {
          // 设置处理开始回调函数,监控线程任务执行开始时长
          beginFunc_("TestBegin");
          // 休眠350ms,模拟任务线程处理事件卡顿场景
          usleep(350 * 1000);
          // 设置处理结束回调函数,监控线程任务执行结束时长
          endFunc_("TestEnd");
          count++;
      }
  }

  static napi_value TestHiCollieJankNdk(napi_env env, napi_callback_info info)
  {
      // 创建子线程
      std::thread threadObj(TestJankDetection);
      // 执行TestJankDetection任务
      threadObj.detach();
      return 0;
  }

  EXTERN_C_START
  static napi_value Init(napi_env env, napi_value exports)
  {
      napi_property_descriptor desc[] = {
          { "testHiCollieJankNdk", nullptr, TestHiCollieJankNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
      };
      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
      return exports;
  }
  EXTERN_C_END

  static napi_module demoModule = {
      .nm_version = 1,
      .nm_flags = 0,
      .nm_filename = nullptr,
      .nm_register_func = Init,
      .nm_modname = "entry",
      .nm_priv = ((void*)0),
      .reserved = { 0 },
  };

  extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  {
      napi_module_register(&demoModule);
  }

(2)应用线程卡死检测: OH_HiCollie_Init_StuckDetection, 示例代码如下:

  #include "napi/native_api.h"
  #include "hilog/log.h"
  #include "hicollie/hicollie.h"
  #include <thread>
  #include <string>
  #include <unistd.h>
  
  #undef LOG_TAG
  #define LOG_TAG "StruckTest"

  // 检查当前线程是否正常执行:1-正常,0-卡死
  const int64_t CHECK_BUSSINESS_THREAD_IS_ALIVE = 1;
  // 自定义休眠时间,模拟卡死场景
  const int64_t BLOCK_TIME = 3; 
  // 设置应用线程执行任务情况标志位, true-正常,false-卡死
  std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true);
  // 设置上报应用线程卡死事件标志位
  std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false);

  void ReportEvent() {
      bool temp = isSixSecondEvent_->load();
      int reportResult = OH_HiCollie_Report(&temp);
      // 成功:0
      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load());
      isSixSecondEvent_->store(temp);
  }

  void SetTimeout()
  {
    int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
      system_clock::now().time_since_epoch()).count();
    sleep(BLOCK_TIME);
    int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
      system_clock::now().time_since_epoch()).count();
    if (currentTime - now < BLOCK_TIME) {
      appThreadIsAlive_->store(true);
      return;
    }
    appThreadIsAlive_->store(false);
  }

  // 开发者可自定义周期性检测任务
  void Timer()
  {
    // 每隔3s检查应用是否正常执行任务
    if (appThreadIsAlive_->load()) {
      OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive.");
      // 更新appThreadIsAlive_,正常执行下次检测时为true
      appThreadIsAlive_->store(false);
      // 模拟超时场景
      SetTimeout();
      return;
    }
    ReportEvent();
  }

  //定义子线程回调函数
  void InitStuckDetection()
  {
    // 初始化线程卡死监控函数
    int initResult = OH_HiCollie_Init_StuckDetection(Timer);
    // 成功结果:0
    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult);
  }

  static napi_value TestHiCollieStuckNdk(napi_env env, napi_callback_info info)
  {
    // 创建子线程
    std::thread threadObj(InitStuckDetection);
    // 执行任务
    threadObj.join();
    return 0;
  }

  EXTERN_C_START
  static napi_value Init(napi_env env, napi_value exports)
  {
      napi_property_descriptor desc[] = {
          { "testHiCollieStuckNdk", nullptr, TestHiCollieStuckNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
      };
      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
      return exports;
  }
  EXTERN_C_END

  static napi_module demoModule = {
      .nm_version = 1,
      .nm_flags = 0,
      .nm_filename = nullptr,
      .nm_register_func = Init,
      .nm_modname = "entry",
      .nm_priv = ((void*)0),
      .reserved = { 0 },
  };

  extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  {
      napi_module_register(&demoModule);
  }

(3)应用线程卡死检测,自定义检测时间: OH_HiCollie_Init_StuckDetectionWithTimeout,示例代码如下:

  #include "napi/native_api.h"
  #include "hilog/log.h"
  #include "hicollie/hicollie.h"
  #include <thread>
  #include <string>
  #include <unistd.h>
  
  #undef LOG_TAG
  #define LOG_TAG "StruckTest"

  // 检查当前线程是否正常执行, 1-正常, 0-卡死
  const int64_t CHECK_BUSSINESS_THREAD_IS_ALIVE = 1;
  // 自定义休眠时间,模拟卡死场景
  const int64_t BLOCK_TIME = 5; 
  // 设置应用线程执行任务情况标志位, true-正常, false-卡死
  std::shared_ptr<std::atomic<bool>> appThreadIsAlive_ = std::make_shared<std::atomic<bool>>(true);
  // 设置上报应用线程卡死事件标志位
  std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false);

  void ReportEvent() {
      bool temp = isSixSecondEvent_->load();
      int reportResult = OH_HiCollie_Report(&temp);
      // 成功:0
      OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Report: %{public}d, isSixSecondEvent: %{public}d", reportResult, isSixSecondEvent_->load());
      isSixSecondEvent_->store(temp);
  }

  void SetTimeout()
  {
    int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
      system_clock::now().time_since_epoch()).count();
    sleep(BLOCK_TIME);
    int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
      system_clock::now().time_since_epoch()).count();
    if (currentTime - now < BLOCK_TIME) {
      appThreadIsAlive_->store(true);
      return;
    }
    appThreadIsAlive_->store(false);
  }

  // 开发者可自定义周期性检测任务
  void Timer()
  {
    // 每隔5s检查应用是否正常执行任务
    if (appThreadIsAlive_->load()) {
      OH_LOG_INFO(LogType::LOG_APP, "Check appThread isAlive.");
      // 更新appThreadIsAlive_,正常执行下次检测时为true
      appThreadIsAlive_->store(false);
      // 模拟超时场景
      SetTimeout();
      return;
    }
    ReportEvent();
  }

  //定义子线程回调函数
  void InitStuckDetectionWithTimeout()
  {
    // 初始化线程卡死监控函数
    int initResult = OH_HiCollie_Init_StuckDetectionWithTimeout(Timer, BLOCK_TIME);
    // 成功结果:0
    OH_LOG_INFO(LogType::LOG_APP, "OH_HiCollie_Init_StuckDetection: %{public}d", initResult);
  }

  static napi_value TestHiCollieStuckWithTimeoutNdk(napi_env env, napi_callback_info info)
  {
    // 创建子线程
    std::thread threadObj(InitStuckDetectionWithTimeout);
    // 执行任务
    threadObj.join();
    return 0;
  }

  EXTERN_C_START
  static napi_value Init(napi_env env, napi_value exports)
  {
      napi_property_descriptor desc[] = {
          { "testHiCollieStuckWithTimeoutNdk", nullptr, TestHiCollieStuckWithTimeoutNdk, nullptr, nullptr, nullptr, napi_default, nullptr },
      };
      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
      return exports;
  }
  EXTERN_C_END

  static napi_module demoModule = {
      .nm_version = 1,
      .nm_flags = 0,
      .nm_filename = nullptr,
      .nm_register_func = Init,
      .nm_modname = "entry",
      .nm_priv = ((void*)0),
      .reserved = { 0 },
  };

  extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  {
      napi_module_register(&demoModule);
  }
  1. 将TestHiCollieNdk注册为ArkTS接口:

(1)OH_HiCollie_Init_JankDetection示例,编辑”index.d.ts”文件,定义ArkTS接口:

  export const testHiCollieJankNdk: () => void;

(2)OH_HiCollie_Init_StuckDetection示例,编辑”index.d.ts”文件,定义ArkTS接口:

  export const testHiCollieStuckNdk: () => void;

(3)OH_HiCollie_Init_StuckDetectionWithTimeout示例,编辑”index.d.ts”文件,定义ArkTS接口:

  export const testHiCollieStuckWithTimeoutNdk: () => void;
  1. 编辑”Index.ets”文件:
  import testNapi from 'libentry.so'
  
  @Entry
  @Component
  struct Index {
    build() {
      RelativeContainer() {
        Column() {
          //选择对应的功能,在此处添加不同的点击事件
          
        }.width('100%')
      }
      .height('100%')
      .width('100%')
    }
  }

(1)添加点击事件,触发OH_HiCollie_Init_JankDetection方法。

  Button("testHiCollieJankNdk", { stateEffect:true, type: ButtonType.Capsule})
    .width('75%')
    .height(50)
    .margin(15)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .onClick(testNapi.testHiCollieJankNdk);

(2)添加点击事件,触发OH_HiCollie_Init_StuckDetection方法。

  Button("testHiCollieStuckNdk", { stateEffect:true, type: ButtonType.Capsule})
    .width('75%')
    .height(50)
    .margin(15)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .onClick(testNapi.testHiCollieStuckNdk);

(3)添加点击事件,触发OH_HiCollie_Init_StuckDetectionWithTimeout方法。

  Button("testHiCollieStuckWithTimeoutNdk", { stateEffect:true, type: ButtonType.Capsule})
    .width('75%')
    .height(50)
    .margin(15)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .onClick(testNapi.testHiCollieStuckWithTimeoutNdk);
  1. 点击DevEco Studio界面中的运行按钮,运行应用工程。

  2. 在DevEco Studio的底部,切换到“Log”窗口,过滤自定义的LOG_TAG。

(1)等待10s,再点击”testHiCollieJankNdk”按钮(线程启动10s内,不进行卡顿检测)。 此时窗口将显示通过OH_HiCollie_Init_JankDetection接口获取的应用业务线程采样栈的超时信息。可以通过订阅hiappevent获取对应的事件:订阅主线程超时事件

(2)点击”testHiCollieStuckNdk”按钮。 此时窗口将显示通过OH_HiCollie_Init_StuckDetection接口,初始化卡死检测回调函数。可以根据实际业务场景,自行定义卡死检测函数。

(3)点击”testHiCollieStuckWithTimeoutNdk”按钮。 此时窗口将显示通过OH_HiCollie_Init_StuckDetectionWithTimeout接口,初始化卡死检测回调函数。可以根据实际业务场景,自行定义卡死检测函数,及卡死检测时间。

你可能感兴趣的鸿蒙文章

harmony 鸿蒙Performance Analysis Kit(性能分析服务)

harmony 鸿蒙分析AppFreeze(应用无响应)

harmony 鸿蒙应用恢复开发指导

harmony 鸿蒙分析CppCrash(进程崩溃)

harmony 鸿蒙错误管理开发指导

harmony 鸿蒙hdc

harmony 鸿蒙事件上报

harmony 鸿蒙HiAppEvent介绍

harmony 鸿蒙订阅踩内存事件(ArkTS)

harmony 鸿蒙订阅踩内存事件(C/C++)

0  赞