关于 perfetto-sql
perfetto-sql 是用于编写和验证 Perfetto 跟踪数据 SQL 查询的技能,指导用户查找模式、构造查询、执行查询并报告结果。
主要功能
- 01查找 Perfetto 模式与模块
- 02构造可重复执行的查询
- 03处理区间重叠
- 04SQL 验证清单
- 05使用 trace_processor 执行查询
适用场景
- 01编写跟踪分析查询
- 02查询前核对数据模式
- 03测量切片时长或区间重叠
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ android/ skills --skill perfetto-sql安装后在 Claude Code 中输入/perfetto-sql确认已加载。
附带文件references/perfetto-stdlib.md
详细介绍
指南和提示
-
幂等性: 确保查询具有幂等性,以避免多次执行时出现“已存在”错误。
- 对于 Perfetto 对象,始终使用
CREATE OR REPLACE:CREATE OR REPLACE PERFETTO TABLE、CREATE OR REPLACE PERFETTO VIEW、CREATE OR REPLACE PERFETTO FUNCTION、CREATE OR REPLACE PERFETTO MACRO。 - 对于 SQLite 虚拟表(例如
SPAN_JOIN),不支持CREATE OR REPLACE。请先显式删除:DROP TABLE IF EXISTS my_table; CREATE VIRTUAL TABLE my_table USING SPAN_JOIN(...); - 对于标准 SQLite 索引,在前面加上
DROP INDEX IF EXISTS index_name;。
- 对于 Perfetto 对象,始终使用
-
如果 同一输入表中的区间有重叠,
SPAN_JOIN会崩溃。始终使用PARTITIONED {column}子句(例如PARTITIONED upid)隔离区间。 -
输入到
SPAN_JOIN的中间表必须使用CREATE PERFETTO TABLE实体化,不能使用CREATE VIEW。 -
跟踪边界(
dur = -1): 如果切片或线程状态在跟踪结束前尚未结束,则会记录为dur = -1。计算边界框(例如ts + dur)或求和时长(SUM(dur))时,请使用以下方式处理不完整时长:IIF(dur = -1, trace_end() - ts, dur)。 -
稳健的状态转换: 避免使用手动时间戳运算(例如
ts + dur = next.ts)连接相邻事件。请依赖标准库模块(例如sched.runnable、linux.perf.counters、intervals.overlap),它们可以安全地处理跟踪间隙和抢占。 -
唯一标识符: 在 Perfetto 中编写 SQL 查询时,必须使用
utid(唯一线程 ID)或upid(唯一进程 ID)连接表,而不是常规的tid或pid。原因:操作系统会循环使用TID和PID,而UTID和UPID在整个跟踪期间保持唯一,从而避免错误连接。 -
安全提取参数: 使用
EXTRACT_ARG(arg_set_id, 'key')从切片或轨道中提取字典或类似 JSON 的属性。不要尝试解析字符串。 -
字符串匹配(始终使用 GLOB): 使用
GLOB而不是LIKE。LIKE会造成性能瓶颈,并将下划线(_)视为通配符,从而导致错误。- 精确匹配: 使用
=。 - 子串匹配: 使用
GLOB和*(例如,name GLOB '*RenderThread*')。 - 不区分大小写的匹配: 使用
LOWER(name) GLOB,并确保搜索字符串全部为小写(例如,LOWER(name) GLOB '*renderthread*')。在处理大小写不一致的跟踪(例如,WakeLock与wakelock)时使用此方法。
- 精确匹配: 使用
-
计算时间重叠: 要计算两个时间区间
[start1, end1]和[start2, end2]之间的重叠时长:优先规则: 计算两组不同区间之间的重叠时,始终优先使用
SPAN_JOIN或标准库函数(例如intervals.overlap)。如果标准库功能或SPAN_JOIN可以实现相同结果,请避免手动运算。只有在不存在内置替代方法时,才使用以下逻辑。-
条件: 当
start1 < end2且start2 < end1时,区间重叠。 -
时长: 重叠时长的计算方式为
MIN(end1, end2) - MAX(start1, start2)重要: 不完整的 Perfetto 切片时长为 -1(
dur = -1)。应用此逻辑之前,始终使用ts + IIF(dur = -1, trace_end() - ts, dur)计算有效结束时间。
-
-
对应用启动请求,请查询
android_thread_slices_for_all_startups。 -
将
counter_track与counter连接,以获取具有特定名称的计数器值。 -
查询 CPU 频率计数器时,请包含
linux.cpu.frequency模块,并使用cpu_frequency_counters表。 -
查找特定时间戳附近的事件时,请从 100 ms 的时间窗口开始。
-
始终在列名前加上表或视图别名,即:
{alias}.{column_name}。 -
若要计算名称匹配特定模式(例如
*{name_pattern}*)的切片总耗时,必须将它们的持续时间相加。为何有用:这有助于量化特定函数或功能在多次调用中对性能造成的总影响。以下是一个查询示例(请注意对未完成切片的安全处理):sql SELECT count(*) as total_count, sum(IIF(slice.dur = -1, trace_end() - slice.ts, slice.dur)) / 1000000.0 as total_dur_ms FROM slice WHERE slice.name GLOB '*{name_pattern}*';
资源
- 文档: Perfetto 标准库文档位于
perfetto-stdlib.md。可将此文件用作参考,以发现可用模块、查找特定表或视图的架构(列和类型),或确定起草 SQL 查询前所需的INCLUDE PERFETTO MODULE语句。 - 执行工具: 使用从 Perfetto 直接下载的官方
trace_processor包装脚本执行查询。输出以纯 CSV 格式返回。
执行流程
必须按顺序遵循以下步骤,仿照多 Agent 流程:
步骤 0:工具设置
获取包装脚本: 必须使用当前项目工作区的顶层目录中的 ./trace_processor。
关键防护措施: 绝不要使用文件系统搜索工具(
find、find_by_name、grep、dir /s、Get-ChildItem)在主目录或工作区中查找trace_processor——在整个工作区内进行不受限制的搜索会导致无响应或超时。
在工作区顶层直接检查文件(例如,ls trace_processor)。如果文件不存在,请将 https://get.perfetto.dev/trace_processor 直接下载到工作区根目录(curl -LO),在 macOS/Linux 上赋予其可执行权限(chmod +x),并确保将 trace_processor 添加到 .gitignore。直接通过 ./trace_processor 执行查询(在 Windows 上,显式调用 python trace_processor)。
重要: 此 URL 提供的文件是一个约
10KB的 Python 包装脚本。不要因为下载内容是人类可读的文本就认为下载失败。这是预期行为。该脚本会在首次运行时自动延迟加载预编译二进制文件。请直接使用此脚本。
步骤 1:问题拆解与架构研究
- 确定核心问题、所需数据点和筛选条件。
- 优先级规则: 如果用户请求中包含 SQL 查询,请原样使用,并跳至步骤 2 进行验证。
- 强制架构和模块搜索: 对于计划使用的每个表或视图,都必须在
perfetto-stdlib.md中查找其架构。不要阅读整个文档文件 --- 这会占用上下文窗口。请遵循以下精确流程:- 发现与搜索: 使用可用的搜索工具(
grep、read_file或文件搜索),并限制行数,根据问题领域和高层意图(例如“CPU 时间”“运行时间”“重叠”“卡顿”)查找相关视图、表或模块。- 原因: 仅搜索确切的表名会漏掉专为此类分析构建的综合性预计算视图。
- 注意: 起草手动算术或自定义函数之前,必须确认标准库模块是否已提供所需抽象。
- 定向限量读取: 确定相关模块后,高效读取该模块部分中的表和视图。
- 提取: 仅提取所需对象的架构、列以及确切的
INCLUDE PERFETTO MODULE语句。 - 验证: 检查列、类型和说明,确保表符合需求。
- 发现与搜索: 使用可用的搜索工具(
- 起草查询前打印研究结果:
- 表/视图:
Schema for {name}:,列出列和类型。
步骤 2:起草与验证循环(最多 3 次迭代)
仅使用步骤 1 中检索到的架构,以 SQLite 语法起草 SQL 查询。起草后,必须按照以下清单进行验证:
-
[ ] SQLite 语法: 查询能否成功解析,且没有语法错误?
-
[ ] 幂等性: 所有对象的创建操作是否都可以安全地重复运行?(虚拟表是否使用了
CREATE OR REPLACE PERFETTO和DROP TABLE IF EXISTS?) -
[ ] 存在性: 文档中是否找到了所有表?
-
[ ] 意图检查: 在手动编写算术逻辑之前,是否存在可满足此意图的标准库表或视图?
-
[ ] 列准确性: 列是否与检索到的架构相符?
-
[ ] 别名检查: 是否所有列名都带有其表或视图的别名作为前缀(例如
alias.column_name)? -
[ ] 模块检查: 是否已为所有非前置模块加入
INCLUDE PERFETTO MODULE语句?必须使用文档中提供的确切模块名称。 -
[ ] Span Join 检查: 如果使用
SPAN_JOIN,表是否已安全地进行PARTITIONED分区,以避免区间重叠导致崩溃?是否使用CREATE PERFETTO TABLE将中间表物化? -
[ ] 禁止使用 LIKE: 是否使用
GLOB或=而非禁止使用的LIKE来匹配字符串? -
[ ] 执行检查: 必须使用独立的
./trace_processor封装程序和--query-string标志运行查询:./trace_processor --query-string "QUERY" {trace_file}。执行规则:
- 文件使用: 如果必须创建 SQL 文件来执行查询(例如,由于查询过长或转义问题),必须在
/tmp/目录中创建。 - 状态: 执行过程完全是临时的。数据库状态不会在不同轮次之间保留。你不能在不同轮次的查询之间共享状态(例如视图或表)。每个查询都必须独立且完整。
- 失败恢复: 查询失败时,应调试并修复 SQL 语法和逻辑错误。不要为了通过验证而简化分析意图。例如,如果用户要求计算重叠或交集,就必须修正交集计算逻辑。不要以互不相交的查询(例如,分别返回总时长)作为变通方案。
- 文件使用: 如果必须创建 SQL 文件来执行查询(例如,由于查询过长或转义问题),必须在
步骤 3:最终输出
- 明确返回最终验证通过的 SQL,并向用户说明结果。
- 完成回复前,删除在
/tmp/目录中创建的所有临时 SQL 文件。