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

Linux 6.18.37 · Tracing

Eprobe - Event 기반 probe tracing

기존 event field를 선택하거나 pointer를 역참조하는 Eprobe 문법과 sched_switch 및 openat synthetic-event 예제를 설명합니다.

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

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

1. 요약·해설

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

요약·해설

eprobetrace.rst:1-269

기존 event field를 선택하거나 pointer를 역참조하는 Eprobe 문법과 sched_switch 및 openat synthetic-event 예제를 설명합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ==================================
4 Eprobe - Event-based Probe Tracing
5 ==================================
6
7 :Author: Steven Rostedt <rostedt@goodmis.org>
8
9 - Written for v6.17
10
11 Overview
12 ========
13
14 Eprobes are dynamic events that are placed on existing events to either
15 dereference a field that is a pointer, or simply to limit what fields are
16 recorded in the trace event.
17
18 Eprobes depend on kprobe events so to enable this feature, build your kernel
19 with CONFIG_EPROBE_EVENTS=y.
20
21 Eprobes are created via the /sys/kernel/tracing/dynamic_events file.
22
23 Synopsis of eprobe_events
24 -------------------------
25 ::
26
27 e[:[EGRP/][EEVENT]] GRP.EVENT [FETCHARGS] : Set a probe
28 -:[EGRP/][EEVENT] : Clear a probe
29
30 EGRP : Group name of the new event. If omitted, use "eprobes" for it.
31 EEVENT : Event name. If omitted, the event name is generated and will
32 be the same event name as the event it attached to.
33 GRP : Group name of the event to attach to.
34 EVENT : Event name of the event to attach to.
35
36 FETCHARGS : Arguments. Each probe can have up to 128 args.
37 $FIELD : Fetch the value of the event field called FIELD.
38 @ADDR : Fetch memory at ADDR (ADDR should be in kernel)
39 @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
40 $comm : Fetch current task comm.
41 +|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
42 \IMM : Store an immediate value to the argument.
43 NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
44 FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
45 (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
46 (x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
47 "string", "ustring", "symbol", "symstr" and "bitfield" are
48 supported.
49
50 Types
51 -----
52 The FETCHARGS above is very similar to the kprobe events as described in
53 Documentation/trace/kprobetrace.rst.
54
55 The difference between eprobes and kprobes FETCHARGS is that eprobes has a
56 $FIELD command that returns the content of the event field of the event
57 that is attached. Eprobes do not have access to registers, stacks and function
58 arguments that kprobes has.
59
60 If a field argument is a pointer, it may be dereferenced just like a memory
61 address using the FETCHARGS syntax.
62
63
64 Attaching to dynamic events
65 ---------------------------
66
67 Eprobes may attach to dynamic events as well as to normal events. It may
68 attach to a kprobe event, a synthetic event or a fprobe event. This is useful
69 if the type of a field needs to be changed. See Example 2 below.
70
71 Usage examples
72 ==============
73
74 Example 1
75 ---------
76
77 The basic usage of eprobes is to limit the data that is being recorded into
78 the tracing buffer. For example, a common event to trace is the sched_switch
79 trace event. That has a format of::
80
81 field:unsigned short common_type; offset:0; size:2; signed:0;
82 field:unsigned char common_flags; offset:2; size:1; signed:0;
83 field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
84 field:int common_pid; offset:4; size:4; signed:1;
85
86 field:char prev_comm[16]; offset:8; size:16; signed:0;
87 field:pid_t prev_pid; offset:24; size:4; signed:1;
88 field:int prev_prio; offset:28; size:4; signed:1;
89 field:long prev_state; offset:32; size:8; signed:1;
90 field:char next_comm[16]; offset:40; size:16; signed:0;
91 field:pid_t next_pid; offset:56; size:4; signed:1;
92 field:int next_prio; offset:60; size:4; signed:1;
93
94 The first four fields are common to all events and can not be limited. But the
95 rest of the event has 60 bytes of information. It records the names of the
96 previous and next tasks being scheduled out and in, as well as their pids and
97 priorities. It also records the state of the previous task. If only the pids
98 of the tasks are of interest, why waste the ring buffer with all the other
99 fields?
100
101 An eprobe can limit what gets recorded. Note, it does not help in performance,
102 as all the fields are recorded in a temporary buffer to process the eprobe.
103 ::
104
105 # echo 'e:sched/switch sched.sched_switch prev=$prev_pid:u32 next=$next_pid:u32' >> /sys/kernel/tracing/dynamic_events
106 # echo 1 > /sys/kernel/tracing/events/sched/switch/enable
107 # cat /sys/kernel/tracing/trace
108
109 # tracer: nop
110 #
111 # entries-in-buffer/entries-written: 2721/2721 #P:8
112 #
113 # _-----=> irqs-off/BH-disabled
114 # / _----=> need-resched
115 # | / _---=> hardirq/softirq
116 # || / _--=> preempt-depth
117 # ||| / _-=> migrate-disable
118 # |||| / delay
119 # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
120 # | | | ||||| | |
121 sshd-session-1082 [004] d..4. 5041.239906: switch: (sched.sched_switch) prev=1082 next=0
122 bash-1085 [001] d..4. 5041.240198: switch: (sched.sched_switch) prev=1085 next=141
123 kworker/u34:5-141 [001] d..4. 5041.240259: switch: (sched.sched_switch) prev=141 next=1085
124 <idle>-0 [004] d..4. 5041.240354: switch: (sched.sched_switch) prev=0 next=1082
125 bash-1085 [001] d..4. 5041.240385: switch: (sched.sched_switch) prev=1085 next=141
126 kworker/u34:5-141 [001] d..4. 5041.240410: switch: (sched.sched_switch) prev=141 next=1085
127 bash-1085 [001] d..4. 5041.240478: switch: (sched.sched_switch) prev=1085 next=0
128 sshd-session-1082 [004] d..4. 5041.240526: switch: (sched.sched_switch) prev=1082 next=0
129 <idle>-0 [001] d..4. 5041.247524: switch: (sched.sched_switch) prev=0 next=90
130 <idle>-0 [002] d..4. 5041.247545: switch: (sched.sched_switch) prev=0 next=16
131 kworker/1:1-90 [001] d..4. 5041.247580: switch: (sched.sched_switch) prev=90 next=0
132 rcu_sched-16 [002] d..4. 5041.247591: switch: (sched.sched_switch) prev=16 next=0
133 <idle>-0 [002] d..4. 5041.257536: switch: (sched.sched_switch) prev=0 next=16
134 rcu_sched-16 [002] d..4. 5041.257573: switch: (sched.sched_switch) prev=16 next=0
135
136 Note, without adding the "u32" after the prev_pid and next_pid, the values
137 would default showing in hexadecimal.
138
139 Example 2
140 ---------
141
142 If a specific system call is to be recorded but the syscalls events are not
143 enabled, the raw_syscalls can still be used (syscalls are system call
144 events are not normal events, but are created from the raw_syscalls events
145 within the kernel). In order to trace the openat system call, one can create
146 an event probe on top of the raw_syscalls event:
147 ::
148
149 # cd /sys/kernel/tracing
150 # cat events/raw_syscalls/sys_enter/format
151 name: sys_enter
152 ID: 395
153 format:
154 field:unsigned short common_type; offset:0; size:2; signed:0;
155 field:unsigned char common_flags; offset:2; size:1; signed:0;
156 field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
157 field:int common_pid; offset:4; size:4; signed:1;
158
159 field:long id; offset:8; size:8; signed:1;
160 field:unsigned long args[6]; offset:16; size:48; signed:0;
161
162 print fmt: "NR %ld (%lx, %lx, %lx, %lx, %lx, %lx)", REC->id, REC->args[0], REC->args[1], REC->args[2], REC->args[3], REC->args[4], REC->args[5]
163
164 From the source code, the sys_openat() has:
165 ::
166
167 int sys_openat(int dirfd, const char *path, int flags, mode_t mode)
168 {
169 return my_syscall4(__NR_openat, dirfd, path, flags, mode);
170 }
171
172 The path is the second parameter, and that is what is wanted.
173 ::
174
175 # echo 'e:openat raw_syscalls.sys_enter nr=$id filename=+8($args):ustring' >> dynamic_events
176
177 This is being run on x86_64 where the word size is 8 bytes and the openat
178 system call __NR_openat is set at 257.
179 ::
180
181 # echo 'nr == 257' > events/eprobes/openat/filter
182
183 Now enable the event and look at the trace.
184 ::
185
186 # echo 1 > events/eprobes/openat/enable
187 # cat trace
188
189 # tracer: nop
190 #
191 # entries-in-buffer/entries-written: 4/4 #P:8
192 #
193 # _-----=> irqs-off/BH-disabled
194 # / _----=> need-resched
195 # | / _---=> hardirq/softirq
196 # || / _--=> preempt-depth
197 # ||| / _-=> migrate-disable
198 # |||| / delay
199 # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
200 # | | | ||||| | |
201 cat-1298 [003] ...2. 2060.875970: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
202 cat-1298 [003] ...2. 2060.876197: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
203 cat-1298 [003] ...2. 2060.879126: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
204 cat-1298 [003] ...2. 2060.879639: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
205
206 The filename shows "(fault)". This is likely because the filename has not been
207 pulled into memory yet and currently trace events cannot fault in memory that
208 is not present. When an eprobe tries to read memory that has not been faulted
209 in yet, it will show the "(fault)" text.
210
211 To get around this, as the kernel will likely pull in this filename and make
212 it present, attaching it to a synthetic event that can pass the address of the
213 filename from the entry of the event to the end of the event, this can be used
214 to show the filename when the system call returns.
215
216 Remove the old eprobe::
217
218 # echo 1 > events/eprobes/openat/enable
219 # echo '-:openat' >> dynamic_events
220
221 This time make an eprobe where the address of the filename is saved::
222
223 # echo 'e:openat_start raw_syscalls.sys_enter nr=$id filename=+8($args):x64' >> dynamic_events
224
225 Create a synthetic event that passes the address of the filename to the
226 end of the event::
227
228 # echo 's:filename u64 file' >> dynamic_events
229 # echo 'hist:keys=common_pid:f=filename if nr == 257' > events/eprobes/openat_start/trigger
230 # echo 'hist:keys=common_pid:file=$f:onmatch(eprobes.openat_start).trace(filename,$file) if id == 257' > events/raw_syscalls/sys_exit/trigger
231
232 Now that the address of the filename has been passed to the end of the
233 system call, create another eprobe to attach to the exit event to show the
234 string::
235
236 # echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events
237 # echo 1 > events/eprobes/openat/enable
238 # cat trace
239
240 # tracer: nop
241 #
242 # entries-in-buffer/entries-written: 4/4 #P:8
243 #
244 # _-----=> irqs-off/BH-disabled
245 # / _----=> need-resched
246 # | / _---=> hardirq/softirq
247 # || / _--=> preempt-depth
248 # ||| / _-=> migrate-disable
249 # |||| / delay
250 # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
251 # | | | ||||| | |
252 cat-1331 [001] ...5. 2944.787977: openat: (synthetic.filename) filename="/etc/ld.so.cache"
253 cat-1331 [001] ...5. 2944.788480: openat: (synthetic.filename) filename="/lib/x86_64-linux-gnu/libc.so.6"
254 cat-1331 [001] ...5. 2944.793426: openat: (synthetic.filename) filename="/usr/lib/locale/locale-archive"
255 cat-1331 [001] ...5. 2944.831362: openat: (synthetic.filename) filename="trace"
256
257 Example 3
258 ---------
259
260 If syscall trace events are available, the above would not need the first
261 eprobe, but it would still need the last one::
262
263 # echo 's:filename u64 file' >> dynamic_events
264 # echo 'hist:keys=common_pid:f=filename' > events/syscalls/sys_enter_openat/trigger
265 # echo 'hist:keys=common_pid:file=$f:onmatch(syscalls.sys_enter_openat).trace(filename,$file)' > events/syscalls/sys_exit_openat/trigger
266 # echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events
267 # echo 1 > events/eprobes/openat/enable
268
269 And this would produce the same result as Example 2.
270

3. 한국어 전문 번역

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

Eprobe 개요와 활성화

1-21

Eprobe는 기존 event 위에 배치하는 dynamic event다. pointer인 field를 역참조하거나 trace event에 기록할 field를 제한하는 데 사용한다.

Eprobe는 kprobe event에 의존하므로 kernel을 `CONFIG_EPROBE_EVENTS=y`로 build해야 한다. `/sys/kernel/tracing/dynamic_events` file을 통해 생성한다.

Eprobe 기본 정보
항목내용
배치 대상기존 normal 또는 dynamic event
용도pointer field 역참조, 기록 field 제한
KconfigCONFIG_EPROBE_EVENTS=y
Control file/sys/kernel/tracing/dynamic_events

기능과 활성화 조건을 요약한다.

Eprobe 배치
Existing eventEprobe
Event field pointerDereference
Selected fieldsNew trace event

기존 event record를 입력으로 받아 새 event field를 만든다.

.. SPDX-License-Identifier: GPL-2.0

==================================
Eprobe - Event-based Probe Tracing
==================================

:Author: Steven Rostedt <rostedt@goodmis.org>

- Written for v6.17

Overview
========

Eprobes are dynamic events that are placed on existing events to either
dereference a field that is a pointer, or simply to limit what fields are
recorded in the trace event.

Eprobes depend on kprobe events so to enable this feature, build your kernel
with CONFIG_EPROBE_EVENTS=y.

Eprobes are created via the /sys/kernel/tracing/dynamic_events file.

eprobe_events 문법, FETCHARGS와 연결 대상

22-69

probe 설정 형식은 `e[:[EGRP/][EEVENT]] GRP.EVENT [FETCHARGS]`이고 삭제 형식은 `-:[EGRP/][EEVENT]`다. `EGRP`를 생략하면 새 event group은 `eprobes`가 된다. `EEVENT`를 생략하면 연결 대상 event와 같은 이름을 자동 생성한다.

`GRP.EVENT`는 연결할 기존 event의 group과 event name이다. probe 하나는 최대 128개 argument를 가질 수 있다.

`$FIELD`는 연결된 event field 값을 가져오고, `@ADDR`는 kernel address의 memory를, `@SYM[+|-offs]`는 data symbol 기준 memory를 가져온다. `$comm`은 current task의 comm을 가져온다.

`+|-[u]OFFS(FETCHARG)`는 FETCHARG 기준 offset address의 memory를 읽는다. `IMM`은 immediate value를 저장한다. `NAME=FETCHARG`는 argument 이름을 지정하고 `FETCHARG:TYPE`은 type을 지정한다.

지원 type에는 기본 정수 `u8`부터 `s64`, 16진수 `x8`부터 `x64`, VFS 공통 type `%pd`와 `%pD`, `char`, `string`, `ustring`, `symbol`, `symstr`, `bitfield`가 있다.

FETCHARGS는 `Documentation/trace/kprobetrace.rst`의 kprobe event 문법과 매우 비슷하다. 차이는 Eprobe의 `$FIELD`가 연결된 event field 내용을 반환한다는 점이다. Eprobe는 kprobe가 접근하는 register, stack, function argument에는 접근할 수 없다.

field argument가 pointer라면 FETCHARGS memory-address 문법으로 역참조할 수 있다. Eprobe는 normal event뿐 아니라 kprobe event, synthetic event, fprobe event에도 연결할 수 있어 field type을 바꿀 때 유용하다.

Eprobe 선언 요소
요소의미
EGRP새 event group, 기본 eprobes
EEVENT새 event name, 기본은 대상 event name
GRP연결 대상 group
EVENT연결 대상 event
FETCHARGS최대 128개 argument

새 event 이름과 연결 대상을 분리한다.

주요 FETCHARGS
문법동작
$FIELD연결 event의 FIELD 값
@ADDRkernel address의 memory
@SYM+offsdata symbol과 offset의 memory
$commcurrent task comm
OFFS(FETCHARG)argument 기준 memory 역참조
\IMMimmediate value
NAME=FETCHARGargument name 지정
FETCHARG:TYPEtype 지정

event field, memory, task, immediate value를 가져오는 문법이다.

Eprobe와 kprobe 비교
기능EprobeKprobe
기존 event field$FIELD로 접근직접 대상 아님
Register / stack접근 불가접근 가능
Function argument접근 불가접근 가능

접근 가능한 context가 다르다.


Synopsis of eprobe_events
-------------------------
::

  e[:[EGRP/][EEVENT]] GRP.EVENT [FETCHARGS]	: Set a probe
  -:[EGRP/][EEVENT]				: Clear a probe

 EGRP		: Group name of the new event. If omitted, use "eprobes" for it.
 EEVENT		: Event name. If omitted, the event name is generated and will
		  be the same event name as the event it attached to.
 GRP		: Group name of the event to attach to.
 EVENT		: Event name of the event to attach to.

 FETCHARGS	: Arguments. Each probe can have up to 128 args.
  $FIELD	: Fetch the value of the event field called FIELD.
  @ADDR		: Fetch memory at ADDR (ADDR should be in kernel)
  @SYM[+|-offs]	: Fetch memory at SYM +|- offs (SYM should be a data symbol)
  $comm		: Fetch current task comm.
  +|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
  \IMM		: Store an immediate value to the argument.
  NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
  FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
		  (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
		  (x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
                  "string", "ustring", "symbol", "symstr" and "bitfield" are
                  supported.

Types
-----
The FETCHARGS above is very similar to the kprobe events as described in
Documentation/trace/kprobetrace.rst.

The difference between eprobes and kprobes FETCHARGS is that eprobes has a
$FIELD command that returns the content of the event field of the event
that is attached. Eprobes do not have access to registers, stacks and function
arguments that kprobes has.

If a field argument is a pointer, it may be dereferenced just like a memory
address using the FETCHARGS syntax.


Attaching to dynamic events
---------------------------

Eprobes may attach to dynamic events as well as to normal events. It may
attach to a kprobe event, a synthetic event or a fprobe event. This is useful
if the type of a field needs to be changed. See Example 2 below.

예제 1: sched_switch field 제한

70-138

Eprobe의 기본 사용법은 tracing buffer에 기록할 data를 제한하는 것이다. 예제의 `sched_switch` event에는 모든 event에 공통인 네 field와 이전·다음 task의 이름, pid, priority, 이전 task state가 있다.

공통 네 field는 제한할 수 없다. 나머지는 60 byte이며 task 전환 전후의 이름, pid, priority, 이전 state를 기록한다. pid만 필요하다면 나머지 field가 ring buffer를 차지할 이유가 없다.

`e:sched/switch sched.sched_switch prev=$prev_pid:u32 next=$next_pid:u32`는 `sched_switch`에 연결해 `prev`와 `next` pid만 u32로 기록하는 Eprobe를 만든다.

Eprobe는 기록량을 줄이지만 성능을 개선하지는 않는다. Eprobe를 처리하기 위해 원래 모든 field를 temporary buffer에 먼저 기록하기 때문이다.

예시 trace에는 각 context switch마다 `prev`와 `next` pid만 나타난다. `prev_pid`와 `next_pid` 뒤에 `u32`를 붙이지 않으면 값은 기본적으로 16진수로 표시된다.

sched_switch field 선택
Field 집합제한결과
common_* 네 field불가항상 기록
prev_comm / next_comm제외새 event에 표시 안 함
prev_prio / next_prio / prev_state제외새 event에 표시 안 함
prev_pid / next_pid포함prev와 next u32

공통 field는 유지하고 필요한 pid만 새 event에 노출한다.

sched_switch Eprobe
sched.sched_switchTemporary full record
$prev_pid and $next_pidEprobe field selection
sched/switchprev=u32 next=u32

기존 event에서 pid field만 선택해 새 `sched/switch` event를 만든다.


Usage examples
==============

Example 1
---------

The basic usage of eprobes is to limit the data that is being recorded into
the tracing buffer. For example, a common event to trace is the sched_switch
trace event. That has a format of::

	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
	field:int common_pid;	offset:4;	size:4;	signed:1;

	field:char prev_comm[16];	offset:8;	size:16;	signed:0;
	field:pid_t prev_pid;	offset:24;	size:4;	signed:1;
	field:int prev_prio;	offset:28;	size:4;	signed:1;
	field:long prev_state;	offset:32;	size:8;	signed:1;
	field:char next_comm[16];	offset:40;	size:16;	signed:0;
	field:pid_t next_pid;	offset:56;	size:4;	signed:1;
	field:int next_prio;	offset:60;	size:4;	signed:1;

The first four fields are common to all events and can not be limited. But the
rest of the event has 60 bytes of information. It records the names of the
previous and next tasks being scheduled out and in, as well as their pids and
priorities. It also records the state of the previous task. If only the pids
of the tasks are of interest, why waste the ring buffer with all the other
fields?

An eprobe can limit what gets recorded. Note, it does not help in performance,
as all the fields are recorded in a temporary buffer to process the eprobe.
::

 # echo 'e:sched/switch sched.sched_switch prev=$prev_pid:u32 next=$next_pid:u32' >> /sys/kernel/tracing/dynamic_events
 # echo 1 > /sys/kernel/tracing/events/sched/switch/enable
 # cat /sys/kernel/tracing/trace

 # tracer: nop
 #
 # entries-in-buffer/entries-written: 2721/2721   #P:8
 #
 #                                _-----=> irqs-off/BH-disabled
 #                               / _----=> need-resched
 #                              | / _---=> hardirq/softirq
 #                              || / _--=> preempt-depth
 #                              ||| / _-=> migrate-disable
 #                              |||| /     delay
 #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
 #              | |         |   |||||     |         |
     sshd-session-1082    [004] d..4.  5041.239906: switch: (sched.sched_switch) prev=1082 next=0
             bash-1085    [001] d..4.  5041.240198: switch: (sched.sched_switch) prev=1085 next=141
    kworker/u34:5-141     [001] d..4.  5041.240259: switch: (sched.sched_switch) prev=141 next=1085
           <idle>-0       [004] d..4.  5041.240354: switch: (sched.sched_switch) prev=0 next=1082
             bash-1085    [001] d..4.  5041.240385: switch: (sched.sched_switch) prev=1085 next=141
    kworker/u34:5-141     [001] d..4.  5041.240410: switch: (sched.sched_switch) prev=141 next=1085
             bash-1085    [001] d..4.  5041.240478: switch: (sched.sched_switch) prev=1085 next=0
     sshd-session-1082    [004] d..4.  5041.240526: switch: (sched.sched_switch) prev=1082 next=0
           <idle>-0       [001] d..4.  5041.247524: switch: (sched.sched_switch) prev=0 next=90
           <idle>-0       [002] d..4.  5041.247545: switch: (sched.sched_switch) prev=0 next=16
      kworker/1:1-90      [001] d..4.  5041.247580: switch: (sched.sched_switch) prev=90 next=0
        rcu_sched-16      [002] d..4.  5041.247591: switch: (sched.sched_switch) prev=16 next=0
           <idle>-0       [002] d..4.  5041.257536: switch: (sched.sched_switch) prev=0 next=16
        rcu_sched-16      [002] d..4.  5041.257573: switch: (sched.sched_switch) prev=16 next=0

Note, without adding the "u32" after the prev_pid and next_pid, the values
would default showing in hexadecimal.

예제 2: raw_syscalls openat와 page fault 제약

139-215

특정 system call을 기록하고 싶지만 syscall event가 활성화되지 않았다면 `raw_syscalls`를 사용할 수 있다. syscall event는 일반 event가 아니라 kernel 내부에서 raw_syscalls event로부터 생성된다.

`raw_syscalls.sys_enter` format에는 공통 field, syscall `id`, 여섯 개의 `args`가 있다. `sys_openat(int dirfd, const char *path, int flags, mode_t mode)`에서 원하는 pathname은 두 번째 argument다.

x86_64 word size가 8 byte이므로 `filename=+8($args):ustring`은 `$args`에서 8 byte offset의 두 번째 argument를 user string으로 역참조한다. 이 platform의 `__NR_openat`은 257이므로 filter `nr == 257`을 적용한다.

event를 활성화하면 `nr=0x101`과 함께 filename이 `(fault)`로 보인다. pathname page가 아직 memory에 들어오지 않았을 가능성이 높고, 현재 trace event는 존재하지 않는 memory를 fault-in할 수 없기 때문이다.

Eprobe가 아직 fault-in되지 않은 memory를 읽으려 하면 `(fault)` text를 출력한다. kernel이 syscall 처리 중 pathname을 memory로 가져올 가능성이 있으므로, entry에서 filename address를 synthetic event로 전달해 syscall return 시점에 문자열을 읽으면 이 제약을 우회할 수 있다.

openat argument 위치
Argument순서주소 계산
dirfd1+0($args)
path2+8($args)
flags3+16($args)
mode4+24($args)

x86_64 raw syscall argument 배열에서 pathname을 찾는다.

첫 openat Eprobe
raw_syscalls.sys_enternr and args
Filter nr == 257Dereference +8($args)
Page not presentfilename=(fault)

sys_enter에서 즉시 pathname을 읽으려다 page가 없으면 fault가 표시된다.

Example 2
---------

If a specific system call is to be recorded but the syscalls events are not
enabled, the raw_syscalls can still be used (syscalls are system call
events are not normal events, but are created from the raw_syscalls events
within the kernel). In order to trace the openat system call, one can create
an event probe on top of the raw_syscalls event:
::

 # cd /sys/kernel/tracing
 # cat events/raw_syscalls/sys_enter/format
 name: sys_enter
 ID: 395
 format:
	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
	field:int common_pid;	offset:4;	size:4;	signed:1;

	field:long id;	offset:8;	size:8;	signed:1;
	field:unsigned long args[6];	offset:16;	size:48;	signed:0;

 print fmt: "NR %ld (%lx, %lx, %lx, %lx, %lx, %lx)", REC->id, REC->args[0], REC->args[1], REC->args[2], REC->args[3], REC->args[4], REC->args[5]

From the source code, the sys_openat() has:
::

 int sys_openat(int dirfd, const char *path, int flags, mode_t mode)
 {
	return my_syscall4(__NR_openat, dirfd, path, flags, mode);
 }

The path is the second parameter, and that is what is wanted.
::

 # echo 'e:openat raw_syscalls.sys_enter nr=$id filename=+8($args):ustring' >> dynamic_events

This is being run on x86_64 where the word size is 8 bytes and the openat
system call __NR_openat is set at 257.
::

 # echo 'nr == 257' > events/eprobes/openat/filter

Now enable the event and look at the trace.
::

 # echo 1 > events/eprobes/openat/enable
 # cat trace

 # tracer: nop
 #
 # entries-in-buffer/entries-written: 4/4   #P:8
 #
 #                                _-----=> irqs-off/BH-disabled
 #                               / _----=> need-resched
 #                              | / _---=> hardirq/softirq
 #                              || / _--=> preempt-depth
 #                              ||| / _-=> migrate-disable
 #                              |||| /     delay
 #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
 #              | |         |   |||||     |         |
              cat-1298    [003] ...2.  2060.875970: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
              cat-1298    [003] ...2.  2060.876197: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
              cat-1298    [003] ...2.  2060.879126: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)
              cat-1298    [003] ...2.  2060.879639: openat: (raw_syscalls.sys_enter) nr=0x101 filename=(fault)

The filename shows "(fault)". This is likely because the filename has not been
pulled into memory yet and currently trace events cannot fault in memory that
is not present. When an eprobe tries to read memory that has not been faulted
in yet, it will show the "(fault)" text.

To get around this, as the kernel will likely pull in this filename and make
it present, attaching it to a synthetic event that can pass the address of the
filename from the entry of the event to the end of the event, this can be used
to show the filename when the system call returns.

Synthetic event로 filename address 전달

216-256

먼저 기존 `openat` Eprobe를 제거한다. 이번에는 `openat_start` Eprobe에서 filename 자체가 아니라 그 address를 `x64`로 저장한다.

`s:filename u64 file`로 address를 운반할 synthetic event를 만든다. entry trigger는 `common_pid`를 key로 filename address를 저장하고, exit trigger는 같은 pid의 값을 찾아 synthetic `filename` event를 발생시킨다.

address가 syscall 끝까지 전달된 뒤 synthetic event에 두 번째 `openat` Eprobe를 붙인다. `filename=+0($file):ustring`으로 그 address의 user string을 읽는다.

결과 trace에는 `/etc/ld.so.cache`, libc path, locale archive, `trace`처럼 실제 filename이 표시된다. syscall 처리 중 pathname page가 memory에 들어왔기 때문에 exit 시점의 역참조가 성공한다.

Entry에서 exit까지 address 전달
openat_start at sys_enterSave filename address x64
Histogram key common_pidStore f=filename
raw_syscalls.sys_exitMatch same PID
synthetic.filenameCarry u64 file
Exit EprobeDereference file as ustring

PID를 key로 histogram과 synthetic event를 연결한다.

Fault 우회 전후
시점저장 값출력
sys_enterpathname pointer 즉시 역참조(fault) 가능
sys_enter to sys_exitpointer address 전달memory가 준비될 시간 확보
sys_exitpointer를 ustring으로 역참조실제 filename

역참조 시점을 entry에서 exit로 옮긴 효과다.

Remove the old eprobe::

 # echo 1 > events/eprobes/openat/enable
 # echo '-:openat' >> dynamic_events

This time make an eprobe where the address of the filename is saved::

 # echo 'e:openat_start raw_syscalls.sys_enter nr=$id filename=+8($args):x64' >> dynamic_events

Create a synthetic event that passes the address of the filename to the
end of the event::

 # echo 's:filename u64 file' >> dynamic_events
 # echo 'hist:keys=common_pid:f=filename if nr == 257' > events/eprobes/openat_start/trigger
 # echo 'hist:keys=common_pid:file=$f:onmatch(eprobes.openat_start).trace(filename,$file) if id == 257' > events/raw_syscalls/sys_exit/trigger

Now that the address of the filename has been passed to the end of the
system call, create another eprobe to attach to the exit event to show the
string::

 # echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events
 # echo 1 > events/eprobes/openat/enable
 # cat trace

 # tracer: nop
 #
 # entries-in-buffer/entries-written: 4/4   #P:8
 #
 #                                _-----=> irqs-off/BH-disabled
 #                               / _----=> need-resched
 #                              | / _---=> hardirq/softirq
 #                              || / _--=> preempt-depth
 #                              ||| / _-=> migrate-disable
 #                              |||| /     delay
 #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
 #              | |         |   |||||     |         |
              cat-1331    [001] ...5.  2944.787977: openat: (synthetic.filename) filename="/etc/ld.so.cache"
              cat-1331    [001] ...5.  2944.788480: openat: (synthetic.filename) filename="/lib/x86_64-linux-gnu/libc.so.6"
              cat-1331    [001] ...5.  2944.793426: openat: (synthetic.filename) filename="/usr/lib/locale/locale-archive"
              cat-1331    [001] ...5.  2944.831362: openat: (synthetic.filename) filename="trace"

예제 3: syscall event가 있는 경우

257-269

syscall trace event를 사용할 수 있다면 예제 2의 첫 번째 raw_syscalls Eprobe는 필요 없다. 다만 synthetic event에서 filename string을 읽는 마지막 Eprobe는 여전히 필요하다.

`sys_enter_openat` trigger가 pid별 filename address를 저장하고 `sys_exit_openat` trigger가 synthetic `filename` event를 발생시킨다. 그 event에 `filename=+0($file):ustring` Eprobe를 붙이면 예제 2와 같은 결과를 얻는다.

Syscall event 사용 경로
syscalls.sys_enter_openatSave filename by PID
syscalls.sys_exit_openatEmit synthetic.filename
Eprobe on synthetic.filenameRead ustring

전용 syscall event가 raw entry Eprobe를 대체한다.

Example 3
---------

If syscall trace events are available, the above would not need the first
eprobe, but it would still need the last one::

 # echo 's:filename u64 file' >> dynamic_events
 # echo 'hist:keys=common_pid:f=filename' > events/syscalls/sys_enter_openat/trigger
 # echo 'hist:keys=common_pid:file=$f:onmatch(syscalls.sys_enter_openat).trace(filename,$file)' > events/syscalls/sys_exit_openat/trigger
 # echo 'e:openat synthetic.filename filename=+0($file):ustring' >> dynamic_events
 # echo 1 > events/eprobes/openat/enable

And this would produce the same result as Example 2.