← Documents Documentation/admin-guide/kdump/gdbmacros.txt GitHub 원문 ↗

Linux 6.18.37 · Administration / Crash Dumping

Kdump GDB macros

Kdump 메모리에서 태스크 스택, 트랩 정보와 printk 링 버퍼를 추출하는 GDB 사용자 정의 명령을 원문 코드와 함께 해설합니다.

Source pathDocumentation/admin-guide/kdump/gdbmacros.txt
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

사용 준비

gdbmacros.txt:1-15

`.gdbinit` 또는 GDB `--command` 옵션으로 매크로 파일을 불러오는 방법을 설명합니다.

스택 추적

gdbmacros.txt:16-172

프레임 포인터 설정에 맞춘 전체 스레드·특정 PID 역추적과 트랩 조회 명령을 정리합니다.

printk 복원

gdbmacros.txt:173-323

printk 레코드의 연속 줄과 사전 데이터를 복원하고 커널 링 버퍼를 순회하는 명령을 설명합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 #
2 # This file contains a few gdb macros (user defined commands) to extract
3 # useful information from kernel crashdump (kdump) like stack traces of
4 # all the processes or a particular process and trapinfo.
5 #
6 # These macros can be used by copying this file in .gdbinit (put in home
7 # directory or current directory) or by invoking gdb command with
8 # --command=<command-file-name> option
9 #
10 # Credits:
11 # Alexander Nyberg <alexn@telia.com>
12 # V Srivatsa <vatsa@in.ibm.com>
13 # Maneesh Soni <maneesh@in.ibm.com>
14 #
15
16 define bttnobp
17 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
18 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
19 set $init_t=&init_task
20 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
21 set var $stacksize = sizeof(union thread_union)
22 while ($next_t != $init_t)
23 set $next_t=(struct task_struct *)$next_t
24 printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm
25 printf "===================\n"
26 set var $stackp = $next_t.thread.sp
27 set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize
28
29 while ($stackp < $stack_top)
30 if (*($stackp) > _stext && *($stackp) < _sinittext)
31 info symbol *($stackp)
32 end
33 set $stackp += 4
34 end
35 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
36 while ($next_th != $next_t)
37 set $next_th=(struct task_struct *)$next_th
38 printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm
39 printf "===================\n"
40 set var $stackp = $next_t.thread.sp
41 set var $stack_top = ($stackp & ~($stacksize - 1)) + stacksize
42
43 while ($stackp < $stack_top)
44 if (*($stackp) > _stext && *($stackp) < _sinittext)
45 info symbol *($stackp)
46 end
47 set $stackp += 4
48 end
49 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
50 end
51 set $next_t=(char *)($next_t->tasks.next) - $tasks_off
52 end
53 end
54 document bttnobp
55 dump all thread stack traces on a kernel compiled with !CONFIG_FRAME_POINTER
56 end
57
58 define btthreadstack
59 set var $pid_task = $arg0
60
61 printf "\npid %d; comm %s:\n", $pid_task.pid, $pid_task.comm
62 printf "task struct: "
63 print $pid_task
64 printf "===================\n"
65 set var $stackp = $pid_task.thread.sp
66 set var $stacksize = sizeof(union thread_union)
67 set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize
68 set var $stack_bot = ($stackp & ~($stacksize - 1))
69
70 set $stackp = *((unsigned long *) $stackp)
71 while (($stackp < $stack_top) && ($stackp > $stack_bot))
72 set var $addr = *(((unsigned long *) $stackp) + 1)
73 info symbol $addr
74 set $stackp = *((unsigned long *) $stackp)
75 end
76 end
77 document btthreadstack
78 dump a thread stack using the given task structure pointer
79 end
80
81
82 define btt
83 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
84 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
85 set $init_t=&init_task
86 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
87 while ($next_t != $init_t)
88 set $next_t=(struct task_struct *)$next_t
89 btthreadstack $next_t
90
91 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
92 while ($next_th != $next_t)
93 set $next_th=(struct task_struct *)$next_th
94 btthreadstack $next_th
95 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
96 end
97 set $next_t=(char *)($next_t->tasks.next) - $tasks_off
98 end
99 end
100 document btt
101 dump all thread stack traces on a kernel compiled with CONFIG_FRAME_POINTER
102 end
103
104 define btpid
105 set var $pid = $arg0
106 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
107 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
108 set $init_t=&init_task
109 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
110 set var $pid_task = 0
111
112 while ($next_t != $init_t)
113 set $next_t=(struct task_struct *)$next_t
114
115 if ($next_t.pid == $pid)
116 set $pid_task = $next_t
117 end
118
119 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
120 while ($next_th != $next_t)
121 set $next_th=(struct task_struct *)$next_th
122 if ($next_th.pid == $pid)
123 set $pid_task = $next_th
124 end
125 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
126 end
127 set $next_t=(char *)($next_t->tasks.next) - $tasks_off
128 end
129
130 btthreadstack $pid_task
131 end
132 document btpid
133 backtrace of pid
134 end
135
136
137 define trapinfo
138 set var $pid = $arg0
139 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
140 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
141 set $init_t=&init_task
142 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
143 set var $pid_task = 0
144
145 while ($next_t != $init_t)
146 set $next_t=(struct task_struct *)$next_t
147
148 if ($next_t.pid == $pid)
149 set $pid_task = $next_t
150 end
151
152 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
153 while ($next_th != $next_t)
154 set $next_th=(struct task_struct *)$next_th
155 if ($next_th.pid == $pid)
156 set $pid_task = $next_th
157 end
158 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
159 end
160 set $next_t=(char *)($next_t->tasks.next) - $tasks_off
161 end
162
163 printf "Trapno %ld, cr2 0x%lx, error_code %ld\n", $pid_task.thread.trap_no, \
164 $pid_task.thread.cr2, $pid_task.thread.error_code
165
166 end
167 document trapinfo
168 Run info threads and lookup pid of thread #1
169 'trapinfo <pid>' will tell you by which trap & possibly
170 address the kernel panicked.
171 end
172
173 define dump_record
174 set var $desc = $arg0
175 set var $info = $arg1
176 if ($argc > 2)
177 set var $prev_flags = $arg2
178 else
179 set var $prev_flags = 0
180 end
181
182 set var $prefix = 1
183 set var $newline = 1
184
185 set var $begin = $desc->text_blk_lpos.begin % (1U << prb->text_data_ring.size_bits)
186 set var $next = $desc->text_blk_lpos.next % (1U << prb->text_data_ring.size_bits)
187
188 # handle data-less record
189 if ($begin & 1)
190 set var $text_len = 0
191 set var $log = ""
192 else
193 # handle wrapping data block
194 if ($begin > $next)
195 set var $begin = 0
196 end
197
198 # skip over descriptor id
199 set var $begin = $begin + sizeof(long)
200
201 # handle truncated message
202 if ($next - $begin < $info->text_len)
203 set var $text_len = $next - $begin
204 else
205 set var $text_len = $info->text_len
206 end
207
208 set var $log = &prb->text_data_ring.data[$begin]
209 end
210
211 # prev & LOG_CONT && !(info->flags & LOG_PREIX)
212 if (($prev_flags & 8) && !($info->flags & 4))
213 set var $prefix = 0
214 end
215
216 # info->flags & LOG_CONT
217 if ($info->flags & 8)
218 # (prev & LOG_CONT && !(prev & LOG_NEWLINE))
219 if (($prev_flags & 8) && !($prev_flags & 2))
220 set var $prefix = 0
221 end
222 # (!(info->flags & LOG_NEWLINE))
223 if (!($info->flags & 2))
224 set var $newline = 0
225 end
226 end
227
228 if ($prefix)
229 printf "[%5lu.%06lu] ", $info->ts_nsec / 1000000000, $info->ts_nsec % 1000000000
230 end
231 if ($text_len)
232 eval "printf \"%%%d.%ds\", $log", $text_len, $text_len
233 end
234 if ($newline)
235 printf "\n"
236 end
237
238 # handle dictionary data
239
240 set var $dict = &$info->dev_info.subsystem[0]
241 set var $dict_len = sizeof($info->dev_info.subsystem)
242 if ($dict[0] != '\0')
243 printf " SUBSYSTEM="
244 set var $idx = 0
245 while ($idx < $dict_len)
246 set var $c = $dict[$idx]
247 if ($c == '\0')
248 loop_break
249 else
250 if ($c < ' ' || $c >= 127 || $c == '\\')
251 printf "\\x%02x", $c
252 else
253 printf "%c", $c
254 end
255 end
256 set var $idx = $idx + 1
257 end
258 printf "\n"
259 end
260
261 set var $dict = &$info->dev_info.device[0]
262 set var $dict_len = sizeof($info->dev_info.device)
263 if ($dict[0] != '\0')
264 printf " DEVICE="
265 set var $idx = 0
266 while ($idx < $dict_len)
267 set var $c = $dict[$idx]
268 if ($c == '\0')
269 loop_break
270 else
271 if ($c < ' ' || $c >= 127 || $c == '\\')
272 printf "\\x%02x", $c
273 else
274 printf "%c", $c
275 end
276 end
277 set var $idx = $idx + 1
278 end
279 printf "\n"
280 end
281 end
282 document dump_record
283 Dump a single record. The first parameter is the descriptor,
284 the second parameter is the info, the third parameter is
285 optional and specifies the previous record's flags, used for
286 properly formatting continued lines.
287 end
288
289 define dmesg
290 # definitions from kernel/printk/printk_ringbuffer.h
291 set var $desc_committed = 1
292 set var $desc_finalized = 2
293 set var $desc_sv_bits = sizeof(long) * 8
294 set var $desc_flags_shift = $desc_sv_bits - 2
295 set var $desc_flags_mask = 3 << $desc_flags_shift
296 set var $id_mask = ~$desc_flags_mask
297
298 set var $desc_count = 1U << prb->desc_ring.count_bits
299 set var $prev_flags = 0
300
301 set var $id = prb->desc_ring.tail_id.counter
302 set var $end_id = prb->desc_ring.head_id.counter
303
304 while (1)
305 set var $desc = &prb->desc_ring.descs[$id % $desc_count]
306 set var $info = &prb->desc_ring.infos[$id % $desc_count]
307
308 # skip non-committed record
309 set var $state = 3 & ($desc->state_var.counter >> $desc_flags_shift)
310 if ($state == $desc_committed || $state == $desc_finalized)
311 dump_record $desc $info $prev_flags
312 set var $prev_flags = $info->flags
313 end
314
315 if ($id == $end_id)
316 loop_break
317 end
318 set var $id = ($id + 1) & $id_mask
319 end
320 end
321 document dmesg
322 print the kernel ring buffer
323 end
324

