| title | Profile Native Code |
|---|---|
| impact | MEDIUM |
| tags | xcode, instruments, android-studio, profiler |
Use Xcode Instruments and Android Studio Profiler to identify native performance bottlenecks.
# iOS: Open Instruments
# Xcode → Open Developer Tool → Instruments → Time Profiler
# Android: Open Profiler
# Android Studio → View → Tool Windows → Profiler- App is slow but JS profiler shows no issues
- Investigating native module performance
- Startup feels slow (native init)
- Battery drain concerns
- Need CPU/memory breakdown by thread
Note: This skill involves interpreting visual profiler output (Xcode Instruments, Android Studio Profiler). AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing the profiler UI manually, or await MCP-based visual feedback integration (see roadmap).
- Run app via Xcode
- Open Debug Navigator (side panel)
- View real-time: CPU, Memory, Disk, Network
CPU percentage can exceed 100% (multi-core usage).
- Open: Xcode → Open Developer Tool → Instruments
- Select Time Profiler
- Choose target device and app
- Click record (red circle)
- Perform actions in app
- Stop recording
Key views:
- Flame Graph: Visual call stack over time
- Call Tree: Hierarchical function breakdown
- Ranked: Functions sorted by time (Bottom-Up)
Useful filters:
- Hide System Libraries
- Invert Call Tree (bottom-up view)
- Filter by thread (main, JS, etc.)
Identifying problems:
- Microhang: Brief UI unresponsiveness
- Hang: Full UI thread block (critical)
- Yellow = most time spent
Pin threads to compare:
- Main thread (SampleApp): UI rendering
- JavaScript thread: React/JS execution
- Background threads: Native modules
Pro tip: JS thread blocking ≠ UI block (React Native design benefit).
- View → Tool Windows → Profiler
- Or: Click "Profile" in toolbar
- Select "Find CPU Hotspots"
- Click "Start profiler task"
- Interact with app
- Stop to analyze
Flame Graph:
- Zoom with scroll/pinch
- Click to expand call stacks
- Filter by keyword (e.g., "hermes")
Views:
- Top Down: From entry points down
- Bottom Up: From slowest functions up
- Flame Chart: Timeline visualization
Example analysis:
JS Thread activity after button press:
- Event handler on main thread
- Triggers JS work via sync JSI calls
- Hermes processes React reconciliation
- ~30% time in "commit" phase (Yoga layout)
Profiler shows:
- 240ms+ JS thread work
- Many 1ms Hermes spikes
- Exceeds 16.6ms frame budget
- Result: Dropped frames, UI jank
Profiler shows:
- Minimal JS work (windowed rendering)
- Smooth main thread
- Stays within frame budget
| Tool | Platform | Use Case |
|---|---|---|
| Time Profiler | iOS | CPU hotspots |
| Leaks | iOS | Memory leaks |
| Hangs | iOS | UI thread blocks |
| CPU Profiler | Android | CPU hotspots |
| Memory Profiler | Android | Memory tracking |
| Perfetto | Android | Advanced trace analysis |
Export traces from Android Studio and analyze at ui.perfetto.dev:
- Cross-process analysis
- Custom trace events
- Additional visualizations
- Profile on low-end devices: Issues appear more clearly
- Use release builds: Debug builds have overhead
- Compare before/after: Export traces for comparison
- Filter by thread: Focus on relevant work
- Look for patterns: Spikes correlating with interactions
- Expo Go: Cannot profile native code directly; JS profiling only
- Dev Client / Prebuild: Full native profiling supported via Xcode/Android Studio
- Run
npx expo prebuildto generate native projects, then profile as bare React Native
| Symptom | Likely Cause |
|---|---|
| Main thread hangs | Heavy UI work, blocked operations |
| JS thread spikes | React re-renders, heavy computation |
| Background thread busy | Native module work |
| Memory climbing | Leak (see memory profiling skills) |
- native-measure-tti.md - Profile startup specifically
- native-memory-leaks.md - Memory profiling
- js-profile-react.md - JS/React profiling