初学安卓framework系列 五 (监控系统的performance, 初探Perfetto工具)

2,692 阅读6分钟

以前作为app开发者,可以通过Android Studio自带的app profiler来查看单一应用的performance状况。比如内存占用,网络请求等等

Screenshot 2023-05-14 at 8.52.17 PM.png

但是成为framework开发之后,就不能只局限于单一的应用的profiler了。因为一个app的performance其实也取决于当前系统的运行状态。比如开启某一个线程运行耗时操作,这个线程到底在某一个短时间内分配了多少CPU cycle,决定了它多快能运行完成。当系统异常繁忙的时候,单一线程所能分配到的cycle自然变少。

所以今天我就简单的介绍一下一个可以profile整个系统的运行状态的工具Perfetto, 来看看怎么监控整个系统的performance。这期文章会以监控线程运行状态,Binder call为重点。

Perfetto初探

Perfetto 是谷歌推出的新一代系统级别performance的监控工具(可以监控Linux和Android),

Screenshot 2023-05-14 at 8.58.41 PM.png

详细的用途和用法可以参考官网 : perfetto.dev/

简单的说,开发者可以通过一个比较傻瓜式的网站UI tool来提取 trace文件,也可以用一个script自行配置。但是原理都是一样的,通过修改data source 来提供想监控的状态。

通过UI工具监控

Screenshot 2023-05-14 at 9.04.35 PM.png

通过script监控

Screenshot 2023-05-14 at 9.04.45 PM.png

值得注意的是Perfetto默认不监控binder和所有app的trace,在这里我们需要改动一下配置开启监控binder和所有app的trace。然后我们也可以自定义duration_ms字段来控制profile的时间长短。我工作的时候需要监控接听电话的performance,所以我一般设置成30秒。

Screenshot 2023-05-14 at 9.07.10 PM.png

开启监控之后,马上进行想监控的操作(无论是app的行为,还是系统的行为),然后Perfetto工具会自动打开一个可视化的网站分析得到的监控trace文件。Trace文件可视化之后长这个样子:

Screenshot 2023-05-14 at 9.03.42 PM.png

左边一列列出了在当前监控session之内开启的所有进程。我们可以借此查看我们想查看的app/进程的运行状态。

点击某一个进程,左边会列出在监控session中启动的所有线程和线程中记录的trace(下面会具体讲一下trace)

Screenshot 2023-05-14 at 9.13.56 PM.png

怎么加Trace

Trace代表了一段你想监控的代码段。在Perfetto 可视化工具里面长这样。

Screenshot 2023-05-14 at 9.20.45 PM.png

以上是一个大Trace里面包含了很多小Trace, 原因也很简单,一个有Trace的方法A可以包含若干个也带有Trace的方法B,C,D,E。。。

在代码里面加Trace也很简单,

new Thread(() -> {
    Trace.beginSection("test2");
    try{
        //模拟耗时工作
        Thread.sleep(4000);
    }
    catch (Exception e) {

    }
    Trace.endSection();
}, "richard").start();

加了这个代码之后,在Perfetto 可视化工具就可以看到该Trace

Screenshot 2023-05-14 at 9.24.29 PM.png

同理如果你在一个方法里面写多个Trace

new Thread(() -> {
    Trace.beginSection("test2");
    try{
        //模拟耗时工作
        Thread.sleep(2000);
        Trace.beginSection("test3");
        //模拟调用另一个带trace的方法
        Thread.sleep(2000);
        Trace.endSection();
    }
    catch (Exception e) {

    }
    Trace.endSection();
}, "richard").start();

Screenshot 2023-05-14 at 9.27.48 PM.png

所以,合理的编写trace代码可以轻松的在监控工具中查看方法调用的耗时和关系。

安卓在系统层很多的地方都加入了默认的trace,不过不少都需要系统开发者在代码中手动开启,需要自己重新编译系统的ROM。比如Telecom component:

