愿历尽千帆 归来仍少年

案例分析:打开应用后操作界面无响应(Systrace)

字数统计: 1,638阅读时长: 6 min
2021/11/08

问题现象

打开应用(热启动)后,概率性出现手指滑动界面无任何响应(界面控件支持上下滑动)

Systrace角度分析

先看下应用区域帧绘制的情况

图片

这个时间段除了开头的几帧,后面几乎是空白的,复现时手指明明是一直滑动的。

先确认下input事件是否正常,在分析input事件流转是否正常之前

我们先简单的介绍下input事件大概的流转过程

1.触摸屏每隔几毫秒扫描一次,如果有触摸事件,那么把事件上报到对应的驱动

2.InputReader 读取触摸事件交给 InputDispatcher 进行事件派发

3.InputDispatcher 将触摸事件发给注册了 Input 事件的 App

4.app 拿到事件之后,进行 Input 事件分发,如果此事件分发的过程中,App的

UI 发生变化,那么会请求 Vsync,则进行一帧的绘制

先看下input down的时间点

图片

后面的事件是连续且密集的,说明我们复现抓取的时候,报点是没有问题的。

这个时候我们注意到一个奇怪的现象,outBoundQueue和waitQueue是空的。

关于outBoundQueue和waitQueue简单介绍下

1.当应用窗口准备就绪,将mPendingEvent转移到outBoundQueue队列,对应的是即将要被派发给对应 AppConnection 的事件。

2.当outBoundQueue不为空,且应用管道对端连接状态正常,则将数据从outboundQueue中取出事件,放入waitQueue队列,记录的是已经派发给 AppConnection 但是 App 还在处理没有返回处理成功的事件。

如果outBoundQueue和waitQueue是空的话,说明此时应用窗口未就绪。我们操作时窗口明明可见的,难道应用D状态了?

带着这个疑问,我们再次回到应用所在区域

图片

可以看到飞书热启后,多个运行线程处于非IO导致的D状态,另外结合基本没有占据CPU资源这一情况。

可以认定之所以应用没有响应down事件,正是由于其处于非IO导致的D状态。

下面就需要调查为何应用D状态了,很快在应用区域注意到一个现象

图片

我们知道匿名页被压缩换到Zram通常出现在kswapd回收内存场景中。

但是还有一种场景我们可能会忽略,那就是adj变化触发的进程压缩同样会消费Zram。

为了验证是进程压缩导致,来到压缩线程所在区域。为了看起来更直观,将down事件和压缩线程放在一起

图片

到这里我们可以确定飞书之所以没有响应到input down事件,正是由于其当时正在进行进程压缩从而导致其处于D状态。

问题完整时间线

到这里我们将这个案例完整的故事情节还原出来

  1. 在一次退出飞书后,此时引起adj变化,进入压缩判断环节,此时满足一系列条件(如匿名页大小, 前后时间差, adj值)后,压缩线程开始对飞书应用进行压缩。

  2. 很快再次打开飞书(29s793ms)

  3. 此时手指按下开始滑动(29s988ms)(不松手),注意这个时候压缩还未完成,飞书仍处于进程压缩导致的D状态中,所以未能响应down事件。

    我们知道一个事件序列是由down+若干move+up组成,由于down事件丢失以至于后面的move事件未能响应。

  4. 飞书压缩完成(30s598ms),该主界面进程压缩过程持续约882.6ms,完成后飞书解除D状态。

这个过程简单点说,在飞书可见后,如果上次压缩未完成,这时候手指按下的down事件都会被丢弃。

这也解释了为何该问题在快速切换场景下更容易出现

压缩速度

这个时候,你可能会觉得疑惑,是不是压缩速度慢导致的这个问题?

下面我们将试着计算问题时的”压缩速度”, 这里对压缩速度之所以加引号后面会解释

图片

从我们这次复现的Systrace来看,飞书压缩完成后,其Swap占据从118.8M增长到232.7M,共计压缩了约114M的匿名页,共计耗时882ms

用压缩量除以耗时时间得出”压缩速度”约129M/s。

影响压缩速度的因素可能有哪些呢?

主要是压缩算法,CPU频率,DDR频率,内存读写性能这几方面因素

我们从Systrace中再看下压缩期间的CPU运行情况

图片

可以看到有较多的Runnable (Preempted),说明该时间段内可能负载较重,存在CPU争抢的情况。

除了等CPU的片段外,还有很多片段跑在了小核上

图片

这也是为何上面提到的”压缩速度”加引号的原因。正好压缩的耗时882ms并不是一直在running状态。

其实这点比较好理解,好比你开车上班,短短的五公里的车程却有二十个红绿灯,这个时候用路程除以耗时得出的”车速”,严格意义上说并不准确。

测试实验

我们前面已经发现压缩期间很多片段跑在了小核上,那么如果给压缩线程更高的CPU资源权重,是否能提升”压缩速度”呢?

于是,对压缩线程占据CPU的权重进行调整后,再次抓取了一份Systrace

图片

从修改后抓取的Systrace可以看到,这次压缩线程确实大部分时候跑在了大核上。

从图中可以看到本次压缩了约144M匿名页,耗时约450ms,”压缩速度”约320M/s

相比修改前,”压缩速度”提升了近三倍,但是其实仍然不够快,还是会有一定概率发生手指按下时,正好落在压缩未完成的时间段内,只是相比之前大幅降低了该问题复现概率。

小结

如果压缩算法已经确认是最优的选择(平衡压缩率和解压缩速度)情况下,通过尽量赋予压缩线程更高的CPU优先级确实能够缩短耗时(假设压缩量固定),但是最大的影响因素在于内存读写性能。


愿你秃顶归来,内心依旧少年

CATALOG
  1. 1. 问题现象
  2. 2. Systrace角度分析
  3. 3. 问题完整时间线
  4. 4. 压缩速度
  5. 5. 测试实验
  6. 6. 小结