一、简介
1. 以.perfetto后缀的trace文件解析窗口中,搜索栏中输入":"后进入SQL查询窗口,查询的结果会显示在trace解析界面下面,点击会跳转到trace上对应位置。若是想单独窗口显示查询结果,可以点击左侧边栏上的"Query(SQL)",输入SQL预计后按Ctrl+Enter触发查询,但是这种查询的结果点击不会跳转。
2. 几个常用的Table/View的格式及关键字段信息:
slice表,横向track上的一条条小片段
ts:片段起始时间戳(单位ns)
dur:片段持续时长(ns)
track_id:属于哪个track(水平timeline)
name:片段标注的名称,对应Trace中打印的方法名、标记等信息
二、Table和关键字段信息
1. slice表,也即片段表,描述横向track上的一条条小片段,也是最重要的一个表
ts:片段起始时间戳(单位ns)
dur: 片段持续时长(ns)
track_id: 属于哪个track(哪个水平timeline)
name: 片段标注的名称,对应trace中打印的方法名、标记等信息
2. thread_track 表,描述线程track
id: track id,就是一个横向的timeline,每个track都有一个唯一id.
utid:标识线程id,并不是调度上的线程tid。只是表里面的一个标志,多个表进程join互联的时候用作一个关键字进行查询的
3. thread表,对应各线程信息
utid:标识线程id,并不是调度上的线程tid。utid 和 thread_track表的 utid 关联,方便两个表之间join连接。
name: 线程名
tid: 调度上的真实的tid
4. process表,对应各进程信息
upid: 进程的标志,并非调度上的进程的pid。其作用是和thread表中的upid关联,表示thread所属的进程。
pid: 表示进程真实的pid.
uid: 进程身份uid
5. sched_slice表,CPU信息区中描述线程调度片段
cpu:当前调用片段运行在哪个cpu上。
end_state: 当前片段结束后的线程的状态。
priority: 线程优先级(0-139)
6. thread_state表,描述各track上边的线程调度片段,标识线程运行状态
utid: 标识线程id,对应thread表同名字段。
cpu: 当前slice所在的线程运行在哪个CPU上。
state: 线程状态
对这些表结构了解后,就可以写SQL进行查询了。
三、常用查询脚本
1. 列出所有doFrame片段,按耗时倒序排列,取前100条
select slice_id, track_id, ts,dur, dur/1e6, name from slice WHERE name like '%doFrame%' order by dur desc limit 100
2. 上面1中查询的基础上,指定process name为systemui,即systemui自身的绘帧信息
select slice_id, track_id, ts, dur, dur/1e6, slice.name from slice JOIN thread_track ON slice.track_id = thread_track.id JOIN thread USING(utid) JOIN process USING(upid) WHERE process.name = 'com.android.systemui' and slice.name like '%doFrame%' order by dur desc limit 100
3. system_server 中各"OPF:"关键字片段(也可以缓冲其它片段)的耗时信息,包含各片段的真实running_time(每个slice可能有一段时间running,一段时间sleep,一段时间runnable,需要借助thread_state表来查询slice中各调度时间片的状态)
select slice_id,track_id,thread.utid,slice.ts,slice.dur,(slice.dur/1e6) as dur_ms, (select total(case when thread_state.ts < slice.ts then MIN(slice.ts+slice.dur,thread_state.ts+thread_state.dur)-slice.ts when (thread_state.ts+thread_state.dur) > (slice.ts+slice.dur) then (slice.ts+slice.dur-MAX(thread_state.ts,slice.ts)) else thread_state.dur end) from thread_state where thread_state.utid=thread.utid and thread_state.state='Running' and thread_state.ts < (slice.ts+slice.dur) and (thread_state.ts+thread_state.dur) > slice.ts)/1e6 as total_running,slice.name from slice JOIN thread_track ON slice.track_id = thread_track.id JOIN thread USING(utid) JOIN process USING(upid) WHERE process.name='system_server' and slice.name like 'OPF:%' order by slice.dur desc limit 400
前面一大堆操作是为了从墙上时间duration中提取真实的running时间。可将 like 'OPF:%' 换成 '%doFrame%',即可查看system_server中各 doFrame 时机运行时间。查询结果显示如下,表示doFrame持续时间是2.373769ms,这之间实际运行时间是1.988307ms:
slice_id track_id utid ts dur dur_ms total_running name 6963 155 205 13304838190408 2373769 2.373769 1.988307 Choreographer#doFrame 15051 ...
4. system_server中的锁竞争情况(lockcontention),lock_depth表示当时参与此对象锁竞争的线程个数
select count(1) as lock_depth, s.slice_id,s.track_id,s.ts,s.dur,s.dur/1e6 as dur_ms,ctn.otid,s.name from slice s, (select slice_id,track_id,ts,dur,name,substr(name, 46, instr(name,')')-46) as otid from slice t WHERE name like 'Lock contention on a monitor lock %' order by dur) ctn JOIN thread_track ON s.track_id=thread_track.id JOIN thread USING(utid) JOIN process USING(upid) WHERE process.name = 'system_server' and s.name like 'Lock contention on a monitor lock %' and substr(s.name, 46, instr(s.name,')')-46) = ctn.otid and ctn.slice_id <> s.slice_id and ctn.ts >= s.ts and (ctn.ts+ctn.dur) <= (s.ts+s.dur) group by s.slice_id order by s.dur desc
5. 查看trace的metadata
select * from metadata
6. 使用SQL定位一个片段
(1) 查看trace的起始时间和结束时间
select * from trace_bounds -- 显示内容如下,单位ns -- start_ts end_ts -- 19768418232311 19787715757089
(2) 因为trace上显示条目是相对时间,比如一个条目开始时间为"18s 938ms 179us 983ns",持续时间为"20ms 117us 447ns",想通过SQL脚本选中它需要换算成绝对时间,19768418232311 + 18938179983 = 19787356412294,写SQL为:
--失败 select * from slice where ts=19787356412294 and dur=20117447 --成功 select * from slice where ts > 19787356402294 and ts < 19787356422294 and dur > 20107447 and dur < 20127447
但是由于数值近似值,不一定能选中,改为一个区间会好一些。
四、补充
1. Perfetto trace UI界面的左侧边栏的"Sample queries"下每个查询条目的SQL脚本都会显示出来,且"Debug SQL"条目下有很多现成的SQL模板脚本可以拷贝修改使用。
参考:https://blog.csdn.net/feelabclihu/article/details/126672666