When high-performance native macOS applications or background daemons experience unexpected CPU spikes, thread stalls, or battery drain, standard activity monitoring tools (like Activity Monitor or top) only display coarse aggregated CPU percentages. They cannot reveal what individual threads are doing inside the Apple XNU kernel or which low-level POSIX system calls are saturating execution queues.
macOS includes a specialized, kernel-instrumented diagnostic profiler located at /usr/bin/sc_usage: sc_usage (System Call Usage). Leveraging Darwin kernel tracepoints, sc_usage samples running processes in real time, calculating the exact frequency, CPU time consumed, and page faults generated by every system call (such as read, write, select, mmap, and kevent). This guide explains how to execute and interpret sc_usage profiling sessions.
Prerequisites and Security Permissions
Because sc_usage attaches kernel-level trace hooks to monitor system calls, executing it requires root administrator privileges via sudo:
sudo sc_usage <Target_Process>
You can target an application using either its Process ID (PID) or its executable name.
Core Profiling Modes and Syntax
1. Profiling a Running Application by Name
To inspect system call activity generated by a specific process (e.g. Safari or Finder):
sudo sc_usage Safari
The terminal presents an updating live ncurses interface listing all executed system calls ranked by execution frequency and CPU consumption.
2. Filtering by Process ID (PID)
For headless daemons or background CLI utilities:
# Identify PID
pgrep -x node
# Profile specific PID
sudo sc_usage 12450
3. Logging Profiling Samples to a File
To record 30 seconds of system call activity for automated analysis:
# Sample for 30 seconds at 1-second intervals
sudo sc_usage -s 30 node > /tmp/syscall_profile.txt
Interpreting sc_usage Columns and Metrics
The profiler interface displays several critical diagnostic metrics:
| Column Header | Definition | Performance Significance |
|---|---|---|
System Call |
Name of kernel syscall | Identifies specific I/O, IPC, or memory operation |
Count |
Total calls during interval | High counts indicate tight polling loops (e.g. nanosleep) |
CPU Time |
Cumulative time spent in kernel | Measures direct operating system overhead |
Page Faults |
Virtual memory page faults | High values indicate heavy memory pressure or thrashing |
Context Switches |
Preemptive & voluntary switches | Identifies thread contention and lock starvation |
Identifying Common Bottlenecks
- Excessive
read/writeCalls: Indicates that an application is performing unbuffered small I/O operations rather than batching data into larger memory blocks. - Frequent
kevent/select: Demonstrates high event-loop socket polling in network microservices. - Surging Zero-Fill Page Faults: Highlights sudden massive dynamic memory allocations.