← Documents Documentation/arch/s390/s390dbf.rst GitHub 원문 ↗

Linux 6.18.37 · Architecture

S390 Debug Feature

S390 DBF의 메모리 ring log 설계, event·exception API, debugfs/sysctl 제어, crash 조사와 사용자 정의 view callback입니다.

Source pathDocumentation/arch/s390/s390dbf.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약과 해설

s390dbf.rst:1-478

S390 DBF는 component별로 여러 연속-page debug area를 두고 한 area를 active log로 사용합니다. 일반 event는 같은 area에서 wrap하지만 exception은 다음 area로 이동해 직전 원인 record가 덮어써질 가능성을 줄입니다.

live system에서는 `/sys/kernel/debug/s390dbf`의 view, level, flush, pages 파일과 `/proc/sys/s390dbf`의 전역 sysctl로 조사·제어합니다. crash dump와 raw memory에서도 log를 복구할 수 있고, `debug_view` callback으로 출력 형식과 입력 동작을 확장할 수 있습니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 ==================
2 S390 Debug Feature
3 ==================
4
5 files:
6 - arch/s390/kernel/debug.c
7 - arch/s390/include/asm/debug.h
8
9 Description:
10 ------------
11 The goal of this feature is to provide a kernel debug logging API
12 where log records can be stored efficiently in memory, where each component
13 (e.g. device drivers) can have one separate debug log.
14 One purpose of this is to inspect the debug logs after a production system crash
15 in order to analyze the reason for the crash.
16
17 If the system still runs but only a subcomponent which uses dbf fails,
18 it is possible to look at the debug logs on a live system via the Linux
19 debugfs filesystem.
20
21 The debug feature may also very useful for kernel and driver development.
22
23 Design:
24 -------
25 Kernel components (e.g. device drivers) can register themselves at the debug
26 feature with the function call :c:func:`debug_register()`.
27 This function initializes a
28 debug log for the caller. For each debug log exists a number of debug areas
29 where exactly one is active at one time. Each debug area consists of contiguous
30 pages in memory. In the debug areas there are stored debug entries (log records)
31 which are written by event- and exception-calls.
32
33 An event-call writes the specified debug entry to the active debug
34 area and updates the log pointer for the active area. If the end
35 of the active debug area is reached, a wrap around is done (ring buffer)
36 and the next debug entry will be written at the beginning of the active
37 debug area.
38
39 An exception-call writes the specified debug entry to the log and
40 switches to the next debug area. This is done in order to be sure
41 that the records which describe the origin of the exception are not
42 overwritten when a wrap around for the current area occurs.
43
44 The debug areas themselves are also ordered in form of a ring buffer.
45 When an exception is thrown in the last debug area, the following debug
46 entries are then written again in the very first area.
47
48 There are four versions for the event- and exception-calls: One for
49 logging raw data, one for text, one for numbers (unsigned int and long),
50 and one for sprintf-like formatted strings.
51
52 Each debug entry contains the following data:
53
54 - Timestamp
55 - Cpu-Number of calling task
56 - Level of debug entry (0...6)
57 - Return Address to caller
58 - Flag, if entry is an exception or not
59
60 The debug logs can be inspected in a live system through entries in
61 the debugfs-filesystem. Under the toplevel directory "``s390dbf``" there is
62 a directory for each registered component, which is named like the
63 corresponding component. The debugfs normally should be mounted to
64 ``/sys/kernel/debug`` therefore the debug feature can be accessed under
65 ``/sys/kernel/debug/s390dbf``.
66
67 The content of the directories are files which represent different views
68 to the debug log. Each component can decide which views should be
69 used through registering them with the function :c:func:`debug_register_view()`.
70 Predefined views for hex/ascii and sprintf data are provided.
71 It is also possible to define other views. The content of
72 a view can be inspected simply by reading the corresponding debugfs file.
73
74 All debug logs have an actual debug level (range from 0 to 6).
75 The default level is 3. Event and Exception functions have a :c:data:`level`
76 parameter. Only debug entries with a level that is lower or equal
77 than the actual level are written to the log. This means, when
78 writing events, high priority log entries should have a low level
79 value whereas low priority entries should have a high one.
80 The actual debug level can be changed with the help of the debugfs-filesystem
81 through writing a number string "x" to the ``level`` debugfs file which is
82 provided for every debug log. Debugging can be switched off completely
83 by using "-" on the ``level`` debugfs file.
84
85 Example::
86
87 > echo "-" > /sys/kernel/debug/s390dbf/dasd/level
88
89 It is also possible to deactivate the debug feature globally for every
90 debug log. You can change the behavior using 2 sysctl parameters in
91 ``/proc/sys/s390dbf``:
92
93 There are currently 2 possible triggers, which stop the debug feature
94 globally. The first possibility is to use the ``debug_active`` sysctl. If
95 set to 1 the debug feature is running. If ``debug_active`` is set to 0 the
96 debug feature is turned off.
97
98 The second trigger which stops the debug feature is a kernel oops.
99 That prevents the debug feature from overwriting debug information that
100 happened before the oops. After an oops you can reactivate the debug feature
101 by piping 1 to ``/proc/sys/s390dbf/debug_active``. Nevertheless, it's not
102 suggested to use an oopsed kernel in a production environment.
103
104 If you want to disallow the deactivation of the debug feature, you can use
105 the ``debug_stoppable`` sysctl. If you set ``debug_stoppable`` to 0 the debug
106 feature cannot be stopped. If the debug feature is already stopped, it
107 will stay deactivated.
108
109 Kernel Interfaces:
110 ------------------
111
112 .. kernel-doc:: arch/s390/kernel/debug.c
113 .. kernel-doc:: arch/s390/include/asm/debug.h
114
115 Predefined views:
116 -----------------
117
118 .. code-block:: c
119
120 extern struct debug_view debug_hex_ascii_view;
121
122 extern struct debug_view debug_sprintf_view;
123
124 Examples
125 --------
126
127 .. code-block:: c
128
129 /*
130 * hex_ascii-view Example
131 */
132
133 #include <linux/init.h>
134 #include <asm/debug.h>
135
136 static debug_info_t *debug_info;
137
138 static int init(void)
139 {
140 /* register 4 debug areas with one page each and 4 byte data field */
141
142 debug_info = debug_register("test", 1, 4, 4 );
143 debug_register_view(debug_info, &debug_hex_ascii_view);
144
145 debug_text_event(debug_info, 4 , "one ");
146 debug_int_exception(debug_info, 4, 4711);
147 debug_event(debug_info, 3, &debug_info, 4);
148
149 return 0;
150 }
151
152 static void cleanup(void)
153 {
154 debug_unregister(debug_info);
155 }
156
157 module_init(init);
158 module_exit(cleanup);
159
160 .. code-block:: c
161
162 /*
163 * sprintf-view Example
164 */
165
166 #include <linux/init.h>
167 #include <asm/debug.h>
168
169 static debug_info_t *debug_info;
170
171 static int init(void)
172 {
173 /* register 4 debug areas with one page each and data field for */
174 /* format string pointer + 2 varargs (= 3 * sizeof(long)) */
175
176 debug_info = debug_register("test", 1, 4, sizeof(long) * 3);
177 debug_register_view(debug_info, &debug_sprintf_view);
178
179 debug_sprintf_event(debug_info, 2 , "first event in %s:%i\n",__FILE__,__LINE__);
180 debug_sprintf_exception(debug_info, 1, "pointer to debug info: %p\n",&debug_info);
181
182 return 0;
183 }
184
185 static void cleanup(void)
186 {
187 debug_unregister(debug_info);
188 }
189
190 module_init(init);
191 module_exit(cleanup);
192
193 Debugfs Interface
194 -----------------
195 Views to the debug logs can be investigated through reading the corresponding
196 debugfs-files:
197
198 Example::
199
200 > ls /sys/kernel/debug/s390dbf/dasd
201 flush hex_ascii level pages
202 > cat /sys/kernel/debug/s390dbf/dasd/hex_ascii | sort -k2,2 -s
203 00 00974733272:680099 2 - 02 0006ad7e 07 ea 4a 90 | ....
204 00 00974733272:682210 2 - 02 0006ade6 46 52 45 45 | FREE
205 00 00974733272:682213 2 - 02 0006adf6 07 ea 4a 90 | ....
206 00 00974733272:682281 1 * 02 0006ab08 41 4c 4c 43 | EXCP
207 01 00974733272:682284 2 - 02 0006ab16 45 43 4b 44 | ECKD
208 01 00974733272:682287 2 - 02 0006ab28 00 00 00 04 | ....
209 01 00974733272:682289 2 - 02 0006ab3e 00 00 00 20 | ...
210 01 00974733272:682297 2 - 02 0006ad7e 07 ea 4a 90 | ....
211 01 00974733272:684384 2 - 00 0006ade6 46 52 45 45 | FREE
212 01 00974733272:684388 2 - 00 0006adf6 07 ea 4a 90 | ....
213
214 See section about predefined views for explanation of the above output!
215
216 Changing the debug level
217 ------------------------
218
219 Example::
220
221
222 > cat /sys/kernel/debug/s390dbf/dasd/level
223 3
224 > echo "5" > /sys/kernel/debug/s390dbf/dasd/level
225 > cat /sys/kernel/debug/s390dbf/dasd/level
226 5
227
228 Flushing debug areas
229 --------------------
230 Debug areas can be flushed with piping the number of the desired
231 area (0...n) to the debugfs file "flush". When using "-" all debug areas
232 are flushed.
233
234 Examples:
235
236 1. Flush debug area 0::
237
238 > echo "0" > /sys/kernel/debug/s390dbf/dasd/flush
239
240 2. Flush all debug areas::
241
242 > echo "-" > /sys/kernel/debug/s390dbf/dasd/flush
243
244 Changing the size of debug areas
245 ------------------------------------
246 It is possible the change the size of debug areas through piping
247 the number of pages to the debugfs file "pages". The resize request will
248 also flush the debug areas.
249
250 Example:
251
252 Define 4 pages for the debug areas of debug feature "dasd"::
253
254 > echo "4" > /sys/kernel/debug/s390dbf/dasd/pages
255
256 Stopping the debug feature
257 --------------------------
258 Example:
259
260 1. Check if stopping is allowed::
261
262 > cat /proc/sys/s390dbf/debug_stoppable
263
264 2. Stop debug feature::
265
266 > echo 0 > /proc/sys/s390dbf/debug_active
267
268 crash Interface
269 ----------------
270 The ``crash`` tool since v5.1.0 has a built-in command
271 ``s390dbf`` to display all the debug logs or export them to the file system.
272 With this tool it is possible
273 to investigate the debug logs on a live system and with a memory dump after
274 a system crash.
275
276 Investigating raw memory
277 ------------------------
278 One last possibility to investigate the debug logs at a live
279 system and after a system crash is to look at the raw memory
280 under VM or at the Service Element.
281 It is possible to find the anchor of the debug-logs through
282 the ``debug_area_first`` symbol in the System map. Then one has
283 to follow the correct pointers of the data-structures defined
284 in debug.h and find the debug-areas in memory.
285 Normally modules which use the debug feature will also have
286 a global variable with the pointer to the debug-logs. Following
287 this pointer it will also be possible to find the debug logs in
288 memory.
289
290 For this method it is recommended to use '16 * x + 4' byte (x = 0..n)
291 for the length of the data field in :c:func:`debug_register()` in
292 order to see the debug entries well formatted.
293
294
295 Predefined Views
296 ----------------
297
298 There are two predefined views: hex_ascii and sprintf.
299 The hex_ascii view shows the data field in hex and ascii representation
300 (e.g. ``45 43 4b 44 | ECKD``).
301
302 The sprintf view formats the debug entries in the same way as the sprintf
303 function would do. The sprintf event/exception functions write to the
304 debug entry a pointer to the format string (size = sizeof(long))
305 and for each vararg a long value. So e.g. for a debug entry with a format
306 string plus two varargs one would need to allocate a (3 * sizeof(long))
307 byte data area in the debug_register() function.
308
309 IMPORTANT:
310 Using "%s" in sprintf event functions is dangerous. You can only
311 use "%s" in the sprintf event functions, if the memory for the passed string
312 is available as long as the debug feature exists. The reason behind this is
313 that due to performance considerations only a pointer to the string is stored
314 in the debug feature. If you log a string that is freed afterwards, you will
315 get an OOPS when inspecting the debug feature, because then the debug feature
316 will access the already freed memory.
317
318 NOTE:
319 If using the sprintf view do NOT use other event/exception functions
320 than the sprintf-event and -exception functions.
321
322 The format of the hex_ascii and sprintf view is as follows:
323
324 - Number of area
325 - Timestamp (formatted as seconds and microseconds since 00:00:00 Coordinated
326 Universal Time (UTC), January 1, 1970)
327 - level of debug entry
328 - Exception flag (* = Exception)
329 - Cpu-Number of calling task
330 - Return Address to caller
331 - data field
332
333 A typical line of the hex_ascii view will look like the following (first line
334 is only for explanation and will not be displayed when 'cating' the view)::
335
336 area time level exception cpu caller data (hex + ascii)
337 --------------------------------------------------------------------------
338 00 00964419409:440690 1 - 00 88023fe
339
340
341 Defining views
342 --------------
343
344 Views are specified with the 'debug_view' structure. There are defined
345 callback functions which are used for reading and writing the debugfs files:
346
347 .. code-block:: c
348
349 struct debug_view {
350 char name[DEBUG_MAX_PROCF_LEN];
351 debug_prolog_proc_t* prolog_proc;
352 debug_header_proc_t* header_proc;
353 debug_format_proc_t* format_proc;
354 debug_input_proc_t* input_proc;
355 void* private_data;
356 };
357
358 where:
359
360 .. code-block:: c
361
362 typedef int (debug_header_proc_t) (debug_info_t* id,
363 struct debug_view* view,
364 int area,
365 debug_entry_t* entry,
366 char* out_buf);
367
368 typedef int (debug_format_proc_t) (debug_info_t* id,
369 struct debug_view* view, char* out_buf,
370 const char* in_buf);
371 typedef int (debug_prolog_proc_t) (debug_info_t* id,
372 struct debug_view* view,
373 char* out_buf);
374 typedef int (debug_input_proc_t) (debug_info_t* id,
375 struct debug_view* view,
376 struct file* file, const char* user_buf,
377 size_t in_buf_size, loff_t* offset);
378
379
380 The "private_data" member can be used as pointer to view specific data.
381 It is not used by the debug feature itself.
382
383 The output when reading a debugfs file is structured like this::
384
385 "prolog_proc output"
386
387 "header_proc output 1" "format_proc output 1"
388 "header_proc output 2" "format_proc output 2"
389 "header_proc output 3" "format_proc output 3"
390 ...
391
392 When a view is read from the debugfs, the Debug Feature calls the
393 'prolog_proc' once for writing the prolog.
394 Then 'header_proc' and 'format_proc' are called for each
395 existing debug entry.
396
397 The input_proc can be used to implement functionality when it is written to
398 the view (e.g. like with ``echo "0" > /sys/kernel/debug/s390dbf/dasd/level``).
399
400 For header_proc there can be used the default function
401 :c:func:`debug_dflt_header_fn()` which is defined in debug.h.
402 and which produces the same header output as the predefined views.
403 E.g::
404
405 00 00964419409:440761 2 - 00 88023ec
406
407 In order to see how to use the callback functions check the implementation
408 of the default views!
409
410 Example:
411
412 .. code-block:: c
413
414 #include <asm/debug.h>
415
416 #define UNKNOWNSTR "data: %08x"
417
418 const char* messages[] =
419 {"This error...........\n",
420 "That error...........\n",
421 "Problem..............\n",
422 "Something went wrong.\n",
423 "Everything ok........\n",
424 NULL
425 };
426
427 static int debug_test_format_fn(
428 debug_info_t *id, struct debug_view *view,
429 char *out_buf, const char *in_buf
430 )
431 {
432 int i, rc = 0;
433
434 if (id->buf_size >= 4) {
435 int msg_nr = *((int*)in_buf);
436 if (msg_nr < sizeof(messages) / sizeof(char*) - 1)
437 rc += sprintf(out_buf, "%s", messages[msg_nr]);
438 else
439 rc += sprintf(out_buf, UNKNOWNSTR, msg_nr);
440 }
441 return rc;
442 }
443
444 struct debug_view debug_test_view = {
445 "myview", /* name of view */
446 NULL, /* no prolog */
447 &debug_dflt_header_fn, /* default header for each entry */
448 &debug_test_format_fn, /* our own format function */
449 NULL, /* no input function */
450 NULL /* no private data */
451 };
452
453 test:
454 =====
455
456 .. code-block:: c
457
458 debug_info_t *debug_info;
459 int i;
460 ...
461 debug_info = debug_register("test", 0, 4, 4);
462 debug_register_view(debug_info, &debug_test_view);
463 for (i = 0; i < 10; i ++)
464 debug_int_event(debug_info, 1, i);
465
466 ::
467
468 > cat /sys/kernel/debug/s390dbf/test/myview
469 00 00964419734:611402 1 - 00 88042ca This error...........
470 00 00964419734:611405 1 - 00 88042ca That error...........
471 00 00964419734:611408 1 - 00 88042ca Problem..............
472 00 00964419734:611411 1 - 00 88042ca Something went wrong.
473 00 00964419734:611414 1 - 00 88042ca Everything ok........
474 00 00964419734:611417 1 - 00 88042ca data: 00000005
475 00 00964419734:611419 1 - 00 88042ca data: 00000006
476 00 00964419734:611422 1 - 00 88042ca data: 00000007
477 00 00964419734:611425 1 - 00 88042ca data: 00000008
478 00 00964419734:611428 1 - 00 88042ca data: 00000009
479

