← 返回课程

调试工具

Camera全栈开发(Qcom Camx) 第 24 / 30 节

第 22 章:调试工具与方法 ⭐⭐


本章导读

高通 camx 的日志分两大块:UMD(User Mode Driver,用户层)KMD(Kernel Mode Driver,内核层)

遇到任何问题,先把"案发现场"固定下来——日志、dump、状态信息。本章讲清楚怎么抓、怎么看、怎么分析。


22.1 UMD 日志(用户层)

22.1.1 日志格式

CamX 的 UMD 日志格式如下:

CamX: [<VerbosityLevel>][<Group>] <File>:<Line Number> <Function Name> <Message>

实际例子:

CamX: [INFO][CORE] camxsession.cpp:123 Session::ProcessCaptureRequest() frame_number=42

22.1.2 日志类型等级

等级 对应属性 说明
ERROR logErrorMask 错误,默认输出
WARNING logWarningMask 警告
INFO logInfoMask 信息
DEBUG logDebugMask 调试
VERBOSE logVerboseMask 详细

22.1.3 日志分组(Log Group)

各组定义在 camx/src/utils/camxtypes.h 中:

分组名 bit 掩码 模块
CamxLogGroupCore 1<<0 0x1 Session/Pipeline/Node
CamxLogGroupCSL 1<<1 0x2 CSL 层(与 Kernel 通信)
CamxLogGroupISP 1<<2 0x4 ISP 硬件
CamxLogGroupStats 1<<3 0x8 3A Stats
CamxLogGroupMeta 1<<4 0x10 Metadata
CamxLogGroupHAL 1<<5 0x20 HAL3 接口
CamxLogGroupCHI 1<<6 0x40 CHI 层
CamxLogGroupSensor 1<<7 0x80 Sensor 驱动
CamxLogGroupAEC 1<<8 0x100 AE 算法
CamxLogGroupAWB 1<<9 0x200 AWB 算法
CamxLogGroupAF 1<<10 0x400 AF 算法

22.1.4 日志控制

# setprop 方式(实时生效)
adb shell setprop persist.vendor.camera.logInfoMask 0x8    # ISP Info
adb shell setprop persist.vendor.camera.logDebugMask 0x7  # Core+CSL+ISP Debug

# camxoverridesettings.txt 方式(重启 Provider 生效)
adb shell "echo logInfoMask=0x8 >> /vendor/etc/camera/camxoverridesettings.txt"
adb shell killall -9 vendor.camera-provider-2-4

掩码计算方法:多个分组同时开启 = 各分组掩码相加(或运算)

例: 同时打开 Core(0x1) + CSL(0x2) + ISP(0x4)
  掩码 = 0x1 + 0x2 + 0x4 = 0x7

例: 同时打开 ISP(0x4) + Stats(0x8) + AEC(0x100)
  掩码 = 0x4 + 0x8 + 0x100 = 0x10C

22.2 KMD 日志(内核层)

控制方式

# 打开 CAM_SENSOR + CAM_CSID 的 kernel 日志
adb shell "echo 0x101 > /sys/module/cam_debug_util/parameters/debug_mdl"
adb shell cat /proc/kmsg > kmd_logs.txt

Kernel 各模块 mask:

CAM_SENSOR  = 0x1      Sensor 驱动
CAM_ICP     = 0x20     ICP 模块
CAM_CSIPHY  = 0x80     CSIPHY(MIPI 物理层)
CAM_CSID    = 0x100    CSID(MIPI 协议解码)
CAM_CCI     = 0x200    CCI(I2C 控制器)
CAM_ISP     = 0x400    ISP

22.3 实战:追踪一次拍照的完整日志

这是一次完整的拍照请求(preview + capture)应该能看到的日志链。按这个顺序找,找到哪一步断了,问题就在哪一步。

正常拍照的完整日志链

// ① App 发起预览请求
// HAL3 入口收到 setRepeatingRequest
[CamX] [HAL] camxhal3entry.cpp: ProcessCaptureRequest() frame_number=1

// ② CHI 层受理
[CHI] chxusecase.cpp: SubmitChiRequest() frame_number=1

// ③ Session 建立 Request 映射
[CamX] [REQMAP] camxsession.cpp: chiFrameNum: 1 <==> requestId: 1

// ④ Pipeline 开始处理预览帧
[CamX] [CORE] camxpipeline.cpp: Pipeline::ProcessRequest() pipelineId=0 req=1

// ⑤ Node 处理(每帧都有)
[CamX] [CORE] camxnode.cpp: ProcessRequestResult() node=IFE req=1 processingTime=2.1ms
[CamX] [CORE] camxnode.cpp: ProcessRequestResult() node=IPE req=1 processingTime=1.8ms

// ⑥ 硬件处理完成(每帧都有,标记帧率)
[CamX] [CORE] camxpipeline.cpp: CSLMessageHandler() requestID=1, frameCount=1

// ⑦ 结果回调
[CHI] chxusecase.cpp: ProcessCaptureResult() requestId=1

// ========== 用户点击拍照按钮 ==========

// ⑧ 拍照请求进入
[CamX] [HAL] camxhal3entry.cpp: ProcessCaptureRequest() frame_number=50

// ⑨ 拍照请求通过 CHI
[CHI] chxusecase.cpp: SubmitChiRequest() frame_number=50
[CamX] [REQMAP] chiFrameNum: 100 <==> requestId: 50

