实战调试:使用 strace、dumpsys、binder 节点信息调试 Binder 问题

说实话,Binder 问题调试起来挺让人头疼的。代码层面看不出毛病,logcat 里也没有明显的错误,但系统就是卡顿、服务就是连不上。这时候,你就得拿出真正的调试工具了。

我个人习惯把 Binder 调试分成三个层次:系统调用层服务状态层内核节点层。今天我就带你把这三层走一遍,看看怎么用 strace、dumpsys 和 binder 节点信息来定位问题。

第一层:strace —— 追踪系统调用

strace 是 Linux 调试的老牌工具了。它能捕获进程发起的每一次系统调用,包括 Binder 相关的 ioctl。你想想看,Binder 驱动本质上就是个字符设备,所有跨进程通信最终都会落到 ioctl 上。

我曾经遇到过一个诡异的问题:某个 App 调用远程服务时,总是超时。代码看了三遍没发现问题,最后 strace 一跑,真相大白。

核心命令:
# 跟踪某个进程的 binder 相关调用
strace -p <pid> -e trace=ioctl -f -t

# 或者直接跟踪 binder 命令
strace -p <pid> -e trace=ioctl -f -t -v 2>&1 | grep BINDER

输出里你会看到类似这样的内容:

ioctl(8, BINDER_WRITE_READ, {write_size=128, ...}) = 0
ioctl(8, BINDER_WRITE_READ, {read_size=256, ...}) = 0

这里有个关键点:看 write_size 和 read_size 的变化。如果 write_size 一直很大但 read_size 始终为 0,说明数据发出去后没人处理——服务端可能卡住了。反过来,如果 read_size 突然暴涨,说明有大量数据涌回来,可能是回调太频繁。

我的小技巧: strace 输出很冗长,建议用 -o output.log 重定向到文件,然后用 grep 过滤 BINDER 关键字。别直接在终端里看,眼睛会瞎的。

第二层:dumpsys —— 查看服务状态

dumpsys 是 Android 自带的瑞士军刀。对于 Binder 调试,我最常用的是 dumpsys servicedumpsys activity

先看服务有没有注册成功:

adb shell dumpsys service | grep "my_service"
# 或者直接列出所有服务
adb shell dumpsys service -l

如果找不到你的服务,那问题就简单了——服务根本没注册。但更多时候,服务在列表里,却连不上。这时候我会用:

adb shell dumpsys activity processes | grep "Binder"

这个命令能显示每个进程的 Binder 线程池状态。我记得有一次,我发现某个服务的 Binder 线程池里只有 1 个线程在跑,其他 15 个全卡住了。嗯,这就是典型的死锁问题。

注意: dumpsys 输出里有个容易被忽略的字段叫 last_calling_uid。如果你发现某个服务的调用方 UID 异常,那可能是权限问题,或者有恶意进程在冒充调用方。

第三层:binder 节点信息 —— 深入内核

这是最硬核的一层。Binder 驱动在内核里维护了丰富的调试信息,通过 /sys/kernel/debug/binder/ 目录可以访问。说白了,这就是 Binder 驱动的“黑匣子”。

常用的节点有:

节点路径 作用
/sys/kernel/debug/binder/proc 显示所有使用 Binder 的进程信息
/sys/kernel/debug/binder/state 全局 Binder 状态,包括节点、引用、事务
/sys/kernel/debug/binder/transactions 当前正在处理的事务列表
/sys/kernel/debug/binder/stats 统计信息,比如事务总数、失败次数

我最常用的是 transactions 节点。当系统卡死时,读这个文件能看到所有“悬而未决”的事务:

adb shell cat /sys/kernel/debug/binder/transactions

输出示例:

proc 1234
  context binder
  thread 5678: l 12 need_return 0
    transaction 9012: from 1234:5678 to 9101:1111 code 0x1 flags 0x10
      data ptr 0x7f12345678 size 128
      node 0x10001

这里的关键信息是 fromto。如果某个事务长时间处于 need_return 0 状态,说明接收方没回复。我曾经靠这个定位过一个 bug:某个系统服务在 onTransact 里做了耗时操作,导致所有调用方都堵死了。

实战案例: 有一次线上反馈说相机打不开。我 strace 一看,camera service 的 ioctl 返回了 -1。再查 dumpsys,发现服务状态是 DEAD。最后看 binder 节点信息,发现有个事务卡了 30 秒没返回。原来是底层 HAL 在等硬件锁,而锁被另一个进程占着没释放。典型的死锁链。

三招组合拳

这三层工具不是孤立的。我一般这样用:

  1. 先跑 strace,看系统调用层面有没有异常。比如 ioctl 返回错误码,或者长时间阻塞。
  2. 再用 dumpsys,确认服务状态和线程池情况。这一步能快速排除“服务没注册”或“线程池耗尽”这类低级问题。
  3. 最后查 binder 节点,深入内核看事务状态。这一步最耗时,但往往能找到根因。

你想想看,这三步走下来,90% 的 Binder 问题都能定位到。剩下的 10% 可能是硬件问题或者驱动 bug,那就得靠内核日志和硬件工程师配合了。

避坑指南: 我曾经在调试时忘了开内核调试选项,结果 /sys/kernel/debug/binder/ 目录是空的。记得编译内核时打开 CONFIG_DEBUG_FSCONFIG_BINDER_DEBUG。不然你连数据都看不到。

知识体系总览

下面这张图总结了我刚才讲的三层调试方法,以及它们之间的关系:

Binder 调试三层架构 strace 系统调用层 dumpsys 服务状态层 binder 节点 内核信息层 strace 关注点 • ioctl 返回值 • write_size / read_size • BINDER_WRITE_READ 频率 • 线程阻塞时间 • 错误码(-1, -22 等) 命令示例: strace -p PID -e ioctl dumpsys 关注点 • 服务是否注册 • Binder 线程池状态 • last_calling_uid • 服务 DEAD 状态 • 进程存活情况 命令示例: dumpsys service -l binder 节点关注点 • 事务 from/to • need_return 状态 • 事务超时 • 节点引用计数 • 线程阻塞链 命令示例: cat /d/binder/transactions 从系统调用 → 服务状态 → 内核信息,逐层深入定位 Binder 问题

这三层工具,说白了就是帮你从不同维度看同一个问题。strace 看“有没有发出去”,dumpsys 看“服务在不在”,binder 节点看“卡在哪里了”。组合起来用,效率最高。

嗯,今天就先聊到这里。调试工具再多,关键还是得理解 Binder 的工作原理。工具只是帮你验证猜想的手段。


专注资料整理