3. 한국어 전문 번역

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

S390 Debug Feature

1-8

S390 Debug Feature의 구현과 공개 인터페이스는 `arch/s390/kernel/debug.c`와 `arch/s390/include/asm/debug.h`에 있습니다.

목적과 사용 환경

9-22

이 기능의 목표는 log record를 메모리에 효율적으로 저장하고 device driver 같은 각 component가 별도의 debug log를 가질 수 있게 하는 kernel debug logging API를 제공하는 것입니다.

주요 용도는 production system crash 뒤 debug log를 조사해 crash 원인을 분석하는 것입니다. 시스템은 계속 실행 중이지만 DBF를 사용하는 하위 component만 실패한 경우에는 Linux debugfs filesystem을 통해 live system에서 log를 볼 수 있습니다.

kernel 및 driver 개발에도 이 debug feature를 활용할 수 있습니다.

debug area와 ring buffer 설계

23-51

kernel component는 `debug_register()`를 호출해 자신을 debug feature에 등록합니다. 이 함수는 호출자를 위한 debug log를 초기화합니다. 각 log에는 여러 debug area가 있고 그중 정확히 하나만 한 시점에 active 상태입니다. 각 area는 메모리의 연속 page로 구성되며 event 호출과 exception 호출이 기록하는 debug entry를 저장합니다.

