No More SDKs
One of the most operationally significant advantages of eBPF-based profilers is that they require no changes to application code. No library imports. No SDK initialization. No restart. No redeployment. You deploy the profiling agent once, and it immediately sees inside every process on the machine — regardless of programming language.
How the agent works in a Kubernetes environment:
- You deploy the Profiler Agent as a DaemonSet — one pod per node, running with elevated privileges to load eBPF programs.
- The agent loads an eBPF program that attaches to the
perf_eventhardware timer at 99 Hz. - On every timer tick, the kernel pauses the currently running thread for a few microseconds, records the CPU instruction pointer and the full call stack (as raw memory addresses), and resumes execution.
- The agent reads these raw addresses from the BPF map.
- The agent performs stack unwinding and symbolization: it maps raw memory addresses back to human-readable function names like
main.processRequestorjava.util.HashMap.get. This requires access to debug symbols (DWARF data) for each binary.
The Symbolization Challenge
Symbolization is the hardest part of eBPF profiling. In production, binaries are often stripped of debug symbols to reduce size. Modern profilers solve this in several ways:
debuginfod: A protocol where the profiler agent fetches debug symbols on demand from a symbol server, keyed by the binary's build ID. Parca uses this extensively.- Frame pointers: Go and Rust binaries include frame pointers by default, making stack unwinding reliable. Java and Python require language-specific unwinders (e.g., reading the JVM's internal stack structures).
- DWARF-based unwinding: For C/C++ binaries compiled with
-g, the agent reads DWARF debug info to reconstruct the call stack even without frame pointers.
A common interview question: "Why does eBPF profiling sometimes show incomplete or broken stack traces for Java applications?" The answer is that the JVM uses JIT compilation, which generates code at runtime that has no pre-compiled debug symbols. The profiler must use a JVM agent (like async-profiler) or read the JVM's internal PerfData structures to correctly unwind Java stacks. Pure eBPF kernel-side unwinding cannot see JIT-compiled frames without additional help.