3. 한국어 전문 번역

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

Kdump 분석용 GDB 매크로

1-15

이 파일에는 커널 크래시 덤프(kdump)에서 유용한 정보를 추출하는 GDB 매크로, 즉 사용자 정의 명령이 들어 있습니다. 모든 프로세스 또는 특정 프로세스의 스택 추적과 트랩 정보를 확인하고 커널 링 버퍼를 출력할 수 있습니다.

매크로를 사용하려면 이 파일의 내용을 홈 디렉터리나 현재 디렉터리의 `.gdbinit`에 복사하거나, GDB를 실행할 때 `--command=<command-file-name>` 옵션으로 파일을 지정합니다.

초기 매크로 작성 기여자는 Alexander Nyberg `<alexn@telia.com>`, V Srivatsa `<vatsa@in.ibm.com>`, Maneesh Soni `<maneesh@in.ibm.com>`입니다.

프레임 포인터가 없는 커널의 전체 스택

16-57

`bttnobp`

`!CONFIG_FRAME_POINTER`로 컴파일된 커널에서 모든 태스크와 스레드 그룹을 순회합니다. 각 커널 스택을 워드 단위로 훑어 `_stext`와 `_sinittext` 사이의 주소를 `info symbol`로 해석하여 모든 스레드의 스택 추적을 덤프합니다.

