Inspect BPF perf Attachments with bpftool perf

Something is hooked into your kernel via perf and you want to know who, so run bpftool perf. It lists the BPF programs attached through perf events, and you can turn the same inventory into JSON for scripts or incident notes. The command is read-only: it does not detach programs, unload BPF objects or change a service. Allow about ten minutes, or longer if the matching kernel-tools package is missing.

This guide follows the installed bpftool-perf(8) manual from linux-tools-common version 6.8.0-139.139. The local package supplies a wrapper, but this machine lacks the kernel-specific executable for kernel 6.8.0-139. That is a packaging problem, not evidence that the host has no BPF attachments.

1. Check the binary first

Start with ordinary, read-only checks, and do not add sudo yet. The first command identifies the program your shell selects. The second asks the wrapper for its version:

$ command -v bpftool
/usr/sbin/bpftool
$ bpftool -V
WARNING: bpftool not found for kernel 6.8.0-139

You may need to install the following packages for this specific kernel:
linux-tools-6.8.0-139-generic
linux-cloud-tools-6.8.0-139-generic

Exact wording varies with the distribution and running kernel. On the machine used for this guide, the wrapper exits after this warning because the matching tool is absent. Check the package version separately when you need an audit trail:

$ dpkg-query -W -f='${Package}\t${Version}\n' linux-tools-common
linux-tools-common  6.8.0-139.139

Checkpoint: if bpftool -V names a missing kernel-specific package, stop here and install it through your normal change process. Do not work around the mismatch by copying a binary from another host.

2. Confirm the perf subcommand syntax

Once a working bpftool binary is present, the perf family accepts show and list as equivalent inventory commands. The default invocation is:

$ bpftool perf
pid 21711  fd 5: prog_id 5  kprobe  func __x64_sys_write  offset 0
pid 21767  fd 5: prog_id 8  tracepoint  sys_enter_nanosleep

The process ID and file descriptor identify the process holding the perf event. prog_id identifies the BPF program. The remaining fields describe the attachment type and point. The sample values are illustrative: your host can legitimately print no records, or a completely different set.

Use the explicit spelling in scripts when clarity matters:

$ bpftool perf show
$ bpftool perf list

Tip: both commands list raw tracepoint, tracepoint and probe attachments known to the running system. They do not list every kind of BPF link, so an empty perf inventory does not prove the kernel has no BPF programs.

3. Read the attachment point

Tracepoint records use a probe name, such as sys_enter_nanosleep. Kernel probes use a function and offset, or a kernel virtual address. User probes use a file name and file offset:

$ bpftool perf show
pid 21765  fd 5: prog_id 7  kretprobe  func __x64_sys_nanosleep  offset 0
pid 21767  fd 5: prog_id 8  tracepoint  sys_enter_nanosleep
pid 21800  fd 5: prog_id 9  uprobe  filename /home/yhs/a.out  offset 1159

4. Produce machine-readable output

Use -j or --json when another tool will consume the result. The output is an array of objects, with field names that match the attachment kind:

$ bpftool -j perf
[{"pid":21711,"fd":5,"prog_id":5,"fd_type":"kprobe","func":"__x64_sys_write","offset":0},
 {"pid":21767,"fd":5,"prog_id":8,"fd_type":"tracepoint","tracepoint":"sys_enter_nanosleep"},
 {"pid":21800,"fd":5,"prog_id":9,"fd_type":"uprobe","filename":"/home/yhs/a.out","offset":1159}]

For readable JSON during a manual review, add -p. It implies JSON:

$ bpftool -p perf

Prefer JSON for scripts over parsing the human-readable spacing. Test for a field such as func, tracepoint or filename according to fd_type, and do not assume every object has all three.

5. Handle access and missing-tool failures

The perf subsystem is subject to the host's security policy. Run the inventory as the account that will normally use it. If it reports a permission error, inspect the error and your organisation's perf policy before trying elevation:

$ bpftool perf show
[permission diagnostic from this host]
$ printf 'exit status: %s\n' "$?"
exit status: 1

6. Keep the inspection reversible

Every command in this workflow reads program and attachment metadata. There is no undo command because nothing was changed.

Warning: do not follow an unexpected attachment by killing the listed process or deleting a pinned BPF object. Those actions can disrupt tracing or an application, and they need a separate ownership and rollback decision.

For an incident record, save JSON to a new file rather than overwriting an existing report:

$ output_file="bpftool-perf-$(date +%Y%m%d-%H%M%S).json"
$ bpftool -j perf > "$output_file"
$ test -s "$output_file" && echo 'inventory saved'

Shell globbing can match several old reports, so use a dedicated empty directory or an explicit destination when automating this. Keep the file access-controlled if attachment names or process IDs are sensitive in your environment.

Done means