实战调试:使用 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 突然暴涨,说明有大量数据涌回来,可能是回调太频繁。
-o output.log 重定向到文件,然后用 grep 过滤 BINDER 关键字。别直接在终端里看,眼睛会瞎的。
第二层:dumpsys —— 查看服务状态
dumpsys 是 Android 自带的瑞士军刀。对于 Binder 调试,我最常用的是 dumpsys service 和 dumpsys activity。
先看服务有没有注册成功:
adb shell dumpsys service | grep "my_service"
# 或者直接列出所有服务
adb shell dumpsys service -l
如果找不到你的服务,那问题就简单了——服务根本没注册。但更多时候,服务在列表里,却连不上。这时候我会用:
adb shell dumpsys activity processes | grep "Binder"
这个命令能显示每个进程的 Binder 线程池状态。我记得有一次,我发现某个服务的 Binder 线程池里只有 1 个线程在跑,其他 15 个全卡住了。嗯,这就是典型的死锁问题。
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
这里的关键信息是 from 和 to。如果某个事务长时间处于 need_return 0 状态,说明接收方没回复。我曾经靠这个定位过一个 bug:某个系统服务在 onTransact 里做了耗时操作,导致所有调用方都堵死了。
三招组合拳
这三层工具不是孤立的。我一般这样用:
- 先跑 strace,看系统调用层面有没有异常。比如 ioctl 返回错误码,或者长时间阻塞。
- 再用 dumpsys,确认服务状态和线程池情况。这一步能快速排除“服务没注册”或“线程池耗尽”这类低级问题。
- 最后查 binder 节点,深入内核看事务状态。这一步最耗时,但往往能找到根因。
你想想看,这三步走下来,90% 的 Binder 问题都能定位到。剩下的 10% 可能是硬件问题或者驱动 bug,那就得靠内核日志和硬件工程师配合了。
/sys/kernel/debug/binder/ 目录是空的。记得编译内核时打开 CONFIG_DEBUG_FS 和 CONFIG_BINDER_DEBUG。不然你连数据都看不到。
知识体系总览
下面这张图总结了我刚才讲的三层调试方法,以及它们之间的关系:
这三层工具,说白了就是帮你从不同维度看同一个问题。strace 看“有没有发出去”,dumpsys 看“服务在不在”,binder 节点看“卡在哪里了”。组合起来用,效率最高。
嗯,今天就先聊到这里。调试工具再多,关键还是得理解 Binder 的工作原理。工具只是帮你验证猜想的手段。