Camera系统稳定性问题排查:从ANR到Crash的实战指南

大家好,我是老张。今天我们来聊聊Camera稳定性问题排查。说实话,这部分内容是我在课程里最想分享的,因为我在项目里踩过的坑,真的能写一本《血泪史》。

Camera稳定性问题,说白了就是三类:ANR(应用无响应)、Crash(崩溃)、资源泄漏。这三类问题往往相互关联,一个DMA-BUF泄漏可能最终导致Crash,而ISP Hang又可能引发ANR。我们一个一个来拆解。

一、Camera ANR问题分析

ANR,全称Application Not Responding。在Camera场景下,最常见的是两种:Input Dispatching TimeoutService Timeout

1.1 Input Dispatching Timeout

这种ANR通常发生在用户点击拍照按钮后,5秒内没有响应。我遇到过最典型的一个案例:某款手机在弱光环境下拍照,点击快门后界面卡死,然后弹出ANR对话框。

为什么会这样?

我们来看一下调用链:

用户点击拍照按钮
  → Activity.onClick()
    → Camera.takePicture()
      → HAL3A::processCaptureRequest()
        → ISP处理(耗时过长)
          → 返回结果超时

问题出在ISP处理环节。弱光下ISP需要做多帧合成降噪,处理时间从正常的200ms飙升到3秒以上。加上前面的一些开销,就超过了5秒阈值。

排查方法:

  • 抓取ANR trace文件:/data/anr/traces.txt
  • 查看主线程堆栈,看是否卡在Binder调用上
  • 检查Camera HAL的processCaptureRequest耗时

我的经验:我曾经在调试时发现,trace里显示主线程在等待一个锁,而这个锁被CameraService的某个回调线程持有。那个线程又在等HAL返回结果。典型的死锁场景。解决办法是把耗时操作放到工作线程,主线程只做UI更新。

1.2 Service Timeout

Service Timeout指的是CameraService在20秒内没有响应。这种情况更严重,通常意味着Camera HAL层出了大问题。

我记得有一次,客户反馈相机打开后黑屏,过一会儿弹出"相机服务异常"。抓log一看,是HAL的open()函数里做了太多初始化操作,包括加载算法库、初始化ISP、分配DMA-BUF等,总耗时超过了25秒。

解决方案其实很简单:把初始化拆成两步,open()只做轻量操作,重活放到configureStreams()里做。

二、Camera Crash问题分析

Crash分两种:SIGSEGV(段错误)SIGABRT(主动中止)

2.1 SIGSEGV

SIGSEGV说白了就是访问了不该访问的内存。在Camera HAL里,最常见的原因是空指针解引用野指针

我遇到过这样一个bug:在某个场景下,HAL返回的buffer地址是NULL,但上层没有做空检查,直接往这个地址写数据,然后就崩了。

// 错误写法
void processBuffer(CameraBuffer* buf) {
    memcpy(buf->data, src, size);  // buf为NULL时崩溃
}

// 正确写法
void processBuffer(CameraBuffer* buf) {
    if (buf == NULL || buf->data == NULL) {
        ALOGE("Invalid buffer");
        return;
    }
    memcpy(buf->data, src, size);
}

避坑指南:我曾经在项目中因为一个回调函数里没有做空指针检查,导致线上崩溃率飙升。从那以后,我要求团队所有HAL层的函数入口都必须做参数合法性校验。别嫌麻烦,这能救你的命。

2.2 SIGABRT

SIGABRT通常是主动调用了abort()assert()失败。在Camera HAL里,常见于状态机异常参数校验失败

举个例子:某个HAL实现里,如果flush()被调用时还有未完成的request,就会触发assert失败。这在正常流程下没问题,但如果用户快速切换相机,就可能触发。

三、DMA-BUF泄漏问题排查

DMA-BUF泄漏是Camera系统里最隐蔽的问题之一。它不会立刻导致Crash,但会慢慢吃掉系统内存,最终导致OOM。

排查方法:

  1. 查看DMA-BUF使用情况:cat /sys/kernel/debug/dma_buf/bufinfo
  2. 对比前后差值:打开相机前记录一次,关闭后再记录一次
  3. 定位泄漏点:通过/proc/<pid>/fd/查看进程打开的文件描述符

我的实战经验:有一次我发现每次拍照后DMA-BUF数量都会增加几个,但关闭相机后不会减少。最后定位到是HAL层在拍照完成后没有调用releaseBuffer()。修复后,内存泄漏问题彻底解决。

四、ISP Hang问题定位

ISP Hang,就是ISP处理单元卡住了。表现是:预览画面不动了,或者拍照后一直不返回结果。

定位步骤:

  • 检查ISP寄存器状态:看是否卡在某个处理阶段
  • 查看中断是否正常:ISP完成处理后会触发中断
  • 检查时钟和电源:ISP是否被意外关闭

我记得有一次,ISP Hang的原因是某个算法库在特定分辨率下会进入死循环。解决办法是加了一个超时机制,超过500ms就强制复位ISP。

五、实战:解决Camera打开慢的问题

好了,我们来看一个完整的实战案例。某款手机相机打开需要3秒,用户反馈强烈。我们来一步步排查。

第一步:抓取log,分析耗时分布

// 在关键节点加log
CameraService::connect() -> 开始
HAL::open() -> 开始
HAL::open() -> 结束,耗时1200ms
HAL::configureStreams() -> 开始
HAL::configureStreams() -> 结束,耗时800ms
CameraService::connect() -> 结束,总耗时2800ms

第二步:定位瓶颈

很明显,HAL::open()耗时1200ms,占了将近一半。进一步分析发现,open函数里做了以下操作:

  • 加载算法库(600ms)
  • 初始化ISP(300ms)
  • 分配DMA-BUF(200ms)
  • 其他(100ms)

第三步:优化方案

我当时的做法是:

  1. 算法库改为懒加载,只在第一次使用时加载
  2. ISP初始化移到configureStreams()
  3. DMA-BUF预分配,减少运行时分配开销

优化后,HAL::open()耗时降到200ms,总打开时间从2.8秒降到1.2秒。

小技巧:在HAL层加一个性能统计模块,每次操作都记录耗时。这样问题复现后,直接看统计结果就能定位瓶颈,不用反复抓log。

六、知识体系总览

下面这张图总结了Camera稳定性问题的核心排查思路:

Camera稳定性问题排查知识体系 ANR问题 Crash问题 资源泄漏 Input Dispatching Timeout Service Timeout SIGSEGV(段错误) SIGABRT(主动中止) DMA-BUF泄漏 ISP Hang 排查方法:抓Trace → 分析Log → 定位瓶颈 → 优化修复 实战:Camera打开慢 → 从3秒优化到1.2秒

这张图把Camera稳定性问题分成了三大类,每一类下面又有具体的子问题。排查时,按照"抓Trace→分析Log→定位瓶颈→优化修复"这个流程走,基本能解决90%的问题。

好了,这一章的内容就到这里。记住,稳定性问题排查没有捷径,多抓log、多分析、多总结,你也能成为Camera稳定性专家。


公众号:蓝海资料掘金营,微信deep3321