← Documents Documentation/trace/fprobe.rst GitHub 원문 ↗

Linux 6.18.37 · Tracing

Fprobe - Function entry/exit probe

ftrace 기반 fprobe의 등록 방식, entry·exit handler context, recursion 보호와 missed counter를 설명합니다.

Source pathDocumentation/trace/fprobe.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

fprobe.rst:1-198

ftrace 기반 fprobe의 등록 방식, entry·exit handler context, recursion 보호와 missed counter를 설명합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

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

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Fprobe 소개

1-19

이 문서는 GPL-2.0 조건을 따르며 Masami Hiramatsu가 작성했다.

Fprobe는 ftrace의 function-graph tracing 기능을 기반으로 function entry와 exit를 탐사하는 probe다.

모든 function을 trace하지 않고 kprobe·kretprobe처럼 특정 function의 진입과 종료에 callback을 연결하려면 fprobe를 사용할 수 있다. 하나의 handler로 여러 function을 계측할 때 fprobe는 kprobe·kretprobe보다 빠른 instrumentation을 제공한다. 이 문서는 fprobe 사용법을 설명한다.

Fprobe 기반
ftrace function-graphFprobe
Selected function entryEntry callback
Selected function exitExit callback

function-graph tracing 위에서 선택한 function의 entry와 exit callback을 실행한다.

.. SPDX-License-Identifier: GPL-2.0

==================================
Fprobe - Function entry/exit probe
==================================

.. Author: Masami Hiramatsu <mhiramat@kernel.org>

Introduction
============

Fprobe is a function entry/exit probe based on the function-graph tracing
feature in ftrace.
Instead of tracing all functions, if you want to attach callbacks on specific
function entry and exit, similar to the kprobes and kretprobes, you can
use fprobe. Compared with kprobes and kretprobes, fprobe gives faster
instrumentation for multiple functions with single handler. This document
describes how to use fprobe.

Fprobe 사용법

20-87

fprobe는 ftrace에 kretprobe와 비슷한 return callback을 더한 wrapper로, 여러 function의 entry와 exit에 callback을 연결한다. 사용자는 `struct fprobe`를 설정해 `register_fprobe()` 계열 함수에 전달한다.

일반적으로 `struct fprobe`의 `entry_handler`와 `exit_handler` 중 필요한 항목을 callback으로 초기화한다.

fprobe를 활성화하는 등록 함수는 `register_fprobe()`, `register_fprobe_ips()`, `register_fprobe_syms()` 세 가지이며 서로 다른 방식으로 탐사 대상을 지정한다.

`register_fprobe()`는 function name filter를 사용한다. `register_fprobe(&fp, "func*", "func2")`는 `func*()`에 일치하는 function 가운데 `func2()`를 제외하고 `fp`를 활성화한다.

`register_fprobe_ips()`는 ftrace location address array와 원소 수로 대상을 지정한다. `register_fprobe_syms()`는 symbol name array와 원소 수로 대상을 지정한다.

등록된 fprobe를 function에서 제거하려면 `unregister_fprobe()`를 호출한다. 등록은 유지한 채 잠시 soft-disable하려면 `disable_fprobe()`를 사용하고, `enable_fprobe()`로 다시 시작한다. 이 API는 `<linux/fprobe.h>`를 include하면 사용할 수 있다.

ftrace와 마찬가지로 등록 callback은 `register_fprobe()` 호출 뒤 반환하기 전 어느 시점부터 호출되기 시작할 수 있다. 자세한 timing 규칙은 `Documentation/trace/ftrace.rst`를 참조한다.

`unregister_fprobe()`는 반환한 뒤 function이 entry handler와 exit handler 어느 쪽도 더 호출하지 않음을 `unregister_ftrace_function()`과 동일하게 보장한다.

Fprobe 등록 방식
API대상 지정
register_fprobeFunction name include/exclude filter
register_fprobe_ipsFtrace location address array
register_fprobe_symsSymbol name array

