简介
使用 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_switch,sched/sched_wakeup,sched/sched_waking,sched/sched_blocked_reason,sched/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_tracehelper script. -
Using directly the
/system/bin/perfettocommand on device [reference].
UI tools:
-
Through the record page in the Perfetto UI.
-
Using the on-device System Tracing App
命令行抓取
直接使用命令行抓取( 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 跟踪可视化
-
Navigate to ui.perfetto.dev in a browser. 在浏览器中导航到 ui.perfetto.dev 。
-
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)。 -
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
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(),而在另一个线程上结束它。您必须在同一线程中调用这两个函数。
跟踪整个函数
- 定义宏:
#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();
}
};
- 在要跟踪的函数中调用此宏:
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");
}
参考资料
官方文档:
了解 Systrace | Android 开源项目 | Android Open Source Project
定义自定义事件 | Android 开发者 | Android Developers