Build1 publisher3 min readPublished
Both eBPF hooks install into the kernel by different routes, but the difference that bites in production is how they hand over arguments, and a kprobe hands over registers by position, trusting whatever sits in them at that moment.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The post's own two listings show the silent-failure path by accident.
The live kprobe hooks `do_mmap` and reads `PT_REGS_PARM1` through `PARM4` as addr, len, prot and flags [9]. The fentry rewrite of the same hook declares its parameters as `struct file *file`, then addr, len, prot, with a comment saying that list follows the kernel prototype [15]. Line the two up and every position slides by one slot [16]. `PT_REGS_PARM3` expands to `rdx` on x86-64, and nothing verifies that `rdx` is holding `prot` [10]. The post names the consequence directly: when the kernel's signature moves, and it has, the offsets keep compiling and keep returning a number that is simply wrong [11]. An agent that reports a plausible value is much harder to notice than one that crashes.
The cost argument is weaker than the correctness argument, because the post supplies no measured per-call delta for either mechanism [19]. It asserts the ordering as a property of the install path rather than a tuning constant [17], which is a reasonable thing to claim about an exception versus a trampoline, and still not a number you can budget against. What can be bounded is the sensitivity, using the author's own framing of a function that fires a million times a second [3]. At that rate, one microsecond of added cost per call is one full CPU-second per second, or one core gone [18]. For that arithmetic to mean anything on your box, you need your call rate, your kernel, and your program body measured, because none of those three are in the source.
Then the adoption bill. The author's project sits at 13 kprobe attach points and uses that as the setting for when he would reach for fentry instead [8]. The 5.5-plus-BTF floor and the ftrace-attachable requirement [7] make that 13 separate eligibility checks, not one migration, and any function that fails the check keeps its `pt_regs` reader with the by-position risk intact.
Where I would spend the effort first is `fexit`, which delivers arguments and return value to a single program, while a `kretprobe` needs a paired entry probe and stashed state to do the same job [14]. That stashed state is code you wrote and now maintain: a map with a lifetime and a cleanup path.
On a single kernel version with BTF present, `BPF_PROG` plus fentry is the right default, and for the reason the post gives rather than the CPU one: a prototype that disagrees with BTF fails at load instead of quietly handing back the wrong register [12][13]. On a fleet with older kernels, the kprobe program stays in the tree as the fallback, and the argument-by-position hazard stays with it.
Ranked by verification strength, evidence, and original report placement.
There are two common ways to attach an eBPF program to a kernel function: kprobe and fentry.
The post argues kprobe and fentry are not interchangeable: they install into the kernel by different mechanisms, hand you the function's arguments in different forms, and cost different amounts of CPU per call on a hot path.
kprobe attaches by patching the target instruction; classically a breakpoint (int3 on x86) that traps into the kprobe machinery, runs the program, then resumes the displaced instruction.
kprobe works almost anywhere and needs nothing special from the kernel build; the program receives a raw struct pt_regs * and must dig the arguments out of registers itself.
fentry attaches to the function's __fentry__ site, the call slot the compiler already left at the top of every traceable function for ftrace, through a generated BPF trampoline, with no exception and a cost closer to a plain call.
fentry hands arguments over already typed and resolved from BTF, and requires a kernel 5.5 or newer built with BTF plus a function that is ftrace-attachable.
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Checkable mechanisms, one voice
The technical spine of dev.to's post is checkable elsewhere: instruction patching and the int3 path, the compiler's __fentry__ call slot, BPF_PROG unpacking a typed argument array, the 5.5 kernel floor and CONFIG_DEBUG_INFO_BTF all match kernel and libbpf documentation. Credit too for the KPROBES_ON_FTRACE concession, which weakens the author's own headline about trap costs. What the post does not survive is its own sample code: the kprobe reads PARM1 as addr while the fentry prototype opens with struct file *file, and no kernel version is attached to either.
One codebase, still on kprobe
The whole adoption picture is the author's own SentinelEdge, and it hooks 13 points with kprobe rather than the path the post recommends. A piece arguing fentry should be the default whose reference implementation has not moved is a weak signal in its own favour, and nothing here shows a third party, distribution or vendor making the switch.
Speed claim outruns the numbers
The typing argument is stated at about the right size: a mismatched prototype failing at load instead of returning a wrong register is a real property, and the post does not oversell it. The speed argument is where the reach shows. A million calls a second and measurable overhead on every request arrive with no per-call figure for either path, and then the piece itself says the gap narrows considerably on modern kernels. Overstated on cost, fair on correctness.
House project, house video
The sample code, the project it comes from and the companion video all belong to the author, and the video is pitched twice as the place to see the instruction-level proof. For a mechanism walkthrough that is ordinary practice rather than a conflict, and the self-undercutting KPROBES_ON_FTRACE note cuts against a pure promotion read. Still, the authority of the piece rests on a codebase nobody outside has inspected, and on a video dev.to did not publish alongside it.
Firm on mechanism, thin elsewhere
Confidence rides on how ordinary the kernel-level claims are; they match widely documented behaviour of kprobes, ftrace and BTF-typed attachment. It is held down by three things our coverage cannot fix from here: a single publisher who is also the author of the code, an unreconciled one-slot mismatch between the two do_mmap listings, and a cost argument with no measurement behind it.
build
The kernel's unlikely() macro buys you fall-through, and bills you when you guess wrong1 publisher
build
Exit code 137 is the kernel collecting on a bet you did not know it placed1 publisher
build
Cross-node pod traffic: routing or encapsulation, and why that choice is a debugging decision1 publisher
build
Coherent or streaming DMA is not a style choice: three ways ownership gets broken1 publisher
Publishers with included, body-backed reporting in this cluster.
1 article · September 7, 2026