define bttnobp
	set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
	set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
	set $init_t=&init_task
	set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
	set var $stacksize = sizeof(union thread_union)
	while ($next_t != $init_t)
		set $next_t=(struct task_struct *)$next_t
		printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm
		printf "===================\n"
		set var $stackp = $next_t.thread.sp
		set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize

		while ($stackp < $stack_top)
			if (*($stackp) > _stext && *($stackp) < _sinittext)
				info symbol *($stackp)
			end
			set $stackp += 4
		end
		set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
		while ($next_th != $next_t)
			set $next_th=(struct task_struct *)$next_th
			printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm
			printf "===================\n"
			set var $stackp = $next_t.thread.sp
			set var $stack_top = ($stackp & ~($stacksize - 1)) + stacksize

			while ($stackp < $stack_top)
				if (*($stackp) > _stext && *($stackp) < _sinittext)
					info symbol *($stackp)
				end
				set $stackp += 4
			end
			set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
		end
		set $next_t=(char *)($next_t->tasks.next) - $tasks_off
	end
end
document bttnobp
	dump all thread stack traces on a kernel compiled with !CONFIG_FRAME_POINTER
end

태스크 구조체로 스레드 스택 추적

58-81

`btthreadstack`

인수로 받은 `task_struct` 포인터에서 스택 포인터와 `union thread_union` 경계를 구합니다. 저장된 프레임 포인터를 따라가며 반환 주소를 `info symbol`로 변환해 해당 스레드의 스택을 출력합니다.

