根据之前的文章,我们详细的了解了perfetto的基本使用技巧,这篇文章我们聚焦在perfetto的sql语句,说明其工作原理和如何进行sql查找
Trace Processor 是 Perfetto 生态中最强大的组件之一,它将复杂的二进制 trace 数据转化为可查询的关系型数据库结构,让你可以用 SQL 的方式精确分析性能数据。
Trace Processor 是一个 C++ 库,核心能力包括:
perf.data 等多种 trace 格式perfetto提供了三种使用sql的办法。分别如下
通过下载perfetto的binary可以获取 trace_processor_shell
bash# wget https://github.com/google/perfetto/releases/download/v54.0/linux-amd64.zip
我们可以直接将sql作为参数输入给 trace_processor_shell
# ./trace_processor_shell --help [290.273] processor_shell.cc:1016 Interactive trace processor shell. Usage: ./trace_processor_shell [FLAGS] trace_file.pb PerfettoSQL: -q, --query-file FILE Read and execute an SQL query from a file. If used with --run-metrics, the query is executed after the selected metrics and the metrics output is suppressed. -Q, --query-string QUERY Execute the SQL query QUERY. If used with --run-metrics, the query is executed after the selected metrics and the metrics output is suppressed. --add-sql-package PATH[@PACKAGE] Registers SQL files from a directory as a package for use with INCLUDE PERFETTO MODULE statements. By default, the directory name becomes the root package name. Use @PACKAGE to override. Given a directory structure: mydir/ utils.sql helpers/common.sql --add-sql-package ./mydir Registers modules as: mydir.utils mydir.helpers.common Usage: INCLUDE PERFETTO MODULE mydir.utils; --add-sql-package ./mydir@foo Registers modules as: foo.utils foo.helpers.common Usage: INCLUDE PERFETTO MODULE foo.utils; --add-sql-package ./mydir@foo.bar.baz Registers modules as: foo.bar.baz.utils foo.bar.baz.helpers.common Usage: INCLUDE PERFETTO MODULE foo.bar.*;
同样,我们可以直接使用perfetto的python包,也能够进行sql查询,只需要按照perfetto即可
pip3 install perfetto
安装完成后,pip list可以查看如下
perfetto 0.14.0
此时代码可以直接加载sql进行查询
pythonfrom perfetto.trace_processor import TraceProcessor
tp = TraceProcessor(trace='trace.perfetto-trace')
qr_it = tp.query('SELECT ts, dur, name FROM slice LIMIT 10')
for row in qr_it:
print(row.ts, row.dur, row.name)
在 Perfetto UI 底部的 "Query (SQL)" 标签中直接输入和执行 SQL 查询。点击即可

Perfetto 中所有时间值以纳秒(ns) 为单位:
ts(timestamp):事件开始时间,纳秒dur(duration):事件持续时间,纳秒dur / 1e6 = 毫秒,dur / 1e9 = 秒Perfetto 使用内部唯一标识符,而非系统的 PID/TID:
utid(Unique TID):线程的唯一标识upid(Unique PID):进程的唯一标识slice存储所有有时长的事件(函数调用、ATrace 标记等),其结构如下
sqlSELECT
id,
ts, -- 开始时间(纳秒)
dur, -- 持续时间(纳秒)
name, -- 事件名称
category, -- 事件类别
track_id, -- 所属轨道
depth, -- 嵌套深度
parent_id, -- 父事件 ID
arg_set_id -- 参数集 ID
FROM slice;
thread 和 process 表提供了关于线程的id信息
sql-- 线程信息
SELECT utid, tid, name AS thread_name, upid FROM thread;
-- 进程信息
SELECT upid, pid, name AS process_name FROM process;
thread_state 表提供了线程的状态信息
sqlSELECT
ts,
dur,
utid,
state, -- 'Running', 'R' (Runnable), 'S' (Sleeping),
-- 'D' (Uninterruptible Sleep), 'T' (Stopped)
cpu, -- 运行在哪个 CPU
blocked_function -- D 状态时阻塞在哪个内核函数
FROM thread_state;
counter 表 默认记录了CPU 频率、内存等随时间变化的数值
sqlSELECT
ts,
value,
track_id
FROM counter;
sched_slice 表提供了调度事件,记录每个 CPU 上的调度切换:
sqlSELECT
ts,
dur,
cpu,
utid,
end_state, -- 线程被切出时的状态
priority
FROM sched_slice;
ftrace_event 表:原始 ftrace 事件
sqlSELECT
ts,
name,
cpu,
utid,
arg_set_id
FROM ftrace_event;
现在以android上的完整示例进行sql演示
sqlSELECT
s.name,
s.dur / 1e6 AS dur_ms,
t.name AS thread_name,
p.name AS process_name
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t ON tt.utid = t.utid
JOIN process p ON t.upid = p.upid
ORDER BY s.dur DESC
LIMIT 20;

