Build a perf script Sample Filter with dlfilter
You will build a shared object that keeps only samples whose instruction pointer resolves to a chosen symbol, then run it through perf script. The same pattern gives you a safe starting point for filters based on process IDs, events, addresses or branch flags. Allow 20 to 30 minutes if you already have a perf development installation; most of the time is compiling and checking the ABI.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide follows the perf-dlfilter(1) interface shipped by linux-tools-common version 6.8.0-142.142. You need a compatible perf, GCC, a recorded perf data file, and the matching perf/perf_dlfilter.h header. The local perf wrapper currently reports that a kernel-specific binary for 6.8.0-139 is not installed, so the commands below are a reproducible build and invocation workflow rather than a claim that this host can replay a data file now.
1. Confirm the inputs before writing code
Check the tool and compiler first:
$ perf --version
$ command -v gcc
$ test -f "$HOME/include/perf/perf_dlfilter.h" && echo 'header found'
The manpage's example assumes a per-user install with the header at ~/include/perf/perf_dlfilter.h. Use the actual include directory supplied by your perf build if it differs. A missing header is a build-environment problem; it is not fixed by changing the filter source.
Checkpoint: do not continue until perf --version identifies a usable perf binary and the header test prints header found. Use a perf data file you are allowed to inspect. This filter only reads samples; it does not record new events or alter the running system.
2. Write a narrow filter
Create dlfilter-symbol.c in a scratch directory. This filter keeps a sample only when its instruction pointer resolves to the symbol named by the first --dlarg argument:
#include <perf/perf_dlfilter.h>
#include <string.h>
struct perf_dlfilter_fns perf_dlfilter_fns;
int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx)
{
int argc = 0;
char **argv;
const struct perf_dlfilter_al *al;
(void)data;
if (!sample->ip)
return 1;
argv = perf_dlfilter_fns.args(ctx, &argc);
if (!argv || argc < 1 || !argv[0])
return 1;
al = perf_dlfilter_fns.resolve_ip(ctx);
if (!al || !al->sym)
return 1;
return strcmp(al->sym, argv[0]) == 0 ? 0 : 1;
}
A return value of 0 keeps the sample, 1 filters it out, and a negative value reports an error. resolve_ip() may not return symbol information, so the null checks are part of the filter rather than optional decoration. The sample and any pointed-to data are valid only during the callback.
3. Build the shared object
Compile position-independent code and link it as a shared object:
$ gcc -c -I "$HOME/include" -fpic -Wall -Wextra -o dlfilter-symbol.o dlfilter-symbol.c
$ gcc -shared -o dlfilter-symbol.so dlfilter-symbol.o
$ file dlfilter-symbol.so
dlfilter-symbol.so: ELF ... shared object ...
These commands change only files in the current directory. The filter is native code loaded into the perf process, so do not load an unreviewed .so, and do not build it from a writable directory shared with less-trusted users. If the link fails, fix the header or compiler environment before trying runtime options.
Checkpoint: confirm that the object exists and that file reports a shared object for the same architecture as perf. Remove the two scratch build files afterwards with rm -- dlfilter-symbol.o dlfilter-symbol.so only when you are certain you no longer need them; that removal is irreversible.
4. Run the filter against recorded data
Pass the shared object and one filter argument to perf script:
$ perf script -i perf.data --dlfilter ./dlfilter-symbol.so --dlarg target_symbol
Replace perf.data with the input file and target_symbol with the symbol you want to keep. A relative path containing / is explicit. If the filename contains no slash, perf searches the current directory, its tools exec path, and the dynamic linker paths. Prefer the explicit path while testing so an unrelated library cannot be selected accidentally.
Expected output is the normal perf script sample listing, reduced to samples accepted by the filter. An empty listing can be correct: the symbol may not occur, symbols may not have been resolved, or the filter may have received no argument. Compare with an unfiltered run:
$ perf script -i perf.data > all-samples.txt
$ perf script -i perf.data --dlfilter ./dlfilter-symbol.so --dlarg target_symbol > filtered-samples.txt
$ wc -l all-samples.txt filtered-samples.txt
5. Diagnose failures without guessing
A loader error usually means a missing file, incompatible architecture, unresolved library or ABI mismatch. Check dependencies with ldd dlfilter-symbol.so; the manpage warns that changing a dependent shared library, or using a different library version from perf, can produce unexpected results. Rebuild the filter when the relevant perf or library versions change.
If you need filtering before perf's internal filtering, implement filter_event_early with the same callback contract. Use filter_description when a human-readable description is useful. For state, implement start and stop, remembering that most function pointers are not valid from those callbacks. For newer sample fields such as machine_pid and vcpu, check the structure's size before reading them so an older perf remains compatible.
Do not retain pointers from a callback after it returns. Do not call resolve_addr() unless addr_correlates_sym permits it, and call al_cleanup() after resolve_address() when the function is available. These are lifetime and ownership rules, not tuning suggestions.
Done means
- The header, compiler and matching perf binary were checked before building.
- The filter returns
0for samples to keep and1for samples to discard. - The shared object was built with position-independent code and loaded by an explicit path.
--dlargsupplied the symbol value consumed byperf_dlfilter_fns.args().- The filtered output was compared with an unfiltered
perf scriptrun. - Native-code and pointer-lifetime risks were reviewed before using the filter on valuable data.