define btthreadstack
	set var $pid_task = $arg0

	printf "\npid %d; comm %s:\n", $pid_task.pid, $pid_task.comm
	printf "task struct: "
	print $pid_task
	printf "===================\n"
	set var $stackp = $pid_task.thread.sp
	set var $stacksize = sizeof(union thread_union)
	set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize
	set var $stack_bot = ($stackp & ~($stacksize - 1))

	set $stackp = *((unsigned long *) $stackp)
	while (($stackp < $stack_top) && ($stackp > $stack_bot))
		set var $addr = *(((unsigned long *) $stackp) + 1)
		info symbol $addr
		set $stackp = *((unsigned long *) $stackp)
	end
end
document btthreadstack
	 dump a thread stack using the given task structure pointer
end

전체 태스크, PID와 트랩 정보

82-172

`btt`

`CONFIG_FRAME_POINTER`로 컴파일된 커널에서 전체 태스크 목록과 각 스레드 그룹을 순회하며 `btthreadstack`을 호출해 모든 스레드의 스택 추적을 덤프합니다.

`btpid`

지정한 PID와 일치하는 태스크 또는 스레드 그룹 구성원을 찾아 `btthreadstack`으로 역추적합니다.

`trapinfo`

먼저 `info threads`를 실행해 스레드 1의 PID를 확인한 다음 `trapinfo <pid>`를 사용합니다. 이 명령은 `trap_no`, `cr2`, `error_code`를 출력하여 커널이 어떤 트랩과 가능한 어느 주소에서 panic했는지 알려 줍니다.

define btt
	set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
	set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
	set $init_t=&init_task
	set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
	while ($next_t != $init_t)
		set $next_t=(struct task_struct *)$next_t
		btthreadstack $next_t

		set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
		while ($next_th != $next_t)
			set $next_th=(struct task_struct *)$next_th
			btthreadstack $next_th
			set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
		end
		set $next_t=(char *)($next_t->tasks.next) - $tasks_off
	end
end
document btt
	dump all thread stack traces on a kernel compiled with CONFIG_FRAME_POINTER
end

define btpid
	set var $pid = $arg0
	set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
	set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
	set $init_t=&init_task
	set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
	set var $pid_task = 0

	while ($next_t != $init_t)
		set $next_t=(struct task_struct *)$next_t

		if ($next_t.pid == $pid)
			set $pid_task = $next_t
		end

		set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
		while ($next_th != $next_t)
			set $next_th=(struct task_struct *)$next_th
			if ($next_th.pid == $pid)
				set $pid_task = $next_th
			end
			set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
		end
		set $next_t=(char *)($next_t->tasks.next) - $tasks_off
	end

	btthreadstack $pid_task
end
document btpid
	backtrace of pid
end


define trapinfo
	set var $pid = $arg0
	set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
	set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next)
	set $init_t=&init_task
	set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
	set var $pid_task = 0

	while ($next_t != $init_t)
		set $next_t=(struct task_struct *)$next_t

		if ($next_t.pid == $pid)
			set $pid_task = $next_t
		end

		set $next_th=(((char *)$next_t->thread_group.next) - $pid_off)
		while ($next_th != $next_t)
			set $next_th=(struct task_struct *)$next_th
			if ($next_th.pid == $pid)
				set $pid_task = $next_th
			end
			set $next_th=(((char *)$next_th->thread_group.next) - $pid_off)
		end
		set $next_t=(char *)($next_t->tasks.next) - $tasks_off
	end

	printf "Trapno %ld, cr2 0x%lx, error_code %ld\n", $pid_task.thread.trap_no, \
				$pid_task.thread.cr2, $pid_task.thread.error_code

end
document trapinfo
	Run info threads and lookup pid of thread #1
	'trapinfo <pid>' will tell you by which trap & possibly
	address the kernel panicked.
end

printk 레코드 한 건 출력

173-288

`dump_record`

첫 번째 인수인 descriptor와 두 번째 인수인 info를 사용해 printk 레코드 하나를 출력합니다. 선택적인 세 번째 인수는 이전 레코드의 flags이며, 이어지는 줄을 올바르게 서식화하는 데 사용합니다.

매크로는 데이터가 없는 레코드, 링 버퍼 끝에서 감긴 데이터 블록, descriptor ID, 잘린 메시지를 처리합니다. `LOG_CONT`와 `LOG_NEWLINE` 플래그에 따라 타임스탬프 접두사와 줄바꿈을 결정한 뒤 텍스트를 출력합니다.

마지막으로 `dev_info`의 `SUBSYSTEM=`과 `DEVICE=` 사전 데이터를 출력합니다. 출력할 수 없는 문자와 역슬래시는 `\xNN` 형식으로 이스케이프합니다.