可以看到用时最多的是,PortraitProc的PortraitProcessor::Process
sqlSELECT
name,
COUNT(*) AS count,
MIN(dur) / 1e6 AS min_ms,
MAX(dur) / 1e6 AS max_ms,
AVG(dur) / 1e6 AS avg_ms,
SUM(dur) / 1e6 AS total_ms
FROM slice
WHERE name GLOB '*Trace GPU completion fence*'
GROUP BY name
ORDER BY total_ms DESC;

可以看到fence 864 耗时0.71224ms,最长
sql-- 使用 GLOB 进行模糊搜索
SELECT ts, dur / 1e6 AS dur_ms, name
FROM slice
WHERE name GLOB '*GPU*'
LIMIT 20;
-- 使用 LIKE 搜索
SELECT ts, dur / 1e6 AS dur_ms, name
FROM slice
WHERE name LIKE '%GPU%'
ORDER BY dur DESC
LIMIT 10;

这个sql经常使用,直接找slice,然后导出track如下

sqlSELECT
state,
SUM(dur) / 1e6 AS total_ms,
COUNT(*) AS count
FROM thread_state
GROUP BY state

sql-- 每个 CPU 的利用率
SELECT
cpu,
SUM(dur) / 1e6 AS busy_ms,
(SELECT MAX(ts) + MAX(dur) - MIN(ts) FROM sched_slice) / 1e6 AS total_ms,
CAST(SUM(dur) AS FLOAT) /
(SELECT MAX(ts) + MAX(dur) - MIN(ts) FROM sched_slice) * 100 AS util_pct
FROM sched_slice
GROUP BY cpu
ORDER BY cpu;

sqlSELECT
EXTRACT_ARG(arg_set_id, 'prev_comm') AS prev_comm,
EXTRACT_ARG(arg_set_id, 'prev_pid') AS prev_pid,
EXTRACT_ARG(arg_set_id, 'next_comm') AS next_comm,
EXTRACT_ARG(arg_set_id, 'next_pid') AS next_pid
FROM ftrace_event
WHERE name = 'sched_switch'
LIMIT 20;

sqlSELECT
c.ts / 1e9 AS ts_sec,
c.value / 1024 AS value_kb,
ct.name AS counter_name
FROM counter c
JOIN counter_track ct ON c.track_id = ct.id
WHERE ct.name LIKE '%mem%'
ORDER BY c.ts;

PerfettoSQL 支持 SQLite 的窗口函数,适合计算滑动平均等:
sqlSELECT
ts / 1e9 AS ts_sec,
value,
AVG(value) OVER (
ORDER BY ts
ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING
) AS moving_avg
FROM counter
WHERE track_id = 50;

Trace Processor 和 PerfettoSQL 是 Perfetto 分析能力的核心。熟练掌握这些sql,能够让你性能分析事半功倍
同样的,如果借助ai给你这些sql,相比于原来手搓sql更加轻松