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 - SleepingRunning-Sleeping.jpg常见于线程工作结束线程使用Thread.sleep延时线程被Binder服务端阻塞线程陷入等锁阻塞状态等具体原因需要结合线程Sleeping结束后线程被唤醒时的信息分析判断是否合理后文会详细分析。2.1.2 Sleeping - RunnableSleeping-Runnable.jpg处于Sleeping状态的线程被唤醒重新进入Runnable待执行状态Systrace上会显示出唤醒该线程的线程号。2.1.3 Running-Runnable-RunningRunning-runnable-running.jpg正在执行的线程任务被更高优先级的线程任务临时抢占Systrace上可以完整清晰的看到发生抢占的原因。2.1.4 Running - Uninterruptible Sleeping - RunnableRunning-uninterrupt-runnable.jpg正在执行的线程陷入内核锁等待状态Systrace上可以看到被内核阻塞的详细信息通过addr2line工具结合symbol table信息就可以查到产生问题的内核代码行号帮助进一步定位问题原因。2.1.5 Runnable - Runningrunnable-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_1Ctid为8289线程然后进入Sleeping状态然后Binder:2323_1C线程中执行完startActivity具体内部逻辑后会唤醒桌面的UI线程。这就是一次从Systarce上看到的应用Binder阻塞调用访问框架服务接口的线程相互等待唤醒的过程。文章部分参考https://mp.weixin.qq.com/s/gZ_-P2Avmm0SyE-hSvs6oA更多fw实战开发干货请关注下面“千里马学框架”