event 호출은 지정한 entry를 active area에 쓰고 해당 area의 log pointer를 갱신합니다. active area 끝에 도달하면 ring buffer처럼 처음으로 wrap around하여 다음 entry를 active area 시작에 씁니다.

exception 호출은 entry를 log에 쓴 뒤 다음 debug area로 전환합니다. 이는 현재 area가 wrap around하더라도 exception의 원인을 설명하는 record가 덮어써지지 않게 합니다. debug area 자체도 ring buffer로 배열되므로 마지막 area에서 exception이 발생하면 이후 entry는 첫 area에 다시 기록됩니다.

event와 exception 호출은 raw data, text, `unsigned int`와 `long` 숫자, sprintf 형식 문자열을 위한 네 종류로 제공됩니다.

S390 DBF 기록 전환
event 호출active area에 entry 기록끝이면 같은 area 처음으로 wrap
exception 호출active area에 entry 기록다음 debug area로 전환
마지막 area에서 exception첫 debug area로 순환이후 entry 기록 계속

일반 event와 exception이 active debug area를 다루는 방식의 차이입니다.

entry 필드와 debugfs view

52-73

각 debug entry에는 다음 정보가 들어 있습니다.

  • Timestamp
  • 호출 task의 CPU 번호
  • debug entry level `0...6`
  • 호출자로 돌아갈 return address
  • entry가 exception인지 나타내는 flag