탐사 대상 지정 형태에 따라 등록 함수를 고른다.

Fprobe lifecycle
Initialize struct fproberegister_fprobe*
Registereddisable_fprobe
Soft-disabledenable_fprobe
Registeredunregister_fprobe
unregister returnsNo further callbacks

등록, 일시 정지, 재개, 제거 순서다.

The usage of fprobe
===================

The fprobe is a wrapper of ftrace (+ kretprobe-like return callback) to
attach callbacks to multiple function entry and exit. User needs to set up
the `struct fprobe` and pass it to `register_fprobe()`.

Typically, `fprobe` data structure is initialized with the `entry_handler`
and/or `exit_handler` as below.

.. code-block:: c

 struct fprobe fp = {
        .entry_handler  = my_entry_callback,
        .exit_handler   = my_exit_callback,
 };

To enable the fprobe, call one of register_fprobe(), register_fprobe_ips(), and
register_fprobe_syms(). These functions register the fprobe with different types
of parameters.

The register_fprobe() enables a fprobe by function-name filters.
E.g. this enables @fp on "func*()" function except "func2()".::

  register_fprobe(&fp, "func*", "func2");

The register_fprobe_ips() enables a fprobe by ftrace-location addresses.
E.g.

.. code-block:: c

  unsigned long ips[] = { 0x.... };

  register_fprobe_ips(&fp, ips, ARRAY_SIZE(ips));

And the register_fprobe_syms() enables a fprobe by symbol names.
E.g.

.. code-block:: c

  char syms[] = {"func1", "func2", "func3"};

  register_fprobe_syms(&fp, syms, ARRAY_SIZE(syms));

To disable (remove from functions) this fprobe, call::

  unregister_fprobe(&fp);

You can temporally (soft) disable the fprobe by::

  disable_fprobe(&fp);

and resume by::

  enable_fprobe(&fp);

The above is defined by including the header::

  #include <linux/fprobe.h>

Same as ftrace, the registered callbacks will start being called some time
after the register_fprobe() is called and before it returns. See
:file:`Documentation/trace/ftrace.rst`.

Also, the unregister_fprobe() will guarantee that both enter and exit
handlers are no longer being called by functions after unregister_fprobe()
returns as same as unregister_ftrace_function().

Fprobe entry·exit handler

88-128

entry callback은 `struct fprobe *`, `entry_ip`, `ret_ip`, `struct ftrace_regs *`, `entry_data`를 받고 `int`를 반환한다. exit callback은 같은 parameter를 받지만 반환 type은 `void`다.

`entry_ip`는 function 진입 때 저장돼 exit handler에도 전달된다. entry callback이 0이 아닌 값을 반환하면 그 진입에 대응하는 exit callback은 취소된다.

`fp`는 이 handler와 연관된 `fprobe` data structure의 주소다. fprobe를 사용자 data structure 안에 embed하고 `container_of()`로 enclosing object를 얻을 수 있다. `fp`는 `NULL`이면 안 된다.

`entry_ip`는 탐사한 function의 ftrace address로 entry와 exit 양쪽에 전달된다. 실제 function entry address가 아니라 ftrace instrumentation이 삽입된 위치일 수 있다.

`ret_ip`는 탐사한 function이 돌아갈 caller 내부의 return address이며 entry와 exit 모두에서 사용할 수 있다.

`fregs`는 entry 또는 exit 시점의 `ftrace_regs` data structure다. function parameter나 return value를 포함하므로 적절한 `ftrace_regs_*` API로 값을 읽을 수 있다.

`entry_data`는 entry handler와 exit handler 사이에서 data를 공유하는 local storage다. 기본은 `NULL`이다. 등록할 때 `exit_handler`와 `entry_data_size`를 지정하면 storage가 할당돼 두 handler에 모두 전달된다.

Handler parameter
Parameter의미
fp연관된 struct fprobe
entry_ipFtrace instrumentation address
ret_ipCaller의 return address
fregsParameter 또는 return value register context
entry_dataEntry·exit 공유 local storage

