# AppFreeze-主线程卡死（THREAD_BLOCK_6S）故障定位

## 问题现象

应用在使用过程中界面失去响应（点击无效、动画静止），持续时间约6秒左右，随后应用进程被系统终止或弹出"应用无响应"提示。

## 背景知识

系统的Watchdog（看门狗）机制会实时监控主线程（Main Thread）的消息队列处理情况。

当主线程处理单个消息（Message）耗时过长，或被同步操作长期挂起，导致看门狗检测任务无法在规定周期内（通用阻塞检测阈值为6秒）被执行时，系统会判定应用主线程处于"卡死"状态。

此时，系统为了恢复交互会触发应用无响应（ANR）流程，生成故障日志（[AppFreeze的日志规格](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/appfreeze-guidelines#日志规格)）并终止进程。

## 问题定位

1. **获取故障日志** ：

   通过DevEco Studio的FaultLog面板，或者通过命令行工具导出日志。
   * **操作方式** ：执行命令**hdc file recv /data/log/faultlog/faultlogger/ .**。
   * **目标文件**：查找以appfreeze-开头，且时间点与故障发生时间吻合的日志文件。
2. **分析日志头部信息（判定卡死时长）** ：

   打开日志文件，通过头部关键字段确认故障类型及时间点。
   * **输入**：查看日志头部的Event、Timestamp及Reason字段。
   * **输出** ：

     ```text
     Generated by HiviewDFX
     ...
     Event: THREAD_BLOCK_6S           <-- 故障类型：主线程卡死6秒
     Timestamp: 2024-05-21 10:00:06   <-- 日志生成时间（系统判定卡死的时间点）
     Reason: THREAD_BLOCK_6S          <-- 触发原因
     ```

   * **分析**：确认Event为THREAD_BLOCK_6S，且Timestamp与用户感知到的卡死时间一致。
3. **分析消息队列（确认阻塞源头）** ：

   查看Main handler dump区域，通过计算时间差定位导致卡死的具体任务。
   * **输入**：定位到日志中的Main handler dump及Current Running字段。
   * **输出** ：

     ```text
     Main handler dump is:
      EventHandler dump begin curTime: 2025-06-28 14:08:34.067  <-- A. 抓取现场的时间
      Current Running: start at 2025-06-28 14:08:27.354,        <-- B. 当前任务开始执行时间
      task name = uv_timer_task,                                <-- C. 任务类型
      caller = [ohos_loop_handler.cpp(OnTriggered:72)]
     ```

   * **计算公式** ：

     **阻塞时长=日志生成时间(Timestamp)-当前任务开始时间(start at)**
   * **实际计算** ：

     14:08:34.067 - 14:08:27.354 ≈ **6.713秒**；
   * **判断**：当前正在运行的任务（Current Running）耗时已超过6秒阈值，直接导致主线程阻塞，需针对该任务进行优化。
4. **分析主线程堆栈（定位代码行）** ：

   在日志下方找到Tid与主线程Pid一致的堆栈信息（通常标记为Name:main），根据堆栈特征定位具体代码。
   * **场景A：JS业务逻辑卡死（纯计算/死循环）** ：
     * **特征**：堆栈包含libark_runtime.so，紧随其后的是应用包名和ArkTS文件名。
     * **示例** ：

       ```text
       #00 pc 000... /system/lib64/libark_runtime.so
       #01 pc 000... /data/.../libentry.so (Index.ets:55)  <-- 业务代码行号
       ```

     * **说明**：Index.ets第55行可能存在复杂计算（如大数组遍历）或死循环（while(true)）。
   * **场景B：IPC通信阻塞（等待系统服务）** ：
     * **特征**：堆栈顶层停留在OHOS::BinderDriver::Transact或IPCThreadState::WaitForCompletion。
     * **示例** ：

       ```text
       #00 pc 000... /system/lib64/libipc_core.z.so (OHOS::BinderDriver::Transact+...)
       #01 pc 000... /system/lib64/libipc_core.z.so (OHOS::IPCThreadState::WaitForCompletion+...)
       #02 pc 000... /system/lib64/libapp_manager_proxy.z.so (OHOS::AppExecFwk::AppMgrProxy::GetRunningProcessInfo+...)
       ```

     * **说明**：主线程在同步调用AppMgr服务时，因服务端无响应而阻塞。需查看日志中的PeerBinder字段确认对端进程状态。
   * **场景C：锁竞争或I/O阻塞** ：
     * **特征**：堆栈停留在Monitor::Wait、std::mutex::lock（锁阻塞）或read/write（I/O阻塞）。
     * **示例** ：

       ```text
       #00 pc 000... /system/lib64/libc.so (syscall+...)
       #01 pc 000... /system/lib64/libark_runtime.so (panda::os::thread::Mutex::Lock+...)
       ```

     * **说明**：主线程正在等待获取一把锁，该锁可能被子线程持有且未及时释放。

## 分析结论

导致问题的根本原因通常归为以下几类，详细场景对照表如下：

|#|细分场景分类|详细成因与代码行为|关键堆栈/函数特征|
|:-|:--------------------|:-----------------------------------------------------------------------|:----------------------------------------------------------------|
|1|**IPC: 等待系统核心服务**|主线程调用AMS/BMS/DMS接口（如查询应用信息、拉起Ability），系统服务忙碌导致卡死。|OHOS::AppExecFwk::BundleMgrProxy OHOS::AAFwk::AbilityManagerProxy|
|2|**IPC: 等待多媒体服务**|主线程调用Camera、Audio或Codec接口，底层硬件驱动或服务进程无响应。|OHOS::Camera::CameraInputOHOS::AudioStandard::AudioRenderer|
|3|**IPC: 跨应用Service调用**|通过connectServiceExtensionAbility连接三方应用服务，服务端代码死锁或Crash，客户端卡在SendRequest。|OHOS::AbilityRuntime::ExtensionManager|
|4|**DB: 数据库版本迁移**|应用升级后，首次启动在主线程执行RdbOpenCallback.onUpgrade，进行大量数据表结构变更或数据清洗。|OHOS::NativeRdb::SqliteConnection::ExecuteSql sqlite3_exec|
|5|**DB: 复杂SQL查询**|主线程执行了全表扫描、多表关联（JOIN）或未命中索引的查询。|OHOS::NativeRdb::RdbStore::Query|
|6|**IO: 配置文件落盘**|使用Preferences（首选项）存储过大内容（如Base64图片），并在主线程调用flush或put。|OHOS::NativePreferences::PreferencesXmlUtils fsync|
|7|**IO: 文件解压/移动**|主线程直接调用zlib解压Zip包，或使用fs.copyFile移动大文件。|OHOS::FileManagement::ModuleFileIO|
|8|**Lock: 跨线程死锁**|主线程持锁A等锁B，Worker线程持锁B等锁A（经典死锁）。|std::mutex::lock pthread_cond_wait|
|9|**Lock: 等待单例初始化**|多个线程同时访问某单例（如埋点SDK），主线程卡在synchronized或static初始化块上。|__cxa_guard_acquire Monitor::Enter|
|10|**Logic: 正则表达式回溯**|使用复杂的正则表达式验证长字符串（如超长URL或HTML），触发ReDoS（灾难性回溯）。|std::regex_search RegExpExec|
|11|**Logic: 大对象序列化**|对超大的JS对象（如数万条记录的Array）执行JSON.stringify或JSON.parse。|BuiltinsJson::Stringify BuiltinsJson::Parse|
|12|**Logic: 错误的循环条件**|业务代码中while循环条件判断错误，或forEach中修改了集合长度导致死循环。|ArkJS::Interpreter (纯JS栈，无系统调用)|

## 修改建议

1. **主线程减负** ：

   在主线程执行超过100ms的任务，例如计算密集型任务（如图片处理、大数据计算），使用[TaskPool](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/taskpool-introduction)进行并发处理。
2. **IPC异步化** ：

   涉及调用系统能力或跨进程通信时，优先查阅API文档，使用Async或Promise版本的接口，避免使用同步（Sync）后缀的接口。
3. **I/O迁移** ：

   将Preferences（首选项）、数据库操作、文件读写等I/O操作移至TaskPool或[Worker](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/worker-introduction)中执行，或使用其提供的异步API（如fs.read而非fs.readSync）。