live system에서는 debugfs 항목으로 log를 조사합니다. 최상위 `s390dbf` 아래에 등록된 component마다 같은 이름의 디렉터리가 생깁니다. debugfs는 보통 `/sys/kernel/debug`에 mount하므로 접근 경로는 `/sys/kernel/debug/s390dbf`입니다.

component 디렉터리의 파일은 debug log를 표현하는 서로 다른 view입니다. component는 `debug_register_view()`로 사용할 view를 등록합니다. hex/ascii와 sprintf 데이터를 위한 사전 정의 view가 제공되며 사용자 정의 view도 만들 수 있습니다. 대응 debugfs 파일을 읽으면 view 내용을 확인할 수 있습니다.

debug level과 전역 중지 제어

74-108

모든 debug log의 현재 level 범위는 0부터 6이며 기본값은 3입니다. event와 exception 함수의 `level` 인수가 현재 level 이하일 때만 entry가 기록됩니다. 따라서 우선순위가 높은 log entry에는 낮은 level 값을, 우선순위가 낮은 entry에는 높은 값을 사용해야 합니다.

각 log의 `level` debugfs 파일에 숫자 문자열 `x`를 쓰면 현재 level을 바꿉니다. `-`를 쓰면 해당 log의 debugging을 완전히 끕니다.

