← Documents Documentation/trace/tracepoint-analysis.rst GitHub 원문 ↗

Linux 6.18.37 · Tracing

Event와 tracepoint를 이용한 동작 분석 참고 사항

tracepoint 탐색·활성화·반복 집계·trace_pipe 후처리와 perf record/report/annotate를 이용해 process에서 instruction까지 원인을 좁히는 방법을 설명합니다.

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

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

1. 요약·해설

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

요약·해설

tracepoint-analysis.rst:1-338

이 문서는 tracepoint event를 찾고 범위를 정해 수집한 뒤 변동성, higher-level event, shared object, symbol, instruction 순으로 분석을 깊게 만드는 실무 흐름을 제시합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 =========================================================
2 Notes on Analysing Behaviour Using Events and Tracepoints
3 =========================================================
4 :Author: Mel Gorman (PCL information heavily based on email from Ingo Molnar)
5
6 1. Introduction
7 ===============
8
9 Tracepoints (see Documentation/trace/tracepoints.rst) can be used without
10 creating custom kernel modules to register probe functions using the event
11 tracing infrastructure.
12
13 Simplistically, tracepoints represent important events that can be
14 taken in conjunction with other tracepoints to build a "Big Picture" of
15 what is going on within the system. There are a large number of methods for
16 gathering and interpreting these events. Lacking any current Best Practises,
17 this document describes some of the methods that can be used.
18
19 This document assumes that debugfs is mounted on /sys/kernel/debug and that
20 the appropriate tracing options have been configured into the kernel. It is
21 assumed that the PCL tool tools/perf has been installed and is in your path.
22
23 2. Listing Available Events
24 ===========================
25
26 2.1 Standard Utilities
27 ----------------------
28
29 All possible events are visible from /sys/kernel/tracing/events. Simply
30 calling::
31
32 $ find /sys/kernel/tracing/events -type d
33
34 will give a fair indication of the number of events available.
35
36 2.2 PCL (Performance Counters for Linux)
37 ----------------------------------------
38
39 Discovery and enumeration of all counters and events, including tracepoints,
40 are available with the perf tool. Getting a list of available events is a
41 simple case of::
42
43 $ perf list 2>&1 | grep Tracepoint
44 ext4:ext4_free_inode [Tracepoint event]
45 ext4:ext4_request_inode [Tracepoint event]
46 ext4:ext4_allocate_inode [Tracepoint event]
47 ext4:ext4_write_begin [Tracepoint event]
48 ext4:ext4_ordered_write_end [Tracepoint event]
49 [ .... remaining output snipped .... ]
50
51
52 3. Enabling Events
53 ==================
54
55 3.1 System-Wide Event Enabling
56 ------------------------------
57
58 See Documentation/trace/events.rst for a proper description on how events
59 can be enabled system-wide. A short example of enabling all events related
60 to page allocation would look something like::
61
62 $ for i in `find /sys/kernel/tracing/events -name "enable" | grep mm_`; do echo 1 > $i; done
63
64 3.2 System-Wide Event Enabling with SystemTap
65 ---------------------------------------------
66
67 In SystemTap, tracepoints are accessible using the kernel.trace() function
68 call. The following is an example that reports every 5 seconds what processes
69 were allocating the pages.
70 ::
71
72 global page_allocs
73
74 probe kernel.trace("mm_page_alloc") {
75 page_allocs[execname()]++
76 }
77
78 function print_count() {
79 printf ("%-25s %-s\n", "#Pages Allocated", "Process Name")
80 foreach (proc in page_allocs-)
81 printf("%-25d %s\n", page_allocs[proc], proc)
82 printf ("\n")
83 delete page_allocs
84 }
85
86 probe timer.s(5) {
87 print_count()
88 }
89
90 3.3 System-Wide Event Enabling with PCL
91 ---------------------------------------
92
93 By specifying the -a switch and analysing sleep, the system-wide events
94 for a duration of time can be examined.
95 ::
96
97 $ perf stat -a \
98 -e kmem:mm_page_alloc -e kmem:mm_page_free \
99 -e kmem:mm_page_free_batched \
100 sleep 10
101 Performance counter stats for 'sleep 10':
102
103 9630 kmem:mm_page_alloc
104 2143 kmem:mm_page_free
105 7424 kmem:mm_page_free_batched
106
107 10.002577764 seconds time elapsed
108
109 Similarly, one could execute a shell and exit it as desired to get a report
110 at that point.
111
112 3.4 Local Event Enabling
113 ------------------------
114
115 Documentation/trace/ftrace.rst describes how to enable events on a per-thread
116 basis using set_ftrace_pid.
117
118 3.5 Local Event Enablement with PCL
119 -----------------------------------
120
121 Events can be activated and tracked for the duration of a process on a local
122 basis using PCL such as follows.
123 ::
124
125 $ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
126 -e kmem:mm_page_free_batched ./hackbench 10
127 Time: 0.909
128
129 Performance counter stats for './hackbench 10':
130
131 17803 kmem:mm_page_alloc
132 12398 kmem:mm_page_free
133 4827 kmem:mm_page_free_batched
134
135 0.973913387 seconds time elapsed
136
137 4. Event Filtering
138 ==================
139
140 Documentation/trace/ftrace.rst covers in-depth how to filter events in
141 ftrace. Obviously using grep and awk of trace_pipe is an option as well
142 as any script reading trace_pipe.
143
144 5. Analysing Event Variances with PCL
145 =====================================
146
147 Any workload can exhibit variances between runs and it can be important
148 to know what the standard deviation is. By and large, this is left to the
149 performance analyst to do it by hand. In the event that the discrete event
150 occurrences are useful to the performance analyst, then perf can be used.
151 ::
152
153 $ perf stat --repeat 5 -e kmem:mm_page_alloc -e kmem:mm_page_free
154 -e kmem:mm_page_free_batched ./hackbench 10
155 Time: 0.890
156 Time: 0.895
157 Time: 0.915
158 Time: 1.001
159 Time: 0.899
160
161 Performance counter stats for './hackbench 10' (5 runs):
162
163 16630 kmem:mm_page_alloc ( +- 3.542% )
164 11486 kmem:mm_page_free ( +- 4.771% )
165 4730 kmem:mm_page_free_batched ( +- 2.325% )
166
167 0.982653002 seconds time elapsed ( +- 1.448% )
168
169 In the event that some higher-level event is required that depends on some
170 aggregation of discrete events, then a script would need to be developed.
171
172 Using --repeat, it is also possible to view how events are fluctuating over
173 time on a system-wide basis using -a and sleep.
174 ::
175
176 $ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
177 -e kmem:mm_page_free_batched \
178 -a --repeat 10 \
179 sleep 1
180 Performance counter stats for 'sleep 1' (10 runs):
181
182 1066 kmem:mm_page_alloc ( +- 26.148% )
183 182 kmem:mm_page_free ( +- 5.464% )
184 890 kmem:mm_page_free_batched ( +- 30.079% )
185
186 1.002251757 seconds time elapsed ( +- 0.005% )
187
188 6. Higher-Level Analysis with Helper Scripts
189 ============================================
190
191 When events are enabled the events that are triggering can be read from
192 /sys/kernel/tracing/trace_pipe in human-readable format although binary
193 options exist as well. By post-processing the output, further information can
194 be gathered on-line as appropriate. Examples of post-processing might include
195
196 - Reading information from /proc for the PID that triggered the event
197 - Deriving a higher-level event from a series of lower-level events.
198 - Calculating latencies between two events
199
200 Documentation/trace/postprocess/trace-pagealloc-postprocess.pl is an example
201 script that can read trace_pipe from STDIN or a copy of a trace. When used
202 on-line, it can be interrupted once to generate a report without exiting
203 and twice to exit.
204
205 Simplistically, the script just reads STDIN and counts up events but it
206 also can do more such as
207
208 - Derive high-level events from many low-level events. If a number of pages
209 are freed to the main allocator from the per-CPU lists, it recognises
210 that as one per-CPU drain even though there is no specific tracepoint
211 for that event
212 - It can aggregate based on PID or individual process number
213 - In the event memory is getting externally fragmented, it reports
214 on whether the fragmentation event was severe or moderate.
215 - When receiving an event about a PID, it can record who the parent was so
216 that if large numbers of events are coming from very short-lived
217 processes, the parent process responsible for creating all the helpers
218 can be identified
219
220 7. Lower-Level Analysis with PCL
221 ================================
222
223 There may also be a requirement to identify what functions within a program
224 were generating events within the kernel. To begin this sort of analysis, the
225 data must be recorded. At the time of writing, this required root:
226 ::
227
228 $ perf record -c 1 \
229 -e kmem:mm_page_alloc -e kmem:mm_page_free \
230 -e kmem:mm_page_free_batched \
231 ./hackbench 10
232 Time: 0.894
233 [ perf record: Captured and wrote 0.733 MB perf.data (~32010 samples) ]
234
235 Note the use of '-c 1' to set the event period to sample. The default sample
236 period is quite high to minimise overhead but the information collected can be
237 very coarse as a result.
238
239 This record outputted a file called perf.data which can be analysed using
240 perf report.
241 ::
242
243 $ perf report
244 # Samples: 30922
245 #
246 # Overhead Command Shared Object
247 # ........ ......... ................................
248 #
249 87.27% hackbench [vdso]
250 6.85% hackbench /lib/i686/cmov/libc-2.9.so
251 2.62% hackbench /lib/ld-2.9.so
252 1.52% perf [vdso]
253 1.22% hackbench ./hackbench
254 0.48% hackbench [kernel]
255 0.02% perf /lib/i686/cmov/libc-2.9.so
256 0.01% perf /usr/bin/perf
257 0.01% perf /lib/ld-2.9.so
258 0.00% hackbench /lib/i686/cmov/libpthread-2.9.so
259 #
260 # (For more details, try: perf report --sort comm,dso,symbol)
261 #
262
263 According to this, the vast majority of events triggered on events
264 within the VDSO. With simple binaries, this will often be the case so let's
265 take a slightly different example. In the course of writing this, it was
266 noticed that X was generating an insane amount of page allocations so let's look
267 at it:
268 ::
269
270 $ perf record -c 1 -f \
271 -e kmem:mm_page_alloc -e kmem:mm_page_free \
272 -e kmem:mm_page_free_batched \
273 -p `pidof X`
274
275 This was interrupted after a few seconds and
276 ::
277
278 $ perf report
279 # Samples: 27666
280 #
281 # Overhead Command Shared Object
282 # ........ ....... .......................................
283 #
284 51.95% Xorg [vdso]
285 47.95% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1
286 0.09% Xorg /lib/i686/cmov/libc-2.9.so
287 0.01% Xorg [kernel]
288 #
289 # (For more details, try: perf report --sort comm,dso,symbol)
290 #
291
292 So, almost half of the events are occurring in a library. To get an idea which
293 symbol:
294 ::
295
296 $ perf report --sort comm,dso,symbol
297 # Samples: 27666
298 #
299 # Overhead Command Shared Object Symbol
300 # ........ ....... ....................................... ......
301 #
302 51.95% Xorg [vdso] [.] 0x000000ffffe424
303 47.93% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1 [.] pixmanFillsse2
304 0.09% Xorg /lib/i686/cmov/libc-2.9.so [.] _int_malloc
305 0.01% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1 [.] pixman_region32_copy_f
306 0.01% Xorg [kernel] [k] read_hpet
307 0.01% Xorg /opt/gfx-test/lib/libpixman-1.so.0.13.1 [.] get_fast_path
308 0.00% Xorg [kernel] [k] ftrace_trace_userstack
309
310 To see where within the function pixmanFillsse2 things are going wrong:
311 ::
312
313 $ perf annotate pixmanFillsse2
314 [ ... ]
315 0.00 : 34eeb: 0f 18 08 prefetcht0 (%eax)
316 : }
317 :
318 : extern __inline void __attribute__((__gnu_inline__, __always_inline__, _
319 : _mm_store_si128 (__m128i *__P, __m128i __B) : {
320 : *__P = __B;
321 12.40 : 34eee: 66 0f 7f 80 40 ff ff movdqa %xmm0,-0xc0(%eax)
322 0.00 : 34ef5: ff
323 12.40 : 34ef6: 66 0f 7f 80 50 ff ff movdqa %xmm0,-0xb0(%eax)
324 0.00 : 34efd: ff
325 12.39 : 34efe: 66 0f 7f 80 60 ff ff movdqa %xmm0,-0xa0(%eax)
326 0.00 : 34f05: ff
327 12.67 : 34f06: 66 0f 7f 80 70 ff ff movdqa %xmm0,-0x90(%eax)
328 0.00 : 34f0d: ff
329 12.58 : 34f0e: 66 0f 7f 40 80 movdqa %xmm0,-0x80(%eax)
330 12.31 : 34f13: 66 0f 7f 40 90 movdqa %xmm0,-0x70(%eax)
331 12.40 : 34f18: 66 0f 7f 40 a0 movdqa %xmm0,-0x60(%eax)
332 12.31 : 34f1d: 66 0f 7f 40 b0 movdqa %xmm0,-0x50(%eax)
333
334 At a glance, it looks like the time is being spent copying pixmaps to
335 the card. Further investigation would be needed to determine why pixmaps
336 are being copied around so much but a starting point would be to take an
337 ancient build of libpixmap out of the library path where it was totally
338 forgotten about from months ago!
339

3. 한국어 전문 번역

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

분석 범위와 사용 가능한 event 찾기

1-51

저자는 Mel Gorman이며 PCL 관련 내용은 Ingo Molnar의 email에 크게 기반합니다. tracepoint는 custom kernel module과 probe function을 따로 만들지 않고 event tracing infrastructure를 통해 사용할 수 있습니다. 자세한 정의는 `Documentation/trace/tracepoints.rst`를 참조합니다.

간단히 말해 tracepoint는 system 안에서 벌어지는 일을 여러 event와 결합해 큰 그림으로 구성할 수 있는 중요한 지점을 나타냅니다. event를 수집하고 해석하는 방법은 많지만 당시 정립된 best practice가 없어, 이 문서는 사용할 수 있는 몇 가지 방법을 소개합니다.

문서는 debugfs가 `/sys/kernel/debug`에 mount되어 있고 kernel에 적절한 tracing option이 구성됐다고 가정합니다. 또한 PCL 도구인 `tools/perf`가 설치되어 PATH에서 실행 가능하다고 가정합니다.

tracepoint 분석 흐름
Kernel tracepointsEvent tracing
perf / SystemTap / trace_pipeCollect and aggregate
Counts and varianceProcesses and symbols
perf annotateInstruction-level hotspot

kernel event를 수집해 process·latency·symbol 수준의 해석으로 확장합니다.

모든 가능한 event는 `/sys/kernel/tracing/events`에서 볼 수 있습니다. `find`로 directory를 나열하면 사용할 수 있는 event 수를 대략 파악할 수 있습니다.

$ find /sys/kernel/tracing/events -type d

`perf`는 tracepoint를 포함한 모든 counter와 event를 탐색하고 열거할 수 있습니다. `perf list` 결과에서 `Tracepoint`를 찾으면 ext4 예제처럼 event 이름과 종류가 표시됩니다.

  $ perf list 2>&1 | grep Tracepoint
  ext4:ext4_free_inode                     [Tracepoint event]
  ext4:ext4_request_inode                  [Tracepoint event]
  ext4:ext4_allocate_inode                 [Tracepoint event]
  ext4:ext4_write_begin                    [Tracepoint event]
  ext4:ext4_ordered_write_end              [Tracepoint event]
  [ .... remaining output snipped .... ]
event 탐색 방법
방법결과
find /sys/kernel/tracing/events -type dtracing event directory 전체
perf list | grep Tracepointperf가 인식하는 tracepoint event

filesystem과 perf가 제공하는 두 목록 경로입니다.

=========================================================
Notes on Analysing Behaviour Using Events and Tracepoints
=========================================================
:Author: Mel Gorman (PCL information heavily based on email from Ingo Molnar)

1. Introduction
===============

Tracepoints (see Documentation/trace/tracepoints.rst) can be used without
creating custom kernel modules to register probe functions using the event
tracing infrastructure.

Simplistically, tracepoints represent important events that can be
taken in conjunction with other tracepoints to build a "Big Picture" of
what is going on within the system. There are a large number of methods for
gathering and interpreting these events. Lacking any current Best Practises,
this document describes some of the methods that can be used.

This document assumes that debugfs is mounted on /sys/kernel/debug and that
the appropriate tracing options have been configured into the kernel. It is
assumed that the PCL tool tools/perf has been installed and is in your path.

2. Listing Available Events
===========================

2.1 Standard Utilities
----------------------

All possible events are visible from /sys/kernel/tracing/events. Simply
calling::

  $ find /sys/kernel/tracing/events -type d

will give a fair indication of the number of events available.

2.2 PCL (Performance Counters for Linux)
----------------------------------------

Discovery and enumeration of all counters and events, including tracepoints,
are available with the perf tool. Getting a list of available events is a
simple case of::

  $ perf list 2>&1 | grep Tracepoint
  ext4:ext4_free_inode                     [Tracepoint event]
  ext4:ext4_request_inode                  [Tracepoint event]
  ext4:ext4_allocate_inode                 [Tracepoint event]
  ext4:ext4_write_begin                    [Tracepoint event]
  ext4:ext4_ordered_write_end              [Tracepoint event]
  [ .... remaining output snipped .... ]

system-wide event 활성화

52-111

system-wide event 활성화의 전체 설명은 `Documentation/trace/events.rst`에 있습니다. 아래 shell loop는 이름에 `mm_`가 들어간 enable file을 찾아 1을 써 page allocation 관련 event를 모두 켭니다.

$ for i in `find /sys/kernel/tracing/events -name "enable" | grep mm_`; do echo 1 > $i; done

SystemTap에서는 `kernel.trace()` function으로 tracepoint에 접근합니다. 예제는 `mm_page_alloc` event마다 실행 process 이름별 page allocation 횟수를 누적하고 5초마다 출력한 뒤 map을 비웁니다.

  global page_allocs

  probe kernel.trace("mm_page_alloc") {
  	page_allocs[execname()]++
  }

  function print_count() {
  	printf ("%-25s %-s\n", "#Pages Allocated", "Process Name")
  	foreach (proc in page_allocs-)
  		printf("%-25d %s\n", page_allocs[proc], proc)
  	printf ("\n")
  	delete page_allocs
  }

  probe timer.s(5) {
          print_count()
  }
SystemTap 집계 구성
구성역할
global page_allocsprocess별 counter map
kernel.trace("mm_page_alloc")allocation마다 execname counter 증가
timer.s(5)5초마다 정렬 출력 후 map 삭제

tracepoint handler와 periodic report를 분리합니다.

PCL에서는 `-a` switch와 `sleep`을 함께 사용해 일정 시간 동안 system-wide event를 셀 수 있습니다. 예제는 10초 동안 page allocation과 두 종류의 free event를 집계합니다.

 $ perf stat -a \
	-e kmem:mm_page_alloc -e kmem:mm_page_free \
	-e kmem:mm_page_free_batched \
	sleep 10
 Performance counter stats for 'sleep 10':

           9630  kmem:mm_page_alloc
           2143  kmem:mm_page_free
           7424  kmem:mm_page_free_batched

   10.002577764  seconds time elapsed

같은 방식으로 shell을 실행하고 원하는 시점에 종료하면 그때까지의 report를 얻을 수 있습니다.

system-wide 측정 선택
tracefs enable filesRaw event stream
SystemTapProcess aggregation every 5s
perf stat -a + sleepSystem-wide discrete counts

목적에 따라 raw enable, programmable aggregation, perf counter를 선택합니다.

3. Enabling Events
==================

3.1 System-Wide Event Enabling
------------------------------

See Documentation/trace/events.rst for a proper description on how events
can be enabled system-wide. A short example of enabling all events related
to page allocation would look something like::

  $ for i in `find /sys/kernel/tracing/events -name "enable" | grep mm_`; do echo 1 > $i; done

3.2 System-Wide Event Enabling with SystemTap
---------------------------------------------

In SystemTap, tracepoints are accessible using the kernel.trace() function
call. The following is an example that reports every 5 seconds what processes
were allocating the pages.
::

  global page_allocs

  probe kernel.trace("mm_page_alloc") {
  	page_allocs[execname()]++
  }

  function print_count() {
  	printf ("%-25s %-s\n", "#Pages Allocated", "Process Name")
  	foreach (proc in page_allocs-)
  		printf("%-25d %s\n", page_allocs[proc], proc)
  	printf ("\n")
  	delete page_allocs
  }

  probe timer.s(5) {
          print_count()
  }

3.3 System-Wide Event Enabling with PCL
---------------------------------------

By specifying the -a switch and analysing sleep, the system-wide events
for a duration of time can be examined.
::

 $ perf stat -a \
	-e kmem:mm_page_alloc -e kmem:mm_page_free \
	-e kmem:mm_page_free_batched \
	sleep 10
 Performance counter stats for 'sleep 10':

           9630  kmem:mm_page_alloc
           2143  kmem:mm_page_free
           7424  kmem:mm_page_free_batched

   10.002577764  seconds time elapsed

Similarly, one could execute a shell and exit it as desired to get a report
at that point.

process-local event와 filtering

112-143

thread별 event 활성화는 `Documentation/trace/ftrace.rst`의 `set_ftrace_pid` 설명을 따릅니다.

PCL에서는 process 실행 시간 동안 local event를 활성화하고 추적할 수 있습니다. 예제는 `./hackbench 10` 실행 동안 page allocation과 free event를 세고 실행 시간과 counter를 출력합니다.

  $ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
		 -e kmem:mm_page_free_batched ./hackbench 10
  Time: 0.909

    Performance counter stats for './hackbench 10':

          17803  kmem:mm_page_alloc
          12398  kmem:mm_page_free
           4827  kmem:mm_page_free_batched

    0.973913387  seconds time elapsed

event filtering은 `Documentation/trace/ftrace.rst`가 자세히 다룹니다. `trace_pipe`를 `grep`·`awk`로 거르거나 trace_pipe를 읽는 script에서 filtering하는 방법도 사용할 수 있습니다.

local 분석 경로
방법범위
set_ftrace_pid지정 thread의 ftrace event
perf stat ... command해당 process 실행 기간
grep / awk / script on trace_pipestream 내용 기반 filtering

대상 thread 또는 process 범위로 event를 제한합니다.

3.4 Local Event Enabling
------------------------

Documentation/trace/ftrace.rst describes how to enable events on a per-thread
basis using set_ftrace_pid.

3.5 Local Event Enablement with PCL
-----------------------------------

Events can be activated and tracked for the duration of a process on a local
basis using PCL such as follows.
::

  $ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
		 -e kmem:mm_page_free_batched ./hackbench 10
  Time: 0.909

    Performance counter stats for './hackbench 10':

          17803  kmem:mm_page_alloc
          12398  kmem:mm_page_free
           4827  kmem:mm_page_free_batched

    0.973913387  seconds time elapsed

4. Event Filtering
==================

Documentation/trace/ftrace.rst covers in-depth how to filter events in
ftrace.  Obviously using grep and awk of trace_pipe is an option as well
as any script reading trace_pipe.

반복 측정과 event 변동성

144-187

workload 결과는 실행마다 달라질 수 있으므로 standard deviation을 아는 것이 중요합니다. 보통 performance analyst가 직접 계산하지만 discrete event 발생 횟수가 유용하다면 `perf stat --repeat`를 사용할 수 있습니다.

첫 예제는 hackbench를 5회 실행하고 각 page event와 elapsed time의 평균 및 `+-` 백분율 변동을 보여 줍니다.

  $ perf stat --repeat 5 -e kmem:mm_page_alloc -e kmem:mm_page_free
			-e kmem:mm_page_free_batched ./hackbench 10
  Time: 0.890
  Time: 0.895
  Time: 0.915
  Time: 1.001
  Time: 0.899

   Performance counter stats for './hackbench 10' (5 runs):

          16630  kmem:mm_page_alloc         ( +-   3.542% )
          11486  kmem:mm_page_free	    ( +-   4.771% )
           4730  kmem:mm_page_free_batched  ( +-   2.325% )

    0.982653002  seconds time elapsed   ( +-   1.448% )
5회 hackbench 변동
항목평균변동
mm_page_alloc16,6303.542%
mm_page_free11,4864.771%
mm_page_free_batched4,7302.325%
elapsed0.982653002s1.448%

원문 출력의 평균 counter와 변동률입니다.

여러 discrete event를 집계해야만 얻을 수 있는 higher-level event가 필요하다면 별도 script를 개발해야 합니다.

`--repeat`에 `-a`와 `sleep`을 결합하면 system-wide event가 시간에 따라 얼마나 변하는지도 볼 수 있습니다. 둘째 예제는 1초 구간을 10회 반복합니다.

  $ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
		-e kmem:mm_page_free_batched \
		-a --repeat 10 \
		sleep 1
  Performance counter stats for 'sleep 1' (10 runs):

           1066  kmem:mm_page_alloc         ( +-  26.148% )
            182  kmem:mm_page_free          ( +-   5.464% )
            890  kmem:mm_page_free_batched  ( +-  30.079% )

    1.002251757  seconds time elapsed   ( +-   0.005% )
변동성 측정
perf stat --repeat NRepeat workload
Per-run countersMean and deviation
-a + sleepSystem-wide time-series samples

같은 workload 또는 같은 시간 구간을 반복해 counter 분산을 얻습니다.

5. Analysing Event Variances with PCL
=====================================

Any workload can exhibit variances between runs and it can be important
to know what the standard deviation is. By and large, this is left to the
performance analyst to do it by hand. In the event that the discrete event
occurrences are useful to the performance analyst, then perf can be used.
::

  $ perf stat --repeat 5 -e kmem:mm_page_alloc -e kmem:mm_page_free
			-e kmem:mm_page_free_batched ./hackbench 10
  Time: 0.890
  Time: 0.895
  Time: 0.915
  Time: 1.001
  Time: 0.899

   Performance counter stats for './hackbench 10' (5 runs):

          16630  kmem:mm_page_alloc         ( +-   3.542% )
          11486  kmem:mm_page_free	    ( +-   4.771% )
           4730  kmem:mm_page_free_batched  ( +-   2.325% )

    0.982653002  seconds time elapsed   ( +-   1.448% )

In the event that some higher-level event is required that depends on some
aggregation of discrete events, then a script would need to be developed.

Using --repeat, it is also possible to view how events are fluctuating over
time on a system-wide basis using -a and sleep.
::

  $ perf stat -e kmem:mm_page_alloc -e kmem:mm_page_free \
		-e kmem:mm_page_free_batched \
		-a --repeat 10 \
		sleep 1
  Performance counter stats for 'sleep 1' (10 runs):

           1066  kmem:mm_page_alloc         ( +-  26.148% )
            182  kmem:mm_page_free          ( +-   5.464% )
            890  kmem:mm_page_free_batched  ( +-  30.079% )

    1.002251757  seconds time elapsed   ( +-   0.005% )

trace_pipe 후처리와 higher-level event

188-219

event가 활성화되면 발생한 event를 `/sys/kernel/tracing/trace_pipe`에서 사람이 읽을 수 있는 형식으로 읽을 수 있으며 binary option도 있습니다. output을 online으로 후처리하면 추가 정보를 계산할 수 있습니다.

후처리 예로 event를 발생시킨 PID의 `/proc` 정보를 읽기, 여러 lower-level event에서 higher-level event 도출, 두 event 사이 latency 계산이 있습니다.

`Documentation/trace/postprocess/trace-pagealloc-postprocess.pl`은 STDIN의 trace_pipe 또는 저장된 trace 사본을 읽는 예제입니다. online 실행 중 interrupt를 한 번 보내면 종료하지 않고 report를 만들고, 두 번 보내면 종료합니다.

script는 기본적으로 STDIN event를 세지만 더 복잡한 해석도 합니다. per-CPU list에서 main allocator로 여러 page가 반환되면 전용 tracepoint가 없어도 하나의 per-CPU drain으로 인식하고, PID나 개별 process 번호로 집계합니다.

외부 fragmentation 발생 시 severe 또는 moderate인지 보고하며, 매우 짧게 사는 process가 많은 event를 만들 때 parent를 기록해 helper를 대량 생성한 책임 process를 찾을 수 있습니다.

후처리 script의 파생 정보
입력파생 결과
여러 page free eventper-CPU drain 하나
PID eventprocess별 집계와 parent 추적
fragmentation eventsevere / moderate 분류
두 event timestamplatency

raw tracepoint보다 높은 의미 수준을 생성합니다.

trace_pipe online 분석
trace_pipeParse events
/proc lookup + aggregationDerived state
First interruptPrint report
Second interruptExit

raw stream을 stateful script가 higher-level report로 바꿉니다.

6. Higher-Level Analysis with Helper Scripts
============================================

When events are enabled the events that are triggering can be read from
/sys/kernel/tracing/trace_pipe in human-readable format although binary
options exist as well. By post-processing the output, further information can
be gathered on-line as appropriate. Examples of post-processing might include

  - Reading information from /proc for the PID that triggered the event
  - Deriving a higher-level event from a series of lower-level events.
  - Calculating latencies between two events

Documentation/trace/postprocess/trace-pagealloc-postprocess.pl is an example
script that can read trace_pipe from STDIN or a copy of a trace. When used
on-line, it can be interrupted once to generate a report without exiting
and twice to exit.

Simplistically, the script just reads STDIN and counts up events but it
also can do more such as

  - Derive high-level events from many low-level events. If a number of pages
    are freed to the main allocator from the per-CPU lists, it recognises
    that as one per-CPU drain even though there is no specific tracepoint
    for that event
  - It can aggregate based on PID or individual process number
  - In the event memory is getting externally fragmented, it reports
    on whether the fragmentation event was severe or moderate.
  - When receiving an event about a PID, it can record who the parent was so
    that if large numbers of events are coming from very short-lived
    processes, the parent process responsible for creating all the helpers
    can be identified

perf record와 report의 하위 수준 분석

220-262

kernel event를 발생시키는 program 내부 function을 식별하려면 먼저 data를 record해야 합니다. 문서 작성 당시 이 작업은 root 권한이 필요했습니다.

예제는 `-c 1`로 sample event period를 1로 설정하고 hackbench 실행 동안 세 page event를 기록합니다. 기본 sample period는 overhead를 줄이기 위해 높지만 그만큼 수집 정보가 거칠어질 수 있습니다.

  $ perf record -c 1 \
	-e kmem:mm_page_alloc -e kmem:mm_page_free \
	-e kmem:mm_page_free_batched \
	./hackbench 10
  Time: 0.894
  [ perf record: Captured and wrote 0.733 MB perf.data (~32010 samples) ]

record 결과는 `perf.data` file에 저장되고 `perf report`로 분석합니다.

  $ perf report
  # Samples: 30922
  #
  # Overhead    Command                     Shared Object
  # ........  .........  ................................
  #
      87.27%  hackbench  [vdso]
       6.85%  hackbench  /lib/i686/cmov/libc-2.9.so
       2.62%  hackbench  /lib/ld-2.9.so
       1.52%       perf  [vdso]
       1.22%  hackbench  ./hackbench
       0.48%  hackbench  [kernel]
       0.02%       perf  /lib/i686/cmov/libc-2.9.so
       0.01%       perf  /usr/bin/perf
       0.01%       perf  /lib/ld-2.9.so
       0.00%  hackbench  /lib/i686/cmov/libpthread-2.9.so
  #
  # (For more details, try: perf report --sort comm,dso,symbol)

report에서 event의 87.27%는 hackbench의 `[vdso]`, 6.85%는 libc, 2.62%는 dynamic linker에서 관찰됩니다. 간단한 binary에서는 VDSO 비중이 크게 나오는 경우가 흔합니다.

hackbench perf report
ObjectOverhead
[vdso]87.27%
libc-2.9.so6.85%
lib/ld-2.9.so2.62%
./hackbench1.22%
[kernel]0.48%

Shared Object별 sample 비중의 주요 항목입니다.

perf 저수준 분석 단계
perf record -c 1perf.data
perf reportCommand / Shared Object
--sort comm,dso,symbolSymbol attribution
perf annotateInstruction attribution

event record에서 object와 symbol 분석으로 내려갑니다.

7. Lower-Level Analysis with PCL
================================

There may also be a requirement to identify what functions within a program
were generating events within the kernel. To begin this sort of analysis, the
data must be recorded. At the time of writing, this required root:
::

  $ perf record -c 1 \
	-e kmem:mm_page_alloc -e kmem:mm_page_free \
	-e kmem:mm_page_free_batched \
	./hackbench 10
  Time: 0.894
  [ perf record: Captured and wrote 0.733 MB perf.data (~32010 samples) ]

Note the use of '-c 1' to set the event period to sample. The default sample
period is quite high to minimise overhead but the information collected can be
very coarse as a result.

This record outputted a file called perf.data which can be analysed using
perf report.
::

  $ perf report
  # Samples: 30922
  #
  # Overhead    Command                     Shared Object
  # ........  .........  ................................
  #
      87.27%  hackbench  [vdso]
       6.85%  hackbench  /lib/i686/cmov/libc-2.9.so
       2.62%  hackbench  /lib/ld-2.9.so
       1.52%       perf  [vdso]
       1.22%  hackbench  ./hackbench
       0.48%  hackbench  [kernel]
       0.02%       perf  /lib/i686/cmov/libc-2.9.so
       0.01%       perf  /usr/bin/perf
       0.01%       perf  /lib/ld-2.9.so
       0.00%  hackbench  /lib/i686/cmov/libpthread-2.9.so
  #
  # (For more details, try: perf report --sort comm,dso,symbol)
  #

Xorg allocation event의 library와 symbol 찾기

263-309

단순 binary의 VDSO 편향을 피하기 위해 문서는 page allocation을 매우 많이 만들던 X process를 다른 예로 분석합니다.

`perf record -c 1 -f`에 X의 PID를 지정해 몇 초 동안 event를 기록합니다.

  $ perf record -c 1 -f \
		-e kmem:mm_page_alloc -e kmem:mm_page_free \
		-e kmem:mm_page_free_batched \
		-p `pidof X`

첫 report에서는 sample의 51.95%가 `[vdso]`, 47.95%가 `libpixman-1.so.0.13.1`에 있으므로 거의 절반이 library에서 발생했음을 알 수 있습니다.

  $ perf report
  # Samples: 27666
  #
  # Overhead  Command                            Shared Object
  # ........  .......  .......................................
  #
      51.95%     Xorg  [vdso]
      47.95%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1
       0.09%     Xorg  /lib/i686/cmov/libc-2.9.so
       0.01%     Xorg  [kernel]
  #
  # (For more details, try: perf report --sort comm,dso,symbol)

어느 symbol인지 확인하려고 `perf report --sort comm,dso,symbol`을 실행하면 pixman library의 `pixmanFillsse2`가 47.93%를 차지합니다. libc의 `_int_malloc`, kernel의 `read_hpet`, `ftrace_trace_userstack` 같은 symbol도 그대로 표시됩니다.

  $ perf report --sort comm,dso,symbol
  # Samples: 27666
  #
  # Overhead  Command                            Shared Object  Symbol
  # ........  .......  .......................................  ......
  #
      51.95%     Xorg  [vdso]                                   [.] 0x000000ffffe424
      47.93%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1  [.] pixmanFillsse2
       0.09%     Xorg  /lib/i686/cmov/libc-2.9.so               [.] _int_malloc
       0.01%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1  [.] pixman_region32_copy_f
       0.01%     Xorg  [kernel]                                 [k] read_hpet
       0.01%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1  [.] get_fast_path
       0.00%     Xorg  [kernel]                                 [k] ftrace_trace_userstack
Xorg event 귀속
단계주요 결과
Shared Objectlibpixman 47.95%
SymbolpixmanFillsse2 47.93%
Other_int_malloc 0.09%, kernel symbols 0.01%

Shared Object에서 symbol까지 분석 범위를 좁힙니다.

According to this, the vast majority of events triggered on events
within the VDSO. With simple binaries, this will often be the case so let's
take a slightly different example. In the course of writing this, it was
noticed that X was generating an insane amount of page allocations so let's look
at it:
::

  $ perf record -c 1 -f \
		-e kmem:mm_page_alloc -e kmem:mm_page_free \
		-e kmem:mm_page_free_batched \
		-p `pidof X`

This was interrupted after a few seconds and
::

  $ perf report
  # Samples: 27666
  #
  # Overhead  Command                            Shared Object
  # ........  .......  .......................................
  #
      51.95%     Xorg  [vdso]
      47.95%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1
       0.09%     Xorg  /lib/i686/cmov/libc-2.9.so
       0.01%     Xorg  [kernel]
  #
  # (For more details, try: perf report --sort comm,dso,symbol)
  #

So, almost half of the events are occurring in a library. To get an idea which
symbol:
::

  $ perf report --sort comm,dso,symbol
  # Samples: 27666
  #
  # Overhead  Command                            Shared Object  Symbol
  # ........  .......  .......................................  ......
  #
      51.95%     Xorg  [vdso]                                   [.] 0x000000ffffe424
      47.93%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1  [.] pixmanFillsse2
       0.09%     Xorg  /lib/i686/cmov/libc-2.9.so               [.] _int_malloc
       0.01%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1  [.] pixman_region32_copy_f
       0.01%     Xorg  [kernel]                                 [k] read_hpet
       0.01%     Xorg  /opt/gfx-test/lib/libpixman-1.so.0.13.1  [.] get_fast_path
       0.00%     Xorg  [kernel]                                 [k] ftrace_trace_userstack

perf annotate로 instruction hotspot 확인

310-338

`pixmanFillsse2` 안의 어느 instruction에서 문제가 나타나는지 `perf annotate pixmanFillsse2`로 확인합니다.

  $ perf annotate pixmanFillsse2
  [ ... ]
    0.00 :         34eeb:       0f 18 08                prefetcht0 (%eax)
         :      }
         :
         :      extern __inline void __attribute__((__gnu_inline__, __always_inline__, _
         :      _mm_store_si128 (__m128i *__P, __m128i __B) :      {
         :        *__P = __B;
   12.40 :         34eee:       66 0f 7f 80 40 ff ff    movdqa %xmm0,-0xc0(%eax)
    0.00 :         34ef5:       ff
   12.40 :         34ef6:       66 0f 7f 80 50 ff ff    movdqa %xmm0,-0xb0(%eax)
    0.00 :         34efd:       ff
   12.39 :         34efe:       66 0f 7f 80 60 ff ff    movdqa %xmm0,-0xa0(%eax)
    0.00 :         34f05:       ff
   12.67 :         34f06:       66 0f 7f 80 70 ff ff    movdqa %xmm0,-0x90(%eax)
    0.00 :         34f0d:       ff
   12.58 :         34f0e:       66 0f 7f 40 80          movdqa %xmm0,-0x80(%eax)
   12.31 :         34f13:       66 0f 7f 40 90          movdqa %xmm0,-0x70(%eax)
   12.40 :         34f18:       66 0f 7f 40 a0          movdqa %xmm0,-0x60(%eax)
   12.31 :         34f1d:       66 0f 7f 40 b0          movdqa %xmm0,-0x50(%eax)

annotate 결과에서 연속된 `movdqa` store instruction마다 약 12.31~12.67%가 귀속됩니다. 얼핏 보면 pixmap을 card로 복사하는 데 시간이 소비됩니다.

pixmap이 왜 이렇게 많이 복사되는지는 추가 조사가 필요하지만, 출발점은 수개월 전 잊힌 채 library path에 남아 있던 오래된 libpixmap build를 제거하는 것입니다.

annotate 결론
증거판단
pixmanFillsse2 47.93%library hotspot
movdqa stores about 12% eachpixmap copy path 집중
old libpixmap in library path우선 제거·재검증할 환경 요인

event를 instruction과 운영 환경 문제까지 연결합니다.

tracepoint에서 수정 후보까지
Page allocation tracepointXorg process
perf reportlibpixman
symbol sortpixmanFillsse2
perf annotatemovdqa copy instructions
Environment reviewstale library build

단계별 evidence가 구체적인 조사 출발점을 만듭니다.

To see where within the function pixmanFillsse2 things are going wrong:
::

  $ perf annotate pixmanFillsse2
  [ ... ]
    0.00 :         34eeb:       0f 18 08                prefetcht0 (%eax)
         :      }
         :
         :      extern __inline void __attribute__((__gnu_inline__, __always_inline__, _
         :      _mm_store_si128 (__m128i *__P, __m128i __B) :      {
         :        *__P = __B;
   12.40 :         34eee:       66 0f 7f 80 40 ff ff    movdqa %xmm0,-0xc0(%eax)
    0.00 :         34ef5:       ff
   12.40 :         34ef6:       66 0f 7f 80 50 ff ff    movdqa %xmm0,-0xb0(%eax)
    0.00 :         34efd:       ff
   12.39 :         34efe:       66 0f 7f 80 60 ff ff    movdqa %xmm0,-0xa0(%eax)
    0.00 :         34f05:       ff
   12.67 :         34f06:       66 0f 7f 80 70 ff ff    movdqa %xmm0,-0x90(%eax)
    0.00 :         34f0d:       ff
   12.58 :         34f0e:       66 0f 7f 40 80          movdqa %xmm0,-0x80(%eax)
   12.31 :         34f13:       66 0f 7f 40 90          movdqa %xmm0,-0x70(%eax)
   12.40 :         34f18:       66 0f 7f 40 a0          movdqa %xmm0,-0x60(%eax)
   12.31 :         34f1d:       66 0f 7f 40 b0          movdqa %xmm0,-0x50(%eax)

At a glance, it looks like the time is being spent copying pixmaps to
the card.  Further investigation would be needed to determine why pixmaps
are being copied around so much but a starting point would be to take an
ancient build of libpixmap out of the library path where it was totally
forgotten about from months ago!