1 Perfetto/Systrace实战线程状态部分分析
分析perfettio/Systrace线程上每一段Trace TAG的详细运行情况如下图所示:


通过分析线程每一段Trace TAG执行过程的线程排程使用状态中Running、Sleeping、Runnable以及Uninterruptible Sleep占用比例和时长等信息,再结合对应的tag信息,我们可以看到:
线程任务是否频繁中断进入sleeping状态导致运行变慢?如果是的话,然后结合具体的tag信息观察线程sleeping原因是Binder通信阻塞?或者是等锁释放阻塞?
线程是否长时间处于Uninterruptible Sleep阻塞状态导致运行变慢?然后结合tag信息观察是否为执行I/O操作导致被Kernel内核阻塞?
线程是否长时间处于Runnable状态得不到执行而导致运行变慢?这个要结合CPU Trace信息观察当前CPU上任务的分布,是否有CPU关核或后台其它线程任务频繁抢占的问题?
如果只是线程Running时长过长导致运行变慢,最终出现上帧超时而掉帧?我们就需要结合具体tag信息查看到底在执行什么逻辑,然后结合自身的代码实现看是否可以优化?又或者是看看当前JIT线程任务是否繁忙,以判断是否是因为应用没有被及时编译成机器码而导致运行时长过长?
Systrace的分析重经验,在深入理解系统运行的基本流程和原理的基础上,无论是系统开发者或应用开发者都需要在平时工作中不断练习与积累,才能在真正遇到问题时,用Systrace工具去分析时做到游刃有余。
2.1 线程运行排程状态转换分析
我们先用一张图来总体看看线程运行状态的变化以及引起变化的原因:

线程排程状态转换.png
从上面的图中可以看出:
一个线程在运行过程中会到受各种因素的影响而在Running、Sleeping、Uninterruptible Sleep和Runnable四个状态之间不断切换。而除了Running状态下时线程工作任务有真正运行在CPU上去执行,其它几种状态下线程任务都无法得到有效的执行,从而可能会引起一些性能相关的问题。所以我们很多时候在分析性能问题,都需要找到线程任务没有持续处于Running状态的原因,还要判断其切换到其它几种状态的原因是否合理。而从Systrace上我们就可以清晰的观察到线程运行状态的变化以及引起状态变化的原因。各种线程运行状态切换场景如下所示:
2.1.1 Running -> Sleeping

Running-Sleeping.jpg
常见于:线程工作结束,线程使用Thread.sleep延时,线程被Binder服务端阻塞,线程陷入等锁阻塞状态等,具体原因需要结合线程Sleeping结束后线程被唤醒时的信息分析判断是否合理,后文会详细分析。
2.1.2 Sleeping -> Runnable

Sleeping-Runnable.jpg
处于Sleeping状态的线程被唤醒,重新进入Runnable待执行状态,Systrace上会显示出唤醒该线程的线程号。
2.1.3 Running->Runnable->Running

Running-runnable-running.jpg
正在执行的线程任务被更高优先级的线程任务临时抢占,Systrace上可以完整清晰的看到发生抢占的原因。
2.1.4 Running -> Uninterruptible Sleeping -> Runnable

Running-uninterrupt-runnable.jpg
正在执行的线程陷入内核锁等待状态,Systrace上可以看到被内核阻塞的详细信息,通过addr2line工具结合symbol table信息就可以查到产生问题的内核代码行号,帮助进一步定位问题原因。
2.1.5 Runnable -> Running

runnable-running.jpg
从Runnable 到Running状态的切换,Systrace上会显示出唤醒这个线程的线程号,从而可以进一步根据该线索分析理清楚线程间的相互等待唤醒关系,找到问题的根本原因。
2.2 线程等待唤醒关系分析
有了上面的分析基础,本小节中我们还是以桌面打开应用冷启动的场景为例,看看如何通过Systrace来分析观察线程之间的唤醒等待关系,从而看清应用进程与系统框架system_server进程之间是如何交互的。只有掌握了如何用Systrace分析线程之间的唤醒等待关系,我们才能去追踪并理清系统内跑在各个进程或线程中的各个功能模块之间是如何相互交互配合去完成一次系统事件流程的处理,个人理解这也就是使用Systrace工具分析问题的精髓所在。

Input2App.jpg
从桌面应用的UI线程从Sleeping状态切换到Runnable状态的Tag信息中可以看到该线程是被一个tid为2796的线程所唤醒,然后我们在Systrace界面右上角的搜索框中输入 2796后,搜索发现该线程的详细信息是属于框架system_server进程中的名为InputDispatcher的工作线程。然后我们选中UI线程Runnable这段时间区域,在system_server进程信息显示区域找到其2796子线程,根据Systrace TAG发现其中正是在执行InputDispatcher#dispatchMotionLocked 事件分发的逻辑。到此我们就理清了这段Input触控事件的传递逻辑,知道Input触控事件是由system_server进程中的名为InputDispatcher的工作线程,唤醒目标应用的主线程进行分发的,这也和上一篇文章中关于Input事件处理机制的理论分析是相符的。
然后桌面应用UI线程中根据连续收到的几条的Input事件判断用户在执行点击应用图标启动应用的动作,然后通过Binder调用框架的startActivity接口尝试去启动应用,从Systrace上分析如下所示:

startActivity.jpg
从线程Runnable状态切换可以看到,桌面的UI线程(线程tid为4893)中会先唤醒system_server进程中的负责处理应用Binder请求的Binder:2323_1C(tid为8289)线程,然后进入Sleeping状态,然后Binder:2323_1C线程中执行完startActivity具体内部逻辑后,会唤醒桌面的UI线程。这就是一次从Systarce上看到的应用Binder阻塞调用访问框架服务接口的线程相互等待唤醒的过程。
文章部分参考:https://mp.weixin.qq.com/s/gZ_-P2Avmm0SyE-hSvs6oA
更多fw实战开发干货,请关注下面“千里马学框架”

1033

被折叠的 条评论
为什么被折叠?



