mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-15 15:15:47 +00:00
4cfc7258f8
Add support for extending the newest data block. For this, introduce a new finalization state (desc_finalized) denoting a committed descriptor that cannot be extended. Until a record is finalized, a writer can reopen that record to append new data. Reopening a record means transitioning from the desc_committed state back to the desc_reserved state. A writer can explicitly finalize a record if there is no intention of extending it. Also, records are automatically finalized when a new record is reserved. This relieves writers of needing to explicitly finalize while also making such records available to readers sooner. (Readers can only traverse finalized records.) Four new memory barrier pairs are introduced. Two of them are insignificant additions (data_realloc:A/desc_read:D and data_realloc:A/data_push_tail:B) because they are alternate path memory barriers that exactly match the purpose, pairing, and context of the two existing memory barrier pairs they provide an alternate path for. The other two new memory barrier pairs are significant additions: desc_reopen_last:A / _prb_commit:B - When reopening a descriptor, ensure the state transitions back to desc_reserved before fully trusting the descriptor data. _prb_commit:B / desc_reserve:D - When committing a descriptor, ensure the state transitions to desc_committed before checking the head ID to see if the descriptor needs to be finalized. 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/20200914123354.832-6-john.ogness@linutronix.de
330 lines
8.7 KiB
Text
330 lines
8.7 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
|
|
if ($argc > 1)
|
|
set var $prev_flags = $arg1
|
|
else
|
|
set var $prev_flags = 0
|
|
end
|
|
|
|
set var $info = &$desc->info
|
|
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 $begin = $desc->dict_blk_lpos.begin % (1U << prb->dict_data_ring.size_bits)
|
|
set var $next = $desc->dict_blk_lpos.next % (1U << prb->dict_data_ring.size_bits)
|
|
|
|
# handle data-less record
|
|
if ($begin & 1)
|
|
set var $dict_len = 0
|
|
set var $dict = ""
|
|
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->dict_len)
|
|
set var $dict_len = $next - $begin
|
|
else
|
|
set var $dict_len = $info->dict_len
|
|
end
|
|
|
|
set var $dict = &prb->dict_data_ring.data[$begin]
|
|
end
|
|
|
|
if ($dict_len > 0)
|
|
set var $idx = 0
|
|
set var $line = 1
|
|
while ($idx < $dict_len)
|
|
if ($line)
|
|
printf " "
|
|
set var $line = 0
|
|
end
|
|
set var $c = $dict[$idx]
|
|
if ($c == '\0')
|
|
printf "\n"
|
|
set var $line = 1
|
|
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
|
|
sequence number, the second 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]
|
|
|
|
# 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 $prev_flags
|
|
set var $prev_flags = $desc->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
|