f2e4cca2f6
@head_id points to the newest record, but the printing loop
exits when it increments to this value (before printing).
Exit the printing loop after the newest record has been printed.
The python-based function in scripts/gdb/linux/dmesg.py already
does this correctly.
Fixes: e60768311a
("scripts/gdb: update for lockless printk ringbuffer")
Cc: stable@vger.kernel.org
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20221229134339.197627-1-john.ogness@linutronix.de
324 lines
8.6 KiB
Plaintext
324 lines
8.6 KiB
Plaintext
#
|
|
# This file contains a few gdb macros (user defined commands) to extract
|
|
# useful information from kernel crashdump (kdump) like stack traces of
|
|
# all the processes or a particular process and trapinfo.
|
|
#
|
|
# These macros can be used by copying this file in .gdbinit (put in home
|
|
# directory or current directory) or by invoking gdb command with
|
|
# --command=<command-file-name> option
|
|
#
|
|
# Credits:
|
|
# Alexander Nyberg <alexn@telia.com>
|
|
# V Srivatsa <vatsa@in.ibm.com>
|
|
# Maneesh Soni <maneesh@in.ibm.com>
|
|
#
|
|
|
|
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
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|