In this lesson, you will learn how to quantify the performance degradation introduced by live binary patching mechanisms like kpatch, kgraft, or user-space equivalents.
The Mechanism of Patching
Binary patching works by intercepting the execution flow of a running process. Most modern Linux live-patching technologies use a "stop-the-world" approach or ftrace-based redirection. When a function is patched, the entry point is modified to jump into a "trampoline" or a new code segment containing the fix.
While this prevents downtime, it introduces two primary sources of overhead:
- Instruction Cache Misses: The CPU must jump to a different memory address, potentially invalidating the pipeline's pre-fetched instructions.
- Redirection Penalty: Each call to a patched function now incurs the cost of the jump instruction, which, while measured in nanoseconds, can compound significantly in tight loops.
Observing the Overhead
To evaluate the impact, you must compare the cycles and instructions retired for a patched function versus an unpatched one. Using perf is the gold standard here. You can isolate the execution time of a specific function before and after the patch is applied:
# Monitor the function before patching
perf stat -e cycles,instructions,cache-misses -p <pid> -e probe:function_name sleep 1
# Apply the patch, then re-run the trace
perf stat -e cycles,instructions,cache-misses -p <pid> -e probe:function_name sleep 1
If the cache-misses count increases significantly after applying the patch, it indicates that the binary modification is causing cache contention, likely because the patched code segment is distant from the original code hot-path.
Analyzing the Tail Latency
Beyond raw instruction count, patching often disrupts the compiler's ability to inline functions. If you notice an increase in nanoseconds per operation during your tracing, you are likely witnessing the loss of inlining optimizations. To verify this, use perf record and generate a report to see if the patched function has dropped out of the top-performing "hot" paths, as the overhead of the jump often pushes the function above the threshold for L1 cache residency.
Key Takeaway: Binary patching overhead is best measured by monitoring the delta in cache misses and instruction cycles, as the primary performance cost stems from broken instruction locality and lost inlining optimizations.