Read and interpret perf.data with perf report
By the end of this guide you will be able to open a profile made by perf record, identify the hottest symbols, narrow the report to one process or library, and inspect recorded call chains. The command does not rerun the workload. It reads samples already stored in perf.data.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 10 minutes for a familiar profile and longer if you need to investigate missing symbols. You need the perf command and a readable perf data file. This guide follows the perf-report(1) shipped in Ubuntu's linux-tools-common package version 6.8.0-142.142 on the reference machine. The local wrapper currently warns that a matching kernel-specific perf package for kernel 6.8.0-139 is not installed, so check your own command before relying on version-specific output.
Checkpoint 1: find the profile
Run the report from the directory containing the data file. With no input option, perf report reads perf.data. If standard input is a FIFO, the default is different, so use an explicit file when a script or pipeline is involved.
cd /path/to/profile-directory
test -r ./perf.data
perf report -i ./perf.data
The interactive interface normally opens in your terminal. If you need output suitable for a log or a review, select the stdio interface explicitly:
perf report --stdio -i ./perf.data
A report contains sampled event data, not a complete execution trace. A symbol with a high percentage received many samples, but that percentage is not automatically the proportion of wall-clock time. The event, sampling frequency, workload and collection permissions all affect what the samples mean.
Checkpoint
If the command says that the file cannot be opened, confirm the path and permissions. If it reports that the data was recorded with an incompatible or unavailable feature, use the same or a compatible perf tool version where possible. Do not delete the original profile while diagnosing it.
Checkpoint 2: read the histogram
The default sort keys are command, shared object or module, and symbol: equivalent to --sort comm,dso,symbol. The exact columns vary with the recorded event and the options used. Start with the symbol or library that has the largest relevant overhead, then ask whether it is in your code, a dependency, the kernel, or an unresolved region.
For a stable text report, request the fields you need and add sample counts when percentages alone are distracting:
perf report --stdio --show-nr-samples \
--sort comm,dso,symbol \
-i ./perf.data
--show-nr-samples displays the number of samples for each symbol. This is useful when comparing a 0.2 percent entry backed by many samples with a tiny entry that appears because of a small profile. It does not make the profile more statistically precise.
To focus on one executable or shared object, use comma-separated filters. These change the denominator used for the overhead column, so a filtered 40 percent is not directly comparable with an unfiltered 40 percent.
perf report --stdio \
--comms=worker \
--dsos=libexample.so \
-i ./perf.data
Replace worker and libexample.so with names that actually appear in your report. For a process or thread ID, use --pid=PID or --tid=TID. For a symbol name, use --symbol-filter=TEXT; the filter is partial, so choose text specific enough to avoid a misleading match.
Checkpoint 3: inspect call chains
Call chains explain how execution reached a sampled function. They must have been recorded by perf record; perf report cannot reconstruct call stacks that were never stored.
perf report --stdio \
--call-graph=graph,0.5 \
-i ./perf.data
The graph form is the default call-chain display. The second value is the minimum percentage threshold, so this example hides branches below 0.5 percent. Lowering it can reveal useful detail, but it also makes a report longer and harder to scan.
When call chains exist, the report can show Children and Self overhead. Self counts samples attributed directly to the function. Children includes samples in functions called below it. A parent can therefore have high Children overhead with little or no Self overhead, and the Children column can sum to more than 100 percent across rows. That is expected accumulation, not duplicate samples in the data file.
To return to direct samples only, disable the default children accumulation:
perf report --stdio --no-children -i ./perf.data
The same behaviour can be configured with report.children = false in the perf configuration, but a command-line option is easier to see and safer for a one-off investigation.
Useful focused views
Choose a different sort key when the default view hides the question you are asking. These examples only make sense when the profile contains the required information:
--sort cpuseparates samples by processor number.--sort srclinegroups by source file and line, and requires DWARF debugging information.--sort symbolgroups by function name and enables IPC and IPC Coverage columns when the recorded data supports them.--sort time,overheadseparates samples into time quanta before ordering by overhead. The default time quantum is 100ms; change it with--time-quantum, for example--time-quantum=1s.
Use --hide-unresolved when you need a clean list of resolved symbols, but do not mistake a shorter list for better data. Unresolved samples can be the part that needs fixing: missing debug information, stripped binaries, unavailable kernel symbols, or a mismatched build can all reduce symbolisation.
Common traps and safe boundaries
Do not treat an empty or tiny histogram as proof that the program was idle. The recording event may not have been available, the workload may have finished before sampling began, or permissions may have limited collection. Check the recording command and its statistics before drawing a conclusion.
Do not add sudo automatically. Reading a profile only needs read access to the file. Elevated privileges may have been needed when recording system-wide events, but that is a collection concern, not a requirement of perf report. If you do use elevated privileges, keep the input path explicit and avoid opening untrusted data with tools that load external symbol or trace metadata unless you understand the trust boundary.
perf report does not alter perf.data in the normal reporting workflow. If you are experimenting with filters, keep the original file and write any redirected text to a new path:
perf report --stdio -i ./perf.data > ./perf-report.txt
test -s ./perf-report.txt
Done means
- You opened the intended file with
-ior confirmed the defaultperf.data. - You identified whether the leading percentage is Self or Children overhead.
- You used a focused filter or sort only when its denominator and data requirements were clear.
- You checked unresolved symbols and call-chain availability before blaming a function.
- You preserved the original profile and saved any text report separately.