如何利用Perfetto实现Android系统级性能追踪?

来源:图像处理网作者:辉辉头衔:草根站长
导读:本期聚焦于辉辉创作的《如何利用Perfetto实现Android系统级性能追踪?》,敬请观看详情。想搞清楚Android应用掉帧时CPU在忙什么、主线程被哪些Binder调用阻塞,单看logcat远远不够。Perfetto作为新一代系统级追踪工具,把ftrace、atrace、堆栈采样和进程内存数据汇总到同一条时间线上,适合做跨进程、跨核的深度分析。录制时既可以执行一条adb shell perfetto命令,也可以用配置文件选择sched、binder_driver、gfx等数据源。拿到trace文件后,在Perfetto UI里通过SQL查询切片表,能直接统计线程运行时间、唤醒延迟和锁等待。本文会从架构机制、录制配置、UI分析三个角度展开,并给出定位主线程耗时与锁竞争的具体SQL。

Perfetto是Android平台从10版本开始主推的系统级追踪方案,它把内核调度、系统服务与应用自定义事件合并到同一个时间线上,适合排查跨进程卡顿、CPU争抢和Binder调用问题。与原先的Systrace相比,Perfetto在数据源数量、trace体积和查询能力上都有明显提升,手机系统里常驻的traced进程负责管理数据生产者,开发者只需要通过perfetto命令录制即可。

如何利用Perfetto实现Android系统级性能追踪?

Perfetto的数据来源与缓冲机制

Perfetto的数据源可以粗略分成内核态和用户态两类。内核态主要依赖linux.ftrace,通过读取tracefs中的事件节点获取调度切换、中断、Binder驱动事务等记录。像sched/sched_switch、sched/sched_wakeup、binder/binder_transaction这些ftrace事件不需要修改内核,只要权限足够就能打开。用户态数据源则包括android.log、linux.process_stats、heapprofd等,其中atrace会自动把用户空间TAG映射到对应的ftrace类别或系统服务事件。例如gfx标签会打开SurfaceFlinger和Choreographer相关trace点,view标签会记录View绘制与输入事件。

缓冲机制方面,Perfetto采用生产者和traced服务之间的共享内存缓冲,避免每次事件都陷入系统调用。缓冲区大小可以通过-b参数或配置文件的buffers.size_kb指定。fill_policy有两种常见模式,DISCARD会丢弃最旧数据以容纳新事件,适合长时间固定容量录制;RING_BUFFER则在缓冲区满后停止写入,更适合短时间但必须保留完整事件的场景。这个设计让Perfetto在录制全系统事件时对被分析应用的干扰明显小于早期工具。

与Systrace的直接差异在于数据模型。Systrace主要输出经过汇总的HTML视图,很多原始事件被折叠,Perfetto则保存protobuf编码的完整事件流,并能通过SQL直接查询。像是某个线程在哪几个CPU核上运行、被谁唤醒、在哪个Binder事务上阻塞,这些信息都能从原始表里取出来。

录制系统级Trace的实用方法

最简单的录制方式是执行一条adb shell命令。下面这条命令会录制15秒,缓冲32MB,并同时打开调度、CPU频率、系统服务、图形、Binder驱动和内存等数据源。数据源名称之间用空格分隔,结束后trace文件保存在设备的/data/misc/perfetto-traces目录。

adb shell perfetto -o /data/misc/perfetto-traces/trace.pftrace -t 15s -b 32mb sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory

其中sched和freq负责CPU调度及频率信息,am、wm对应ActivityManager和WindowManager,gfx与view覆盖图形渲染和View层,binder_driver用于追踪跨进程调用。实际排查掉帧问题时,sched、gfx、view、binder_driver通常要一起打开,否则时间线上会缺少关键上下文。

如果希望更精确控制ftrace事件和缓冲区策略,可以写一个文本配置文件。下面配置文件打开调度切换、唤醒和Binder事务事件,同时扫描全部进程信息,录制时长10秒。把配置保存为config.txt后推送到设备,再通过-c参数读取即可。

buffers {
  size_kb: 32768
  fill_policy: DISCARD
}
data_sources {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "sched/sched_switch"
      ftrace_events: "sched/sched_wakeup"
      ftrace_events: "binder/binder_transaction"
    }
  }
}
data_sources {
  config {
    name: "linux.process_stats"
    target_buffer: 1
    process_stats_config {
      scan_all_processes_on_start: true
    }
  }
}
duration_ms: 10000