Screenshot 2023-05-14 at 9.30.59 PM.png

Screenshot 2023-05-14 at 9.31.13 PM.png

所以也侧面说明了,要想能合理的调试系统performance,其实要先有对要监控的部位的基础认知。。。。不然在哪打log,或者trace都不知道的话,光看现有的trace可能并不会特别有用。

最后安卓也支持异步的trace。比如开始trace和结束trace在不同线程 (但是这种trace貌似出来查看耗时之外并不是特别有用,因为这种线程不同的情况下,trace会单独列出来不和线程绑定)

Trace.beginAsyncSection("test1", 1);
new Thread(new Runnable() {
    @Override
    public void run() {

        try{
            Thread.sleep(4000);
        }
        catch (Exception e) {

        }
        Trace.endAsyncSection("test1", 1);
    }
}, "richard").start();

Screenshot 2023-05-14 at 9.36.11 PM.png

线程运行状态

如果你仔细观察,会发现同一个线程会有两行。

Screenshot 2023-05-14 at 9.39.06 PM.png

其实这两行的目的是不一样的,第一行一般记录线程的运行状态,第二行记录线程的trace。

我们把第一行放大看看

Screenshot 2023-05-14 at 9.41.59 PM.png

可以发现第一行记录了线程在这个session中什么时候是running的状态(被分配了CPU cycle,在CPU中运行),什么时候是runnable的状态(没分配到CPU,在等待中)。

通过鼠标的拖拽,可以把某一段trace包起来,查看这个trace总体被分配的CPU情况

Screenshot 2023-05-14 at 9.43.53 PM.png

可以看到这一段trace里面,正在在CPU中运行的时间只有10微秒左右,等待的时间是运行的十倍左右。

如果这段代码不是刻意的在代码中等待线程或者进行IO的话,说明当前线程被分配的CPU cycle不是很够。这就是值得注意需要优化的地方了。

Binder call的追踪

和以前的App profiler相比,Perfetto最大的提高之一就是在可视化工具中提供了Binder call的追踪。开发者可以查看任何一个IPC的caller 和 consumer。

比如我们在App中调用系统API,去查看对某个系统API从哪里call,到哪个进程。

new Thread(new Runnable() {
    @Override
    public void run() {
        Trace.beginSection("test1");
        TelephonyManager manager = MainActivity.this.getSystemService(TelephonyManager.class);
        try{
            manager.getEmergencyNumberList();
            Thread.sleep(4000);
        }
        catch (Exception e) {

        }
        Trace.endSection();
    }
}, "richard").start();

以上代码中我们调用了系统API TelephonyManager#getEmergencyNumberList, 开启监控之后运行该代码,查看可视化工具

Screenshot 2023-05-14 at 9.51.27 PM.png

可以看到在trace test1 下面果然有一个binder call的trace,点击该trace的binder reply,可视化工具会自动跳转到binder的reply进程

Screenshot 2023-05-14 at 9.54.43 PM.png

可以看到该binder的reply是在com.android.phone(也就是Telephony)进程。

查看源代码的package name和 服务端实现

Screenshot 2023-05-14 at 9.56.04 PM.png

Screenshot 2023-05-14 at 9.56.17 PM.png

果然!!!

download.jpeg

通过监控binder call的trace,我们作为系统开发者可以了解某个app在调用IPC的时候,为何会耗时,耗时的进程在哪,服务端的进程的运行状态等等,从而做出优化选择。

总结

这篇文章简单的介绍了一下Perfetto的使用方法,其实Perfetto除了以上三个用途还有很多强大的功能,这里只做简单的介绍,下篇文章会用一个实际的例子来讲述怎样通过Perfetto 监控工具来发现可以优化的系统代码的 (呵呵又水了一篇文章!!!!!)

最近工作实在太忙了,更新速度肉眼可见变慢。。。。。。求原谅

download (1).jpeg