经常遇到binder泄露的问题,要怎么分析呢
一 加log
1: binder 驱动log :要先其他log锁定时间点,因为他的进程号一直是0 所以不建议这里加log。
可以直接打印对应的文件信息:/dev/binderfs/binder_logs/state
里面列出了各个进程对应的binder node信息,可以大致看下有没有binder句柄泄露的情况
2: writeStrongBinder 客户端只要有binder请求,都会调用这个接口
对应的服务端也会调用这个readStrongBinder接口(native层对应的接口也有)
3:给binder 传入自己的 ProxyTransactListener 可以实现客户端调用开始结束等的监听
参考 MediaProvider里面的Binder.setProxyTransactListener(mTransactListener);
4: BinderProxy内部的map缓存也会在set的时候打印关键信息
加log信息:看对应进程的binder连接数,kenel看node数,必要时候打印堆栈信息以获取具体泄露调用链,或hprop文件查看对应的泄露对象和调用链。
打印hprop文件
Debug.dumpHprofData("/data/system/dropbox/" + file);
打印堆棧信息:
Log.i("ygn",new Exception("Custom Exception")
二 案例总结
1 surfaceflinger 中layer大于最大值会提示报错:
这种问题就是binder句柄泄露导致的,这种情况往往会有两种情况导致,一是wms端 的binder(surface,windowstate) 泄露,或者 app端 mWindow 对应的binder泄露
2 system_server崩溃重启:
这种往往会搜到对应的各个进程的bpbinder句柄数,进而能大概知道哪个进程binder泄露,而如果要进一步查找对应的泄漏对象及其引用堆栈,需要打印 hprof 文件协助分析
3 app端 提示binder对端可能已死:
这种原因往往是binder缓存空间用尽导致,两种情况导致
一是 该端频繁调用未释放,一是 该端使用了oneway,频繁调用且服务端又比较耗时,导致堵死在异步todo队列里占满了缓存空间
4 app端比较常见的一种 window was originally added here 这种泄漏,这个问题原因
就是子窗口需要依附父窗口,但父窗口却先结束了,导致这个子窗口泄漏,子窗口往往是dialog等,父窗口一般是activiy。
5 binger内存异常或线程池异常
binder 内存异常
比如oneway操作单次会返回,但服务端同类型的请求会排队处理,携带的数据就会停留再内存里,多了的话就会内存异常
binder 线程池耗尽
systemserver好像是最大是33个吧,普通servier是15个
如果请求频繁,或请求的比较耗时都会导致线程池耗尽,不严重的会卡顿,严重的就是anr了
以上这些都是有log信息可以查看的
sys/kernel/debug/binder/下面就是调试信息,开关也是可以动态打开的
主要看里的transactions 文件 一个是buffer,一条就是对应一次请求的内存大小,多条就是多次,如果过多就会内存异常
里面还有outgoing transaction,和ingoing transaction,这些都是传输任务,代表了一个线程,多了的话自然就会耗尽并卡顿。
transactions文件内容参考:
proc 1523
context hwbinder
thread 1542: l 02 need_return 0 tr 0
incoming transaction 126150: 0000000000000000 from 1744:1838 to 1523:1542 code 4 flags 10 pri 1:89 r1 node 234 size 44:0 data 0000000000000000 //代表了一个线程
buffer 126150: 0000000000000000 size 44:0:0 active //代表了一块内存
6 binder 泄漏
这种一般会有lowmemory的文件,里面会打印binder的相关信息
关键字:“context ” 可以区隔每个进程对应的binder引用的个数
行 3631: context binder
行 3649: context binder
根据行号可知 小于18个
一般大于2000个都是有可疑的,出问题的大部分都是再1万以上
ref 6714697: desc 0 node 3 s 1 w 1 d 0000000000000000(ref: binder客户端引用的值,node则是本地binder的值)如果大量打印,说明该进程对于binder的引用很多,很有可能是binder句柄泄漏了