|
| 1 | +.. SPDX-License-Identifier: GPL-2.0 |
| 2 | +
|
| 3 | +================================== |
| 4 | +Fprobe - Function entry/exit probe |
| 5 | +================================== |
| 6 | + |
| 7 | +.. Author: Masami Hiramatsu <[email protected]> |
| 8 | +
|
| 9 | +Introduction |
| 10 | +============ |
| 11 | + |
| 12 | +Fprobe is a function entry/exit probe mechanism based on ftrace. |
| 13 | +Instead of using ftrace full feature, if you only want to attach callbacks |
| 14 | +on function entry and exit, similar to the kprobes and kretprobes, you can |
| 15 | +use fprobe. Compared with kprobes and kretprobes, fprobe gives faster |
| 16 | +instrumentation for multiple functions with single handler. This document |
| 17 | +describes how to use fprobe. |
| 18 | + |
| 19 | +The usage of fprobe |
| 20 | +=================== |
| 21 | + |
| 22 | +The fprobe is a wrapper of ftrace (+ kretprobe-like return callback) to |
| 23 | +attach callbacks to multiple function entry and exit. User needs to set up |
| 24 | +the `struct fprobe` and pass it to `register_fprobe()`. |
| 25 | + |
| 26 | +Typically, `fprobe` data structure is initialized with the `entry_handler` |
| 27 | +and/or `exit_handler` as below. |
| 28 | + |
| 29 | +.. code-block:: c |
| 30 | +
|
| 31 | + struct fprobe fp = { |
| 32 | + .entry_handler = my_entry_callback, |
| 33 | + .exit_handler = my_exit_callback, |
| 34 | + }; |
| 35 | +
|
| 36 | +To enable the fprobe, call one of register_fprobe(), register_fprobe_ips(), and |
| 37 | +register_fprobe_syms(). These functions register the fprobe with different types |
| 38 | +of parameters. |
| 39 | + |
| 40 | +The register_fprobe() enables a fprobe by function-name filters. |
| 41 | +E.g. this enables @fp on "func*()" function except "func2()".:: |
| 42 | + |
| 43 | + register_fprobe(&fp, "func*", "func2"); |
| 44 | + |
| 45 | +The register_fprobe_ips() enables a fprobe by ftrace-location addresses. |
| 46 | +E.g. |
| 47 | + |
| 48 | +.. code-block:: c |
| 49 | +
|
| 50 | + unsigned long ips[] = { 0x.... }; |
| 51 | +
|
| 52 | + register_fprobe_ips(&fp, ips, ARRAY_SIZE(ips)); |
| 53 | +
|
| 54 | +And the register_fprobe_syms() enables a fprobe by symbol names. |
| 55 | +E.g. |
| 56 | + |
| 57 | +.. code-block:: c |
| 58 | +
|
| 59 | + char syms[] = {"func1", "func2", "func3"}; |
| 60 | +
|
| 61 | + register_fprobe_syms(&fp, syms, ARRAY_SIZE(syms)); |
| 62 | +
|
| 63 | +To disable (remove from functions) this fprobe, call:: |
| 64 | + |
| 65 | + unregister_fprobe(&fp); |
| 66 | + |
| 67 | +You can temporally (soft) disable the fprobe by:: |
| 68 | + |
| 69 | + disable_fprobe(&fp); |
| 70 | + |
| 71 | +and resume by:: |
| 72 | + |
| 73 | + enable_fprobe(&fp); |
| 74 | + |
| 75 | +The above is defined by including the header:: |
| 76 | + |
| 77 | + #include <linux/fprobe.h> |
| 78 | + |
| 79 | +Same as ftrace, the registered callbacks will start being called some time |
| 80 | +after the register_fprobe() is called and before it returns. See |
| 81 | +:file:`Documentation/trace/ftrace.rst`. |
| 82 | + |
| 83 | +Also, the unregister_fprobe() will guarantee that the both enter and exit |
| 84 | +handlers are no longer being called by functions after unregister_fprobe() |
| 85 | +returns as same as unregister_ftrace_function(). |
| 86 | + |
| 87 | +The fprobe entry/exit handler |
| 88 | +============================= |
| 89 | + |
| 90 | +The prototype of the entry/exit callback function is as follows: |
| 91 | + |
| 92 | +.. code-block:: c |
| 93 | +
|
| 94 | + void callback_func(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs); |
| 95 | +
|
| 96 | +Note that both entry and exit callbacks have same ptototype. The @entry_ip is |
| 97 | +saved at function entry and passed to exit handler. |
| 98 | + |
| 99 | +@fp |
| 100 | + This is the address of `fprobe` data structure related to this handler. |
| 101 | + You can embed the `fprobe` to your data structure and get it by |
| 102 | + container_of() macro from @fp. The @fp must not be NULL. |
| 103 | + |
| 104 | +@entry_ip |
| 105 | + This is the ftrace address of the traced function (both entry and exit). |
| 106 | + Note that this may not be the actual entry address of the function but |
| 107 | + the address where the ftrace is instrumented. |
| 108 | + |
| 109 | +@regs |
| 110 | + This is the `pt_regs` data structure at the entry and exit. Note that |
| 111 | + the instruction pointer of @regs may be different from the @entry_ip |
| 112 | + in the entry_handler. If you need traced instruction pointer, you need |
| 113 | + to use @entry_ip. On the other hand, in the exit_handler, the instruction |
| 114 | + pointer of @regs is set to the currect return address. |
| 115 | + |
| 116 | +Share the callbacks with kprobes |
| 117 | +================================ |
| 118 | + |
| 119 | +Since the recursion safeness of the fprobe (and ftrace) is a bit different |
| 120 | +from the kprobes, this may cause an issue if user wants to run the same |
| 121 | +code from the fprobe and the kprobes. |
| 122 | + |
| 123 | +Kprobes has per-cpu 'current_kprobe' variable which protects the kprobe |
| 124 | +handler from recursion in all cases. On the other hand, fprobe uses |
| 125 | +only ftrace_test_recursion_trylock(). This allows interrupt context to |
| 126 | +call another (or same) fprobe while the fprobe user handler is running. |
| 127 | + |
| 128 | +This is not a matter if the common callback code has its own recursion |
| 129 | +detection, or it can handle the recursion in the different contexts |
| 130 | +(normal/interrupt/NMI.) |
| 131 | +But if it relies on the 'current_kprobe' recursion lock, it has to check |
| 132 | +kprobe_running() and use kprobe_busy_*() APIs. |
| 133 | + |
| 134 | +Fprobe has FPROBE_FL_KPROBE_SHARED flag to do this. If your common callback |
| 135 | +code will be shared with kprobes, please set FPROBE_FL_KPROBE_SHARED |
| 136 | +*before* registering the fprobe, like: |
| 137 | + |
| 138 | +.. code-block:: c |
| 139 | +
|
| 140 | + fprobe.flags = FPROBE_FL_KPROBE_SHARED; |
| 141 | +
|
| 142 | + register_fprobe(&fprobe, "func*", NULL); |
| 143 | +
|
| 144 | +This will protect your common callback from the nested call. |
| 145 | + |
| 146 | +The missed counter |
| 147 | +================== |
| 148 | + |
| 149 | +The `fprobe` data structure has `fprobe::nmissed` counter field as same as |
| 150 | +kprobes. |
| 151 | +This counter counts up when; |
| 152 | + |
| 153 | + - fprobe fails to take ftrace_recursion lock. This usually means that a function |
| 154 | + which is traced by other ftrace users is called from the entry_handler. |
| 155 | + |
| 156 | + - fprobe fails to setup the function exit because of the shortage of rethook |
| 157 | + (the shadow stack for hooking the function return.) |
| 158 | + |
| 159 | +The `fprobe::nmissed` field counts up in both cases. Therefore, the former |
| 160 | +skips both of entry and exit callback and the latter skips the exit |
| 161 | +callback, but in both case the counter will increase by 1. |
| 162 | + |
| 163 | +Note that if you set the FTRACE_OPS_FL_RECURSION and/or FTRACE_OPS_FL_RCU to |
| 164 | +`fprobe::ops::flags` (ftrace_ops::flags) when registering the fprobe, this |
| 165 | +counter may not work correctly, because ftrace skips the fprobe function which |
| 166 | +increase the counter. |
| 167 | + |
| 168 | + |
| 169 | +Functions and structures |
| 170 | +======================== |
| 171 | + |
| 172 | +.. kernel-doc:: include/linux/fprobe.h |
| 173 | +.. kernel-doc:: kernel/trace/fprobe.c |
| 174 | + |
0 commit comments