Perfetto工具简介

2,707 阅读7分钟

简介

使用 perfetto 工具,您可以通过 Android 调试桥 (adb) 在 Android 设备上收集性能信息。使用 adb shell perfetto ... 命令调用 perfetto 工具。 perfetto 从您的设备上收集性能跟踪数据时会使用多种来源,例如:

  • 使用 ftrace 收集内核信息

  • 使用 atrace 收集服务和应用中的用户空间注释

  • 使用 heapprofd 收集服务和应用的本地内存使用情况信息

ftrace:是一个内核函数跟踪器,function tracer,旨在帮助开发人员和系统设计者可以找到内核内部发生的事情。

atrace:Android tracer,使用ftrace来跟踪Android上层的函数调用。为数据采集部分

systrace:Android的trace数据分析工具,将atrace采集上来的数据,以图形化的方式展现出来。

ftrace

ftrace是Function Trace的意思,最开始主要用于记录内核函数运行轨迹;随着功能的逐渐增加,演变成一个跟踪框架。

包含了静态tracepoint,针对不同subsystem提供一个目录进行开关;还包括不同的动态跟踪器,function、function_graph、wakeup等等。

ftrace的帮助文档在Documentation/trace,ftrace代码主要在kernel/trace,ftrace相关头文件在include/trace中。

WiKiPedia有关于ftrace的简单介绍。

ftrace的作者在LinuxCon 2010有一篇关于**Ftrace Linux Kernel Tracing**的slides值得一读。

ftrace 文档:<kernel tree>/Documentation/trace/ftrace.txt

  • ftrace ftrace 是 Linux Kernel 自带的调试框架,它在内核态工作,用户可以通过 debugfs 接口来控制和使用 ftrace。目前 debugfs 一般挂载在 /sys/kernel/debug/tracing/
  • atrace 这个是 Android 针对 ftrace 的一个封装,它将 ftrace 的各种 Event 抽象为 tag,一个 atrace tag 对应多个 ftrace Event,例如 sched 这个 tag 分别包括了 sched/sched_switchsched/sched_wakeupsched/sched_wakingsched/sched_blocked_reasonsched/sched_cpu_hotplug 等这些 Event。
  • systrace systrace 其实是 Android SDK 里的一个工具,是对 atrace 的主机端封装,利用 atrace 来使能 ftrace,然后读取 ftrace 的缓冲区并将其全部封装到一个独立的 HTML 文件。

具体的可以看一下内核工匠的这篇文章,写得很详细。

而我们在 framework 中,Java 代码常用的 Trace.traceBegin()Trace.traceEnd();C++ 有 atrace_begin()atrace_end() 以及一系列 ATRACE_ 打头的宏,其实都是利用 atrace 来实现 trace 的抓取。

Systrace抓取的trace数据,总体上可以分为两类,一类是Java和Native在用户层发生的函数调用,一类是内核态的事件信息。

用户层的函数调用,其实最终事件记录都是同一个文件节点/sys/kernel/debug/tracing/trace_marker。

此节点允许用户层写入字符串,ftrace会记录该写入操作时的时间戳,当用户在上层调用不同函数时,写入不同的调用信息,比如函数进入和退出分别写入,那么ftrace就可以记录跟踪函数的运行时间。

atrace在处理用户层的多种trace类别时,仅仅是激活不同的TAG,如用户选择了Input类别,则激活ATRACE_TAG_INPUT;选中Graphics则激活ATRACE_TAG_GRAPHICS,记录调用的流程都是一样的,最终都是记录到trace_marker。

内核态的事件信息,则不是统一的,需要激活各自对应的事件节点,让ftrace记录下不同事件的tracepoint。例如激活进程调度信息记录,需要激活如下相关节点:

events/sched/sched_switch/enable

events/sched/sched_wakeup/enable

内核在运行时,根据节点的使能状态,会往ftrace缓冲中打点记录事件。

最终,上述两类事件记录都汇集到同一缓冲中,然后atrace工具会回读抓取,保存下来就是原始的systrace信息。

使用

启用

Perfetto is based on platform services that are available since Android 9 (P) but are enabled by default only since Android 11 (R). On Android 9 (P) and 10 (Q) you need to do the following to ensure that the tracing services are enabled before getting started:

# Needed only on Android 9 (P) and 10 (Q) on non-Pixel phones.
adb shell setprop persist.traced.enable 1

抓取trace

有两种方式抓取trace:命令行与 Perfetto UI 在线抓取

Command line tools (usage examples below in this page):

  • Using the tools/record_android_trace helper script.

  • Using directly the /system/bin/perfetto command on device [reference].

UI tools:

命令行抓取

直接使用命令行抓取( Linux and Mac):

curl -O https://raw.githubusercontent.com/google/perfetto/master/tools/record_android_trace
chmod u+x record_android_trace

# See ./record_android_trace --help for more
./record_android_trace -o trace_file.perfetto-trace -t 10s -b 32mb \
sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory

或者直接在Android设备上运行:

