perfetto-sql について
perfetto-sqlは、Perfettoのトレースデータを対象とするSQLクエリの作成と検証を支援するスキルです。スキーマ調査から実行、結果の報告までを案内します。
主な機能
- 01Perfettoのスキーマとモジュールの調査
- 02再実行可能なクエリの作成
- 03区間の重なりの処理
- 04SQL検証チェックリスト
- 05trace_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 VIEWではなくCREATE PERFETTO TABLEを使用して実体化する必要があります。 -
トレースの境界(
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 クエリを記述する際は、通常の
tidやpidではなく、utid(一意のスレッド ID)またはupid(一意のプロセス ID)を使用してテーブルを結合する必要があります。利点: オペレーティングシステムはTIDとPIDを再利用しますが、UTIDとUPIDはトレースの存続期間中、一意のままです。そのため、誤った結合を防げます。 -
安全な引数の抽出: スライスまたはトラックから辞書形式や JSON 形式のプロパティを抽出するには、
EXTRACT_ARG(arg_set_id, 'key')を使用してください。文字列解析は行わないでください。 -
文字列の照合(必ず GLOB を使用):
LIKEの代わりにGLOBを使用してください。LIKEはパフォーマンスのボトルネックになり、アンダースコア(_)をワイルドカードとして扱うため、バグの原因になります。- 完全一致:
=を使用してください。 - 部分一致:
*を使ったGLOBを使用してください(例:name GLOB '*RenderThread*')。 - 大文字と小文字を区別しない照合:
LOWER(name) GLOBを使用し、検索文字列がすべて小文字であることを確認してください(例:LOWER(name) GLOB '*renderthread*')。トレース内で大文字と小文字が一貫していない場合(例:WakeLockとwakelock)に使用します。
- 完全一致:
-
時間の重なりの計算: 2 つの時間区間
[start1, end1]と[start2, end2]の重なり時間を計算するには、次のようにします。優先順位のルール: 2 つの異なる区間セット間の重なりを計算する場合は、必ず
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テーブルを使用してください。 -
特定のタイムスタンプ周辺のイベントを検索する場合は、ウィンドウサイズを 100ms に設定して開始してください。
-
列名には、常にテーブルまたはビューのエイリアスを付けてください。つまり、次の形式にします:
{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 Standard Library のドキュメントは
perfetto-stdlib.mdにあります。このファイルを参照して、利用可能なモジュールを確認したり、特定のテーブルやビューのスキーマ(列と型)を調べたり、SQL クエリの作成前に必要なINCLUDE PERFETTO MODULEステートメントを確認したりしてください。 - 実行ツール: クエリは、Perfetto から直接ダウンロードした公式の
trace_processorラッパースクリプトを使用して実行されます。出力は純粋な CSV 形式で返されます。
実行プロトコル
マルチエージェントパイプラインを再現するため、以下の手順を順番どおりに実行してください。
ステップ 0:ツールのセットアップ
ラッパーの取得: 現在のプロジェクトワークスペースのトップレベル(./trace_processor)を使用する必要があります。
重要なガードレール:
trace_processorを探すために、ホームディレクトリやワークスペース全体に対してファイルシステム検索ツール(find、find_by_name、grep、dir /s、Get-ChildItem)を絶対に使用しないでください。ワークスペース全体を対象とする制約のない検索は、応答が停止したりタイムアウトしたりする原因になります。
ワークスペースのトップレベルでファイルを直接確認します(例: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 時間」、「実行時間」、「重複」、「ジャンク」)に基づいて、関連するビュー、テーブル、モジュールを見つけます。- 理由: テーブル名だけを完全一致で検索すると、こうした分析向けに構築された包括的な事前計算済みビューを見逃します。
- 注意: 手動で計算したりカスタム関数を作成したりする前に、Standard Library モジュールが必要な抽象化をすでに提供しているかどうかを確認する必要があります。
- 対象を絞った範囲限定の読み取り: 関連するモジュールを特定したら、そのモジュールのセクションにあるテーブルとビューを効率的に読み取ります。
- 抽出: 必要なオブジェクトについて、スキーマ、列、および正確な
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 の制約なし: 禁止されている
LIKEではなく、GLOBまたは=を使用して文字列の一致を指定しましたか? -
[ ] 実行の確認: スタンドアロンの
./trace_processorラッパーと--query-stringフラグを使って、必ずクエリを実行してください。./trace_processor --query-string "QUERY" {trace_file}実行ルール:
- ファイルの使用:クエリが長い場合やエスケープに問題がある場合など、実行のために SQL ファイルを作成する必要がある場合は、
/tmp/ディレクトリに作成してください。 - 状態: 実行は一時的なもので、データベースの状態はターンをまたいで保持されません。別のターンのクエリ間で、ビューやテーブルなどの状態を共有することはできません。各クエリは単独で実行できる、完全に自己完結したものにしてください。
- 失敗への対応: クエリが失敗した場合は、SQL の構文エラーとロジックエラーを調査して修正してください。検証に通すために、分析の意図を単純化してはいけません。たとえば、重なりや交差の計算を求められた場合は、交差の計算方法を修正してください。回避策として、互いに独立したクエリ(たとえば、個別の合計時間を返すもの)に置き換えてはいけません。
- ファイルの使用:クエリが長い場合やエスケープに問題がある場合など、実行のために SQL ファイルを作成する必要がある場合は、
ステップ 3:最終出力
- 検証済みの最終 SQL を明示的に提示し、結果をユーザーに説明します。
- 回答を終える前に、
/tmp/ディレクトリに作成した一時 SQL ファイルをすべて削除します。