// ⑩ Pipeline 0(RT,预览)和 Pipeline 1(Offline,JPEG)同时工作
[CamX] [CORE] camxpipeline.cpp: Pipeline::ProcessRequest() pipelineId=0 req=50
[CamX] [CORE] camxpipeline.cpp: Pipeline::ProcessRequest() pipelineId=1 req=50

// ⑪ Offline Pipeline 的 JPEG Node 开始编码
[CamX] [CORE] camxnode.cpp: ProcessRequestResult() node=JPEG req=50 processingTime=80ms

// ⑫ 拍照结果回调
[CHI] chxusecase.cpp: ProcessCaptureResult() requestId=50

日志断在哪里的问题定位

日志断在 ①    → App 没发起请求 / HAL3 入口有问题
日志断在 ③    → Session 映射失败
日志断在 ⑤    → Node 依赖一直不满足(死锁)
日志断在 ⑥    → HW 处理异常(requestID=0 → KMD 丢帧)
日志断在 ⑨    → CHI 层受理异常
日志断在 ⑩    → Offline Pipeline 没激活
日志断在 ⑪    → JPEG 编码异常(ProcessingTime 异常长或返回失败)

22.4 实战:预览卡顿问题排查

症状: 预览画面一卡一卡的,帧率不稳定

① 先看实际帧率
   logcat 中搜 CSLMessageHandler,计算相邻帧的时间戳差
   正常 30fps → 间隔 ~33ms
   如果出现 66ms(15fps)或 99ms(10fps)→ 掉帧了

② 定位掉帧原因
   开 dumpNodeProcessingInfo=1
   看哪个 Node 的 processingTime 异常

   如果 BPS 耗时突然从 3ms 跳到 15ms:
     → ISP 带宽不够 → 降低输出分辨率/减少 Stream

   如果 Stats 耗时从 2ms 跳到 20ms:
     → Stats 处理过重 → 降低采样频率

③ 如果帧间隔稳定但用户觉得卡
   → 可能是 AE 在来回调整曝光(AE 振荡)
   → logcat 中搜 exposureTime,看是否在两个值之间反复跳

④ 如果暗光下卡,亮光下不卡
   → AE 曝光时间太长导致帧率下降
   → 检查 exposureTime 是否超过帧间隔

22.5 实战:拍照黑屏问题排查

症状: 按了拍照按钮,拿到的 JPEG 是全黑的

排查:

① 先看 Raw Dump,确认 sensor 是否有输出
   → /data/vendor/camera/raw_*.raw
   → 如果 RAW 数据正常 → sensor 和 ISP 前端没问题
   → 如果 RAW 全黑 → sensor 没工作(查 power/MCLK/I2C)

② 再看 ISP Dump,看 ISP 处理后的 YUV
   → /data/vendor/camera/isp_*.yuv
   → 如果 YUV 正常 → JPEG 编码有问题
   → 如果 YUV 全黑 → ISP 配置有问题

③ 查 JPEG Node 的 processingTime
   如果 processingTime=0 → JPEG Node 根本没执行
   如果 processingTime 正常但输出全黑 → JPEG 编码参数问题

22.6 Session Dump 分析

adb shell dumpsys media.camera > dump.txt

重点看三块:

Pipeline Info:
  Status: STREAM_ON    ← 正常
  Status: STREAM_OFF   ← Pipeline 意外关闭(检查 StreamOff 原因)

Node Info:
  State: PROCESSING    ← 正常
  State: WAITING       ← 在等依赖(看 waited 了多久)
  ProcessingTime       ← Node 处理耗时

Buffer Info:
  Total: 4, InUse: 2, Free: 2  ← 正常
  Total: 4, InUse: 4, Free: 0  ← Buffer 泄漏!

22.7 Crash 和 ANR

# Provider crash → 查 tombstone
adb shell ls /data/tombstones/
adb pull /data/tombstones/tombstone_XX

# App ANR → 查 traces
adb shell ls /data/anr/
adb pull /data/anr/traces.txt

# logcat 中搜
adb logcat -d | grep -E "CRASH|SIGSEGV|SIGABRT|tombstone|ANR"

22.8 本章总结

# 完整的"案发现场"采集
adb logcat -v threadtime | grep -E "CamX|CHI|CSL|ERROR|FATAL" > camx_log.txt
adb shell dmesg > dmesg.txt
adb shell dumpsys media.camera > dumpsys.txt
adb shell ls /data/tombstones/
UMD 日志: adb logcat -s "[CamX]"         ← 用户层
KMD 日志: dmesg | grep cam_sensor       ← 内核层
UMD 控制: camxoverridesettings.txt + setprop
KMD 控制: /sys/module/cam_debug_util/parameters/debug_mdl

拍照流程 12 步日志链: 从入口到 JPEG 回调
预览卡顿排查: CSLMessageHandler 帧间隔 → Node ProcessingTime → AE exposureTime
黑屏排查: Raw Dump → ISP Dump → JPEG ProcessingTime

动手验证

# 打开 CamX Core Info 日志,追踪一次拍照
adb shell setprop persist.vendor.camera.logInfoMask 0x1
adb logcat -c
# 拍一张照片
adb logcat -v threadtime | grep -E "CamX|CHI" > capture_trace.txt
# 在 capture_trace.txt 中找拍照流程的 12 步日志
adb shell setprop persist.vendor.camera.logInfoMask 0