-
性能提升:改进 Systrace 文件实时写 atrace 数据方式,性能提升最大 400 % 以上。
-
实用性提升:额外提供更详细 IO 等数据,大幅提升方法耗时归因效率;使用独创方案彻底来解决方法因执行异常引起 trace 数据不闭合问题。
项目地址:
https://github.com/bytedance/btrace
目前字节跳动已有多款 App 接入,包括抖音、TikTok、今日头条、幸福里等均已接入 RheaTrace,并为其体验优化提供强有力支持。借助 RheaTrace 将为您的 App 带来极致流畅体验,RheaTrace 使用效果如下(因保密原则,每个方法用 ID 表示)。
Systrace 简介
如果我们使用过 Systrace 分析应用性能,我们都知道 Systrace 提供 Category 配置让用户决定采集哪些系统 atrace 数据,如下命令,从 sched 开始后续是不同类别的 atrace 数据。
python systrace.py -o mynewtrace.html sched freq idle am wm gfx view \
binder_driver hal dalvik camera input res
atrace 的数据类型见下图:
其中,用户空间 atrace 类型包括应用层自定义 Trace 事件、系统层 gfx 渲染相关 Trace、系统层锁相关 Trace 信息等,其最终都是通过调用 Android SDK 提供 Trace.beginSection
或者 ATRACE_BEGIN
记录到同一个文件 /sys/kernel/debug/tracing/trace_marker
中。此节点允许用户层写入数据,ftrace 会记录该写入操作时间戳。当用户层发生函数调用时,ftrace 可以记录被跟踪函数的运行时间。atrace 若需记录用户层某一 trace 类型,只需激活对应 TAG 类型即可。如选择 gfx,则会激活 ATRACE_TAG_GRAPHICS
,并将渲染事件记录到 trace_marker
文件中。
内核空间的数据主要是一些补充分析数据,如 freq、sched、binder 等,常用 CPU 调度相关信息包括:
-
CPU 频率变化情况。
-
任务执行情况。
-
大小核调度情况。
-
CPU Boost 调度情况。
关于图中一些标签释义。
-
CPU 使用率:右边柱状图越高,表明使用率越高。
-
CPU 序号:标识 CPU 核心序号,表示该设备有 8 个核心,编号 0 -7。
-
CPU 频率:右边对应的粉色柱状图表示其频率变化趋势。
-
调度任务:标识在该 CPU 核心上正在运行的任务,点击任务可查看其 ID、优先级等信息。
这些信息 App 可以直接读取 /sys/devices/system/cpu
节点下相关信息获得,而另外一部分标识线程状态信息则只能通过系统或者 adb 才能获取,且这些信息不是统一节点控制,需要激活各自对应的事件节点,让 ftrace 记录下不同事件的 tracepoint。内核在运行时,根据节点的使能状态,会往 ftrace 缓冲中记录事件。
例如,激活线程调度状态信息记录,需要激活类似如下相关节点。
events/sched/sched_switch/enable
events/sched/sched_wakeup/enable
激活后,则可以获取到线程调度状态相关的信息,比如:
-
Running: 线程在正常执行代码逻辑。
-
Runnable: 可执行状态,等待调度,如果长时间调度不到,说明 CPU 繁忙。
-
Sleeping: 休眠,一般是在等待事件驱动。
-
Uninterruptible Sleep: 不可中断的休眠,需要看 Args 描述来确定当时状态。
-
Uninterruptible Sleep - Block I/O: IO 阻塞。
最终,上述两大类事件记录都汇集到内核态同一缓冲中, Systrace 工具是通过指定抓取 trace 类别等参数,然后触发手机端 /system/bin/atrace
开启对应文件节点信息,接着 atrace 会读取 ftrace 缓存,生成只包含 ftrace 信息的 atrace_raw 信息,最终通过脚本转换成可视化 HTML 文件,大致流程如下。
RheaTrace 揭秘
本章节将从 RheaTrace 重点优势一一介绍。
Systrace 源码分析
Systrace 提供 Trace#beginSection(String)
和 Trace.endSection()
采集 atrace 数据,首先,我们大致了解下 atrace 工作原理,以 android.os.Trace#beginSection
作为分析入口。
public static void beginSection(@NonNull String sectionName) {
if (isTagEnabled(TRACE_TAG_APP)) {
if (sectionName.length() > MAX_SECTION_NAME_LEN) {
throw new IllegalArgumentException(“sectionName is too long”);
}
nativeTraceBegin(TRACE_TAG_APP, sectionName);
}
}
android.os.Tra