home / skills / steipete / agent-scripts / instruments-profiling

instruments-profiling skill

/skills/instruments-profiling

This skill helps you profile macOS/iOS native apps with Instruments by selecting correct binaries, launching methods, and exporting meaningful time-profile

npx playbooks add skill steipete/agent-scripts --skill instruments-profiling

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.7 KB
---
name: instruments-profiling
description: Use when profiling native macOS or iOS apps with Instruments/xctrace. Covers correct binary selection, CLI arguments, exports, and common gotchas.
metadata:
  short-description: Instruments profiling for macOS/iOS apps
---

# Instruments Profiling (macOS/iOS)

Use this skill when the user wants performance profiling or stack analysis for native apps.
Focus: Time Profiler, `xctrace` CLI, and picking the correct binary/app instance.

## Quick Start (CLI)

- List templates: `xcrun xctrace list templates`
- Record Time Profiler (launch):
  - `xcrun xctrace record --template 'Time Profiler' --time-limit 60s --output /tmp/App.trace --launch -- /path/To/App.app`
- Record Time Profiler (attach):
  - Launch app yourself, get PID, then:
  - `xcrun xctrace record --template 'Time Profiler' --time-limit 60s --output /tmp/App.trace --attach <pid>`
- Open trace in Instruments:
  - `open -a Instruments /tmp/App.trace`

Note: `xcrun xctrace --help` is not a valid subcommand. Use `xcrun xctrace help record`.

## Picking the Correct Binary (Critical)

**Gotcha: Instruments may profile the wrong app** (e.g., one in `/Applications`) if LaunchServices resolves a different bundle.
Use these rules:

- Prefer direct binary path for deterministic launch:
  - `xcrun xctrace record ... --launch -- /path/App.app/Contents/MacOS/App`
- If launching `.app`, ensure it’s the intended bundle:
  - `open -n /path/App.app`
  - Verify with `ps -p <pid> -o comm= -o command=`
- If both `/Applications/App.app` and a local build exist, explicitly target the local build path.
- After launch, confirm the process path before trusting the trace.

## Command Arguments (xctrace)

- `--template 'Time Profiler'`: template name from `xctrace list templates`.
- `--launch -- <cmd>`: everything after `--` is the target command (binary or app bundle).
- `--attach <pid|name>`: attach to running process.
- `--output <path>`: `.trace` output. If omitted, file saved in CWD.
- `--time-limit 60s|5m`: set capture duration.
- `--device <name|UDID>`: required for iOS device runs.
- `--target-stdout -`: stream launched process stdout to terminal (useful for CLI tools).

## Exporting Stacks (CLI)

- Inspect trace tables:
  - `xcrun xctrace export --input /tmp/App.trace --toc`
- Export raw time-profile samples:
  - `xcrun xctrace export --input /tmp/App.trace --xpath '/trace-toc/run[@number="1"]/data/table[@schema="time-profile"]' --output /tmp/time-profile.xml`
- Post-process in a script (Python/Rust) to aggregate stacks.

## Instruments UI Workflow

- Template: Time Profiler
- Use “Record” and capture the slow path (startup vs steady-state)
- Call Tree tips:
  - Hide System Libraries
  - Invert Call Tree
  - Separate by Thread
  - Focus on hot frames and call counts

## Gotchas & Fixes

- **Wrong app profiled**: LaunchServices resolves installed app instead of local build.
  - Fix: use direct binary path or `--attach` with known PID.
- **No samples / empty trace**: App exits quickly or never hits work.
  - Fix: longer capture, trigger workload during recording.
- **Privacy prompts**: `xctrace` may need Developer Tools permission.
  - Fix: System Settings → Privacy & Security → Developer Tools → allow Terminal/Xcode.
- **Large XML exports**: `time-profile` exports are huge.
  - Fix: filter with XPath and aggregate offline; don’t print to terminal.

## iOS Specific Notes

- Device: use `xcrun xctrace list devices` and `--device <UDID>`.
- Launch via Xcode if needed; attach with `xctrace --attach`.
- Ensure debug symbols for meaningful stacks.

## Verification Checklist

- Confirm trace process path matches target build.
- Confirm stacks show expected app frames.
- Capture covers the slow operation (startup/refresh). 
- Export stacks for automated diffing if optimizing.

Overview

This skill helps profile native macOS and iOS apps using Instruments / xctrace. It focuses on selecting the correct binary or bundle, constructing reliable xctrace CLI commands, exporting time-profile stacks, and avoiding common gotchas. Follow the steps to produce deterministic traces and usable stack exports for performance analysis.

How this skill works

The skill inspects how you launch or attach Instruments to the target process and guides you to use direct binary paths or PID attaches to avoid LaunchServices resolving the wrong app. It describes xctrace CLI flags for recording, listing templates, device selection for iOS, and exporting time-profile data. It also covers basic Instruments UI workflows and post-export processing for automated analysis.

When to use it

  • Collecting CPU samples with Time Profiler for startup or steady-state performance.
  • Verifying a local build is the process actually profiled (avoiding system-installed bundles).
  • Recording on a physical iOS device or selecting the correct simulator/device UDID.
  • Exporting stacks for offline aggregation, diffing, or CI-driven performance checks.
  • Troubleshooting empty traces, privacy permission issues, or massive XML exports.

Best practices

  • Launch the exact binary path (…/Contents/MacOS/App) to ensure deterministic profiling.
  • If using a .app bundle, open it with open -n and confirm the process path with ps before trusting the trace.
  • Prefer --attach <pid> when possible to guarantee the target process is profiled.
  • Use --time-limit to bound captures and trigger the workload while recording to avoid empty traces.
  • Export only the time-profile table via XPath and aggregate offline to avoid huge terminal output.
  • Ensure debug symbols are available on iOS/device runs for meaningful frame names.

Example use cases

  • Record a 60s Time Profiler run: xcrun xctrace record --template 'Time Profiler' --time-limit 60s --output /tmp/App.trace --launch -- /path/App.app/Contents/MacOS/App
  • Attach to a running process: xcrun xctrace record --template 'Time Profiler' --output /tmp/App.trace --attach <pid>
  • List available templates or devices: xcrun xctrace list templates and xcrun xctrace list devices
  • Verify captured frames: open -a Instruments /tmp/App.trace and use Invert Call Tree, Hide System Libraries, and Separate by Thread

FAQ

Why did Instruments profile the system-installed app instead of my build?

LaunchServices can resolve a different bundle when you launch by bundle path. Use the direct binary path or open -n the bundle and confirm the process path with ps, or attach by PID to be deterministic.

My trace is empty or has no samples—what next?

Increase the time-limit, ensure the app stays running during capture, and trigger the workload while recording. Also check Developer Tools permission in System Settings.