mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
![John Ogness](/assets/img/avatar_default.png)
Dictionaries are only used for SUBSYSTEM and DEVICE properties. The current implementation stores the property names each time they are used. This requires more space than otherwise necessary. Also, because the dictionary entries are currently considered optional, it cannot be relied upon that they are always available, even if the writer wanted to store them. These issues will increase should new dictionary properties be introduced. Rather than storing the subsystem and device properties in the dict ring, introduce a struct dev_printk_info with separate fields to store only the property values. Embed this struct within the struct printk_info to provide guaranteed availability. 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/87mu1jl6ne.fsf@jogness.linutronix.de
323 lines
8.6 KiB
Text
323 lines
8.6 KiB
Text
#
|
|
# 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
|
|
|
|
set var $id = ($id + 1) & $id_mask
|
|
if ($id == $end_id)
|
|
loop_break
|
|
end
|
|
end
|
|
end
|
|
document dmesg
|
|
print the kernel ring buffer
|
|
end
|