How to Use the Linux perf Tool for Advanced CPU Profiling

In high-performance Linux environments, identifying performance bottlenecks is critical for ensuring optimal system efficiency. While standard tools like top and htop provide an excellent high-level overview of system resource utilisation, they lack the granularity required to analyse performance at the function or system-call level. For deep, architectural performance analysis, system administrators and developers rely on the perf tool.

The perf utility, officially known as Performance Counters for Linux (PCL), is deeply integrated into the Linux kernel. It leverages hardware performance monitoring units (PMUs) embedded within modern processors to track hardware events such as CPU cycles, cache misses, and branch mispredictions, alongside software events like context switches and page faults. This guide outlines the essential workflows for utilising perf to conduct advanced CPU profiling and identify the root causes of system degradation.

Installing the Performance Analysis Tools

Because perf is intrinsically tied to the Linux kernel version, the utility is typically packaged separately from standard user-space applications. You must ensure you install the version of perf that explicitly matches your currently running kernel.

On Debian and Ubuntu-based systems, you can install the required packages using the apt package manager:

sudo apt update && sudo apt install linux-tools-common linux-tools-generic linux-tools-`uname -r`

On Red Hat Enterprise Linux (RHEL), CentOS, or Fedora, the tool is included within the standard repository:

sudo dnf install perf

Once installed, verify the utility is functioning correctly by querying the kernel for available performance events:

sudo perf list

This command outputs a comprehensive list of all hardware and software events that the kernel and CPU can currently monitor.

Conducting a High-Level Performance Stat Analysis

Before diving into function-level profiling, it is often necessary to gather a macroscopic view of application performance. The perf stat command executes a specified application and records general performance metrics throughout its lifecycle.

To analyse a specific command, simply prepend it with perf stat. For example, to evaluate the performance profile of a complex directory listing:

sudo perf stat ls -lR /usr > /dev/null

Upon completion, the tool will present a summary encompassing vital metrics. Pay close attention to the number of context switches (which indicate the CPU rapidly switching between tasks) and the instructions per cycle (IPC). A low IPC value, particularly alongside a high percentage of stalled frontend cycles, strongly suggests that the CPU is waiting on memory or cache retrievals rather than actively processing instructions.

Profiling CPU Usage with Perf Record

When you need to determine exactly which functions are consuming CPU cycles, you must sample the CPU state at high frequencies. The perf record command achieves this by interrupting the CPU at a specified sampling rate and logging the current instruction pointer and call stack.

To profile the entire system for a set duration (for instance, 30 seconds), use the following command:

sudo perf record -F 99 -a -g -- sleep 30

Let’s break down the parameters used in this command:

  • -F 99: Sets the sampling frequency to 99 Hertz. Using 99 rather than exactly 100 helps prevent the sampling intervals from synchronising with periodic system timers, which can skew the data.
  • -a: Profiles all active CPU cores across the entire system.
  • -g: Instructs the tool to record the call graph (stack traces), allowing you to see the function call hierarchy that led to the performance cost.

This operation generates a binary data file named perf.data in the current working directory, which contains the raw sampling information.

Analysing the Profiling Data

Raw binary profiling data is unreadable without parsing. The perf report command interprets the perf.data file and presents the information in an interactive, terminal-based interface.

sudo perf report

The interface ranks functions by the percentage of CPU overhead they consumed during the recording period. By expanding specific entries (usually by pressing Enter), you can traverse the call graph to identify exactly which parent functions are responsible for invoking the resource-intensive child processes.

If you encounter a high percentage of overhead attributed to functions labeled as [kernel.kallsyms], the bottleneck lies within the Linux kernel itself—often pointing to excessive system calls, intensive I/O operations, or memory allocation pressure. Conversely, if user-space library functions (such as libc) dominate the report, the application logic requires optimisation.

Generating Flame Graphs for Visual Analysis

While the terminal interface of perf report is powerful, deeply nested call stacks can be difficult to interpret textually. Flame graphs provide a highly intuitive, hierarchical visualization of CPU profiling data.

To generate a flame graph, you first need to export the binary data to a human-readable text format:

sudo perf script > out.perf

Next, utilize the open-source FlameGraph toolkit (typically acquired by cloning the repository from GitHub) to process the output and render a Scalable Vector Graphics (SVG) file:

./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
./FlameGraph/flamegraph.pl out.folded > perf-flamegraph.svg

Opening the resulting perf-flamegraph.svg in any modern web browser allows you to interactively explore the performance profile. The x-axis represents the population of the samples, visually indicating which functions consumed the most CPU time, while the y-axis represents the depth of the call stack, providing an immediate understanding of exactly where the system resources were spent.

Get the best tech tips delivered straight to your inbox.

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