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	if ($argc > 1)
176		set var $prev_flags = $arg1
177	else
178		set var $prev_flags = 0
179	end
180
181	set var $info = &$desc->info
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 $begin = $desc->dict_blk_lpos.begin % (1U << prb->dict_data_ring.size_bits)
241	set var $next = $desc->dict_blk_lpos.next % (1U << prb->dict_data_ring.size_bits)
242
243	# handle data-less record
244	if ($begin & 1)
245		set var $dict_len = 0
246		set var $dict = ""
247	else
248		# handle wrapping data block
249		if ($begin > $next)
250			set var $begin = 0
251		end
252
253		# skip over descriptor id
254		set var $begin = $begin + sizeof(long)
255
256		# handle truncated message
257		if ($next - $begin < $info->dict_len)
258			set var $dict_len = $next - $begin
259		else
260			set var $dict_len = $info->dict_len
261		end
262
263		set var $dict = &prb->dict_data_ring.data[$begin]
264	end
265
266	if ($dict_len > 0)
267		set var $idx = 0
268		set var $line = 1
269		while ($idx < $dict_len)
270			if ($line)
271				printf " "
272				set var $line = 0
273			end
274			set var $c = $dict[$idx]
275			if ($c == '\0')
276				printf "\n"
277				set var $line = 1
278			else
279				if ($c < ' ' || $c >= 127 || $c == '\\')
280					printf "\\x%02x", $c
281				else
282					printf "%c", $c
283				end
284			end
285			set var $idx = $idx + 1
286		end
287		printf "\n"
288	end
289end
290document dump_record
291	Dump a single record. The first parameter is the descriptor
292	sequence number, the second is optional and specifies the
293	previous record's flags, used for properly formatting
294	continued lines.
295end
296
297define dmesg
298	set var $desc_committed = 1UL << ((sizeof(long) * 8) - 1)
299	set var $flags_mask = 3UL << ((sizeof(long) * 8) - 2)
300	set var $id_mask = ~$flags_mask
301
302	set var $desc_count = 1U << prb->desc_ring.count_bits
303	set var $prev_flags = 0
304
305	set var $id = prb->desc_ring.tail_id.counter
306	set var $end_id = prb->desc_ring.head_id.counter
307
308	while (1)
309		set var $desc = &prb->desc_ring.descs[$id % $desc_count]
310
311		# skip non-committed record
312		if (($desc->state_var.counter & $flags_mask) == $desc_committed)
313			dump_record $desc $prev_flags
314			set var $prev_flags = $desc->info.flags
315		end
316
317		set var $id = ($id + 1) & $id_mask
318		if ($id == $end_id)
319			loop_break
320		end
321	end
322end
323document dmesg
324	print the kernel ring buffer
325end
326