AI工具为啥可以分析Perfetto性能?手把手教你常用SQL分析性能问题
背景
近来AI在平时工作中频繁使用,经常有人说使用AI来分析Perfetto等场景,其实大家第一次提到可能会感叹惊讶为啥AI还可以分析Perfetto呢?
其实大家去体验一下AI分析Perfetto相关文件就会知道,其实后面的本质原理就是使用Perfetto本身支持的SQL语句对文件的相关表进行查询和统计等操作。

所以说理解AI背后的原理就会觉得其实AI也没有那么神秘,下面列出与AI可以分析Perfetto的原理:
AI理解用户的自然语言 —》 AI会去查找生成达到用户要求的 SQL语句 —》使用生成的SQL语句对Perfetto文件相关表进行检索查询情况 —》AI对查询到的结果进行整理
其实Perfetto的sql使用对于我们黑盒情况下分析统计性能问题也是作用很大的,所以学习Perfetto SQL的使用是非常有必要的。

不过要说明,ai分析Perfetto只能说实现sql查询统计的一些分析,它的场景更适用于一些自动化统计性能分析,对于很多疑难的性能问题,依然还是需要人进行ui分析,所以暂时无法完全取代人的这块经验。
一、为什么 Perfetto 使用 SQL?
1.1 传统性能分析的痛点
|
|
|
|
|---|---|---|
| 数据孤岛 |
|
|
| 分析困难 |
|
|
| 无法量化 |
|
|
| 难以对比 |
|
|
| 学习成本高 |
|
|
1.2 SQL 查询的优势
精确量化示例:
-- 精确找出所有超过 100ms 的卡顿
SELECTname, dur/1e6as ms FROM slice WHERE dur > 100000000;
多表关联示例:
-- 关联 slice -> thread_track -> thread -> process 四张表
SELECT process.name, thread.name, slice.name, slice.dur
FROM slice
JOIN thread_track ON slice.track_id = thread_track.id
JOINthreadON thread_track.utid = thread.utid
JOIN process ON thread.upid = process.upid;
二、环境准备
2.1 下载工具
curl -LO https://github.com/google/perfetto/releases/latest/download/trace_processor_linux_amd64
mv trace_processor_linux_amd64 trace_processor_shell
chmod +x trace_processor_shell
2.2 验证安装
$ ./trace_processor_shell --version
Perfetto Trace Processor v47.0
三、表关系说明(重要!)
关联路径:slice → thread_track → thread → process
slice.track_id = thread_track.id
thread_track.utid = thread.utid
thread.upid = process.upid
⚠️ 常见错误:直接 JOIN process 而不经过 thread_track 和 thread,会导致笛卡尔积,结果完全错误!
四、基础 SQL 查询(全部实测验证)
4.1 Trace 基本信息
SELECT
'Trace时长'as metric,
ROUND((MAX(ts) - MIN(ts))/1e9, 2) asvalue,
'秒'as unit
FROM slice
UNIONALL
SELECT'Slice总数', COUNT(*), '个'FROM slice
UNIONALL
SELECT'进程数量', COUNT(DISTINCT upid), '个'FROMthreadWHERE upid ISNOTNULL;
执行:
./trace_processor_shell trace.pftrace -q /dev/stdin << 'EOF'
SELECT
'Trace时长' as metric,
ROUND((MAX(ts) - MIN(ts))/1e9, 2) as value,
'秒' as unit
FROM slice
UNION ALL
SELECT 'Slice总数', COUNT(*), '个' FROM slice
UNION ALL
SELECT '进程数量', COUNT(DISTINCT upid), '个' FROM thread WHERE upid IS NOT NULL;
EOF
输出:
"metric","value","unit"
"Trace时长",5.37,"秒"
"Slice总数",10090,"个"
"进程数量",105,"个"
4.2 进程耗时排名(✅ 完整 JOIN 条件)
SELECT
p.name,
COUNT(*) as cnt,
ROUND(SUM(s.dur)/1e9, 2) as total_s
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id -- 关键:关联 track
JOINthread t ON tt.utid = t.id -- 关键:关联 thread
JOIN process p ON t.upid = p.id -- 关键:关联 process
GROUPBY p.id
ORDERBY total_s DESC
LIMIT5;
执行:
./trace_processor_shell trace.pftrace -q /dev/stdin << 'EOF'
SELECT
p.name,
COUNT(*) as cnt,
ROUND(SUM(s.dur)/1e9, 2) as total_s
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
GROUP BY p.id
ORDER BY total_s DESC
LIMIT 5;
EOF
输出:
"name","cnt","total_s"
"/system/bin/surfaceflinger",8111,14.42
"system_server",273,9.47
"/vendor/bin/hw/android.hardware.sensors@1.0-service",247,9.18
"com.android.launcher3",640,0.73
"mdss_fb0",153,0.22
❌ 错误写法(缺少 JOIN 条件):
-- 错误!会产生笛卡尔积,所有结果都一样
SELECT p.name, SUM(s.dur)/1e6astime
FROM slice s
JOIN process p -- 缺少 ON 条件!
GROUPBY p.id;
4.3 最耗时的操作
SELECT
name,
COUNT(*) as cnt,
ROUND(MAX(dur)/1e6, 2) as max_ms
FROM slice
GROUPBYname
ORDERBY max_ms DESC
LIMIT5;
输出:
"name","cnt","max_ms"
"threadMain",132,175.71
"vsyncRequested = 0 ",24,173.54
"mCondition.wait",24,173.09
"HIDL::ISensors::poll::client",122,47.33
"binder transaction",172,46.92
4.4 长耗时操作(>10ms)
SELECT
name,
ROUND(ts/1e9, 2) as time_s,
ROUND(dur/1e6, 2) as ms
FROM slice
WHERE dur > 10000000
ORDERBY dur DESC
LIMIT5;
输出:
"name","time_s","ms"
"threadMain",41825.02,175.71
"threadMain",41824.82,174.60
"threadMain",41823.02,173.92
"vsyncRequested = 0 ",41825.02,173.54
"vsyncRequested = 0 ",41824.82,173.19
4.5 线程耗时统计(✅ 完整 JOIN 条件)
SELECT
t.name asthread,
p.name as proc,
ROUND(SUM(s.dur)/1e9, 2) as total_s
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOINthread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
GROUPBY t.id
ORDERBY total_s DESC
LIMIT5;
输出:
"thread","proc","total_s"
"app","/system/bin/surfaceflinger",13.61
"SensorService","system_server",9.41
"HwBinder:991_1","/vendor/bin/hw/android.hardware.sensors@1.0-service",6.99
"sensors@1.0-ser","/vendor/bin/hw/android.hardware.sensors@1.0-service",2.19
"droid.launcher3","com.android.launcher3",0.41
4.6 Binder 通信分析
SELECT
name,
COUNT(*) as cnt,
ROUND(AVG(dur)/1e6, 2) as avg_ms,
ROUND(MAX(dur)/1e6, 2) as max_ms
FROM slice
WHEREcategory = 'binder'
GROUPBYname;
输出:
"name","cnt","avg_ms","max_ms"
"binder async rcv",150,0.00,0.00
"binder reply",172,27.29,46.44
"binder transaction",172,28.00,46.92
"binder transaction async",150,0.00,0.00
4.7 调度延迟分析(✅ 完整 JOIN 条件)
SELECT
thread.name,
sched.ts/1e9as time_s,
sched.dur/1e6as ms,
sched.cpu
FROM sched
JOINthreadUSING (utid) -- 使用 USING 简化写法
WHERE sched.dur > 10000000
ORDERBY sched.dur DESC
LIMIT5;
输出:
"name","time_s","ms","cpu"
"swapper",41822.44,2986.34,6
"swapper",41822.44,1556.84,7
"swapper",41824.00,913.56,7
"swapper",41822.55,749.87,4
"swapper",41825.02,409.86,7
4.8 帧渲染分析
SELECT
name,
COUNT(*) as cnt,
ROUND(AVG(dur)/1e6, 2) as avg_ms,
ROUND(MAX(dur)/1e6, 2) as max_ms
FROM slice
WHEREnameLIKE'%vsync%'
GROUPBYname
ORDERBY cnt DESC
LIMIT5;
输出:
"name","cnt","avg_ms","max_ms"
" connection->vsyncRequest = None",2400,0.10,0.51
"vsyncRequested = 1 ",108,5.34,18.96
"VSyncDispatchTimerQueue 0x74f0397af0 cancelTimer...",80,0.03,0.11
"0x74f0397af0 VSyncDispatchTimerQueue::schedule...",78,0.85,1.43
"VSyncDispatchTimerQueue 0x74f0397af0 timerCallback...",58,0.29,0.71
五、主线程与渲染分析(全部实测验证)
5.1 RenderThread 最耗时方法(✅ 完整 JOIN 条件)
SELECT
s.name as method_name,
p.name as process_name,
ROUND(s.dur/1e6, 2) as duration_ms,
ROUND(s.ts/1e9, 2) as timestamp_s
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOINthread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
WHERE t.name = 'RenderThread'
ORDERBY s.dur DESC
LIMIT10;
执行:
./trace_processor_shell trace.pftrace -q /dev/stdin << 'EOF'
SELECT
s.name as method_name,
p.name as process_name,
ROUND(s.dur/1e6, 2) as duration_ms,
ROUND(s.ts/1e9, 2) as timestamp_s
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
WHERE t.name = 'RenderThread'
ORDER BY s.dur DESC
LIMIT 10;
EOF
输出:
"method_name","process_name","duration_ms","timestamp_s"
"DrawFrames 277526","com.android.launcher3",17.88,41824.63
"DrawFrames 277483","com.android.launcher3",16.79,41823.64
"Drawing 437.00 1186.00 644.00 1457.00","com.android.launcher3",15.73,41824.63
"Drawing 437.00 1186.00 644.00 1457.00","com.android.launcher3",13.92,41823.64
"DrawFrames 277569","com.android.launcher3",13.36,41825.63
5.2 掉帧统计(帧耗时 > 16.67ms)
SELECT
'掉帧统计'as metric,
COUNT(*) as frame_count,
ROUND(AVG(dur)/1e6, 2) as avg_frame_ms,
ROUND(MAX(dur)/1e6, 2) as max_frame_ms
FROM slice
WHEREnameLIKE'%DrawFrame%'AND dur > 16667000
UNIONALL
SELECT
'总帧数',
COUNT(*),
ROUND(AVG(dur)/1e6, 2),
ROUND(MAX(dur)/1e6, 2)
FROM slice
WHEREnameLIKE'%DrawFrame%';
输出:
"metric","frame_count","avg_frame_ms","max_frame_ms"
"掉帧统计",2,17.33,17.88
"总帧数",5,13.99,17.88
分析:5 帧中有 2 帧掉帧(40% 掉帧率)
5.3 Handler 消息处理耗时(✅ 完整 JOIN 条件)
SELECT
s.name as handler_msg,
p.name as process,
COUNT(*) ascount,
ROUND(AVG(s.dur)/1e6, 2) as avg_ms,
ROUND(MAX(s.dur)/1e6, 2) as max_ms
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOINthread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
WHERE s.name LIKE'%handler%'OR s.name LIKE'%Message%'OR s.name LIKE'%Looper%'
GROUPBY s.name, p.name
HAVINGcount > 0
ORDERBY max_ms DESC
LIMIT10;
输出:
"handler_msg","process","count","avg_ms","max_ms"
"handleMessage","/system/bin/surfaceflinger",5,8.76,13.02
"JIT compiling android.os.Looper...","com.android.launcher3",1,4.07,4.07
"monitor contention with owner temporary-7...","com.tencent.android.qqdownloader:daemon",1,2.00,2.00
5.4 IO 操作耗时(✅ 完整 JOIN 条件)
SELECT
s.name as operation,
p.name as process,
COUNT(*) ascount,
ROUND(AVG(s.dur)/1e6, 2) as avg_ms,
ROUND(MAX(s.dur)/1e6, 2) as max_ms
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOINthread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
WHERE s.name LIKE'%read%'OR s.name LIKE'%write%'OR s.name LIKE'%fsync%'OR s.name LIKE'%open%'
GROUPBY s.name, p.name
HAVINGcount > 0
ORDERBY max_ms DESC
LIMIT10;
输出:
"operation","process","count","avg_ms","max_ms"
"threadMain","/system/bin/surfaceflinger",132,35.06,175.71
"Lock contention on thread list lock...","com.tencent.android.qqdownloader:daemon",1,2.58,2.58
"mdss_mdp_cmd_readptr_done","[NULL]",15,0.84,1.85
六、ANR 与卡顿排查(全部实测验证)
6.1 ANR WatchDog 相关(✅ 完整 JOIN 条件)
SELECT
s.name as operation,
p.name as process,
t.name asthread,
ROUND(s.dur/1e6, 2) as duration_ms,
ROUND(s.ts/1e9, 2) as timestamp_s
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOINthread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
WHERE s.name LIKE'%ANR%'OR s.name LIKE'%anr%'OR s.name LIKE'%WatchDog%'OR s.name LIKE'%freeze%'
ORDERBY s.dur DESC
LIMIT10;
输出:
"operation","process","thread","duration_ms","timestamp_s"
"monitor contention with owner |ANR-WatchDog| (106)...","com.zhihu.android","m.zhihu.android",0.33,41823.53
6.2 锁竞争分析(✅ 完整 JOIN 条件)
SELECT
s.name as lock_contention,
p.name as process,
ROUND(s.dur/1e6, 2) as duration_ms,
ROUND(s.ts/1e9, 2) as timestamp_s
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOINthread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
WHERE s.name LIKE'%Lock contention%'OR s.name LIKE'%monitor contention%'
ORDERBY s.dur DESC
LIMIT10;
6.3 卡顿时间分布
SELECT
CASE
WHEN dur < 1000000THEN'<1ms'
WHEN dur < 5000000THEN'1-5ms'
WHEN dur < 10000000THEN'5-10ms'
WHEN dur < 16667000THEN'10-16.67ms'
WHEN dur < 33333000THEN'16.67-33.33ms (1-2帧)'
ELSE'>33.33ms (严重卡顿)'
ENDas duration_range,
COUNT(*) ascount,
ROUND(100.0 * COUNT(*) / (SELECTCOUNT(*) FROM slice), 2) as percentage
FROM slice
GROUPBY duration_range
ORDERBYMIN(dur);
七、常用 SQL 速查表
7.1 基础查询
|
|
|
|
|---|---|---|
|
|
|
SELECT COUNT(*), MAX(ts)-MIN(ts) FROM slice |
|
|
|
SELECT p.name, SUM(s.dur) FROM slice s JOIN thread_track tt ON s.track_id=tt.id JOIN thread t ON tt.utid=t.id JOIN process p ON t.upid=p.id GROUP BY p.id |
|
|
|
SELECT name, MAX(dur) FROM slice GROUP BY name |
|
|
|
SELECT * FROM slice WHERE dur > 10000000 |
|
|
|
SELECT t.name, SUM(s.dur) FROM slice s JOIN thread_track tt ON s.track_id=tt.id JOIN thread t ON tt.utid=t.id GROUP BY t.id |
|
|
|
SELECT * FROM slice WHERE category='binder' |
|
|
|
SELECT * FROM sched WHERE dur > 10000000 |
|
|
|
SELECT * FROM slice WHERE name LIKE '%vsync%' |
7.2 主线程与渲染
|
|
|
|
|---|---|---|
|
|
|
SELECT s.name, p.name, s.dur FROM slice s JOIN thread_track tt ON s.track_id=tt.id JOIN thread t ON tt.utid=t.id JOIN process p ON t.upid=p.id WHERE t.name='RenderThread' |
|
|
|
SELECT COUNT(*) FROM slice WHERE name LIKE '%DrawFrame%' AND dur > 16667000 |
|
|
|
SELECT * FROM slice WHERE name LIKE '%handler%' |
|
|
|
SELECT * FROM slice WHERE name LIKE '%read%' OR name LIKE '%write%' |
7.3 ANR 与卡顿
|
|
|
|
|---|---|---|
|
|
|
SELECT * FROM slice WHERE name LIKE '%ANR%' |
|
|
|
SELECT * FROM slice WHERE name LIKE '%contention%' |
|
|
|
SELECT CASE WHEN dur > 16667000 THEN '卡顿' END, COUNT(*) FROM slice |
八、重要限制说明
8.1 SQL 文件限制
❌ 错误做法(多个 SELECT):
-- analysis.sql - 会报错!
SELECT'查询1';
SELECT'查询2'; -- 多个 SELECT 报错
错误信息:
Result rows were returned for multiples queries.
Ensure that only the final statement is a SELECT statement...
✅ 正确做法(单个 SELECT 或 UNION):
-- 只保留一个 SELECT
SELECT'查询1';
-- 或使用 UNION 合并
SELECT'查询1'as q UNIONALLSELECT'查询2';
8.2 JOIN 条件必须完整
❌ 错误(笛卡尔积):
-- 错误!所有结果都一样
SELECT p.name, SUM(s.dur)
FROM slice s
JOIN process p -- 缺少 ON 条件
GROUPBY p.id;
✅ 正确(完整 JOIN 链):
-- 正确!结果准确
SELECT p.name, SUM(s.dur)
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOINthread t ON tt.utid = t.id
JOIN process p ON t.upid = p.id
GROUPBY p.id;
8.3 保存结果到文件
./trace_processor_shell trace.pftrace -q query.sql > result.csv
九、性能优化建议总结
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
参考:
-
Perfetto 文档:https://perfetto.dev/docs/ -
SQL 表参考:https://perfetto.dev/docs/analysis/sql-tables
更多vip免费系统开发经典大厂面试题库获取,课程优惠购买成为vip学员进入vip群,积极讨论各种行业难点痛点疑难问题,答疑服务等。
请联系马哥微信:

夜雨聆风