第23章 性能分析与优化:Systrace使用指南、Perfetto性能分析、CPU/GPU频率调节、内存泄漏检测

性能优化这事儿,说难也难,说简单也简单。我做了这么多年嵌入式,最大的体会就是——你得先会看数据,再谈优化。没有数据支撑的优化,都是瞎蒙。

这一章,咱们就聊聊Android Automotive系统里最常用的几个性能分析工具。Systrace、Perfetto、CPU/GPU调频、内存泄漏检测,这四个东西你玩明白了,系统卡顿、掉帧、发热这些问题,基本都能找到根因。

23.1 Systrace使用指南

Systrace是Google官方提供的性能追踪工具。说白了,它能把系统里各个进程在干什么、花了多长时间,用时间轴的方式展示出来。你想想看,这比靠猜靠谱多了。

我在项目中遇到过好几次,车机启动慢,UI掉帧,大家都说是驱动问题。结果一抓Systrace,发现是某个Service在启动时做了大量IO操作,把主线程堵死了。嗯,工具不会骗人。

23.1.1 基本用法

抓取Systrace的方式很简单,命令行搞定:

# 抓取5秒的trace,包含所有标签
python systrace.py -t 5 -o trace.html

# 抓取特定标签,减少数据量
python systrace.py -t 5 -o trace.html gfx input view webview

我个人习惯在抓取时加上 -a 包名 参数,这样能追踪到应用层的事件:

python systrace.py -t 5 -o trace.html -a com.android.car
小技巧:抓取时间不要太长,5-10秒足够。时间太长,生成的HTML文件会很大,浏览器打开都费劲。

23.1.2 关键指标怎么看

Systrace打开后,你会看到一堆彩色的条条。别慌,重点看这几个:

  • SurfaceFlinger:负责合成显示,如果这里出现大段的空白,说明GPU在等数据
  • Choreographer:负责帧率控制,掉帧的话这里会有红色标记
  • CPU频率:看CPU是不是在偷懒,频率上不去会导致任务堆积
  • Binder事务:跨进程调用,如果Binder调用耗时太长,说明服务端响应慢

我曾经遇到一个案例,车机滑动列表卡顿。看Systrace发现SurfaceFlinger每帧都在等一个Binder调用返回。查下去,是某个ContentProvider查询数据库太慢。优化SQL索引后,问题解决。

23.1.3 自定义Trace点

有时候系统自带的Trace点不够用,你可以在自己的代码里加:

// Java层
Trace.beginSection("my_custom_trace");
// 你的代码
Trace.endSection();

// Native层
ATRACE_BEGIN("my_native_trace");
// 你的代码
ATRACE_END();
注意:Trace.beginSection和endSection必须成对出现,否则会导致trace数据异常。我见过有人忘记调用endSection,结果整个trace都乱了。

23.2 Perfetto性能分析

Perfetto是Systrace的升级版。说实话,Systrace在Android 10之后基本被Perfetto取代了。Perfetto能抓的数据更多,分析能力更强,而且支持SQL查询。

23.2.1 抓取Perfetto数据

Perfetto有两种抓取方式:命令行和网页工具。我推荐用命令行,可控性更强:

# 抓取10秒数据
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace -t 10s sched freq idle am wm gfx view

# 拉取到本地
adb pull /data/misc/perfetto-traces/trace.perfetto-trace .

抓完后,打开 https://ui.perfetto.dev,把trace文件拖进去就能看了。

23.2.2 Perfetto的核心优势

特性 Systrace Perfetto
数据量 有限,容易丢数据 支持长时间抓取,数据完整
SQL查询 不支持 支持,可以精确筛选
多进程分析 基本支持 支持更细粒度
内存分析 不支持 支持Native内存追踪

我个人特别喜欢Perfetto的SQL查询功能。比如我想看所有耗时超过10ms的Binder调用:

SELECT name, dur FROM slice 
WHERE name GLOB '*binder*' AND dur > 10000000
ORDER BY dur DESC

这比肉眼在时间轴上找快多了。

23.2.3 实战:分析启动卡顿

我记得有一次,客户反馈车机冷启动时,中控屏要等3秒才亮。用Perfetto抓了启动trace,发现SurfaceFlinger在等待一个叫 car_service_init 的Binder调用。查代码,原来是CarService在启动时同步加载了所有车载设备的配置,里面有个设备响应超时,等了2秒。

优化方案很简单:把配置加载改成异步,先让UI显示出来,后台慢慢加载。启动时间从3秒降到了0.8秒。

