mcpField
P
perfetto-sql

perfetto-sql

github.com/android数据与数据库10.0k802 次安装2 天前更新
代码仓库

关于 perfetto-sql

perfetto-sql 是用于编写和验证 Perfetto 跟踪数据 SQL 查询的技能,指导用户查找模式、构造查询、执行查询并报告结果。

主要功能

  1. 01查找 Perfetto 模式与模块
  2. 02构造可重复执行的查询
  3. 03处理区间重叠
  4. 04SQL 验证清单
  5. 05使用 trace_processor 执行查询

适用场景

  1. 01编写跟踪分析查询
  2. 02查询前核对数据模式
  3. 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;。
  • 如果 同一输入表中的区间有重叠,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 可以实现相同结果,请避免手动运算。只有在不存在内置替代方法时,才使用以下逻辑。

    1. 条件: 当 start1 < end2 且 start2 < end1 时,区间重叠。

    2. 时长: 重叠时长的计算方式为 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:问题拆解与架构研究
  1. 确定核心问题、所需数据点和筛选条件。
  2. 优先级规则: 如果用户请求中包含 SQL 查询,请原样使用,并跳至步骤 2 进行验证。
  3. 强制架构和模块搜索: 对于计划使用的每个表或视图,都必须在 perfetto-stdlib.md 中查找其架构。不要阅读整个文档文件 --- 这会占用上下文窗口。请遵循以下精确流程:
    • 发现与搜索: 使用可用的搜索工具(grep、read_file 或文件搜索),并限制行数,根据问题领域和高层意图(例如“CPU 时间”“运行时间”“重叠”“卡顿”)查找相关视图、表或模块。
      • 原因: 仅搜索确切的表名会漏掉专为此类分析构建的综合性预计算视图。
      • 注意: 起草手动算术或自定义函数之前,必须确认标准库模块是否已提供所需抽象。
    • 定向限量读取: 确定相关模块后,高效读取该模块部分中的表和视图。
    • 提取: 仅提取所需对象的架构、列以及确切的 INCLUDE PERFETTO MODULE 语句。
    • 验证: 检查列、类型和说明,确保表符合需求。
  4. 起草查询前打印研究结果:
  5. 表/视图: 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 语法和逻辑错误。不要为了通过验证而简化分析意图。例如,如果用户要求计算重叠或交集,就必须修正交集计算逻辑。不要以互不相交的查询(例如,分别返回总时长)作为变通方案。
步骤 3:最终输出
  1. 明确返回最终验证通过的 SQL,并向用户说明结果。
  2. 完成回复前,删除在 /tmp/ 目录中创建的所有临时 SQL 文件。

评论

支持 Markdown