> echo "-" > /sys/kernel/debug/s390dbf/dasd/level

모든 debug log를 전역으로 비활성화하는 동작은 `/proc/sys/s390dbf`의 두 sysctl로 제어합니다. `debug_active=1`이면 실행 중이고 `debug_active=0`이면 꺼집니다.

두 번째 전역 중지 trigger는 kernel oops입니다. oops 전의 debug 정보를 덮어쓰지 않도록 기록을 멈춥니다. 이후 `/proc/sys/s390dbf/debug_active`에 1을 쓰면 다시 활성화할 수 있지만, oops가 발생한 kernel을 production 환경에서 계속 사용하는 것은 권장하지 않습니다.

`debug_stoppable=0`으로 설정하면 debug feature의 비활성화를 금지합니다. 단, 이미 중지된 상태라면 비활성 상태를 유지합니다.

kernel-doc와 사전 정의 view

109-123

kernel interface 문서는 `arch/s390/kernel/debug.c`와 `arch/s390/include/asm/debug.h`에서 kernel-doc로 가져옵니다.

사전 정의 view 두 개는 다음과 같이 선언됩니다.

extern struct debug_view debug_hex_ascii_view;

extern struct debug_view debug_sprintf_view;

hex_ascii와 sprintf 등록 예제

124-192

