手机解锁卡顿
diag记录数据慢导致系统卡顿
在大量的日志中发现Slow Looper的日志,都是DisplayPowerController的DisplayControllerHandler处理消息超时,并且有大量的dvm_lock_sample提示registerListenerImpl耗时了,亮屏过程中无论system_server还是systemui都会去注册很多sensor,由于大量异常的
registerListenerImpl慢不得不怀疑是sensor注册慢导致的:
|
怀疑归怀疑,但是有没有直接的证据来证明是sensor注册慢导致的,光看这日志没法继续分析到底是哪里慢了
在想办法复现的过程中,刚好被拉进一卡顿群,说有个卡顿的问题想找framework的同学帮忙看一下。那我就去看了一下,发现他们有一个大概率复现的方法。打开打开小爱同学(其实和小爱没关系),设置指纹解锁,然后不断的指纹解锁就能复现卡顿的问题了。
我就用这个方法去复现,发现还真的很好复现,复现之后当然需要知道在哪里卡顿了,这时候十八般武艺可以轮番上阵了:
1.查看system_server卡住的原因
既然是system_server卡顿,那么就在卡顿的时候抓system_server 的trace,具体命令为kill -3 pid(system_server的pid),然后我抓到的关键trace如下:
|
我们能发现PowerMangerService在亮屏的时候去注册sensor卡住了,在等一个锁。那么我们继续看看这个锁被哪个线程拿着的,最后发现是main现场拿着这个锁。main线程拿着这个锁去注册activate 对应的sensor,那么我们就得去看看对端为什么卡住了。
2.查看[email protected]卡在的原因
查看native进程的trace我们一般用的是debuggerd -b pid([email protected]进程的pid),我抓取的关键trace如下:
|
我们用addr2line工具来找到对应的代码行号:
|
我们找到对应的代码:
sensor_diag_log.cpp
|
找到log_commit定义的地方:
diag_lsm_log.c
|
我们通过相关的日志还有trace可以定位到是write超时了,一般通过linux系统调用超时了的情况我们可以猜到是这个线程进入了D状态,那么我们继续看看write在kernel中卡在了哪里,首先我们先找到[email protected]进程,然后去看它有哪些子线程
|
我们去复现问题,然后分别查看tid比较小的线程(一部分是hwbinder线程),然后再看对应的线程状态
|
从这个日志中我们能看到几个信息:
[email protected] 有两个现场进入D状态,一个现场是在write,这个已经可以解释我们系统为什么卡了(因为这里卡住了,导致system_server那边也卡住了,最终系统卡住了)
2.我们还能看到其他的几个线程都是在diagchar_read/diagchar_write的时候进入D状态,还有一个是diagchar_open,并且这个进程是shell进程启动的
查看对应的kernel日志:
|
我们发现这里在频繁的执行“start diag_mdlog_start”,“start diag_mdlog_stop”(不太正常),这两个命令分别是抓子系统命令的日志,通过这两个关键字查找到对应的代码:
|
到了这里我们就已经大概可以猜到问题的原因了,肯定是这个重复触发抓diag日志功能引起的问题。那么我们继续分析刚才我们提到的两个问题:
[email protected] 有两个现场进入D状态,一个现场是在write,这个已经可以解释我们系统为什么卡了(因为这里卡住了,导致system_server那边也卡住了,最终系统卡住了)
首先我们通过对应的trace看write在kernel中到底卡在了哪里:
|
这两个trace是D状态的,那么我们下载对应的vmlinux然后用gdb看一下到底卡在了哪里:
|
我们反编译后找到write时和read时分别卡在了diagchar_core.c 3577行和diagchar_core.c 3632行,对应的代码为:
diagchar_core.c
|
因为光有这个日志不同好分析谁持有锁,所以我们需要触发一个ramdump再继续分析
光通过日志可能不太好分析,我们再复现触发一个ramdump进行分析:
randump加载参考:RAMDUMP抓取方法
|
这个时候我们就得去看看为什么没有及时的调用到diagchar_close。加了很多日志发现一个可疑的卡顿点:
|
对应的代码:
|
我们在ramdump中也找到了对应的调用栈:
|
经过上面的分析我们大概知道了整个流程:
1.首先diagchar驱动初始化的时候有一个work会定时将flushed设置为1.
2.我们的diag_mdlog_system在记录日志时调用diagchar_write卡顿,导致不能及时调用diagchar_close将flushed设置为0,并且调用wake_up_interruptible
3.这时候system_server注册sensor时向[email protected]发起binder call去注册,注册时会调用diagchar_write,因为之前flushed已经被设置为了1所以[email protected]就会停在wait_event_interruptible这里,并进入D状态,导致系统卡住,直到diagchar_close结束