Camera系统稳定性问题排查:从ANR到Crash的实战指南
大家好,我是老张。今天我们来聊聊Camera稳定性问题排查。说实话,这部分内容是我在课程里最想分享的,因为我在项目里踩过的坑,真的能写一本《血泪史》。
Camera稳定性问题,说白了就是三类:ANR(应用无响应)、Crash(崩溃)、资源泄漏。这三类问题往往相互关联,一个DMA-BUF泄漏可能最终导致Crash,而ISP Hang又可能引发ANR。我们一个一个来拆解。
一、Camera ANR问题分析
ANR,全称Application Not Responding。在Camera场景下,最常见的是两种:Input Dispatching Timeout和Service 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。
排查方法:
- 查看DMA-BUF使用情况:
cat /sys/kernel/debug/dma_buf/bufinfo - 对比前后差值:打开相机前记录一次,关闭后再记录一次
- 定位泄漏点:通过
/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)
第三步:优化方案
我当时的做法是:
- 算法库改为懒加载,只在第一次使用时加载
- ISP初始化移到
configureStreams()里 - DMA-BUF预分配,减少运行时分配开销
优化后,HAL::open()耗时降到200ms,总打开时间从2.8秒降到1.2秒。
小技巧:在HAL层加一个性能统计模块,每次操作都记录耗时。这样问题复现后,直接看统计结果就能定位瓶颈,不用反复抓log。
六、知识体系总览
下面这张图总结了Camera稳定性问题的核心排查思路:
这张图把Camera稳定性问题分成了三大类,每一类下面又有具体的子问题。排查时,按照"抓Trace→分析Log→定位瓶颈→优化修复"这个流程走,基本能解决90%的问题。
好了,这一章的内容就到这里。记住,稳定性问题排查没有捷径,多抓log、多分析、多总结,你也能成为Camera稳定性专家。
公众号:蓝海资料掘金营,微信deep3321