entry와 exit callback에 전달되는 context다.

The fprobe entry/exit handler
=============================

The prototype of the entry/exit callback function are as follows:

.. code-block:: c

 int entry_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data);

 void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data);

Note that the @entry_ip is saved at function entry and passed to exit
handler.
If the entry callback function returns !0, the corresponding exit callback
will be cancelled.

@fp
        This is the address of `fprobe` data structure related to this handler.
        You can embed the `fprobe` to your data structure and get it by
        container_of() macro from @fp. The @fp must not be NULL.

@entry_ip
        This is the ftrace address of the traced function (both entry and exit).
        Note that this may not be the actual entry address of the function but
        the address where the ftrace is instrumented.

@ret_ip
        This is the return address that the traced function will return to,
        somewhere in the caller. This can be used at both entry and exit.

@fregs
        This is the `ftrace_regs` data structure at the entry and exit. This
        includes the function parameters, or the return values. So user can
        access thos values via appropriate `ftrace_regs_*` APIs.

@entry_data
        This is a local storage to share the data between entry and exit handlers.
        This storage is NULL by default. If the user specify `exit_handler` field
        and `entry_data_size` field when registering the fprobe, the storage is
        allocated and passed to both `entry_handler` and `exit_handler`.

같은 function의 entry data 크기와 exit handler

129-139

entry data는 크기가 제한된 per-task stack을 통해 전달되므로 probe 하나의 `entry_data_size`는 `15 * sizeof(long)`으로 제한된다.

서로 다른 fprobe가 같은 function을 탐사하면 사용할 수 있는 한도가 더 작아진다. entry data 크기는 `sizeof(long)` 경계로 정렬되고, exit handler를 가진 각 fprobe는 stack에서 `sizeof(long)` 공간을 추가로 사용한다. 따라서 같은 function에 거는 fprobe 수는 가능한 한 적게 유지해야 한다.

Per-task stack 소비
항목공간
Probe별 entry_data 상한15 * sizeof(long)
정렬sizeof(long)
Exit handler가 있는 fprobe각각 sizeof(long) 추가

같은 function에 여러 fprobe를 걸 때의 공간 제약이다.

Entry data size and exit handlers on the same function
======================================================

Since the entry data is passed via per-task stack and it has limited size,
the entry data size per probe is limited to `15 * sizeof(long)`. You also need
to take care that the different fprobes are probing on the same function, this
limit becomes smaller. The entry data size is aligned to `sizeof(long)` and
each fprobe which has exit handler uses a `sizeof(long)` space on the stack,
you should keep the number of fprobes on the same function as small as
possible.

Kprobe와 callback 공유

140-169

fprobe와 ftrace의 recursion 안전 방식은 kprobe와 조금 다르므로 같은 callback code를 fprobe와 kprobe에서 함께 실행하면 문제가 생길 수 있다.

kprobe는 per-CPU `current_kprobe` variable로 모든 경우에 handler recursion을 막는다. 반면 fprobe는 `ftrace_test_recursion_trylock()`만 사용한다. 따라서 fprobe user handler가 실행 중이어도 interrupt context가 다른 fprobe 또는 같은 fprobe를 다시 호출할 수 있다.

공통 callback code가 자체 recursion detection을 갖거나 normal·interrupt·NMI의 서로 다른 context에서 recursion을 처리할 수 있다면 문제가 되지 않는다. 하지만 `current_kprobe` recursion lock에 의존한다면 `kprobe_running()`을 검사하고 `kprobe_busy_*()` API를 사용해야 한다.

이 처리를 위해 fprobe는 `FPROBE_FL_KPROBE_SHARED` flag를 제공한다. callback code를 kprobe와 공유한다면 fprobe 등록 전에 이 flag를 설정해야 하며, 그러면 공통 callback을 nested call로부터 보호한다.