define dump_record
	set var $desc = $arg0
	set var $info = $arg1
	if ($argc > 2)
		set var $prev_flags = $arg2
	else
		set var $prev_flags = 0
	end

	set var $prefix = 1
	set var $newline = 1

	set var $begin = $desc->text_blk_lpos.begin % (1U << prb->text_data_ring.size_bits)
	set var $next = $desc->text_blk_lpos.next % (1U << prb->text_data_ring.size_bits)

	# handle data-less record
	if ($begin & 1)
		set var $text_len = 0
		set var $log = ""
	else
		# handle wrapping data block
		if ($begin > $next)
			set var $begin = 0
		end

		# skip over descriptor id
		set var $begin = $begin + sizeof(long)

		# handle truncated message
		if ($next - $begin < $info->text_len)
			set var $text_len = $next - $begin
		else
			set var $text_len = $info->text_len
		end

		set var $log = &prb->text_data_ring.data[$begin]
	end

	# prev & LOG_CONT && !(info->flags & LOG_PREIX)
	if (($prev_flags & 8) && !($info->flags & 4))
		set var $prefix = 0
	end

	# info->flags & LOG_CONT
	if ($info->flags & 8)
		# (prev & LOG_CONT && !(prev & LOG_NEWLINE))
		if (($prev_flags & 8) && !($prev_flags & 2))
			set var $prefix = 0
		end
		# (!(info->flags & LOG_NEWLINE))
		if (!($info->flags & 2))
			set var $newline = 0
		end
	end

	if ($prefix)
		printf "[%5lu.%06lu] ", $info->ts_nsec / 1000000000, $info->ts_nsec % 1000000000
	end
	if ($text_len)
		eval "printf \"%%%d.%ds\", $log", $text_len, $text_len
	end
	if ($newline)
		printf "\n"
	end

	# handle dictionary data

	set var $dict = &$info->dev_info.subsystem[0]
	set var $dict_len = sizeof($info->dev_info.subsystem)
	if ($dict[0] != '\0')
		printf " SUBSYSTEM="
		set var $idx = 0
		while ($idx < $dict_len)
			set var $c = $dict[$idx]
			if ($c == '\0')
				loop_break
			else
				if ($c < ' ' || $c >= 127 || $c == '\\')
					printf "\\x%02x", $c
				else
					printf "%c", $c
				end
			end
			set var $idx = $idx + 1
		end
		printf "\n"
	end

	set var $dict = &$info->dev_info.device[0]
	set var $dict_len = sizeof($info->dev_info.device)
	if ($dict[0] != '\0')
		printf " DEVICE="
		set var $idx = 0
		while ($idx < $dict_len)
			set var $c = $dict[$idx]
			if ($c == '\0')
				loop_break
			else
				if ($c < ' ' || $c >= 127 || $c == '\\')
					printf "\\x%02x", $c
				else
					printf "%c", $c
				end
			end
			set var $idx = $idx + 1
		end
		printf "\n"
	end
end
document dump_record
	Dump a single record. The first parameter is the descriptor,
	the second parameter is the info, the third parameter is
	optional and specifies the previous record's flags, used for
	properly formatting continued lines.
end

커널 링 버퍼 출력

289-323

`dmesg`

`kernel/printk/printk_ringbuffer.h`의 descriptor 상태 정의를 이용합니다. `prb`의 tail ID부터 head ID까지 링을 순회하면서 committed 또는 finalized 상태인 레코드만 `dump_record`로 출력하고, 이어지는 레코드 서식을 위해 이전 flags를 전달합니다.

define dmesg
	# definitions from kernel/printk/printk_ringbuffer.h
	set var $desc_committed = 1
	set var $desc_finalized = 2
	set var $desc_sv_bits = sizeof(long) * 8
	set var $desc_flags_shift = $desc_sv_bits - 2
	set var $desc_flags_mask = 3 << $desc_flags_shift
	set var $id_mask = ~$desc_flags_mask

	set var $desc_count = 1U << prb->desc_ring.count_bits
	set var $prev_flags = 0

	set var $id = prb->desc_ring.tail_id.counter
	set var $end_id = prb->desc_ring.head_id.counter

	while (1)
		set var $desc = &prb->desc_ring.descs[$id % $desc_count]
		set var $info = &prb->desc_ring.infos[$id % $desc_count]

		# skip non-committed record
		set var $state = 3 & ($desc->state_var.counter >> $desc_flags_shift)
		if ($state == $desc_committed || $state == $desc_finalized)
			dump_record $desc $info $prev_flags
			set var $prev_flags = $info->flags
		end

		if ($id == $end_id)
			loop_break
		end
		set var $id = ($id + 1) & $id_mask
	end
end
document dmesg
	print the kernel ring buffer
end