How to Use the sample Command in macOS Terminal to Profile Application Call Stacks and CPU Bottlenecks

When native macOS software experiences heavy CPU usage, spinning beachballs, or background processing freezes, diagnosing the root cause traditionally requires attaching heavy GUI profiling tools like Xcode Instruments. However, on remote servers, continuous integration build machines, or user workstations where installing Xcode is impractical, developers need a lightweight command-line tool that can capture accurate execution call stacks instantly.

macOS includes the native sample command-line utility located at /usr/bin/sample. Built directly into the operating system, sample halts the target process periodically for microseconds, records the exact program counter instruction of every thread, symbolicates function addresses against system dSYMs, and outputs a formatted call graph detailing where the application spends its execution time. This guide covers how to execute and analyze sampling sessions.

Basic Syntax and Running a Sample Session

Because inspecting process memory allocations requires Mach task port debugging entitlements, executing sample typically requires administrator privileges:

sudo sample <Target_Process> <Duration_In_Seconds> [Sample_Interval_In_Milliseconds] -f <Output_File>

1. Profiling an Unresponsive Application by Name

To record a 10-second sample of an active process (sampling every 1 millisecond by default):

sudo sample Safari 10 -f /tmp/safari_sample.txt

2. Targeting a Background Process by PID

To profile a headless daemon or terminal process:

# Identify PID
pgrep -x node

# Sample PID for 5 seconds
sudo sample 14820 5 -f /tmp/node_sample.txt

Reading and Analyzing the Symbolicated Report

Open the generated text report in Terminal using less or your code editor:

less /tmp/safari_sample.txt

The report contains three critical sections:

  1. Process Header: Details process architecture (arm64e or x86_64), parent process, Mach thread counts, and total CPU time consumed during the sample interval.
  2. Call Graph (Call Tree): The central diagnostic element. It displays an indented call tree where each row represents a nested function call. Numbers beside each function indicate how many sample ticks occurred inside that function:
    • High sample counts in leaf nodes indicate hot CPU calculation loops.
    • Sample counts grouped inside mach_msg_trap or semaphore_wait_trap indicate thread idle or lock-waiting states.
  3. Binary Images: Lists all loaded dynamic libraries (dylibs) and frameworks alongside their memory addresses and UUIDs.

Comparison: sample vs spindump vs leaks

macOS Diagnostic Tool Primary Scope Best Diagnostic Use Case
sample Single specific target process Profiling CPU bottlenecks and slow algorithms
spindump Entire system (All running processes) Diagnosing system-wide freezes and kernel deadlocks
leaks Process heap memory Detecting allocated memory blocks without valid retainers

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.