Shared callback recursion 보호
Common fprobe/kprobe callbackSet FPROBE_FL_KPROBE_SHARED
Register fprobeNested call protection
No shared flagInterrupt-context recursion possible

등록 전에 shared flag를 설정해 kprobe 호환 recursion guard를 적용한다.

Share the callbacks with kprobes
================================

Since the recursion safeness of the fprobe (and ftrace) is a bit different
from the kprobes, this may cause an issue if user wants to run the same
code from the fprobe and the kprobes.

Kprobes has per-cpu 'current_kprobe' variable which protects the kprobe
handler from recursion in all cases. On the other hand, fprobe uses
only ftrace_test_recursion_trylock(). This allows interrupt context to
call another (or same) fprobe while the fprobe user handler is running.

This is not a matter if the common callback code has its own recursion
detection, or it can handle the recursion in the different contexts
(normal/interrupt/NMI.)
But if it relies on the 'current_kprobe' recursion lock, it has to check
kprobe_running() and use kprobe_busy_*() APIs.

Fprobe has FPROBE_FL_KPROBE_SHARED flag to do this. If your common callback
code will be shared with kprobes, please set FPROBE_FL_KPROBE_SHARED
*before* registering the fprobe, like:

.. code-block:: c

 fprobe.flags = FPROBE_FL_KPROBE_SHARED;

 register_fprobe(&fprobe, "func*", NULL);

This will protect your common callback from the nested call.

Missed counter

170-192

`struct fprobe`에는 kprobe와 마찬가지로 `fprobe::nmissed` counter field가 있다.

첫 번째 증가 조건은 fprobe가 `ftrace_recursion` lock을 얻지 못한 경우다. 일반적으로 `entry_handler`가 호출한 function을 다른 ftrace user가 trace하고 있음을 뜻한다. 이 경우 entry와 exit callback이 모두 건너뛰어진다.

두 번째 조건은 per-task shadow stack에서 data buffer를 할당하지 못해 function exit 설정에 실패한 경우다. 이 경우에는 exit callback만 건너뛴다.

두 상황 모두 `fprobe::nmissed`가 1 증가하므로 counter만으로 어느 실패였는지는 구분할 수 없다.

등록할 때 `fprobe::ops::flags`, 즉 `ftrace_ops::flags`에 `FTRACE_OPS_FL_RECURSION` 또는 `FTRACE_OPS_FL_RCU`를 설정하면 이 counter가 정확히 동작하지 않을 수 있다. counter를 증가시키는 fprobe function 자체를 ftrace가 건너뛸 수 있기 때문이다.

nmissed 증가
실패생략Counter
ftrace_recursion lock 획득 실패Entry와 exit+1
Shadow-stack buffer 할당 실패Exit+1

실패 지점에 따라 생략되는 callback이 다르다.

The missed counter
==================

The `fprobe` data structure has `fprobe::nmissed` counter field as same as
kprobes.
This counter counts up when;

 - fprobe fails to take ftrace_recursion lock. This usually means that a function
   which is traced by other ftrace users is called from the entry_handler.

 - fprobe fails to setup the function exit because of failing to allocate the
   data buffer from the per-task shadow stack.

The `fprobe::nmissed` field counts up in both cases. Therefore, the former
skips both of entry and exit callback and the latter skips the exit
callback, but in both case the counter will increase by 1.

Note that if you set the FTRACE_OPS_FL_RECURSION and/or FTRACE_OPS_FL_RCU to
`fprobe::ops::flags` (ftrace_ops::flags) when registering the fprobe, this
counter may not work correctly, because ftrace skips the fprobe function which
increase the counter.

함수와 구조체 문서

193-198

`include/linux/fprobe.h`와 `kernel/trace/fprobe.c`의 kernel-doc에서 fprobe 함수와 구조체의 상세 API 문서를 확인할 수 있다.

Functions and structures
========================

.. kernel-doc:: include/linux/fprobe.h
.. kernel-doc:: kernel/trace/fprobe.c