ios-ettrace-performance について
iOS シミュレーターアプリの処理を絞り、シンボル化した ETTrace プロファイルを取得・分析するスキルです。準備やトレースの保存から、注意点を含むボトルネックの報告までを扱います。
主な機能
- 01対象を絞ったアプリ操作の記録
- 02シミュレーター分析用 ETTrace のリンク
- 03対応する dSYM の収集と検証
- 04処理済みフレームグラフ JSON の保存
- 05ボトルネックと分析上の注意点の報告
活用シーン
- 01アプリ起動や初回描画の測定
- 02単一の実行時操作のプロファイリング
- 03シンボル化された自社コードのスタック確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ openai/ plugins --skill ios-ettrace-performanceインストール後、Claude Code で/ios-ettrace-performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlscripts/analyze_flamegraph_json.pycollect_ios_dsyms.sh詳細
iOS ETTrace のパフォーマンス
このスキルを使って、iOS シミュレーターアプリの対象を絞ったシンボリケート済み ETTrace プロファイルを取得します。シミュレーターのビルド、インストール、起動、UI 操作、ログ、スクリーンショットも必要な場合は、../ios-debugger-agent/SKILL.md と併用してください。
基本的なワークフロー
- 対象を絞ったフローを 1 つ選び、開始点と終了点として想定する箇所を書き留めます。
- インストールしてプロファイルする対象のシミュレーターアプリを正確にビルドします。
- シミュレーター/デバッグプロファイル用に、ETTrace を一時的にアプリターゲットへリンクします。
- アプリ実行ファイルと組み込みの動的フレームワークについて、UUID が一致する dSYM を収集します。
- 起動時または実行時のトレースを 1 回取得します。
- 処理済みのフレームグラフ JSON を、実行直後に保存します。
- 処理済み JSON のみを分析し、対象フロー、成果物、ホットスポット、注意点を報告します。
「アプリをしばらく使う」といった範囲の広いキャプチャは避けてください。1 つのトレースが、ユーザーに見える 1 つのフローに対応するようにします。
セットアップ
プロファイリングセッションごとに、書き込み可能な実行フォルダーを使用します。
if [ -z "${RUN_DIR:-}" ]; then
RUN_DIR="$(mktemp -d "${TMPDIR:-/tmp}/codex-ios-ettrace.XXXXXX")"
fi
mkdir -p "$RUN_DIR"
ETTrace ランナー CLI がまだ利用できない場合は、インストールします。
brew install emergetools/homebrew-tap/ettrace
ettrace はホスト側の macOS ランナーです。アプリは iOS シミュレーターのアーキテクチャに対応する ETTrace.xcframework もリンクする必要があります。
このワークフローは、トップレベルに nodes を持つ ETTrace v1.1.0 の処理済み output_<thread>.json ファイルで検証されています。
ETTrace をアプリにリンクする
プロファイル対象の正確なアプリターゲットに ETTrace を組み込みます。統合は一時的なパッチであることが明確に分かる形にし、ユーザーから明示的に保持するよう求められない限り、プロファイリング作業が終わったら削除します。
推奨される方法:
- リポジトリにシミュレーター対応の
ETTrace.xcframeworkがすでに含まれている場合は、それを再利用します。 - 含まれていない場合は、上流の ETTrace パッケージからシミュレーター専用のコピーを
RUN_DIRにビルドします。 - フレームワークはアプリターゲットに直接リンクします。テスト、リソース、データファイル、またはネストされたランチャーターゲットだけにリンクしてはいけません。
- 起動ログに
Starting ETTraceと表示されることを確認します。 - シミュレーターモードでは固定の localhost ポートで待ち受けるため、ETTrace を組み込んだシミュレーターアプリのプロファイルは一度に 1 つだけ実施します。
必要に応じてシミュレーターフレームワークをビルドします。
ETTRACE_TAG="${ETTRACE_TAG:-v1.1.0}" # Override to match the installed runner when Homebrew updates.
ETTRACE_SRC="$RUN_DIR/ETTrace-src"
if [ ! -d "$ETTRACE_SRC" ]; then
git clone --depth 1 --branch "$ETTRACE_TAG" https://github.com/EmergeTools/ETTrace "$ETTRACE_SRC"
fi
rm -rf "$RUN_DIR/ETTrace-iphonesimulator.xcarchive" "$RUN_DIR/ETTrace.xcframework"
pushd "$ETTRACE_SRC" >/dev/null
xcodebuild archive \
-scheme ETTrace \
-archivePath "$RUN_DIR/ETTrace-iphonesimulator.xcarchive" \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
INSTALL_PATH='Library/Frameworks' \
SKIP_INSTALL=NO \
CLANG_CXX_LANGUAGE_STANDARD=c++17
xcodebuild -create-xcframework \
-framework "$RUN_DIR/ETTrace-iphonesimulator.xcarchive/Products/Library/Frameworks/ETTrace.framework" \
-output "$RUN_DIR/ETTrace.xcframework"
popd >/dev/null
Bazel アプリでは、通常、一時的なインポートは次のようになります。
load("@rules_apple//apple:apple.bzl", "apple_dynamic_xcframework_import")
package(default_visibility = ["//visibility:public"])
apple_dynamic_xcframework_import(
name = "ETTrace",
xcframework_imports = glob(["ETTrace.xcframework/**"]),
)
Xcode プロジェクトでは、プロファイル対象のデバッグ用シミュレータービルドのアプリターゲットにある Link Binary With Libraries/Embed Frameworks フェーズへシミュレーター用 ETTrace.xcframework を一時的に追加し、プロファイリング後にその設定を削除します。
シンボリケーションの確認
シンボリケートされていないフレームグラフから結論を導き出してはいけません。キャプチャの前に、アプリの dSYM と組み込みのファーストパーティ製動的フレームワークの dSYM を含む dSYM フォルダーを準備します。
インストールしたアプリを生成した最終ビルドの後に、dSYM を収集します。
SKILL_DIR="<absolute path to this loaded skill folder>"
APP="<path-to-built-simulator-App.app>"
DSYMS="$RUN_DIR/dsyms"
"$SKILL_DIR/scripts/collect_ios_dsyms.sh" \
--app "$APP" \
--out-dir "$DSYMS" \
--search-root "$(dirname "$APP")" \
--search-root "$PWD" \
--extra-dsym "$RUN_DIR/ETTrace-iphonesimulator.xcarchive/dSYMs/ETTrace.framework.dSYM"
シンボリケートが必要なアプリ所有の動的フレームワークには --require-framework <FrameworkName> を追加します。--require-all-frameworks は、組み込みのすべてのフレームワークがアプリ所有であるか、シンボルが存在すると想定される場合に限って使用します。ヘルパーが必須のアプリまたはフレームワークの dSYM がないと報告した場合は、トレースの前に、dSYM を生成する設定で正確なシミュレーターアプリを再ビルドするか、該当する dSYM を含むビルド出力ディレクトリを別の --search-root として追加します。
報告内容に疑わしい点がある場合は、トレースの前に重要な UUID を確認します。
dwarfdump --uuid "$APP/$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$APP/Info.plist")"
find "$DSYMS" -maxdepth 1 -type d -name '*.dSYM' -print -exec dwarfdump --uuid {} \;
ETTrace の終了後に、シンボリケーションの概要を確認します。重要なファーストパーティ製コードについて「ライブラリはあるがシンボルがない」という行が出ている場合、ごくわずかなノイズでない限り、そのトレースは失敗と見なします。シンボリケートされていないシステムフレームワークや ETTrace 内部のバケットは、通常は問題ありません。
キャプチャ
起動トレースの場合:
cd "$RUN_DIR"
CAPTURE_MARKER="$RUN_DIR/.ettrace-capture-start"
: > "$CAPTURE_MARKER"
find "$RUN_DIR" -maxdepth 1 \( -name 'output.json' -o -name 'output_*.json' \) -delete
ettrace --simulator --launch --verbose --dsyms "$DSYMS"
起動や初回描画を測定する場合に限り、--launch を使用してください。初回の起動接続によってアプリが強制終了されることがあります。プロンプトが表示された場合は、Xcode からではなくシミュレーターのホーム画面から再起動してください。インストール後の初回起動トレースでは、アプリの Info.plist で一時的に ETTraceRunAtStartup=YES を設定してから、ettrace --simulator を実行し、ホーム画面から起動してください。
実行時のフロートレースの場合:
cd "$RUN_DIR"
CAPTURE_MARKER="$RUN_DIR/.ettrace-capture-start"
: > "$CAPTURE_MARKER"
find "$RUN_DIR" -maxdepth 1 \( -name 'output.json' -o -name 'output_*.json' \) -delete
ettrace --simulator --verbose --dsyms "$DSYMS"
安定した画面から開始し、ETTrace を起動して、対象を絞ったフローを正確に 1 つ実行します。表示される処理が完了するまで待ってから、ランナーを停止してください。より広範囲のアトリビューションを行う場合は --multi-thread を追加してください。それ以外の場合は、まずメインスレッドから開始してください。
Codex では、TTY を使って ettrace を実行し、write_stdin でプロンプトに応答してください。TTY がない場合、ランナーは有用なトレースを出力せずに終了することがあります。
出力の保存
次回の ETTrace 実行によって処理済みのフレームグラフファイルが上書きされることがあるため、新しく生成された output_<thread-id>.json ファイルはすぐに保存してください。保存済みの output.json は分析しないでください。ETTrace では同じ名前のビューアルートも提供されます。また、生の emerge-output/output.json ファイルは、このワークフローで必要とする処理済みフレームグラフの成果物ではありません。
PRESERVED_DIR="$(mktemp -d "$RUN_DIR/run-$(date +%Y%m%d-%H%M%S).XXXXXX")"
: > "$PRESERVED_DIR/summary.txt"
if [ ! -e "$CAPTURE_MARKER" ]; then
echo "error: capture marker missing; start a fresh ETTrace capture before preserving outputs" >&2
exit 1
fi
find "$RUN_DIR" -maxdepth 1 -name 'output_*.json' -newer "$CAPTURE_MARKER" -print | while IFS= read -r json; do
preserved="$PRESERVED_DIR/${json##*/}"
cp "$json" "$preserved"
{
echo "## ${preserved##*/}"
python3 "$SKILL_DIR/scripts/analyze_flamegraph_json.py" "$preserved"
} >> "$PRESERVED_DIR/summary.txt"
done
if [ ! -s "$PRESERVED_DIR/summary.txt" ]; then
echo "error: no fresh processed ETTrace output JSON found in $RUN_DIR" >&2
exit 1
fi
RUN_DIR 内の処理済み output_*.json ファイルのみを分析してください。ETTrace 自体のデバッグを行う場合を除き、output.json と生の emerge-output/output.json ファイルは無視してください。アナライザーが JSON の形式を受け付けない場合は、却下されたファイルの解釈を試みるのではなく、Homebrew の ETTrace ランナーと、それに対応するアプリ側の ETTrace.xcframework タグを使って再度キャプチャしてください。
プロファイルの確認
まず run-*/summary.txt を確認し、必要に応じて処理済み JSON を直接調べてください。
以下を報告してください。
- 正確なフロー、アプリのビルド、シミュレーターのモデル/ランタイム、実行回数
- 処理済みフレームグラフ JSON のパス
- アクティブ時間が長い末端ノードと、サンプルの重みまたは割合を含むファーストパーティーのインクルーシブスタック
- アプリ所有のバイナリについてシンボルが完全だったかどうか
- 初回セットアップ、シミュレーター限定のコスト、ネットワークの変動、サンプル数の少なさなどの注意事項
- 比較可能な設定で同じフローをキャプチャした場合に限り、変更前後の差分
クリーンアップ
プロファイリングが完了したら、一時的な ETTrace のアプリ連携を削除してください。ただし、ユーザーから保持するよう依頼された場合は除きます。実行成果物は、現在のタスクに応じて保持または破棄してください。