첫 번째 예제는 각각 한 page인 debug area 4개와 4 byte data field를 가진 `test` log를 등록하고 `debug_hex_ascii_view`를 연결합니다. 이어 text event, integer exception, raw event를 기록하고 module 정리 시 unregister합니다.

/*
 * hex_ascii-view Example
 */

#include <linux/init.h>
#include <asm/debug.h>

static debug_info_t *debug_info;

static int init(void)
{
    /* register 4 debug areas with one page each and 4 byte data field */

    debug_info = debug_register("test", 1, 4, 4 );
    debug_register_view(debug_info, &debug_hex_ascii_view);

    debug_text_event(debug_info, 4 , "one ");
    debug_int_exception(debug_info, 4, 4711);
    debug_event(debug_info, 3, &debug_info, 4);

    return 0;
}

static void cleanup(void)
{
    debug_unregister(debug_info);
}

module_init(init);
module_exit(cleanup);

두 번째 예제는 format string pointer와 vararg 두 개를 담도록 `sizeof(long) * 3` 크기의 data field를 만들고 `debug_sprintf_view`를 연결합니다. `debug_sprintf_event()`와 `debug_sprintf_exception()`이 형식화 entry를 기록합니다.

/*
 * sprintf-view Example
 */

#include <linux/init.h>
#include <asm/debug.h>

static debug_info_t *debug_info;

static int init(void)
{
    /* register 4 debug areas with one page each and data field for */
    /* format string pointer + 2 varargs (= 3 * sizeof(long))       */

    debug_info = debug_register("test", 1, 4, sizeof(long) * 3);
    debug_register_view(debug_info, &debug_sprintf_view);

    debug_sprintf_event(debug_info, 2 , "first event in %s:%i\n",__FILE__,__LINE__);
    debug_sprintf_exception(debug_info, 1, "pointer to debug info: %p\n",&debug_info);

    return 0;
}

static void cleanup(void)
{
    debug_unregister(debug_info);
}

module_init(init);
module_exit(cleanup);

debugfs log 조회

193-215

대응 debugfs 파일을 읽어 debug log view를 조사합니다. 다음 예는 DASD log 디렉터리의 `flush`, `hex_ascii`, `level`, `pages` 파일을 나열하고, `hex_ascii` view를 timestamp 열 기준으로 안정 정렬해 표시합니다.

> ls /sys/kernel/debug/s390dbf/dasd
flush  hex_ascii  level pages
> cat /sys/kernel/debug/s390dbf/dasd/hex_ascii | sort -k2,2 -s
00 00974733272:680099 2 - 02 0006ad7e  07 ea 4a 90 | ....
00 00974733272:682210 2 - 02 0006ade6  46 52 45 45 | FREE
00 00974733272:682213 2 - 02 0006adf6  07 ea 4a 90 | ....
00 00974733272:682281 1 * 02 0006ab08  41 4c 4c 43 | EXCP
01 00974733272:682284 2 - 02 0006ab16  45 43 4b 44 | ECKD
01 00974733272:682287 2 - 02 0006ab28  00 00 00 04 | ....
01 00974733272:682289 2 - 02 0006ab3e  00 00 00 20 | ...
01 00974733272:682297 2 - 02 0006ad7e  07 ea 4a 90 | ....
01 00974733272:684384 2 - 00 0006ade6  46 52 45 45 | FREE
01 00974733272:684388 2 - 00 0006adf6  07 ea 4a 90 | ....

각 출력 열의 의미는 아래 사전 정의 view 절에서 설명합니다.

debug level 변경

216-227

다음 예는 DASD debug log의 현재 level 3을 확인한 뒤 5를 쓰고 변경 결과를 다시 확인합니다.

> cat /sys/kernel/debug/s390dbf/dasd/level
3
> echo "5" > /sys/kernel/debug/s390dbf/dasd/level
> cat /sys/kernel/debug/s390dbf/dasd/level
5

debug area 비우기

228-243

`flush` debugfs 파일에 원하는 area 번호 `0...n`을 쓰면 해당 area를 비웁니다. `-`를 쓰면 모든 debug area를 비웁니다.

