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
16define 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
53end
54document bttnobp
55	dump all thread stack traces on a kernel compiled with !CONFIG_FRAME_POINTER
56end
57
58define 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
76end
77document btthreadstack
78	 dump a thread stack using the given task structure pointer
79end
80
81
82define 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
99end
100document btt
101	dump all thread stack traces on a kernel compiled with CONFIG_FRAME_POINTER
102end
103
104define 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
131end
132document btpid
133	backtrace of pid
134end
135
136
137define 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
166end
167document 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.
171end
172
173define 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
281end
282document 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.
287end
288
289define 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		set var $id = ($id + 1) & $id_mask
316		if ($id == $end_id)
317			loop_break
318		end
319	end
320end
321document dmesg
322	print the kernel ring buffer
323end
324