把config.txt推送到/data/local/tmp后执行adb shell perfetto -c /data/local/tmp/config.txt -o /data/misc/perfetto-traces/trace.pftrace。如果使用Windows终端且adb不在PATH中,可以写成C:\Android\platform-tools\adb.exe shell perfetto,这里盘符后的反斜杠必须保留。录制完成后用adb pull /data/misc/perfetto-traces/trace.pftrace .拉到本地。

在Perfetto UI中通过SQL定位耗时线程

拿到trace.pftrace后,在浏览器中打开Perfetto UI并加载文件。时间线视图适合观察整体阶段,但精确到某个线程在哪段时间做了什么,还是要靠SQL查询。Perfetto UI提供查询编辑器,底层数据表按事件类型划分,其中sched表每一行是一次CPU调度切片,thread表存储线程名与所属进程,process表存储进程信息。

下面这条SQL查询所有运行时长超过10ms的调度切片,并按耗时降序排列。dur字段单位是纳秒,除以1000000得到毫秒。通过这条查询能快速看出哪些线程长时间占用CPU。

SELECT
  thread.name AS thread_name,
  process.name AS process_name,
  sched.dur / 1000000 AS dur_ms,
  sched.ts / 1000000 AS start_ms
FROM sched
JOIN thread USING (utid)
JOIN process USING (upid)
WHERE sched.dur > 10000000
ORDER BY sched.dur DESC
LIMIT 20;

如果想统计某个应用主线程在整个trace期间实际占用CPU的总时长,可以使用聚合查询。下面的SQL按线程名汇总运行时间,适合对比主线程、渲染线程和后台线程的CPU消耗。

SELECT
  thread.name AS thread_name,
  SUM(sched.dur) / 1000000 AS total_ms
FROM sched
JOIN thread USING (utid)
JOIN process USING (upid)
WHERE process.name = 'com.ipipp.app'
  AND thread.name = 'main'
GROUP BY thread.name;

除了Running状态,sched表也能反映线程处于Runnable也就是等待CPU的时间。如果某个线程总耗时不长但Runnable时间占比很高,说明CPU竞争激烈;如果线程长时间处于Sleeping但用户操作仍然卡顿,那就不能只看调度切片,需要继续分析Binder调用和锁等待。

锁竞争与Binder阻塞的实战排查

跨进程通信在Android中非常常见,主线程经常会因为等待Binder返回而进入阻塞状态。这类耗时不体现在CPU运行时长上,但在用户看来就是点击无响应或列表滑动掉帧。Perfetto抓取binder_driver数据源后,Binder事务会以slice形式出现在线程轨道上。下面SQL按名字过滤包含binder的切片,列出耗时最长的Binder调用及其所在线程。

SELECT
  slice.name AS slice_name,
  slice.dur / 1000000 AS dur_ms,
  thread.name AS thread_name
FROM slice
JOIN thread_track ON slice.track_id = thread_track.id
JOIN thread USING (utid)
WHERE slice.name LIKE '%binder%'
ORDER BY slice.dur DESC
LIMIT 20;

如果查询结果显示主线程卡在某个Binder事务上,可以进一步根据slice的ts时间点回到时间线视图,查看同一时间段服务端进程在做什么。如果服务端也在等锁或等磁盘IO,就会发现阻塞链条。Binder阻塞的典型表现是主线程长时间处于Sleeping,且相邻的Binder事务切片明显超过预期。

Java层的锁竞争也可以定位。部分Android版本会把monitor contention以slice形式记录在trace中,名字通常包含Lock contention或monitor。用上面类似的SQL过滤slice.name即可。锁竞争高发时,主线程可能并没有执行太多代码,但时间被拆成了大量小片段,每个片段对应一次竞争后的调度延迟。

综合来看,Perfetto系统级追踪的价值在于把CPU调度、图形生产、Binder调用和用户自定义trace放在同一条时间线上,能够回答为什么掉帧、谁在等待谁的问题。分析时不要只盯住主线程自己的Running时间,还要结合Runnable、Sleeping以及Binder事务的持续时间,才能还原真实的阻塞路径。

PerfettoAndroid性能追踪系统级追踪修改时间:2026-09-29 04:52:15

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0929/63270.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。