Native App Performance Profiling (CLI)
Overview
Record Time Profiler via xctrace, extract samples, symbolicate, and identify hotspots without opening Instruments.
Quick Start
1) Record Time Profiler
Attach to running process:
# Get the PID first
pgrep -x "AppName"
# Record for 90 seconds
xcrun xctrace record \
--template 'Time Profiler' \
--time-limit 90s \
--output /tmp/App.trace \
--attach <pid>
Launch and record:
xcrun xctrace record \
--template 'Time Profiler' \
--time-limit 90s \
--output /tmp/App.trace \
--launch -- /path/to/App.app/Contents/MacOS/App
2) Export Time Samples
List available schemas in the trace:
xcrun xctrace export --input /tmp/App.trace --toc
Export time profile data:
xcrun xctrace export \
--input /tmp/App.trace \
--xpath '/trace-toc/run/data/table[@schema="time-profile"]' \
--output /tmp/time-profile.xml
3) Get Load Address for Symbolication
While the app is running, get the __TEXT segment load address:
vmmap <pid> | grep "__TEXT"
Look for the load address (typically starts with 0x1...).
4) Symbolicate Stack Frames
Use atos to symbolicate addresses:
atos -o /path/to/App.app/Contents/MacOS/App -l 0x100000000 <address>
Workflow Notes
- Correct binary: Confirm you're profiling the right build (local vs /Applications)
- Trigger the slow path: During capture, perform the action that's slow
- Capture duration: If stacks are empty, capture longer or avoid idle time
- Symbol matching: Binary symbols must match the trace (same build)
Available Templates
List all profiling templates:
xcrun xctrace list templates
Common templates:
Time Profiler- CPU samplingAllocations- Memory allocationsLeaks- Memory leak detectionSystem Trace- System-level activityAnimation Hitches- UI performance
Common Commands
| Task | Command |
|------|---------|
| List templates | xcrun xctrace list templates |
| List devices | xcrun xctrace list devices |
| Record help | xcrun xctrace help record |
| Export help | xcrun xctrace help export |
| Get PID | pgrep -x "AppName" |
| Get load address | vmmap <pid> \| grep __TEXT |
| Symbolicate | atos -o <binary> -l <load-addr> <address> |
Analyzing Results
Manual Analysis
- Export the trace to XML
- Parse the call tree data
- Look for frames with high sample counts
- Focus on your app's code (filter out system frameworks)
Identify Hotspots
Look for:
- Functions with high self-time (time spent in function itself)
- Deep call stacks indicating inefficient algorithms
- Repeated patterns suggesting optimization opportunities
Gotchas
- ASLR: Runtime
__TEXTload address changes each launch - get it fromvmmap - Build mismatch: Symbols must match the exact build that was profiled
- Idle time: Profiling idle app produces empty/useless data
- Permissions: May need to run with
sudofor some operations
iOS Profiling
For iOS apps on simulator:
xcrun xctrace record \
--template 'Time Profiler' \
--device <simulator-udid> \
--time-limit 60s \
--output /tmp/iOS-App.trace \
--launch -- <bundle-id>
Get simulator UDID:
xcrun simctl list devices | grep Booted
Automation Script
Basic recording script:
#!/bin/bash
set -e
APP_NAME="$1"
DURATION="${2:-60}"
OUTPUT="${3:-/tmp/$APP_NAME.trace}"
if [ -z "$APP_NAME" ]; then
echo "Usage: $0 <app-name> [duration-seconds] [output-path]"
exit 1
fi
PID=$(pgrep -x "$APP_NAME" || true)
if [ -n "$PID" ]; then
echo "Attaching to running $APP_NAME (PID: $PID)"
xcrun xctrace record \
--template 'Time Profiler' \
--time-limit "${DURATION}s" \
--output "$OUTPUT" \
--attach "$PID"
else
echo "App not running. Please start $APP_NAME first."
exit 1
fi
echo "Trace saved to: $OUTPUT"
echo "To analyze: xcrun xctrace export --input $OUTPUT --toc"
Checklist
- [ ] Correct binary path identified
- [ ] App running or launch command ready
- [ ] Slow path reproducible
- [ ] Trace recorded during problematic behavior
- [ ] Load address captured for symbolication
- [ ] Results analyzed for hotspots