核心思路:性能优化不是盲目改代码,而是先定位瓶颈。Perfetto就是你的手术刀,精准切割。

23.3 CPU/GPU频率调节

CPU和GPU的频率调节,直接影响系统性能和功耗。调高了,性能好但发热;调低了,省电但卡顿。怎么找到平衡点?

23.3.1 CPU调频策略

Android系统默认使用 interactiveschedutil 调频器。你可以通过 /sys/devices/system/cpu/cpu*/cpufreq/ 下的节点查看和修改:

# 查看当前调频策略
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# 修改为performance模式(最高性能)
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# 查看可用频率
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies

在车载场景下,我建议不要用 performance 模式,发热太严重。用 schedutil 配合自定义的调频阈值,效果更好。

23.3.2 GPU调频

GPU调频和CPU类似,节点在 /sys/class/kgsl/kgsl-3d0/ 下:

# 查看GPU当前频率
cat /sys/class/kgsl/kgsl-3d0/gpuclk

# 设置最大频率
echo 500000000 > /sys/class/kgsl/kgsl-3d0/max_gpuclk

我曾经在项目里遇到一个问题:车机导航时,地图滑动卡顿。看Perfetto发现GPU频率一直上不去,只有200MHz。查了调频策略,发现温度传感器误报高温,导致GPU被限频。修复温度传感器后,GPU能跑到600MHz,卡顿消失。

避坑指南:我曾经在量产前修改了CPU调频策略,结果高温测试时设备频繁重启。后来发现是调频阈值设得太激进,CPU温度飙升触发了热保护。所以,调频策略一定要在高温环境下验证。

23.4 内存泄漏检测

内存泄漏是Android系统的老大难问题。尤其是车载系统,7x24小时运行,内存泄漏会导致系统越来越慢,最后OOM重启。

23.4.1 使用LeakCanary

LeakCanary是Square公司开源的Android内存泄漏检测库。集成简单,效果显著:

// build.gradle
dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
}

// 自动检测,无需额外代码
// 当发生内存泄漏时,会弹出通知并生成dump文件

LeakCanary适合开发阶段使用。但在车载系统上,我建议只在debug版本开启,release版本一定要关掉,否则会影响性能。

23.4.2 使用Android Studio Profiler

Android Studio自带的Profiler工具,可以实时查看内存使用情况:

  1. 连接设备,打开Android Studio
  2. 点击右下角的 Profiler 标签
  3. 选择你的进程,点击 Memory
  4. 录制一段操作,然后点击 Dump Java heap
  5. 分析dump文件,查看哪些对象没有被释放

我习惯在Dump heap后,用 Analyzer Tasks 自动检测泄漏。它会列出所有可能的泄漏路径,省去手动分析的麻烦。

23.4.3 Native内存泄漏检测

Java层的内存泄漏相对好查,Native层就麻烦多了。我推荐使用 AddressSanitizer (ASan)HWASan

# 在Android.mk中启用ASan
LOCAL_SANITIZE := address
LOCAL_SANITIZE_DIAG := address

# 或者在Android.bp中
sanitize: {
    address: true,
    diag: {
        address: true,
    },
}

启用ASan后,如果有内存越界或泄漏,程序会直接crash并打印详细的调用栈。虽然会降低运行速度,但定位问题非常精准。

经验之谈:内存泄漏检测,不要等到系统卡死了再查。我建议在CI流程中加入内存泄漏检测脚本,每次提交代码都跑一遍。早期发现问题,修复成本最低。

23.5 本章知识体系

下面这张图,把本章的核心内容串起来了。你可以看到,性能分析是一个闭环:先发现问题,再定位根因,然后优化,最后验证。

性能分析与优化知识体系 Systrace 时间轴追踪 Perfetto SQL分析 CPU/GPU调频 性能与功耗平衡 内存泄漏 LeakCanary/ASan 关键指标 SurfaceFlinger Choreographer Binder事务 核心优势 大数据量 SQL查询 多进程分析 调频策略 schedutil performance 热保护 检测工具 LeakCanary Profiler ASan 优化目标:流畅 + 低功耗 + 稳定 发现问题 → 定位根因 → 优化 → 验证(闭环)

这张图把四个工具串起来了。Systrace和Perfetto负责发现问题,CPU/GPU调频负责性能调节,内存泄漏检测负责稳定性。三者配合,才能打造一个流畅稳定的车载系统。

好了,这一章的内容就到这里。工具和方法都给你了,剩下的就是多练。性能优化没有捷径,唯手熟尔。


公众号:蓝海资料掘金营,微信deep3321