작업명령
debug area 0 비우기`> echo "0" > /sys/kernel/debug/s390dbf/dasd/flush`
모든 debug area 비우기`> echo "-" > /sys/kernel/debug/s390dbf/dasd/flush`

debug area 크기 변경

244-255

`pages` debugfs 파일에 page 수를 쓰면 debug area 크기를 바꿀 수 있습니다. resize 요청은 debug area도 함께 비웁니다. 다음 예는 `dasd` debug feature의 area 크기를 4 page로 정합니다.

> echo "4" > /sys/kernel/debug/s390dbf/dasd/pages

debug feature 중지

256-267

중지 허용 여부는 `debug_stoppable`에서 확인하고, `debug_active`에 0을 쓰면 debug feature를 중지합니다.

> cat /proc/sys/s390dbf/debug_stoppable
> echo 0 > /proc/sys/s390dbf/debug_active

crash 도구 인터페이스

268-275

`crash` 도구는 v5.1.0부터 모든 debug log를 표시하거나 filesystem으로 export하는 내장 명령 `s390dbf`를 제공합니다. live system뿐 아니라 system crash 뒤 memory dump에서도 log를 조사할 수 있습니다.

raw memory 조사

276-294

live system과 system crash 뒤 VM 또는 Service Element에서 raw memory를 직접 확인하는 방법도 있습니다. System map의 `debug_area_first` symbol로 debug log anchor를 찾은 뒤 `debug.h`에 정의된 data structure의 올바른 pointer를 따라가 debug area를 찾습니다.

debug feature를 사용하는 module은 보통 debug log pointer를 담은 global variable도 가지므로 이 pointer를 따라가 log를 찾을 수 있습니다.

이 방법으로 entry를 보기 좋게 정렬하려면 `debug_register()`의 data field 길이에 `16 * x + 4` byte(`x = 0..n`)를 사용하는 것이 권장됩니다.

사전 정의 hex_ascii와 sprintf view

295-321

사전 정의 view는 `hex_ascii`와 `sprintf` 두 개입니다. `hex_ascii`는 data field를 `45 43 4b 44 | ECKD`처럼 16진수와 ASCII로 표시합니다.

`sprintf` view는 `sprintf` 함수와 같은 방식으로 entry를 형식화합니다. sprintf event/exception 함수는 format string pointer 하나와 각 vararg의 `long` 값을 entry에 씁니다. 따라서 format string과 vararg 두 개를 담으려면 `debug_register()`에서 `3 * sizeof(long)` byte data area를 할당해야 합니다.

sprintf event 함수의 `%s` 사용은 위험합니다. 성능을 위해 문자열 자체가 아니라 pointer만 저장하므로, 전달한 문자열 메모리가 debug feature의 전체 수명 동안 유지될 때만 `%s`를 사용할 수 있습니다. 이후 해제된 문자열을 기록하면 view 조사 중 이미 해제된 메모리에 접근해 OOPS가 발생합니다.

`sprintf` view를 사용할 때는 sprintf event와 sprintf exception 함수 이외의 event/exception 함수를 사용하지 마십시오.

view 출력 형식

322-340

`hex_ascii`와 `sprintf` view는 다음 필드를 순서대로 출력합니다.

순서필드
1area 번호
21970-01-01 00:00:00 UTC 이후의 초와 마이크로초로 표시한 timestamp
3debug entry level
4exception flag, `*`는 exception
5호출 task의 CPU 번호
6호출자로 돌아갈 return address
7data field

다음은 전형적인 `hex_ascii` 한 줄입니다. 첫 줄의 field 이름은 설명을 위한 것이며 실제 view를 `cat`할 때는 표시되지 않습니다.

area  time           level exception cpu caller    data (hex + ascii)
--------------------------------------------------------------------------
00    00964419409:440690 1 -         00  88023fe

debug_view와 callback 형식

341-381

view는 `debug_view` 구조체로 지정하며 debugfs 파일의 읽기와 쓰기에 사용할 callback을 담습니다.

struct debug_view {
      char name[DEBUG_MAX_PROCF_LEN];
      debug_prolog_proc_t* prolog_proc;
      debug_header_proc_t* header_proc;
      debug_format_proc_t* format_proc;
      debug_input_proc_t*  input_proc;
      void*                private_data;
};

각 callback type은 다음 원형을 사용합니다.

typedef int (debug_header_proc_t) (debug_info_t* id,
                                   struct debug_view* view,
                                   int area,
                                   debug_entry_t* entry,
                                   char* out_buf);

typedef int (debug_format_proc_t) (debug_info_t* id,
                                   struct debug_view* view, char* out_buf,
                                   const char* in_buf);
typedef int (debug_prolog_proc_t) (debug_info_t* id,
                                   struct debug_view* view,
                                   char* out_buf);
