← Documents Documentation/trace/ftrace-design.rst GitHub 원문 ↗

Linux 6.18.37 · Tracing

함수 추적기 설계

아키텍처가 기본 함수 추적, 함수 그래프 복귀 가로채기, 시스템 호출 tracepoint, 동적 런타임 패치를 구현하는 순서와 ABI 책임을 설명합니다.

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

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

1. 요약·해설

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

요약·해설

ftrace-design.rst:1-395

아키텍처가 기본 함수 추적, 함수 그래프 복귀 가로채기, 시스템 호출 tracepoint, 동적 런타임 패치를 구현하는 순서와 ABI 책임을 설명합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 ======================
2 Function Tracer Design
3 ======================
4
5 :Author: Mike Frysinger
6
7 .. caution::
8 This document is out of date. Some of the description below doesn't
9 match current implementation now.
10
11 Introduction
12 ------------
13
14 Here we will cover the architecture pieces that the common function tracing
15 code relies on for proper functioning. Things are broken down into increasing
16 complexity so that you can start simple and at least get basic functionality.
17
18 Note that this focuses on architecture implementation details only. If you
19 want more explanation of a feature in terms of common code, review the common
20 ftrace.txt file.
21
22 Ideally, everyone who wishes to retain performance while supporting tracing in
23 their kernel should make it all the way to dynamic ftrace support.
24
25
26 Prerequisites
27 -------------
28
29 Ftrace relies on these features being implemented:
30 - STACKTRACE_SUPPORT - implement save_stack_trace()
31 - TRACE_IRQFLAGS_SUPPORT - implement include/asm/irqflags.h
32
33
34 HAVE_FUNCTION_TRACER
35 --------------------
36
37 You will need to implement the mcount and the ftrace_stub functions.
38
39 The exact mcount symbol name will depend on your toolchain. Some call it
40 "mcount", "_mcount", or even "__mcount". You can probably figure it out by
41 running something like::
42
43 $ echo 'main(){}' | gcc -x c -S -o - - -pg | grep mcount
44 call mcount
45
46 We'll make the assumption below that the symbol is "mcount" just to keep things
47 nice and simple in the examples.
48
49 Keep in mind that the ABI that is in effect inside of the mcount function is
50 *highly* architecture/toolchain specific. We cannot help you in this regard,
51 sorry. Dig up some old documentation and/or find someone more familiar than
52 you to bang ideas off of. Typically, register usage (argument/scratch/etc...)
53 is a major issue at this point, especially in relation to the location of the
54 mcount call (before/after function prologue). You might also want to look at
55 how glibc has implemented the mcount function for your architecture. It might
56 be (semi-)relevant.
57
58 The mcount function should check the function pointer ftrace_trace_function
59 to see if it is set to ftrace_stub. If it is, there is nothing for you to do,
60 so return immediately. If it isn't, then call that function in the same way
61 the mcount function normally calls __mcount_internal -- the first argument is
62 the "frompc" while the second argument is the "selfpc" (adjusted to remove the
63 size of the mcount call that is embedded in the function).
64
65 For example, if the function foo() calls bar(), when the bar() function calls
66 mcount(), the arguments mcount() will pass to the tracer are:
67
68 - "frompc" - the address bar() will use to return to foo()
69 - "selfpc" - the address bar() (with mcount() size adjustment)
70
71 Also keep in mind that this mcount function will be called *a lot*, so
72 optimizing for the default case of no tracer will help the smooth running of
73 your system when tracing is disabled. So the start of the mcount function is
74 typically the bare minimum with checking things before returning. That also
75 means the code flow should usually be kept linear (i.e. no branching in the nop
76 case). This is of course an optimization and not a hard requirement.
77
78 Here is some pseudo code that should help (these functions should actually be
79 implemented in assembly)::
80
81 void ftrace_stub(void)
82 {
83 return;
84 }
85
86 void mcount(void)
87 {
88 /* save any bare state needed in order to do initial checking */
89
90 extern void (*ftrace_trace_function)(unsigned long, unsigned long);
91 if (ftrace_trace_function != ftrace_stub)
92 goto do_trace;
93
94 /* restore any bare state */
95
96 return;
97
98 do_trace:
99
100 /* save all state needed by the ABI (see paragraph above) */
101
102 unsigned long frompc = ...;
103 unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
104 ftrace_trace_function(frompc, selfpc);
105
106 /* restore all state needed by the ABI */
107 }
108
109 Don't forget to export mcount for modules !
110 ::
111
112 extern void mcount(void);
113 EXPORT_SYMBOL(mcount);
114
115
116 HAVE_FUNCTION_GRAPH_TRACER
117 --------------------------
118
119 Deep breath ... time to do some real work. Here you will need to update the
120 mcount function to check ftrace graph function pointers, as well as implement
121 some functions to save (hijack) and restore the return address.
122
123 The mcount function should check the function pointers ftrace_graph_return
124 (compare to ftrace_stub) and ftrace_graph_entry (compare to
125 ftrace_graph_entry_stub). If either of those is not set to the relevant stub
126 function, call the arch-specific function ftrace_graph_caller which in turn
127 calls the arch-specific function prepare_ftrace_return. Neither of these
128 function names is strictly required, but you should use them anyway to stay
129 consistent across the architecture ports -- easier to compare & contrast
130 things.
131
132 The arguments to prepare_ftrace_return are slightly different than what are
133 passed to ftrace_trace_function. The second argument "selfpc" is the same,
134 but the first argument should be a pointer to the "frompc". Typically this is
135 located on the stack. This allows the function to hijack the return address
136 temporarily to have it point to the arch-specific function return_to_handler.
137 That function will simply call the common ftrace_return_to_handler function and
138 that will return the original return address with which you can return to the
139 original call site.
140
141 Here is the updated mcount pseudo code::
142
143 void mcount(void)
144 {
145 ...
146 if (ftrace_trace_function != ftrace_stub)
147 goto do_trace;
148
149 +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
150 + extern void (*ftrace_graph_return)(...);
151 + extern void (*ftrace_graph_entry)(...);
152 + if (ftrace_graph_return != ftrace_stub ||
153 + ftrace_graph_entry != ftrace_graph_entry_stub)
154 + ftrace_graph_caller();
155 +#endif
156
157 /* restore any bare state */
158 ...
159
160 Here is the pseudo code for the new ftrace_graph_caller assembly function::
161
162 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
163 void ftrace_graph_caller(void)
164 {
165 /* save all state needed by the ABI */
166
167 unsigned long *frompc = &...;
168 unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
169 /* passing frame pointer up is optional -- see below */
170 prepare_ftrace_return(frompc, selfpc, frame_pointer);
171
172 /* restore all state needed by the ABI */
173 }
174 #endif
175
176 For information on how to implement prepare_ftrace_return(), simply look at the
177 x86 version (the frame pointer passing is optional; see the next section for
178 more information). The only architecture-specific piece in it is the setup of
179 the fault recovery table (the asm(...) code). The rest should be the same
180 across architectures.
181
182 Here is the pseudo code for the new return_to_handler assembly function. Note
183 that the ABI that applies here is different from what applies to the mcount
184 code. Since you are returning from a function (after the epilogue), you might
185 be able to skimp on things saved/restored (usually just registers used to pass
186 return values).
187 ::
188
189 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
190 void return_to_handler(void)
191 {
192 /* save all state needed by the ABI (see paragraph above) */
193
194 void (*original_return_point)(void) = ftrace_return_to_handler();
195
196 /* restore all state needed by the ABI */
197
198 /* this is usually either a return or a jump */
199 original_return_point();
200 }
201 #endif
202
203
204 HAVE_FUNCTION_GRAPH_FP_TEST
205 ---------------------------
206
207 An arch may pass in a unique value (frame pointer) to both the entering and
208 exiting of a function. On exit, the value is compared and if it does not
209 match, then it will panic the kernel. This is largely a sanity check for bad
210 code generation with gcc. If gcc for your port sanely updates the frame
211 pointer under different optimization levels, then ignore this option.
212
213 However, adding support for it isn't terribly difficult. In your assembly code
214 that calls prepare_ftrace_return(), pass the frame pointer as the 3rd argument.
215 Then in the C version of that function, do what the x86 port does and pass it
216 along to ftrace_push_return_trace() instead of a stub value of 0.
217
218 Similarly, when you call ftrace_return_to_handler(), pass it the frame pointer.
219
220 HAVE_SYSCALL_TRACEPOINTS
221 ------------------------
222
223 You need very few things to get the syscalls tracing in an arch.
224
225 - Support HAVE_ARCH_TRACEHOOK (see arch/Kconfig).
226 - Have a NR_syscalls variable in <asm/unistd.h> that provides the number
227 of syscalls supported by the arch.
228 - Support the TIF_SYSCALL_TRACEPOINT thread flags.
229 - Put the trace_sys_enter() and trace_sys_exit() tracepoints calls from ptrace
230 in the ptrace syscalls tracing path.
231 - If the system call table on this arch is more complicated than a simple array
232 of addresses of the system calls, implement an arch_syscall_addr to return
233 the address of a given system call.
234 - If the symbol names of the system calls do not match the function names on
235 this arch, define ARCH_HAS_SYSCALL_MATCH_SYM_NAME in asm/ftrace.h and
236 implement arch_syscall_match_sym_name with the appropriate logic to return
237 true if the function name corresponds with the symbol name.
238 - Tag this arch as HAVE_SYSCALL_TRACEPOINTS.
239
240
241 HAVE_DYNAMIC_FTRACE
242 -------------------
243
244 See scripts/recordmcount.pl for more info. Just fill in the arch-specific
245 details for how to locate the addresses of mcount call sites via objdump.
246 This option doesn't make much sense without also implementing dynamic ftrace.
247
248 You will first need HAVE_FUNCTION_TRACER, so scroll your reader back up if you
249 got over eager.
250
251 Once those are out of the way, you will need to implement:
252 - asm/ftrace.h:
253 - MCOUNT_ADDR
254 - ftrace_call_adjust()
255 - struct dyn_arch_ftrace{}
256 - asm code:
257 - mcount() (new stub)
258 - ftrace_caller()
259 - ftrace_call()
260 - ftrace_stub()
261 - C code:
262 - ftrace_dyn_arch_init()
263 - ftrace_make_nop()
264 - ftrace_make_call()
265 - ftrace_update_ftrace_func()
266
267 First you will need to fill out some arch details in your asm/ftrace.h.
268
269 Define MCOUNT_ADDR as the address of your mcount symbol similar to::
270
271 #define MCOUNT_ADDR ((unsigned long)mcount)
272
273 Since no one else will have a decl for that function, you will need to::
274
275 extern void mcount(void);
276
277 You will also need the helper function ftrace_call_adjust(). Most people
278 will be able to stub it out like so::
279
280 static inline unsigned long ftrace_call_adjust(unsigned long addr)
281 {
282 return addr;
283 }
284
285 <details to be filled>
286
287 Lastly you will need the custom dyn_arch_ftrace structure. If you need
288 some extra state when runtime patching arbitrary call sites, this is the
289 place. For now though, create an empty struct::
290
291 struct dyn_arch_ftrace {
292 /* No extra data needed */
293 };
294
295 With the header out of the way, we can fill out the assembly code. While we
296 did already create a mcount() function earlier, dynamic ftrace only wants a
297 stub function. This is because the mcount() will only be used during boot
298 and then all references to it will be patched out never to return. Instead,
299 the guts of the old mcount() will be used to create a new ftrace_caller()
300 function. Because the two are hard to merge, it will most likely be a lot
301 easier to have two separate definitions split up by #ifdefs. Same goes for
302 the ftrace_stub() as that will now be inlined in ftrace_caller().
303
304 Before we get confused anymore, let's check out some pseudo code so you can
305 implement your own stuff in assembly::
306
307 void mcount(void)
308 {
309 return;
310 }
311
312 void ftrace_caller(void)
313 {
314 /* save all state needed by the ABI (see paragraph above) */
315
316 unsigned long frompc = ...;
317 unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
318
319 ftrace_call:
320 ftrace_stub(frompc, selfpc);
321
322 /* restore all state needed by the ABI */
323
324 ftrace_stub:
325 return;
326 }
327
328 This might look a little odd at first, but keep in mind that we will be runtime
329 patching multiple things. First, only functions that we actually want to trace
330 will be patched to call ftrace_caller(). Second, since we only have one tracer
331 active at a time, we will patch the ftrace_caller() function itself to call the
332 specific tracer in question. That is the point of the ftrace_call label.
333
334 With that in mind, let's move on to the C code that will actually be doing the
335 runtime patching. You'll need a little knowledge of your arch's opcodes in
336 order to make it through the next section.
337
338 Every arch has an init callback function. If you need to do something early on
339 to initialize some state, this is the time to do that. Otherwise, this simple
340 function below should be sufficient for most people::
341
342 int __init ftrace_dyn_arch_init(void)
343 {
344 return 0;
345 }
346
347 There are two functions that are used to do runtime patching of arbitrary
348 functions. The first is used to turn the mcount call site into a nop (which
349 is what helps us retain runtime performance when not tracing). The second is
350 used to turn the mcount call site into a call to an arbitrary location (but
351 typically that is ftracer_caller()). See the general function definition in
352 linux/ftrace.h for the functions::
353
354 ftrace_make_nop()
355 ftrace_make_call()
356
357 The rec->ip value is the address of the mcount call site that was collected
358 by the scripts/recordmcount.pl during build time.
359
360 The last function is used to do runtime patching of the active tracer. This
361 will be modifying the assembly code at the location of the ftrace_call symbol
362 inside of the ftrace_caller() function. So you should have sufficient padding
363 at that location to support the new function calls you'll be inserting. Some
364 people will be using a "call" type instruction while others will be using a
365 "branch" type instruction. Specifically, the function is::
366
367 ftrace_update_ftrace_func()
368
369
370 HAVE_DYNAMIC_FTRACE + HAVE_FUNCTION_GRAPH_TRACER
371 ------------------------------------------------
372
373 The function grapher needs a few tweaks in order to work with dynamic ftrace.
374 Basically, you will need to:
375
376 - update:
377 - ftrace_caller()
378 - ftrace_graph_call()
379 - ftrace_graph_caller()
380 - implement:
381 - ftrace_enable_ftrace_graph_caller()
382 - ftrace_disable_ftrace_graph_caller()
383
384 <details to be filled>
385
386 Quick notes:
387
388 - add a nop stub after the ftrace_call location named ftrace_graph_call;
389 stub needs to be large enough to support a call to ftrace_graph_caller()
390 - update ftrace_graph_caller() to work with being called by the new
391 ftrace_caller() since some semantics may have changed
392 - ftrace_enable_ftrace_graph_caller() will runtime patch the
393 ftrace_graph_call location with a call to ftrace_graph_caller()
394 - ftrace_disable_ftrace_graph_caller() will runtime patch the
395 ftrace_graph_call location with nops
396

3. 한국어 전문 번역

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

문서 정보와 주의 사항

1-10

이 문서는 Mike Frysinger가 작성한 함수 추적기 아키텍처 설계 안내서다.

주의: 이 문서는 오래되었으며 아래 설명 가운데 일부는 현재 구현과 일치하지 않는다. 따라서 포팅의 개념적 순서와 인터페이스 의도를 이해하는 자료로 사용하되, 실제 구현은 Linux v6.18.37 소스와 해당 아키텍처 코드를 함께 확인해야 한다.

======================
Function Tracer Design
======================

:Author: Mike Frysinger

.. caution::
	This document is out of date. Some of the description below doesn't
	match current implementation now.

소개

11-25

이 문서는 공통 함수 추적 코드가 올바르게 동작하기 위해 아키텍처가 제공해야 하는 구성 요소를 설명한다. 요구 사항을 복잡도가 높아지는 순서로 나누므로, 먼저 기본 기능을 구현한 뒤 단계적으로 고급 기능을 추가할 수 있다.

초점은 공통 코드의 사용법이 아니라 아키텍처 구현 세부 사항이다. 공통 기능의 의미와 사용자 인터페이스는 공통 `ftrace.txt` 문서를 참고한다.

추적 기능을 제공하면서 비활성 상태의 실행 성능도 유지하려면 최종적으로 동적 ftrace까지 지원하는 것이 바람직하다. 동적 ftrace는 추적하지 않는 호출 지점을 런타임에 패치해 비용을 줄인다.

아키텍처 포팅 단계
HAVE_FUNCTION_TRACER기본 mcount 콜백
기본 mcount 콜백HAVE_FUNCTION_GRAPH_TRACER
HAVE_FUNCTION_GRAPH_TRACERHAVE_DYNAMIC_FTRACE
HAVE_DYNAMIC_FTRACE동적 함수 그래프 추적

기본 함수 진입 추적에서 동적 패치와 함수 그래프 추적으로 확장한다.

Introduction
------------

Here we will cover the architecture pieces that the common function tracing
code relies on for proper functioning.  Things are broken down into increasing
complexity so that you can start simple and at least get basic functionality.

Note that this focuses on architecture implementation details only.  If you
want more explanation of a feature in terms of common code, review the common
ftrace.txt file.

Ideally, everyone who wishes to retain performance while supporting tracing in
their kernel should make it all the way to dynamic ftrace support.

선행 조건

26-33

Ftrace는 두 아키텍처 기능을 전제로 한다. `STACKTRACE_SUPPORT`는 `save_stack_trace()`를 구현해 호출 스택을 수집할 수 있어야 하고, `TRACE_IRQFLAGS_SUPPORT`는 `include/asm/irqflags.h`의 인터럽트 플래그 추적 지원을 제공해야 한다.

필수 아키텍처 기능
설정구현 요구 사항
STACKTRACE_SUPPORTsave_stack_trace() 구현
TRACE_IRQFLAGS_SUPPORTinclude/asm/irqflags.h 구현

함수 추적 포팅에 앞서 준비할 항목이다.

Prerequisites
-------------

Ftrace relies on these features being implemented:
  - STACKTRACE_SUPPORT - implement save_stack_trace()
  - TRACE_IRQFLAGS_SUPPORT - implement include/asm/irqflags.h

HAVE_FUNCTION_TRACER

34-115

기본 함수 추적기를 지원하려면 `mcount`와 `ftrace_stub` 함수를 구현해야 한다. 정확한 심볼 이름은 도구 체인에 따라 `mcount`, `_mcount`, `__mcount` 등으로 달라질 수 있으므로, 문서의 `gcc -pg` 예제처럼 어셈블리 출력을 검사해 실제 이름을 확인한다. 아래 설명은 편의를 위해 `mcount`를 사용한다.

`mcount` 내부에서 유효한 ABI는 아키텍처와 도구 체인에 매우 의존적이다. 인자 레지스터와 임시 레지스터의 보존 규칙뿐 아니라 `mcount` 호출이 함수 프롤로그 앞인지 뒤인지도 중요하다. 해당 아키텍처의 오래된 ABI 문서와 glibc의 `mcount` 구현이 참고 자료가 될 수 있다.

`mcount`는 먼저 함수 포인터 `ftrace_trace_function`이 `ftrace_stub`인지 확인한다. 같으면 활성 추적기가 없으므로 즉시 반환한다. 다르면 전통적인 `__mcount_internal` 호출과 같은 방식으로 첫 번째 인자 `frompc`와 두 번째 인자 `selfpc`를 넘겨 활성 추적 함수를 호출한다.

`frompc`는 추적 대상 함수가 호출자에게 돌아갈 주소다. 예를 들어 `foo()`가 `bar()`를 호출했다면 `bar()`의 `mcount()`가 전달하는 `frompc`는 `foo()` 안의 복귀 지점이다. `selfpc`는 추적 대상 함수 `bar()`의 주소이며, 함수 안에 삽입된 `mcount` 호출 명령 길이 `MCOUNT_INSN_SIZE`만큼 복귀 주소를 조정해 계산한다.

`mcount`는 거의 모든 함수 진입에서 매우 자주 실행된다. 따라서 추적기가 없는 기본 경로는 최소한의 상태만 저장하고 검사한 뒤 곧바로 반환하도록 최적화해야 한다. nop 경로에서는 분기 없이 선형으로 흐르게 만드는 것이 일반적이지만, 이는 성능 최적화이지 기능상 강제 조건은 아니다.

문서의 의사 코드는 `ftrace_stub()`이 즉시 반환하고, `mcount()`가 최소 상태를 저장한 뒤 활성 추적기 여부를 검사하는 구조다. 활성 추적기가 있으면 ABI가 요구하는 전체 상태를 저장하고 `frompc`와 `selfpc`를 계산해 `ftrace_trace_function(frompc, selfpc)`를 호출한 다음 상태를 복원한다. 실제 구현은 어셈블리로 작성해야 한다.

모듈에서도 계측 호출을 해결할 수 있도록 `mcount`를 선언하고 `EXPORT_SYMBOL(mcount)`로 내보내는 것을 잊지 않아야 한다.

mcount 추적 인자
인자의미계산
frompcbar()가 foo()로 돌아갈 주소스택의 원래 복귀 주소
selfpc추적 중인 bar()의 주소복귀 주소 - MCOUNT_INSN_SIZE

foo()가 bar()를 호출한 경우를 기준으로 주소의 의미를 구분한다.

기본 mcount 경로
함수 진입mcount
mcountftrace_trace_function 검사
ftrace_stub즉시 반환
활성 추적기frompc/selfpc 계산
frompc/selfpc 계산추적 함수 호출
추적 함수 호출상태 복원 후 반환

비활성 경로를 짧게 유지하고 활성 추적기에서만 전체 ABI 상태를 보존한다.

HAVE_FUNCTION_TRACER
--------------------

You will need to implement the mcount and the ftrace_stub functions.

The exact mcount symbol name will depend on your toolchain.  Some call it
"mcount", "_mcount", or even "__mcount".  You can probably figure it out by
running something like::

	$ echo 'main(){}' | gcc -x c -S -o - - -pg | grep mcount
	        call    mcount

We'll make the assumption below that the symbol is "mcount" just to keep things
nice and simple in the examples.

Keep in mind that the ABI that is in effect inside of the mcount function is
*highly* architecture/toolchain specific.  We cannot help you in this regard,
sorry.  Dig up some old documentation and/or find someone more familiar than
you to bang ideas off of.  Typically, register usage (argument/scratch/etc...)
is a major issue at this point, especially in relation to the location of the
mcount call (before/after function prologue).  You might also want to look at
how glibc has implemented the mcount function for your architecture.  It might
be (semi-)relevant.

The mcount function should check the function pointer ftrace_trace_function
to see if it is set to ftrace_stub.  If it is, there is nothing for you to do,
so return immediately.  If it isn't, then call that function in the same way
the mcount function normally calls __mcount_internal -- the first argument is
the "frompc" while the second argument is the "selfpc" (adjusted to remove the
size of the mcount call that is embedded in the function).

For example, if the function foo() calls bar(), when the bar() function calls
mcount(), the arguments mcount() will pass to the tracer are:

  - "frompc" - the address bar() will use to return to foo()
  - "selfpc" - the address bar() (with mcount() size adjustment)

Also keep in mind that this mcount function will be called *a lot*, so
optimizing for the default case of no tracer will help the smooth running of
your system when tracing is disabled.  So the start of the mcount function is
typically the bare minimum with checking things before returning.  That also
means the code flow should usually be kept linear (i.e. no branching in the nop
case).  This is of course an optimization and not a hard requirement.

Here is some pseudo code that should help (these functions should actually be
implemented in assembly)::

	void ftrace_stub(void)
	{
		return;
	}

	void mcount(void)
	{
		/* save any bare state needed in order to do initial checking */

		extern void (*ftrace_trace_function)(unsigned long, unsigned long);
		if (ftrace_trace_function != ftrace_stub)
			goto do_trace;

		/* restore any bare state */

		return;

	do_trace:

		/* save all state needed by the ABI (see paragraph above) */

		unsigned long frompc = ...;
		unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
		ftrace_trace_function(frompc, selfpc);

		/* restore all state needed by the ABI */
	}

Don't forget to export mcount for modules !
::

	extern void mcount(void);
	EXPORT_SYMBOL(mcount);

HAVE_FUNCTION_GRAPH_TRACER

116-203

함수 그래프 추적은 함수 진입뿐 아니라 복귀도 관찰하므로 `mcount`를 확장하고 원래 복귀 주소를 임시로 가로챈 뒤 복원하는 아키텍처별 코드를 추가해야 한다.

`mcount`는 `ftrace_graph_return`을 `ftrace_stub`과 비교하고 `ftrace_graph_entry`를 `ftrace_graph_entry_stub`과 비교한다. 둘 중 하나라도 대응 스텁이 아니면 아키텍처별 `ftrace_graph_caller`를 호출하고, 이 함수가 다시 아키텍처별 `prepare_ftrace_return`을 호출한다. 이름 자체가 필수 ABI는 아니지만 다른 포트와 비교하기 쉽도록 이 이름을 유지하는 것이 좋다.

`prepare_ftrace_return`의 두 번째 인자 `selfpc`는 기본 추적기와 같다. 첫 번째 인자는 `frompc` 값이 아니라 그 값을 담은 위치의 포인터이며, 대개 스택에 있다. 이 포인터를 통해 원래 복귀 주소를 아키텍처별 `return_to_handler` 주소로 잠시 바꿀 수 있다.

추적 대상 함수가 끝나면 가로챈 주소 때문에 `return_to_handler`가 실행된다. 이 루틴은 공통 `ftrace_return_to_handler()`를 호출해 저장해 둔 원래 복귀 주소를 받고, 필요한 상태를 복원한 뒤 그 주소로 반환하거나 점프한다.

갱신된 `mcount` 의사 코드는 일반 함수 추적기 검사 뒤에 `CONFIG_FUNCTION_GRAPH_TRACER` 조건부 코드를 둔다. 그래프 진입 또는 복귀 함수가 활성화되어 있으면 `ftrace_graph_caller()`를 부른 뒤 기존의 최소 상태 복원 경로로 돌아간다.

`ftrace_graph_caller`는 ABI 상태를 저장하고 스택에서 `frompc`의 주소를 구하며, `selfpc`를 계산한 다음 선택적인 세 번째 인자인 프레임 포인터와 함께 `prepare_ftrace_return(frompc, selfpc, frame_pointer)`를 호출한다.

`prepare_ftrace_return()`의 구현은 x86 포트를 참고할 수 있다. 아키텍처에 따라 달라지는 핵심은 어셈블리로 구성하는 fault recovery table이고, 나머지 로직은 여러 아키텍처에서 같아야 한다. 프레임 포인터 전달은 선택 사항이며 다음 절의 검사를 사용할 때 필요하다.

`return_to_handler`는 함수 에필로그 이후의 복귀 경로에서 실행되므로 `mcount`와 적용 ABI가 다르다. 보통 반환 값을 전달하는 레지스터 정도만 저장하고 복원하면 되지만, 정확한 집합은 해당 아키텍처 ABI에 맞춰야 한다.

함수 그래프 복귀 가로채기
mcountftrace_graph_caller
ftrace_graph_callerprepare_ftrace_return
prepare_ftrace_returnfrompc를 return_to_handler로 교체
추적 대상 함수 반환return_to_handler
return_to_handlerftrace_return_to_handler
ftrace_return_to_handler원래 복귀 주소

원래 복귀 주소를 보관하고 공통 핸들러를 거쳐 호출자에게 돌아간다.

그래프 추적 함수의 역할
함수역할
ftrace_graph_caller진입 상태 저장 및 prepare_ftrace_return 호출
prepare_ftrace_return원래 복귀 주소 저장과 주소 가로채기
return_to_handler공통 복귀 핸들러 호출 후 원래 지점으로 이동

진입과 복귀 경로에 나뉜 아키텍처별 책임이다.

HAVE_FUNCTION_GRAPH_TRACER
--------------------------

Deep breath ... time to do some real work.  Here you will need to update the
mcount function to check ftrace graph function pointers, as well as implement
some functions to save (hijack) and restore the return address.

The mcount function should check the function pointers ftrace_graph_return
(compare to ftrace_stub) and ftrace_graph_entry (compare to
ftrace_graph_entry_stub).  If either of those is not set to the relevant stub
function, call the arch-specific function ftrace_graph_caller which in turn
calls the arch-specific function prepare_ftrace_return.  Neither of these
function names is strictly required, but you should use them anyway to stay
consistent across the architecture ports -- easier to compare & contrast
things.

The arguments to prepare_ftrace_return are slightly different than what are
passed to ftrace_trace_function.  The second argument "selfpc" is the same,
but the first argument should be a pointer to the "frompc".  Typically this is
located on the stack.  This allows the function to hijack the return address
temporarily to have it point to the arch-specific function return_to_handler.
That function will simply call the common ftrace_return_to_handler function and
that will return the original return address with which you can return to the
original call site.

Here is the updated mcount pseudo code::

	void mcount(void)
	{
	...
		if (ftrace_trace_function != ftrace_stub)
			goto do_trace;

	+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	+	extern void (*ftrace_graph_return)(...);
	+	extern void (*ftrace_graph_entry)(...);
	+	if (ftrace_graph_return != ftrace_stub ||
	+	    ftrace_graph_entry != ftrace_graph_entry_stub)
	+		ftrace_graph_caller();
	+#endif

		/* restore any bare state */
	...

Here is the pseudo code for the new ftrace_graph_caller assembly function::

	#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	void ftrace_graph_caller(void)
	{
		/* save all state needed by the ABI */

		unsigned long *frompc = &...;
		unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
		/* passing frame pointer up is optional -- see below */
		prepare_ftrace_return(frompc, selfpc, frame_pointer);

		/* restore all state needed by the ABI */
	}
	#endif

For information on how to implement prepare_ftrace_return(), simply look at the
x86 version (the frame pointer passing is optional; see the next section for
more information).  The only architecture-specific piece in it is the setup of
the fault recovery table (the asm(...) code).  The rest should be the same
across architectures.

Here is the pseudo code for the new return_to_handler assembly function.  Note
that the ABI that applies here is different from what applies to the mcount
code.  Since you are returning from a function (after the epilogue), you might
be able to skimp on things saved/restored (usually just registers used to pass
return values).
::

	#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	void return_to_handler(void)
	{
		/* save all state needed by the ABI (see paragraph above) */

		void (*original_return_point)(void) = ftrace_return_to_handler();

		/* restore all state needed by the ABI */

		/* this is usually either a return or a jump */
		original_return_point();
	}
	#endif

HAVE_FUNCTION_GRAPH_FP_TEST

204-219

아키텍처는 함수 진입과 종료에 같은 고유 값, 즉 프레임 포인터를 전달할 수 있다. 종료 시 값이 일치하지 않으면 커널 패닉을 일으킨다. 이 기능은 주로 GCC가 잘못된 코드를 생성했는지 확인하는 건전성 검사다. 여러 최적화 수준에서 프레임 포인터가 정상적으로 갱신되는 포트라면 사용하지 않아도 된다.

지원하려면 `prepare_ftrace_return()`을 호출하는 어셈블리 코드에서 프레임 포인터를 세 번째 인자로 넘긴다. C 구현은 x86처럼 스텁 값 0 대신 이 값을 `ftrace_push_return_trace()`에 전달한다. `ftrace_return_to_handler()` 호출에도 같은 프레임 포인터를 넘겨 진입과 종료 값을 비교할 수 있게 한다.

프레임 포인터 건전성 검사
함수 진입프레임 포인터 저장
함수 종료프레임 포인터 전달
값 일치정상 복귀
값 불일치커널 패닉

함수 진입과 종료의 프레임 포인터가 다르면 손상으로 처리한다.

HAVE_FUNCTION_GRAPH_FP_TEST
---------------------------

An arch may pass in a unique value (frame pointer) to both the entering and
exiting of a function.  On exit, the value is compared and if it does not
match, then it will panic the kernel.  This is largely a sanity check for bad
code generation with gcc.  If gcc for your port sanely updates the frame
pointer under different optimization levels, then ignore this option.

However, adding support for it isn't terribly difficult.  In your assembly code
that calls prepare_ftrace_return(), pass the frame pointer as the 3rd argument.
Then in the C version of that function, do what the x86 port does and pass it
along to ftrace_push_return_trace() instead of a stub value of 0.

Similarly, when you call ftrace_return_to_handler(), pass it the frame pointer.

HAVE_SYSCALL_TRACEPOINTS

220-240

아키텍처가 시스템 호출 추적을 지원하는 데 필요한 항목은 많지 않다. 먼저 `arch/Kconfig`의 `HAVE_ARCH_TRACEHOOK`을 지원하고, `<asm/unistd.h>`에 아키텍처가 제공하는 시스템 호출 수를 나타내는 `NR_syscalls`를 둔다.

스레드 플래그 `TIF_SYSCALL_TRACEPOINT`를 지원하고 ptrace 시스템 호출 추적 경로에서 `trace_sys_enter()`와 `trace_sys_exit()` tracepoint를 호출해야 한다.

시스템 호출 테이블이 단순한 함수 주소 배열보다 복잡하면 주어진 시스템 호출의 주소를 반환하는 `arch_syscall_addr`를 구현한다. 시스템 호출 심볼 이름과 함수 이름 규칙이 다르면 `asm/ftrace.h`에 `ARCH_HAS_SYSCALL_MATCH_SYM_NAME`을 정의하고, 두 이름의 대응 여부를 판정하는 `arch_syscall_match_sym_name`을 구현한다.

이 요구 사항을 모두 갖춘 뒤 아키텍처를 `HAVE_SYSCALL_TRACEPOINTS`로 표시한다.

시스템 호출 추적 체크리스트
항목요구 사항
HAVE_ARCH_TRACEHOOKarch/Kconfig trace hook 지원
NR_syscalls지원하는 시스템 호출 수
TIF_SYSCALL_TRACEPOINT스레드 추적 플래그
trace_sys_enter/exitptrace 시스템 호출 경로에서 호출
arch_syscall_addr복잡한 테이블에서 선택적으로 구현
arch_syscall_match_sym_name심볼과 함수 이름 규칙이 다를 때 구현

아키텍처가 제공해야 할 설정과 훅이다.

HAVE_SYSCALL_TRACEPOINTS
------------------------

You need very few things to get the syscalls tracing in an arch.

  - Support HAVE_ARCH_TRACEHOOK (see arch/Kconfig).
  - Have a NR_syscalls variable in <asm/unistd.h> that provides the number
    of syscalls supported by the arch.
  - Support the TIF_SYSCALL_TRACEPOINT thread flags.
  - Put the trace_sys_enter() and trace_sys_exit() tracepoints calls from ptrace
    in the ptrace syscalls tracing path.
  - If the system call table on this arch is more complicated than a simple array
    of addresses of the system calls, implement an arch_syscall_addr to return
    the address of a given system call.
  - If the symbol names of the system calls do not match the function names on
    this arch, define ARCH_HAS_SYSCALL_MATCH_SYM_NAME in asm/ftrace.h and
    implement arch_syscall_match_sym_name with the appropriate logic to return
    true if the function name corresponds with the symbol name.
  - Tag this arch as HAVE_SYSCALL_TRACEPOINTS.

HAVE_DYNAMIC_FTRACE

241-369

동적 ftrace를 구현하려면 먼저 `scripts/recordmcount.pl`이 `objdump` 출력에서 `mcount` 호출 지점 주소를 찾도록 아키텍처별 세부 사항을 채운다. 이 단계는 `HAVE_FUNCTION_TRACER` 구현을 전제로 한다.

`asm/ftrace.h`에는 `MCOUNT_ADDR`, `ftrace_call_adjust()`, `struct dyn_arch_ftrace`를 제공한다. 어셈블리에는 부팅 때만 쓰는 새 `mcount()` 스텁, `ftrace_caller()`, 패치 지점 `ftrace_call()`, `ftrace_stub()`을 구현한다. C에는 `ftrace_dyn_arch_init()`, `ftrace_make_nop()`, `ftrace_make_call()`, `ftrace_update_ftrace_func()`를 구현한다.

`MCOUNT_ADDR`은 보통 `((unsigned long)mcount)`로 정의하며 다른 선언이 없으므로 `extern void mcount(void);`도 추가한다. `ftrace_call_adjust()`는 기록된 주소를 아키텍처가 사용하는 실제 호출 지점으로 보정하는 함수다. 별도 보정이 필요 없는 포트는 입력 주소를 그대로 반환할 수 있다.

원문에는 `ftrace_call_adjust()` 뒤의 설명이 아직 작성되지 않았음을 나타내는 리터럴 `<details to be filled>`이 남아 있다. 이 미완성 표시는 원문 상태 그대로 보존하며 임의의 구현 규칙으로 보충하지 않는다.

`struct dyn_arch_ftrace`는 임의 호출 지점을 런타임에 패치할 때 필요한 아키텍처별 상태를 보관한다. 추가 상태가 없으면 주석만 있는 빈 구조체로 만들 수 있다.

동적 ftrace에서 `mcount()`는 부팅 과정에만 사용되는 즉시 반환 스텁이다. 초기화 뒤 모든 계측 호출은 패치되어 다시 `mcount`로 돌아오지 않는다. 기존 `mcount`의 실제 추적 로직은 새 `ftrace_caller()`로 옮기며, 두 정의는 합치기 어렵기 때문에 보통 `#ifdef`로 분리한다. `ftrace_stub()` 로직도 `ftrace_caller()`에 인라인된다.

`ftrace_caller()`는 ABI 상태를 저장하고 `frompc`와 `selfpc`를 계산한다. 내부의 `ftrace_call` 레이블에는 처음에 `ftrace_stub(frompc, selfpc)` 호출에 해당하는 패치 가능 공간을 둔다. 이후 상태를 복원하고 스텁 반환 경로로 나간다.

런타임 패치는 두 층으로 일어난다. 먼저 실제 추적할 함수의 계측 지점만 `ftrace_caller()`를 호출하도록 바꾼다. 다음으로 한 번에 하나인 활성 추적기에 맞춰 `ftrace_caller()` 안의 `ftrace_call` 위치를 해당 추적 함수 호출로 바꾼다.

`ftrace_dyn_arch_init()`는 초기 아키텍처 상태가 필요할 때 준비 작업을 수행한다. 별도 작업이 없다면 0을 반환하는 간단한 초기화 함수면 충분하다.

`ftrace_make_nop()`는 임의 함수의 `mcount` 호출 지점을 nop으로 바꿔 추적 비활성 성능을 확보한다. `ftrace_make_call()`은 그 지점을 임의 목적지, 보통 `ftrace_caller()` 호출로 바꾼다. 두 함수가 받는 `rec->ip`는 빌드 때 `scripts/recordmcount.pl`이 수집한 `mcount` 호출 지점 주소다.

`ftrace_update_ftrace_func()`는 `ftrace_caller()` 내부의 `ftrace_call` 심볼 위치를 현재 활성 추적기 호출로 패치한다. 아키텍처가 call 명령을 쓰든 branch 명령을 쓰든 새 명령을 넣을 수 있도록 해당 위치에 충분한 패딩을 확보해야 한다.

동적 ftrace 구현 표면
위치구현 항목
asm/ftrace.hMCOUNT_ADDR, ftrace_call_adjust(), dyn_arch_ftrace
어셈블리mcount, ftrace_caller, ftrace_call, ftrace_stub
C 코드dyn init, make_nop, make_call, update_ftrace_func

헤더, 어셈블리, C 코드에 필요한 요소를 구분한다.

동적 호출 지점 패치
recordmcount.plrec->ip 수집
부팅 초기화mcount 호출을 nop으로 패치
필터로 함수 선택선택 지점을 ftrace_caller 호출로 패치
추적기 활성화ftrace_call을 활성 추적기 호출로 패치
추적기 비활성화호출 지점을 nop으로 복원

빌드 시 수집한 주소를 부팅과 런타임에 선택적으로 바꾼다.

HAVE_DYNAMIC_FTRACE
-------------------

See scripts/recordmcount.pl for more info.  Just fill in the arch-specific
details for how to locate the addresses of mcount call sites via objdump.
This option doesn't make much sense without also implementing dynamic ftrace.

You will first need HAVE_FUNCTION_TRACER, so scroll your reader back up if you
got over eager.

Once those are out of the way, you will need to implement:
	- asm/ftrace.h:
		- MCOUNT_ADDR
		- ftrace_call_adjust()
		- struct dyn_arch_ftrace{}
	- asm code:
		- mcount() (new stub)
		- ftrace_caller()
		- ftrace_call()
		- ftrace_stub()
	- C code:
		- ftrace_dyn_arch_init()
		- ftrace_make_nop()
		- ftrace_make_call()
		- ftrace_update_ftrace_func()

First you will need to fill out some arch details in your asm/ftrace.h.

Define MCOUNT_ADDR as the address of your mcount symbol similar to::

	#define MCOUNT_ADDR ((unsigned long)mcount)

Since no one else will have a decl for that function, you will need to::

	extern void mcount(void);

You will also need the helper function ftrace_call_adjust().  Most people
will be able to stub it out like so::

	static inline unsigned long ftrace_call_adjust(unsigned long addr)
	{
		return addr;
	}

<details to be filled>

Lastly you will need the custom dyn_arch_ftrace structure.  If you need
some extra state when runtime patching arbitrary call sites, this is the
place.  For now though, create an empty struct::

	struct dyn_arch_ftrace {
		/* No extra data needed */
	};

With the header out of the way, we can fill out the assembly code.  While we
did already create a mcount() function earlier, dynamic ftrace only wants a
stub function.  This is because the mcount() will only be used during boot
and then all references to it will be patched out never to return.  Instead,
the guts of the old mcount() will be used to create a new ftrace_caller()
function.  Because the two are hard to merge, it will most likely be a lot
easier to have two separate definitions split up by #ifdefs.  Same goes for
the ftrace_stub() as that will now be inlined in ftrace_caller().

Before we get confused anymore, let's check out some pseudo code so you can
implement your own stuff in assembly::

	void mcount(void)
	{
		return;
	}

	void ftrace_caller(void)
	{
		/* save all state needed by the ABI (see paragraph above) */

		unsigned long frompc = ...;
		unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;

	ftrace_call:
		ftrace_stub(frompc, selfpc);

		/* restore all state needed by the ABI */

	ftrace_stub:
		return;
	}

This might look a little odd at first, but keep in mind that we will be runtime
patching multiple things.  First, only functions that we actually want to trace
will be patched to call ftrace_caller().  Second, since we only have one tracer
active at a time, we will patch the ftrace_caller() function itself to call the
specific tracer in question.  That is the point of the ftrace_call label.

With that in mind, let's move on to the C code that will actually be doing the
runtime patching.  You'll need a little knowledge of your arch's opcodes in
order to make it through the next section.

Every arch has an init callback function.  If you need to do something early on
to initialize some state, this is the time to do that.  Otherwise, this simple
function below should be sufficient for most people::

	int __init ftrace_dyn_arch_init(void)
	{
		return 0;
	}

There are two functions that are used to do runtime patching of arbitrary
functions.  The first is used to turn the mcount call site into a nop (which
is what helps us retain runtime performance when not tracing).  The second is
used to turn the mcount call site into a call to an arbitrary location (but
typically that is ftracer_caller()).  See the general function definition in
linux/ftrace.h for the functions::

	ftrace_make_nop()
	ftrace_make_call()

The rec->ip value is the address of the mcount call site that was collected
by the scripts/recordmcount.pl during build time.

The last function is used to do runtime patching of the active tracer.  This
will be modifying the assembly code at the location of the ftrace_call symbol
inside of the ftrace_caller() function.  So you should have sufficient padding
at that location to support the new function calls you'll be inserting.  Some
people will be using a "call" type instruction while others will be using a
"branch" type instruction.  Specifically, the function is::

	ftrace_update_ftrace_func()

HAVE_DYNAMIC_FTRACE와 HAVE_FUNCTION_GRAPH_TRACER

370-395

함수 그래프 추적기를 동적 ftrace와 함께 쓰려면 `ftrace_caller()`, `ftrace_graph_call()`, `ftrace_graph_caller()`를 갱신하고 `ftrace_enable_ftrace_graph_caller()`와 `ftrace_disable_ftrace_graph_caller()`를 구현해야 한다.

이 절에도 원문 저자가 세부 내용을 채우지 않았음을 나타내는 `<details to be filled>` 표시가 남아 있다. 아래의 빠른 메모가 제공하는 범위 이상을 원문 요구 사항으로 단정하지 않는다.

`ftrace_call` 위치 뒤에는 `ftrace_graph_call`이라는 nop 스텁을 둔다. 이 공간은 `ftrace_graph_caller()` 호출 명령을 넣을 만큼 커야 한다. 또한 새 `ftrace_caller()`에서 호출될 때 달라진 의미에 맞춰 `ftrace_graph_caller()`를 조정한다.

그래프 추적을 켤 때 `ftrace_enable_ftrace_graph_caller()`가 `ftrace_graph_call` 위치를 `ftrace_graph_caller()` 호출로 패치한다. 끌 때는 `ftrace_disable_ftrace_graph_caller()`가 같은 위치를 nop으로 되돌린다.

동적 그래프 추적 전환
ftrace_graph_call nop 스텁그래프 추적 활성화
그래프 추적 활성화ftrace_graph_caller 호출로 패치
그래프 추적 비활성화nop으로 패치

별도 패치 지점을 호출과 nop 사이에서 전환한다.

HAVE_DYNAMIC_FTRACE + HAVE_FUNCTION_GRAPH_TRACER
------------------------------------------------

The function grapher needs a few tweaks in order to work with dynamic ftrace.
Basically, you will need to:

	- update:
		- ftrace_caller()
		- ftrace_graph_call()
		- ftrace_graph_caller()
	- implement:
		- ftrace_enable_ftrace_graph_caller()
		- ftrace_disable_ftrace_graph_caller()

<details to be filled>

Quick notes:

	- add a nop stub after the ftrace_call location named ftrace_graph_call;
	  stub needs to be large enough to support a call to ftrace_graph_caller()
	- update ftrace_graph_caller() to work with being called by the new
	  ftrace_caller() since some semantics may have changed
	- ftrace_enable_ftrace_graph_caller() will runtime patch the
	  ftrace_graph_call location with a call to ftrace_graph_caller()
	- ftrace_disable_ftrace_graph_caller() will runtime patch the
	  ftrace_graph_call location with nops