Linux perf
is an essential tool for performance troubleshooting. One of its core functionalities is perf record
. Here’s an example:
sudo perf record -g -- dd if=/dev/zero of=/dev/null count=1024 bs=1M
Output:
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.0760135 s, 14.1 GB/s
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (314 samples) ]
This command collects samples and saves them to a file named perf.data
in the current working directory. You can open it with:
sudo perf report
If you omit sudo
, you won’t be able to open the file—because perf.data
is owned by root (since perf record
was invoked with sudo
). So far, everything looks good: most kernel symbols are resolved properly. This is because all steps were performed with root privileges, avoiding the common permission-related symbol resolution issues.
What Happens Without Root?
Sometimes, we want to automate processing of perf.data
using a script. It might be tempting to simply change the ownership of perf.data
to a non-root user. However, when you do that, perf report
will show unresolved addresses instead of symbols:
+ 98.92% 0.00% dd [kernel.kallsyms] [k] 0xffffffff9d200156
+ 98.92% 0.00% dd [kernel.kallsyms] [k] 0xffffffff9d1cacc6
[...]
In this output, [kernel.kallsyms]
is a pseudo shared object representing all kernel symbols. As you may have guessed, this happens because perf
is unable to access certain sensitive files that are only readable by root.
So, what exactly is missing? From Brendan Gregg’s blog, we know that the kernel must be compiled with CONFIG_KALLSYMS=y
to allow symbol resolution. You can check this with:
sudo grep "CONFIG_KALLSYMS" /boot/config-$(uname -r)
This is enabled by default on Ubuntu 22.04, as expected. But then, why are symbols still missing?
Where Kernel Symbols Live
Kernel symbols are exposed to userspace through the virtual file /proc/kallsyms
. While this file is readable by normal users, the access is limited. Try:
tail /proc/kallsyms
Sample output:
0000000000000000 d pmt_pci_driver [intel_pmt]
0000000000000000 t pmt_pci_driver_exit [intel_pmt]
0000000000000000 r pmt_pci_ids [intel_pmt]
0000000000000000 r tgl_info [intel_pmt]
0000000000000000 r dg1_info [intel_pmt]
0000000000000000 d dg1_capabilities [intel_pmt]
0000000000000000 d dg1_telemetry [intel_pmt]
0000000000000000 d __this_module [intel_pmt]
0000000000000000 t cleanup_module [intel_pmt]
0000000000000000 r __mod_pci__pmt_pci_ids_device_table [intel_pmt]
As you can see, the addresses are all zeroed out. This is a security measure to prevent kernel address leaks. Only the root user has full access to this file. Since both perf report
and perf script
depend on /proc/kallsyms
for symbol resolution, running them without root privileges results in missing kernel symbols.
Accessing Kernel Symbols Without Root
Is it possible to resolve kernel symbols without root? Yes—but only if you adjust some kernel settings. You need to disable two protections:
sudo sysctl -w kernel.kptr_restrict=0
sudo sysctl -w kernel.perf_event_paranoid=0
A Note on KASLR and Reproducibility
Modern Linux distributions enable Kernel Address Space Layout Randomization (KASLR) by default. This means the addresses of kernel symbols can vary across reboots. To verify it's enabled, try:
sysctl kernel.randomize_va_space
Example output:
kernel.randomize_va_space = 2
To ensure reproducibility when analyzing perf.data
, you should take a snapshot of the kernel symbol table at the time of recording:
sudo cat /proc/kallsyms > kallsyms.snapshot
Then, when running perf report
or perf script
, use the --kallsyms
option:
perf report --kallsyms=kallsyms.snapshot
Comments NOTHING