npx skills add ...
npx skills add openai/plugins --skill ios-ettrace-performance
Capture and interpret iOS Simulator ETTrace profiles. Use when profiling launch or runtime latency, comparing traces, or finding CPU-heavy stacks.
npx skills add openai/plugins --skill ios-ettrace-performance
Use this skill to capture a focused, symbolicated ETTrace profile from an iOS simulator app. Pair it with ../ios-debugger-agent/SKILL.md when the task also needs simulator build, install, launch, UI driving, logs, or screenshots.
Avoid broad "use the app for a while" captures. One trace should correspond to one user-visible flow.
Use a writable run folder for each profiling session:
Install the ETTrace runner CLI if it is not already available:
ettrace is the host-side macOS runner. The app must also link an ETTrace.xcframework for the iOS Simulator architecture.
This workflow is validated for ETTrace v1.1.0 processed output_<thread>.json files with top-level nodes.
Wire ETTrace into the exact app target being profiled. Keep the integration in a clearly temporary patch and remove it when the profiling task is done unless the user explicitly asks to keep it.
Preferred options:
ETTrace.xcframework if the repo already vendors one.RUN_DIR from the upstream ETTrace package.Starting ETTrace.Build a simulator framework when needed:
For Bazel apps, a temporary import usually looks like:
For Xcode projects, temporarily add the simulator ETTrace.xcframework to the app target's Link Binary With Libraries / Embed Frameworks phases for the debug simulator build you are profiling, then remove that wiring after profiling.
Do not draw conclusions from an unsymbolicated flamegraph. Before every capture, prepare a dSYM folder that includes the app dSYM and any embedded first-party dynamic framework dSYMs.
Collect dSYMs after the final build that produced the installed app:
Add --require-framework <FrameworkName> for app-owned dynamic frameworks that must symbolicate; use --require-all-frameworks only when every embedded framework is app-owned or expected to have symbols. If the helper reports a missing required app or framework dSYM, rebuild the exact simulator app with dSYM generation before tracing, or add the build output directory that contains those dSYMs as another --search-root.
Verify important UUIDs before tracing when the report looks suspicious:
After ETTrace exits, read its symbolication summary. Treat meaningful first-party "have library but no symbol" lines as a failed trace unless they are tiny noise. Unsymbolicated system-framework or ETTrace internal buckets are usually acceptable.
For launch traces:
Use --launch only when measuring startup or first render. The first launch connection can force quit the app; relaunch from the simulator home screen rather than Xcode if prompted. For first-launch-after-install traces, temporarily set ETTraceRunAtStartup=YES in the app Info.plist, then run ettrace --simulator and launch from the home screen.
For runtime flow traces:
Start from a stable screen, start ETTrace, perform exactly one focused flow, wait until visible work is complete, then stop the runner. For wider attribution, add --multi-thread; otherwise start with the main thread.
In Codex, run ettrace with a TTY and answer prompts with write_stdin. Without a TTY, the runner can exit without a useful trace.
The next ETTrace run can overwrite processed flamegraph files, so preserve fresh output_<thread-id>.json files immediately. Do not analyze a saved output.json; ETTrace also serves a viewer route with that name, and raw emerge-output/output.json files are not the processed flamegraph artifacts this workflow expects.
Analyze only processed output_*.json files in RUN_DIR. Ignore output.json and raw emerge-output/output.json files unless debugging ETTrace itself. If the analyzer rejects the JSON shape, capture again with the Homebrew ETTrace runner and matching app-side ETTrace.xcframework tag instead of trying to interpret the rejected file.
Start from run-*/summary.txt, then inspect processed JSON directly if needed.
Report:
Remove temporary ETTrace app wiring when profiling is complete unless the user asked to keep it. Keep or discard run artifacts based on the active task.