요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=================================
Using ftrace to hook to functions
=================================
.. Copyright 2017 VMware Inc.
.. Author: Steven Rostedt <srostedt@goodmis.org>
.. License: The GNU Free Documentation License, Version 1.2
.. (dual licensed under the GPL v2)
Written for: 4.14
Introduction
============
The ftrace infrastructure was originally created to attach callbacks to the
beginning of functions in order to record and trace the flow of the kernel.
But callbacks to the start of a function can have other use cases. Either
for live kernel patching, or for security monitoring. This document describes
how to use ftrace to implement your own function callbacks.
The ftrace context
==================
.. warning::
The ability to add a callback to almost any function within the
kernel comes with risks. A callback can be called from any context
(normal, softirq, irq, and NMI). Callbacks can also be called just before
going to idle, during CPU bring up and takedown, or going to user space.
This requires extra care to what can be done inside a callback. A callback
can be called outside the protective scope of RCU.
There are helper functions to help against recursion, and making sure
RCU is watching. These are explained below.
The ftrace_ops structure
========================
To register a function callback, a ftrace_ops is required. This structure
is used to tell ftrace what function should be called as the callback
as well as what protections the callback will perform and not require
ftrace to handle.
There is only one field that is needed to be set when registering
an ftrace_ops with ftrace:
.. code-block:: c
struct ftrace_ops ops = {
.func = my_callback_func,
.flags = MY_FTRACE_FLAGS
.private = any_private_data_structure,
};
Both .flags and .private are optional. Only .func is required.
To enable tracing call::
register_ftrace_function(&ops);
To disable tracing call::
unregister_ftrace_function(&ops);
The above is defined by including the header::
#include <linux/ftrace.h>
The registered callback will start being called some time after the
register_ftrace_function() is called and before it returns. The exact time
that callbacks start being called is dependent upon architecture and scheduling
of services. The callback itself will have to handle any synchronization if it
must begin at an exact moment.
The unregister_ftrace_function() will guarantee that the callback is
no longer being called by functions after the unregister_ftrace_function()
returns. Note that to perform this guarantee, the unregister_ftrace_function()
may take some time to finish.
The callback function
=====================
The prototype of the callback function is as follows (as of v4.14):
.. code-block:: c
void callback_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs);
@ip
This is the instruction pointer of the function that is being traced.
(where the fentry or mcount is within the function)
@parent_ip
This is the instruction pointer of the function that called the
the function being traced (where the call of the function occurred).
@op
This is a pointer to ftrace_ops that was used to register the callback.
This can be used to pass data to the callback via the private pointer.
@regs
If the FTRACE_OPS_FL_SAVE_REGS or FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED
flags are set in the ftrace_ops structure, then this will be pointing
to the pt_regs structure like it would be if an breakpoint was placed
at the start of the function where ftrace was tracing. Otherwise it
either contains garbage, or NULL.
Protect your callback
=====================
As functions can be called from anywhere, and it is possible that a function
called by a callback may also be traced, and call that same callback,
recursion protection must be used. There are two helper functions that
can help in this regard. If you start your code with:
.. code-block:: c
int bit;
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;
and end it with:
.. code-block:: c
ftrace_test_recursion_unlock(bit);
The code in between will be safe to use, even if it ends up calling a
function that the callback is tracing. Note, on success,
ftrace_test_recursion_trylock() will disable preemption, and the
ftrace_test_recursion_unlock() will enable it again (if it was previously
enabled). The instruction pointer (ip) and its parent (parent_ip) is passed to
ftrace_test_recursion_trylock() to record where the recursion happened
(if CONFIG_FTRACE_RECORD_RECURSION is set).
Alternatively, if the FTRACE_OPS_FL_RECURSION flag is set on the ftrace_ops
(as explained below), then a helper trampoline will be used to test
for recursion for the callback and no recursion test needs to be done.
But this is at the expense of a slightly more overhead from an extra
function call.
If your callback accesses any data or critical section that requires RCU
protection, it is best to make sure that RCU is "watching", otherwise
that data or critical section will not be protected as expected. In this
case add:
.. code-block:: c
if (!rcu_is_watching())
return;
Alternatively, if the FTRACE_OPS_FL_RCU flag is set on the ftrace_ops
(as explained below), then a helper trampoline will be used to test
for rcu_is_watching for the callback and no other test needs to be done.
But this is at the expense of a slightly more overhead from an extra
function call.
The ftrace FLAGS
================
The ftrace_ops flags are all defined and documented in include/linux/ftrace.h.
Some of the flags are used for internal infrastructure of ftrace, but the
ones that users should be aware of are the following:
FTRACE_OPS_FL_SAVE_REGS
If the callback requires reading or modifying the pt_regs
passed to the callback, then it must set this flag. Registering
a ftrace_ops with this flag set on an architecture that does not
support passing of pt_regs to the callback will fail.
FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED
Similar to SAVE_REGS but the registering of a
ftrace_ops on an architecture that does not support passing of regs
will not fail with this flag set. But the callback must check if
regs is NULL or not to determine if the architecture supports it.
FTRACE_OPS_FL_RECURSION
By default, it is expected that the callback can handle recursion.
But if the callback is not that worried about overhead, then
setting this bit will add the recursion protection around the
callback by calling a helper function that will do the recursion
protection and only call the callback if it did not recurse.
Note, if this flag is not set, and recursion does occur, it could
cause the system to crash, and possibly reboot via a triple fault.
Note, if this flag is set, then the callback will always be called
with preemption disabled. If it is not set, then it is possible
(but not guaranteed) that the callback will be called in
preemptible context.
FTRACE_OPS_FL_IPMODIFY
Requires FTRACE_OPS_FL_SAVE_REGS set. If the callback is to "hijack"
the traced function (have another function called instead of the
traced function), it requires setting this flag. This is what live
kernel patches uses. Without this flag the pt_regs->ip can not be
modified.
Note, only one ftrace_ops with FTRACE_OPS_FL_IPMODIFY set may be
registered to any given function at a time.
FTRACE_OPS_FL_RCU
If this is set, then the callback will only be called by functions
where RCU is "watching". This is required if the callback function
performs any rcu_read_lock() operation.
RCU stops watching when the system goes idle, the time when a CPU
is taken down and comes back online, and when entering from kernel
to user space and back to kernel space. During these transitions,
a callback may be executed and RCU synchronization will not protect
it.
FTRACE_OPS_FL_PERMANENT
If this is set on any ftrace ops, then the tracing cannot disabled by
writing 0 to the proc sysctl ftrace_enabled. Equally, a callback with
the flag set cannot be registered if ftrace_enabled is 0.
Livepatch uses it not to lose the function redirection, so the system
stays protected.
Filtering which functions to trace
==================================
If a callback is only to be called from specific functions, a filter must be
set up. The filters are added by name, or ip if it is known.
.. code-block:: c
int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset);
@ops
The ops to set the filter with
@buf
The string that holds the function filter text.
@len
The length of the string.
@reset
Non-zero to reset all filters before applying this filter.
Filters denote which functions should be enabled when tracing is enabled.
If @buf is NULL and reset is set, all functions will be enabled for tracing.
The @buf can also be a glob expression to enable all functions that
match a specific pattern.
See Filter Commands in :file:`Documentation/trace/ftrace.rst`.
To just trace the schedule function:
.. code-block:: c
ret = ftrace_set_filter(&ops, "schedule", strlen("schedule"), 0);
To add more functions, call the ftrace_set_filter() more than once with the
@reset parameter set to zero. To remove the current filter set and replace it
with new functions defined by @buf, have @reset be non-zero.
To remove all the filtered functions and trace all functions:
.. code-block:: c
ret = ftrace_set_filter(&ops, NULL, 0, 1);
Sometimes more than one function has the same name. To trace just a specific
function in this case, ftrace_set_filter_ip() can be used.
.. code-block:: c
ret = ftrace_set_filter_ip(&ops, ip, 0, 0);
Although the ip must be the address where the call to fentry or mcount is
located in the function. This function is used by perf and kprobes that
gets the ip address from the user (usually using debug info from the kernel).
If a glob is used to set the filter, functions can be added to a "notrace"
list that will prevent those functions from calling the callback.
The "notrace" list takes precedence over the "filter" list. If the
two lists are non-empty and contain the same functions, the callback will not
be called by any function.
An empty "notrace" list means to allow all functions defined by the filter
to be traced.
.. code-block:: c
int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset);
This takes the same parameters as ftrace_set_filter() but will add the
functions it finds to not be traced. This is a separate list from the
filter list, and this function does not modify the filter list.
A non-zero @reset will clear the "notrace" list before adding functions
that match @buf to it.
Clearing the "notrace" list is the same as clearing the filter list
.. code-block:: c
ret = ftrace_set_notrace(&ops, NULL, 0, 1);
The filter and notrace lists may be changed at any time. If only a set of
functions should call the callback, it is best to set the filters before
registering the callback. But the changes may also happen after the callback
has been registered.
If a filter is in place, and the @reset is non-zero, and @buf contains a
matching glob to functions, the switch will happen during the time of
the ftrace_set_filter() call. At no time will all functions call the callback.
.. code-block:: c
ftrace_set_filter(&ops, "schedule", strlen("schedule"), 1);
register_ftrace_function(&ops);
msleep(10);
ftrace_set_filter(&ops, "try_to_wake_up", strlen("try_to_wake_up"), 1);
is not the same as:
.. code-block:: c
ftrace_set_filter(&ops, "schedule", strlen("schedule"), 1);
register_ftrace_function(&ops);
msleep(10);
ftrace_set_filter(&ops, NULL, 0, 1);
ftrace_set_filter(&ops, "try_to_wake_up", strlen("try_to_wake_up"), 0);
As the latter will have a short time where all functions will call
the callback, between the time of the reset, and the time of the
new setting of the filter.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
문서 정보
1-11이 문서는 VMware Inc.의 2017년 저작물이며 Steven Rostedt가 작성했다. GNU Free Documentation License 1.2와 GPL v2로 이중 라이선스되며, Linux 4.14를 기준으로 작성되었다.
=================================
Using ftrace to hook to functions
=================================
.. Copyright 2017 VMware Inc.
.. Author: Steven Rostedt <srostedt@goodmis.org>
.. License: The GNU Free Documentation License, Version 1.2
.. (dual licensed under the GPL v2)
Written for: 4.14
소개
12-21ftrace 기반 구조는 원래 함수 시작 지점에 콜백을 붙여 커널 실행 흐름을 기록하고 추적하기 위해 만들어졌다. 그러나 함수 진입 콜백은 라이브 커널 패치나 보안 모니터링에도 사용할 수 있다.
이 문서는 사용자가 자신의 함수 콜백을 구현하고 ftrace에 등록하는 방법을 설명한다.
같은 ftrace 훅이 추적 외의 커널 기능에도 쓰인다.
Introduction
============
The ftrace infrastructure was originally created to attach callbacks to the
beginning of functions in order to record and trace the flow of the kernel.
But callbacks to the start of a function can have other use cases. Either
for live kernel patching, or for security monitoring. This document describes
how to use ftrace to implement your own function callbacks.
ftrace 실행 문맥
22-36경고: 거의 모든 커널 함수에 콜백을 추가할 수 있다는 점에는 큰 위험이 따른다. 콜백은 일반 문맥, softirq, irq, NMI 어느 곳에서든 호출될 수 있다.
또한 시스템이 idle에 들어가기 직전, CPU가 온라인 또는 오프라인으로 전환되는 동안, 커널과 사용자 공간 사이를 오갈 때도 콜백이 실행될 수 있다. 이 때문에 콜백 내부에서 허용되는 연산을 매우 신중히 선택해야 한다.
콜백은 RCU 보호 범위 밖에서 실행될 수 있다. ftrace는 재귀를 막고 RCU가 현재 관찰 중인지 확인하는 도우미를 제공하며, 뒤 절에서 사용법을 설명한다.
콜백 코드는 모든 문맥을 견딜 수 있어야 한다.
The ftrace context
==================
.. warning::
The ability to add a callback to almost any function within the
kernel comes with risks. A callback can be called from any context
(normal, softirq, irq, and NMI). Callbacks can also be called just before
going to idle, during CPU bring up and takedown, or going to user space.
This requires extra care to what can be done inside a callback. A callback
can be called outside the protective scope of RCU.
There are helper functions to help against recursion, and making sure
RCU is watching. These are explained below.
ftrace_ops 구조체
37-81함수 콜백을 등록하려면 `struct ftrace_ops`가 필요하다. 이 구조체는 호출할 콜백과 콜백 자체가 담당할 보호 기능을 ftrace에 알려 준다.
필수 필드는 `.func`뿐이다. `.flags`와 `.private`는 선택 사항이며, `.private`에는 콜백이 사용할 임의의 사설 데이터 구조를 연결할 수 있다.
`register_ftrace_function(&ops)`로 추적을 활성화하고 `unregister_ftrace_function(&ops)`로 비활성화한다. 선언을 사용하려면 `<linux/ftrace.h>`를 포함한다.
등록된 콜백은 `register_ftrace_function()` 호출 뒤부터 그 함수가 반환하기 전 사이의 어느 시점에 호출되기 시작한다. 정확한 시작 시점은 아키텍처와 서비스 스케줄링에 달려 있으므로 특정 순간부터 정확히 시작해야 한다면 콜백 사용자가 동기화를 구현해야 한다.
`unregister_ftrace_function()`이 반환한 뒤에는 어떤 함수도 해당 콜백을 더 호출하지 않는다는 보장이 있다. 이 보장을 만들기 위한 동기화 때문에 등록 해제 함수가 완료되는 데 시간이 걸릴 수 있다.
등록에 필요한 필드와 선택 필드를 구분한다.
등록과 해제의 반환 시점에 제공되는 보장을 보여 준다.
The ftrace_ops structure
========================
To register a function callback, a ftrace_ops is required. This structure
is used to tell ftrace what function should be called as the callback
as well as what protections the callback will perform and not require
ftrace to handle.
There is only one field that is needed to be set when registering
an ftrace_ops with ftrace:
.. code-block:: c
struct ftrace_ops ops = {
.func = my_callback_func,
.flags = MY_FTRACE_FLAGS
.private = any_private_data_structure,
};
Both .flags and .private are optional. Only .func is required.
To enable tracing call::
register_ftrace_function(&ops);
To disable tracing call::
unregister_ftrace_function(&ops);
The above is defined by including the header::
#include <linux/ftrace.h>
The registered callback will start being called some time after the
register_ftrace_function() is called and before it returns. The exact time
that callbacks start being called is dependent upon architecture and scheduling
of services. The callback itself will have to handle any synchronization if it
must begin at an exact moment.
The unregister_ftrace_function() will guarantee that the callback is
no longer being called by functions after the unregister_ftrace_function()
returns. Note that to perform this guarantee, the unregister_ftrace_function()
may take some time to finish.
콜백 함수
82-110Linux 4.14 기준 콜백 원형은 `void callback_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)`다.
`ip`는 추적 중인 함수 안에서 `fentry` 또는 `mcount`가 놓인 명령 포인터다. `parent_ip`는 그 함수를 호출한 부모 함수의 호출 지점 명령 포인터다.
`op`는 콜백 등록에 사용한 `ftrace_ops`의 포인터다. 따라서 `op->private`를 통해 등록 시 연결한 데이터를 콜백으로 전달할 수 있다.
`regs`는 `FTRACE_OPS_FL_SAVE_REGS` 또는 `FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED`가 설정된 경우 함수 시작에 브레이크포인트를 둔 것과 유사한 `pt_regs` 문맥을 가리킨다. 이 플래그가 없으면 값은 쓰레기이거나 `NULL`일 수 있으므로 접근해서는 안 된다.
함수 진입에서 전달되는 주소와 문맥이다.
The callback function
=====================
The prototype of the callback function is as follows (as of v4.14):
.. code-block:: c
void callback_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs);
@ip
This is the instruction pointer of the function that is being traced.
(where the fentry or mcount is within the function)
@parent_ip
This is the instruction pointer of the function that called the
the function being traced (where the call of the function occurred).
@op
This is a pointer to ftrace_ops that was used to register the callback.
This can be used to pass data to the callback via the private pointer.
@regs
If the FTRACE_OPS_FL_SAVE_REGS or FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED
flags are set in the ftrace_ops structure, then this will be pointing
to the pt_regs structure like it would be if an breakpoint was placed
at the start of the function where ftrace was tracing. Otherwise it
either contains garbage, or NULL.
콜백 보호
111-163콜백이 호출한 함수도 같은 콜백의 추적 대상일 수 있으므로 재귀 보호가 필수다. 보호하지 않으면 콜백이 자신을 간접적으로 계속 호출할 수 있다.
직접 보호할 때는 시작에서 `ftrace_test_recursion_trylock(ip, parent_ip)`를 호출한다. 반환값이 음수면 재귀이므로 즉시 반환하고, 성공한 경우 본문을 실행한 뒤 반드시 `ftrace_test_recursion_unlock(bit)`을 호출한다.
재귀 잠금 성공은 preemption을 비활성화하고 unlock은 이전에 활성 상태였을 때 다시 켠다. `ip`와 `parent_ip`는 `CONFIG_FTRACE_RECORD_RECURSION` 설정 시 재귀가 발생한 위치를 기록하는 데 쓰인다.
대안으로 `ftrace_ops`에 `FTRACE_OPS_FL_RECURSION`을 설정하면 도우미 트램펄린이 콜백 앞에서 재귀를 검사한다. 콜백 자체의 검사 코드는 필요 없지만 함수 호출 한 번만큼의 오버헤드가 늘어난다.
콜백이 RCU 보호가 필요한 데이터나 임계 구역에 접근한다면 `rcu_is_watching()`을 검사하고 거짓일 때 반환해야 한다. 그렇지 않으면 기대한 RCU 보호가 성립하지 않는다.
`FTRACE_OPS_FL_RCU`를 설정하면 별도 트램펄린이 `rcu_is_watching()`을 확인한 뒤 조건을 만족할 때만 콜백을 부른다. 이 방법도 추가 함수 호출 비용이 있다.
잠금 실패 시 콜백을 건너뛰고 성공 경로에서 preemption 상태를 복원한다.
직접 검사와 플래그 기반 트램펄린의 차이다.
Protect your callback
=====================
As functions can be called from anywhere, and it is possible that a function
called by a callback may also be traced, and call that same callback,
recursion protection must be used. There are two helper functions that
can help in this regard. If you start your code with:
.. code-block:: c
int bit;
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;
and end it with:
.. code-block:: c
ftrace_test_recursion_unlock(bit);
The code in between will be safe to use, even if it ends up calling a
function that the callback is tracing. Note, on success,
ftrace_test_recursion_trylock() will disable preemption, and the
ftrace_test_recursion_unlock() will enable it again (if it was previously
enabled). The instruction pointer (ip) and its parent (parent_ip) is passed to
ftrace_test_recursion_trylock() to record where the recursion happened
(if CONFIG_FTRACE_RECORD_RECURSION is set).
Alternatively, if the FTRACE_OPS_FL_RECURSION flag is set on the ftrace_ops
(as explained below), then a helper trampoline will be used to test
for recursion for the callback and no recursion test needs to be done.
But this is at the expense of a slightly more overhead from an extra
function call.
If your callback accesses any data or critical section that requires RCU
protection, it is best to make sure that RCU is "watching", otherwise
that data or critical section will not be protected as expected. In this
case add:
.. code-block:: c
if (!rcu_is_watching())
return;
Alternatively, if the FTRACE_OPS_FL_RCU flag is set on the ftrace_ops
(as explained below), then a helper trampoline will be used to test
for rcu_is_watching for the callback and no other test needs to be done.
But this is at the expense of a slightly more overhead from an extra
function call.
ftrace 플래그
164-227`ftrace_ops` 플래그는 `include/linux/ftrace.h`에 정의되고 문서화되어 있다. 일부는 내부용이며, 사용자가 알아야 할 주요 플래그는 다음과 같다.
`FTRACE_OPS_FL_SAVE_REGS`는 콜백이 전달받은 `pt_regs`를 읽거나 수정해야 할 때 필수다. 레지스터 전달을 지원하지 않는 아키텍처에서 이 플래그로 등록하면 실패한다.
`FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED`는 레지스터 저장을 지원하지 않는 아키텍처에서도 등록 자체는 실패하지 않는다. 대신 콜백이 `regs == NULL`인지 검사해 지원 여부를 판정해야 한다.
`FTRACE_OPS_FL_RECURSION`은 도우미가 콜백을 재귀 보호로 감싼다. 플래그가 없으면 콜백 스스로 재귀를 처리해야 하며, 실패하면 시스템 충돌이나 triple fault에 따른 재부팅까지 일어날 수 있다. 플래그를 설정하면 콜백은 항상 preemption이 비활성화된 상태에서 호출된다. 설정하지 않으면 preemptible 문맥일 수도 있지만 보장되지는 않는다.
`FTRACE_OPS_FL_IPMODIFY`는 `FTRACE_OPS_FL_SAVE_REGS`를 함께 요구한다. 추적 대상 대신 다른 함수를 실행하도록 함수를 가로채려면 이 플래그가 필요하며 라이브 커널 패치가 이 기능을 사용한다. 플래그 없이는 `pt_regs->ip`를 수정할 수 없다. 같은 함수에는 한 시점에 `IPMODIFY` ops 하나만 등록할 수 있다.
`FTRACE_OPS_FL_RCU`는 RCU가 watching 상태인 함수에서만 콜백을 호출한다. 콜백이 `rcu_read_lock()`을 수행한다면 필수다. RCU는 idle, CPU 오프라인과 온라인 전환, 커널과 사용자 공간 전환 중 watching을 멈추므로 이 구간의 콜백은 일반적인 RCU 동기화로 보호되지 않는다.
`FTRACE_OPS_FL_PERMANENT`가 하나라도 설정되면 `/proc` sysctl의 `ftrace_enabled`에 0을 써서 추적을 비활성화할 수 없다. 반대로 `ftrace_enabled`가 0인 상태에서는 이 플래그를 가진 콜백을 등록할 수 없다. 라이브패치는 함수 리디렉션이 사라지지 않아 시스템 보호가 유지되도록 이 플래그를 사용한다.
콜백이 요구하는 기능과 등록 제약을 정리한다.
The ftrace FLAGS
================
The ftrace_ops flags are all defined and documented in include/linux/ftrace.h.
Some of the flags are used for internal infrastructure of ftrace, but the
ones that users should be aware of are the following:
FTRACE_OPS_FL_SAVE_REGS
If the callback requires reading or modifying the pt_regs
passed to the callback, then it must set this flag. Registering
a ftrace_ops with this flag set on an architecture that does not
support passing of pt_regs to the callback will fail.
FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED
Similar to SAVE_REGS but the registering of a
ftrace_ops on an architecture that does not support passing of regs
will not fail with this flag set. But the callback must check if
regs is NULL or not to determine if the architecture supports it.
FTRACE_OPS_FL_RECURSION
By default, it is expected that the callback can handle recursion.
But if the callback is not that worried about overhead, then
setting this bit will add the recursion protection around the
callback by calling a helper function that will do the recursion
protection and only call the callback if it did not recurse.
Note, if this flag is not set, and recursion does occur, it could
cause the system to crash, and possibly reboot via a triple fault.
Note, if this flag is set, then the callback will always be called
with preemption disabled. If it is not set, then it is possible
(but not guaranteed) that the callback will be called in
preemptible context.
FTRACE_OPS_FL_IPMODIFY
Requires FTRACE_OPS_FL_SAVE_REGS set. If the callback is to "hijack"
the traced function (have another function called instead of the
traced function), it requires setting this flag. This is what live
kernel patches uses. Without this flag the pt_regs->ip can not be
modified.
Note, only one ftrace_ops with FTRACE_OPS_FL_IPMODIFY set may be
registered to any given function at a time.
FTRACE_OPS_FL_RCU
If this is set, then the callback will only be called by functions
where RCU is "watching". This is required if the callback function
performs any rcu_read_lock() operation.
RCU stops watching when the system goes idle, the time when a CPU
is taken down and comes back online, and when entering from kernel
to user space and back to kernel space. During these transitions,
a callback may be executed and RCU synchronization will not protect
it.
FTRACE_OPS_FL_PERMANENT
If this is set on any ftrace ops, then the tracing cannot disabled by
writing 0 to the proc sysctl ftrace_enabled. Equally, a callback with
the flag set cannot be registered if ftrace_enabled is 0.
Livepatch uses it not to lose the function redirection, so the system
stays protected.
추적할 함수 필터링
228-348콜백을 특정 함수에서만 호출하려면 이름 또는 알려진 명령 포인터로 필터를 설정한다. `ftrace_set_filter(ops, buf, len, reset)`에서 `ops`는 대상 ops, `buf`는 필터 문자열, `len`은 길이, `reset`은 기존 필터를 먼저 지울지 지정한다.
필터는 추적이 활성화되었을 때 콜백을 부를 함수 집합이다. `buf`가 `NULL`이고 `reset`이 0이 아니면 모든 함수가 추적 대상이 된다. `buf`에는 glob 표현식을 넣어 패턴과 일치하는 함수를 한꺼번에 선택할 수 있으며 자세한 명령은 `Documentation/trace/ftrace.rst`의 Filter Commands를 참고한다.
`schedule`만 추적하려면 문서의 예처럼 `ftrace_set_filter(&ops, "schedule", strlen("schedule"), 0)`을 사용한다. `reset`을 0으로 두고 여러 번 호출하면 기존 집합에 함수를 추가한다. `reset`을 0이 아닌 값으로 두면 기존 집합을 지우고 `buf`가 지정한 새 집합으로 교체한다.
모든 필터를 제거해 모든 함수를 추적하려면 `ftrace_set_filter(&ops, NULL, 0, 1)`을 호출한다.
동일한 이름을 가진 함수가 여러 개일 때 특정 함수 하나만 추적하려면 `ftrace_set_filter_ip()`를 사용한다. 전달하는 IP는 함수 시작 주소 자체가 아니라 함수 안에서 `fentry` 또는 `mcount`를 호출하는 위치여야 한다. perf와 kprobes는 보통 커널 디버그 정보에서 사용자가 지정한 IP를 얻어 이 API를 쓴다.
glob 필터를 사용할 때 콜백을 금지할 함수는 별도의 `notrace` 목록에 넣을 수 있다. `notrace`가 `filter`보다 우선하므로 두 목록이 모두 비어 있지 않고 같은 함수만 담으면 어떤 함수도 콜백을 부르지 않는다. 빈 `notrace` 목록은 필터가 허용한 모든 함수를 추적하도록 허용한다.
`ftrace_set_notrace(ops, buf, len, reset)`는 필터 함수와 같은 인자를 받지만 별도 제외 목록만 수정한다. `reset`이 0이 아니면 기존 `notrace` 목록을 지운 뒤 `buf`와 일치하는 함수를 넣는다. `ftrace_set_notrace(&ops, NULL, 0, 1)`은 제외 목록 전체를 비운다.
`filter`와 `notrace` 목록은 언제든 바꿀 수 있다. 특정 함수 집합만 호출해야 한다면 콜백 등록 전에 필터를 설정하는 편이 좋지만 등록 뒤 변경도 지원된다.
기존 필터가 있는 상태에서 `reset`을 0이 아닌 값으로 하고 실제 함수와 일치하는 비어 있지 않은 glob을 전달하면 `ftrace_set_filter()` 호출 안에서 이전 집합에서 새 집합으로 원자적으로 전환된다. 그 사이 모든 함수가 콜백을 부르는 순간은 없다.
반면 `buf = NULL`, `reset = 1`로 먼저 필터를 비워 모든 함수를 허용한 다음, 별도의 호출에서 `reset = 0`으로 새 함수를 추가하면 두 호출 사이에 잠깐 모든 함수가 콜백을 부른다. 문서의 두 코드 시퀀스가 같지 않은 이유다.
포함 목록과 제외 목록의 갱신 규칙이다.
한 번의 호출에서 일치하는 새 필터로 교체하면 전역 허용 창이 생기지 않는다.
필터를 먼저 비운 뒤 새 필터를 더하면 호출 사이에 모든 함수가 허용된다.
Filtering which functions to trace
==================================
If a callback is only to be called from specific functions, a filter must be
set up. The filters are added by name, or ip if it is known.
.. code-block:: c
int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset);
@ops
The ops to set the filter with
@buf
The string that holds the function filter text.
@len
The length of the string.
@reset
Non-zero to reset all filters before applying this filter.
Filters denote which functions should be enabled when tracing is enabled.
If @buf is NULL and reset is set, all functions will be enabled for tracing.
The @buf can also be a glob expression to enable all functions that
match a specific pattern.
See Filter Commands in :file:`Documentation/trace/ftrace.rst`.
To just trace the schedule function:
.. code-block:: c
ret = ftrace_set_filter(&ops, "schedule", strlen("schedule"), 0);
To add more functions, call the ftrace_set_filter() more than once with the
@reset parameter set to zero. To remove the current filter set and replace it
with new functions defined by @buf, have @reset be non-zero.
To remove all the filtered functions and trace all functions:
.. code-block:: c
ret = ftrace_set_filter(&ops, NULL, 0, 1);
Sometimes more than one function has the same name. To trace just a specific
function in this case, ftrace_set_filter_ip() can be used.
.. code-block:: c
ret = ftrace_set_filter_ip(&ops, ip, 0, 0);
Although the ip must be the address where the call to fentry or mcount is
located in the function. This function is used by perf and kprobes that
gets the ip address from the user (usually using debug info from the kernel).
If a glob is used to set the filter, functions can be added to a "notrace"
list that will prevent those functions from calling the callback.
The "notrace" list takes precedence over the "filter" list. If the
two lists are non-empty and contain the same functions, the callback will not
be called by any function.
An empty "notrace" list means to allow all functions defined by the filter
to be traced.
.. code-block:: c
int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset);
This takes the same parameters as ftrace_set_filter() but will add the
functions it finds to not be traced. This is a separate list from the
filter list, and this function does not modify the filter list.
A non-zero @reset will clear the "notrace" list before adding functions
that match @buf to it.
Clearing the "notrace" list is the same as clearing the filter list
.. code-block:: c
ret = ftrace_set_notrace(&ops, NULL, 0, 1);
The filter and notrace lists may be changed at any time. If only a set of
functions should call the callback, it is best to set the filters before
registering the callback. But the changes may also happen after the callback
has been registered.
If a filter is in place, and the @reset is non-zero, and @buf contains a
matching glob to functions, the switch will happen during the time of
the ftrace_set_filter() call. At no time will all functions call the callback.
.. code-block:: c
ftrace_set_filter(&ops, "schedule", strlen("schedule"), 1);
register_ftrace_function(&ops);
msleep(10);
ftrace_set_filter(&ops, "try_to_wake_up", strlen("try_to_wake_up"), 1);
is not the same as:
.. code-block:: c
ftrace_set_filter(&ops, "schedule", strlen("schedule"), 1);
register_ftrace_function(&ops);
msleep(10);
ftrace_set_filter(&ops, NULL, 0, 1);
ftrace_set_filter(&ops, "try_to_wake_up", strlen("try_to_wake_up"), 0);
As the latter will have a short time where all functions will call
the callback, between the time of the reset, and the time of the
new setting of the filter.
요약·해설
ftrace-uses.rst:1-348ftrace_ops 콜백의 실행 문맥, 재귀·RCU 보호, 레지스터 및 함수 리디렉션 플래그, filter와 notrace 목록의 안전한 갱신 방법을 설명합니다.