typedef int (debug_input_proc_t) (debug_info_t* id,
                                  struct debug_view* view,
                                  struct file* file, const char* user_buf,
                                  size_t in_buf_size, loff_t* offset);

`private_data` member는 view 전용 data를 가리키는 pointer로 사용할 수 있으며 debug feature 자체는 이 값을 사용하지 않습니다.

view 읽기·쓰기 callback 흐름

382-409

debugfs 파일을 읽을 때 출력은 다음 구조로 생성됩니다.

debug_view 출력 조립
`prolog_proc` 1회prolog 출력entry 순회 시작
`header_proc`entry header`format_proc` data 출력
다음 entry`header_proc``format_proc`반복

prolog는 한 번, header와 format callback은 기존 debug entry마다 한 번씩 호출됩니다.

view를 읽으면 Debug Feature가 `prolog_proc`를 한 번 호출해 prolog를 쓰고, 각 기존 debug entry마다 `header_proc`와 `format_proc`를 호출합니다.

`input_proc`는 view에 데이터를 쓸 때 동작을 구현합니다. 예를 들어 `echo "0" > /sys/kernel/debug/s390dbf/dasd/level` 같은 입력을 처리할 수 있습니다.

`header_proc`에는 `debug.h`의 기본 함수 `debug_dflt_header_fn()`을 사용할 수 있습니다. 이 함수는 사전 정의 view와 같은 header 출력을 만듭니다.

00 00964419409:440761 2 - 00 88023ec

callback 사용법은 기본 view 구현을 참고하십시오.

사용자 정의 view 예제

410-452

예제의 `debug_test_format_fn()`은 data field의 첫 4 byte를 message 번호로 읽습니다. 번호가 `messages` 배열 범위 안이면 대응 문자열을 출력하고, 범위를 벗어나면 `UNKNOWNSTR` 형식으로 번호를 출력합니다.

`debug_test_view`는 이름을 `myview`로 지정하고 prolog와 input callback 없이 기본 header callback과 사용자 정의 format callback을 연결합니다.

#include <asm/debug.h>

#define UNKNOWNSTR "data: %08x"

const char* messages[] =
{"This error...........\n",
 "That error...........\n",
 "Problem..............\n",
 "Something went wrong.\n",
 "Everything ok........\n",
 NULL
};

static int debug_test_format_fn(
   debug_info_t *id, struct debug_view *view,
   char *out_buf, const char *in_buf
)
{
  int i, rc = 0;

  if (id->buf_size >= 4) {
     int msg_nr = *((int*)in_buf);
     if (msg_nr < sizeof(messages) / sizeof(char*) - 1)
        rc += sprintf(out_buf, "%s", messages[msg_nr]);
     else
        rc += sprintf(out_buf, UNKNOWNSTR, msg_nr);
  }
  return rc;
}

struct debug_view debug_test_view = {
  "myview",                 /* name of view */
  NULL,                     /* no prolog */
  &debug_dflt_header_fn,    /* default header for each entry */
  &debug_test_format_fn,    /* our own format function */
  NULL,                     /* no input function */
  NULL                      /* no private data */
};

사용자 정의 view 시험과 출력

453-478

시험 코드는 area당 4 page, 4 byte data field를 가진 `test` log를 등록하고 `myview`를 연결한 뒤 정수 0부터 9까지를 level 1 event로 기록합니다.

debug_info_t *debug_info;
int i;
...
debug_info = debug_register("test", 0, 4, 4);
debug_register_view(debug_info, &debug_test_view);
for (i = 0; i < 10; i ++)
  debug_int_event(debug_info, 1, i);

`/sys/kernel/debug/s390dbf/test/myview`를 읽으면 0부터 4까지는 `messages`의 문자열로, 5부터 9까지는 `data: 0000000N` 형식으로 출력됩니다.

> cat /sys/kernel/debug/s390dbf/test/myview
00 00964419734:611402 1 - 00 88042ca   This error...........
00 00964419734:611405 1 - 00 88042ca   That error...........
00 00964419734:611408 1 - 00 88042ca   Problem..............
00 00964419734:611411 1 - 00 88042ca   Something went wrong.
00 00964419734:611414 1 - 00 88042ca   Everything ok........
00 00964419734:611417 1 - 00 88042ca   data: 00000005
00 00964419734:611419 1 - 00 88042ca   data: 00000006
00 00964419734:611422 1 - 00 88042ca   data: 00000007
00 00964419734:611425 1 - 00 88042ca   data: 00000008
00 00964419734:611428 1 - 00 88042ca   data: 00000009