adb shell perfetto -o /data/misc/perfetto-traces/trace_file.perfetto-trace -t 20s \
sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory

通过设置中开发者选项抓取

1、打开开发者选项中的“System Tracing”,位置在Develop options -> System Tracing。

2、点击“Record trace”选项,打开此功能,这时状态栏会显示“Trace is being recorded”。

3、做你想要的操作,完成操作后,可以点击状态栏中的“Trace is being recorded”选项,也可以再点击“System Tracing”选项,会自动保留trace文件。

4、默认保存的trace文件路径在:/data/local/traces目录中。

xxx:/data/local/traces # ls
trace-xxx-RD2A.211001.002-2022-02-16-14-17-46.perfetto-trace

5、adb pull把trace文件弄出来。拖动到ui.perfetto.dev/ 中,即可分析。

Visualizing the trace 跟踪可视化

  1. Navigate to ui.perfetto.dev in a browser. 在浏览器中导航到 ui.perfetto.dev

  2. Click the Open trace file on the left-hand menu, and load the captured trace (by default at /tmp/trace.protobuf). 单击左侧菜单上的 “Open trace file” ,并加载捕获的跟踪(默认情况下在 /tmp/trace.protobuf )。

  3. Explore the trace by zooming/panning using WASD, and mouse for expanding process tracks (rows) into their constituent thread tracks. Press "?" for further navigation controls. 通过使用 WASD 进行缩放、平移来浏览,鼠标点击可将流程跟踪(行)展开为其组成的线程跟踪。按 “?” 键进行进一步的导航、控制。

Frame Timeline

perfetto.dev/docs/data-s…

Frame Timeline需要Android12才能生效:

示例:

添加自定义trace Label

Java层的通过android.os.Trace类完成,native层通过ATrace宏完成

APP层(java层)

定义自定义事件  |  Android 开发者  |  Android Developers

自定义trace label:

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
    Trace.beginSection("Fragement_onCreateView");
    // .. 其他代码
    // ...
    // .. 结束处
    Trace.endSection();
}

systrace官方文档说待trace的App必须是debuggable的。

如果我们想要待分析的App尽可能接近真实情况,那么必须要在非Debug的App中能启用systrace功能;因为相同情况下Debug的App性能比非Debuggable的差,你无法确保在debuggable版本上分析出来的结论能准确推广到非debuggable的版本上。

把如下这段代码放在Application的attachBaseContext 中,这样就可以手动开启App自定义Label的Trace功能,在非debuggable的版本中也适用!

Class<?> trace = Class.forName("android.os.Trace");
Method setAppTracingAllowed = trace.getDeclaredMethod("setAppTracingAllowed", boolean.class);
setAppTracingAllowed.invoke(null, true);

例子:

native层(C++层)

原生代码中的自定义跟踪事件  |  Android 开发者  |  Android Developers

#include <android/trace.h>

char *customEventName = new char[32];
sprintf(customEventName, "User tapped %s button", buttonName);

ATrace_beginSection(customEventName);
// Your app or game's response to the button being pressed.
ATrace_endSection();

注意:如果您多次调用 ATrace_beginSection() ,调用 ATrace_endSection() 只会结束最后调用的 ATrace_beginSection() 方法。因此,对于嵌套调用,请务必将每次对 ATrace_beginSection() 的调用与一次对 ATrace_endSection() 的调用正确匹配。

此外,您不能在一个线程上调用 ATrace_beginSection() ,而在另一个线程上结束它。您必须在同一线程中调用这两个函数。

跟踪整个函数

  1. 定义宏:
#define ATRACE_NAME(name) ScopedTrace ___tracer(name)

// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)

class ScopedTrace {
  public:
    inline ScopedTrace(const char *name) {
      ATrace_beginSection(name);
    }

    inline ~ScopedTrace() {
      ATrace_endSection();
    }
};
  1. 在要跟踪的函数中调用此宏:
void myExpensiveFunction() {
  ATRACE_CALL();
  // Code that you want to trace.
}

例子:

为线程命名

您可以为发生事件的每个线程命名,如以下代码段所示。此步骤可让您更轻松地识别属于游戏中特定操作的线程。

#include <pthread.h>

static void *render_scene(void *parm) {
    // Code for preparing your app or game's visual components.
}

static void *load_main_menu(void *parm) {
    // Code that executes your app or game's main logic.
}

void init_threads() {
    pthread_t render_thread, main_thread;

    pthread_create(&render_thread, NULL, render_scene, NULL);
    pthread_create(&main_thread, NULL, load_main_menu, NULL);

    pthread_setname_np(render_thread, "MyRenderer");
    pthread_setname_np(main_thread, "MyMainMenu");
}

参考资料

官方文档:

perfetto.dev/docs/

perfetto.dev/docs/data-s…

了解 Systrace  |  Android 开源项目  |  Android Open Source Project

定义自定义事件  |  Android 开发者  |  Android Developers

呈现速度缓慢  |  Android 开发者  |  Android Developers

Systrace 的原理、流程及定制