Commit e6076831 authored by John Ogness's avatar John Ogness Committed by Petr Mladek

scripts/gdb: update for lockless printk ringbuffer

With the introduction of the lockless printk ringbuffer, the data
structure for the kernel log buffer was changed. Update the gdb
scripts to be able to parse/print the new log buffer structure.

Fixes: 896fbe20 ("printk: use the lockless ringbuffer")
Signed-off-by: default avatarJohn Ogness <john.ogness@linutronix.de>
Reported-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Tested-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Tested-by: default avatarPetr Mladek <pmladek@suse.com>
[akpm@linux-foundation.org: A typo fix.]
Signed-off-by: default avatarPetr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20200814212525.6118-3-john.ogness@linutronix.de
parent 3e0d075c
...@@ -170,57 +170,111 @@ document trapinfo ...@@ -170,57 +170,111 @@ document trapinfo
address the kernel panicked. address the kernel panicked.
end end
define dump_log_idx define dump_record
set $idx = $arg0 set var $desc = $arg0
if ($argc > 1) if ($argc > 1)
set $prev_flags = $arg1 set var $prev_flags = $arg1
else else
set $prev_flags = 0 set var $prev_flags = 0
end end
set $msg = ((struct printk_log *) (log_buf + $idx))
set $prefix = 1 set var $info = &$desc->info
set $newline = 1 set var $prefix = 1
set $log = log_buf + $idx + sizeof(*$msg) set var $newline = 1
# prev & LOG_CONT && !(msg->flags & LOG_PREIX) set var $begin = $desc->text_blk_lpos.begin % (1U << prb->text_data_ring.size_bits)
if (($prev_flags & 8) && !($msg->flags & 4)) set var $next = $desc->text_blk_lpos.next % (1U << prb->text_data_ring.size_bits)
set $prefix = 0
# 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 end
# msg->flags & LOG_CONT # info->flags & LOG_CONT
if ($msg->flags & 8) if ($info->flags & 8)
# (prev & LOG_CONT && !(prev & LOG_NEWLINE)) # (prev & LOG_CONT && !(prev & LOG_NEWLINE))
if (($prev_flags & 8) && !($prev_flags & 2)) if (($prev_flags & 8) && !($prev_flags & 2))
set $prefix = 0 set var $prefix = 0
end end
# (!(msg->flags & LOG_NEWLINE)) # (!(info->flags & LOG_NEWLINE))
if (!($msg->flags & 2)) if (!($info->flags & 2))
set $newline = 0 set var $newline = 0
end end
end end
if ($prefix) if ($prefix)
printf "[%5lu.%06lu] ", $msg->ts_nsec / 1000000000, $msg->ts_nsec % 1000000000 printf "[%5lu.%06lu] ", $info->ts_nsec / 1000000000, $info->ts_nsec % 1000000000
end end
if ($msg->text_len != 0) if ($text_len)
eval "printf \"%%%d.%ds\", $log", $msg->text_len, $msg->text_len eval "printf \"%%%d.%ds\", $log", $text_len, $text_len
end end
if ($newline) if ($newline)
printf "\n" printf "\n"
end end
if ($msg->dict_len > 0)
set $dict = $log + $msg->text_len # handle dictionary data
set $idx = 0
set $line = 1 set var $begin = $desc->dict_blk_lpos.begin % (1U << prb->dict_data_ring.size_bits)
while ($idx < $msg->dict_len) 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) if ($line)
printf " " printf " "
set $line = 0 set var $line = 0
end end
set $c = $dict[$idx] set var $c = $dict[$idx]
if ($c == '\0') if ($c == '\0')
printf "\n" printf "\n"
set $line = 1 set var $line = 1
else else
if ($c < ' ' || $c >= 127 || $c == '\\') if ($c < ' ' || $c >= 127 || $c == '\\')
printf "\\x%02x", $c printf "\\x%02x", $c
...@@ -228,33 +282,40 @@ define dump_log_idx ...@@ -228,33 +282,40 @@ define dump_log_idx
printf "%c", $c printf "%c", $c
end end
end end
set $idx = $idx + 1 set var $idx = $idx + 1
end end
printf "\n" printf "\n"
end end
end end
document dump_log_idx document dump_record
Dump a single log given its index in the log buffer. The first Dump a single record. The first parameter is the descriptor
parameter is the index into log_buf, the second is optional and sequence number, the second is optional and specifies the
specified the previous log buffer's flags, used for properly previous record's flags, used for properly formatting
formatting continued lines. continued lines.
end end
define dmesg define dmesg
set $i = log_first_idx set var $desc_committed = 1UL << ((sizeof(long) * 8) - 1)
set $end_idx = log_first_idx set var $flags_mask = 3UL << ((sizeof(long) * 8) - 2)
set $prev_flags = 0 set var $id_mask = ~$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) while (1)
set $msg = ((struct printk_log *) (log_buf + $i)) set var $desc = &prb->desc_ring.descs[$id % $desc_count]
if ($msg->len == 0)
set $i = 0 # skip non-committed record
else if (($desc->state_var.counter & $flags_mask) == $desc_committed)
dump_log_idx $i $prev_flags dump_record $desc $prev_flags
set $i = $i + $msg->len set var $prev_flags = $desc->info.flags
set $prev_flags = $msg->flags
end end
if ($i == $end_idx)
set var $id = ($id + 1) & $id_mask
if ($id == $end_id)
loop_break loop_break
end end
end end
......
...@@ -16,8 +16,13 @@ import sys ...@@ -16,8 +16,13 @@ import sys
from linux import utils from linux import utils
printk_log_type = utils.CachedType("struct printk_log") printk_info_type = utils.CachedType("struct printk_info")
prb_data_blk_lpos_type = utils.CachedType("struct prb_data_blk_lpos")
prb_desc_type = utils.CachedType("struct prb_desc")
prb_desc_ring_type = utils.CachedType("struct prb_desc_ring")
prb_data_ring_type = utils.CachedType("struct prb_data_ring")
printk_ringbuffer_type = utils.CachedType("struct printk_ringbuffer")
atomic_long_type = utils.CachedType("atomic_long_t")
class LxDmesg(gdb.Command): class LxDmesg(gdb.Command):
"""Print Linux kernel log buffer.""" """Print Linux kernel log buffer."""
...@@ -26,44 +31,102 @@ class LxDmesg(gdb.Command): ...@@ -26,44 +31,102 @@ class LxDmesg(gdb.Command):
super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA) super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty): def invoke(self, arg, from_tty):
log_buf_addr = int(str(gdb.parse_and_eval(
"(void *)'printk.c'::log_buf")).split()[0], 16)
log_first_idx = int(gdb.parse_and_eval("'printk.c'::log_first_idx"))
log_next_idx = int(gdb.parse_and_eval("'printk.c'::log_next_idx"))
log_buf_len = int(gdb.parse_and_eval("'printk.c'::log_buf_len"))
inf = gdb.inferiors()[0] inf = gdb.inferiors()[0]
start = log_buf_addr + log_first_idx
if log_first_idx < log_next_idx: # read in prb structure
log_buf_2nd_half = -1 prb_addr = int(str(gdb.parse_and_eval("(void *)'printk.c'::prb")).split()[0], 16)
length = log_next_idx - log_first_idx sz = printk_ringbuffer_type.get_type().sizeof
log_buf = utils.read_memoryview(inf, start, length).tobytes() prb = utils.read_memoryview(inf, prb_addr, sz).tobytes()
else:
log_buf_2nd_half = log_buf_len - log_first_idx # read in descriptor ring structure
a = utils.read_memoryview(inf, start, log_buf_2nd_half) off = printk_ringbuffer_type.get_type()['desc_ring'].bitpos // 8
b = utils.read_memoryview(inf, log_buf_addr, log_next_idx) addr = prb_addr + off
log_buf = a.tobytes() + b.tobytes() sz = prb_desc_ring_type.get_type().sizeof
desc_ring = utils.read_memoryview(inf, addr, sz).tobytes()
length_offset = printk_log_type.get_type()['len'].bitpos // 8
text_len_offset = printk_log_type.get_type()['text_len'].bitpos // 8 # read in descriptor array
time_stamp_offset = printk_log_type.get_type()['ts_nsec'].bitpos // 8 off = prb_desc_ring_type.get_type()['count_bits'].bitpos // 8
text_offset = printk_log_type.get_type().sizeof desc_ring_count = 1 << utils.read_u32(desc_ring, off)
desc_sz = prb_desc_type.get_type().sizeof
pos = 0 off = prb_desc_ring_type.get_type()['descs'].bitpos // 8
while pos < log_buf.__len__(): addr = utils.read_ulong(desc_ring, off)
length = utils.read_u16(log_buf, pos + length_offset) descs = utils.read_memoryview(inf, addr, desc_sz * desc_ring_count).tobytes()
if length == 0:
if log_buf_2nd_half == -1: # read in text data ring structure
gdb.write("Corrupted log buffer!\n") off = printk_ringbuffer_type.get_type()['text_data_ring'].bitpos // 8
addr = prb_addr + off
sz = prb_data_ring_type.get_type().sizeof
text_data_ring = utils.read_memoryview(inf, addr, sz).tobytes()
# read in text data
off = prb_data_ring_type.get_type()['size_bits'].bitpos // 8
text_data_sz = 1 << utils.read_u32(text_data_ring, off)
off = prb_data_ring_type.get_type()['data'].bitpos // 8
addr = utils.read_ulong(text_data_ring, off)
text_data = utils.read_memoryview(inf, addr, text_data_sz).tobytes()
counter_off = atomic_long_type.get_type()['counter'].bitpos // 8
sv_off = prb_desc_type.get_type()['state_var'].bitpos // 8
off = prb_desc_type.get_type()['text_blk_lpos'].bitpos // 8
begin_off = off + (prb_data_blk_lpos_type.get_type()['begin'].bitpos // 8)
next_off = off + (prb_data_blk_lpos_type.get_type()['next'].bitpos // 8)
off = prb_desc_type.get_type()['info'].bitpos // 8
ts_off = off + printk_info_type.get_type()['ts_nsec'].bitpos // 8
len_off = off + printk_info_type.get_type()['text_len'].bitpos // 8
# definitions from kernel/printk/printk_ringbuffer.h
desc_sv_bits = utils.get_long_type().sizeof * 8
desc_committed_mask = 1 << (desc_sv_bits - 1)
desc_reuse_mask = 1 << (desc_sv_bits - 2)
desc_flags_mask = desc_committed_mask | desc_reuse_mask
desc_id_mask = ~desc_flags_mask
# read in tail and head descriptor ids
off = prb_desc_ring_type.get_type()['tail_id'].bitpos // 8
tail_id = utils.read_u64(desc_ring, off + counter_off)
off = prb_desc_ring_type.get_type()['head_id'].bitpos // 8
head_id = utils.read_u64(desc_ring, off + counter_off)
did = tail_id
while True:
ind = did % desc_ring_count
desc_off = desc_sz * ind
# skip non-committed record
state = utils.read_u64(descs, desc_off + sv_off + counter_off) & desc_flags_mask
if state != desc_committed_mask:
if did == head_id:
break break
pos = log_buf_2nd_half did = (did + 1) & desc_id_mask
continue continue
text_len = utils.read_u16(log_buf, pos + text_len_offset) begin = utils.read_ulong(descs, desc_off + begin_off) % text_data_sz
text_start = pos + text_offset end = utils.read_ulong(descs, desc_off + next_off) % text_data_sz
text = log_buf[text_start:text_start + text_len].decode(
encoding='utf8', errors='replace') # handle data-less record
time_stamp = utils.read_u64(log_buf, pos + time_stamp_offset) if begin & 1 == 1:
text = ""
else:
# handle wrapping data block
if begin > end:
begin = 0
# skip over descriptor id
text_start = begin + utils.get_long_type().sizeof
text_len = utils.read_u16(descs, desc_off + len_off)
# handle truncated message
if end - text_start < text_len:
text_len = end - text_start
text = text_data[text_start:text_start + text_len].decode(
encoding='utf8', errors='replace')
time_stamp = utils.read_u64(descs, desc_off + ts_off)
for line in text.splitlines(): for line in text.splitlines():
msg = u"[{time:12.6f}] {line}\n".format( msg = u"[{time:12.6f}] {line}\n".format(
...@@ -75,7 +138,9 @@ class LxDmesg(gdb.Command): ...@@ -75,7 +138,9 @@ class LxDmesg(gdb.Command):
msg = msg.encode(encoding='utf8', errors='replace') msg = msg.encode(encoding='utf8', errors='replace')
gdb.write(msg) gdb.write(msg)
pos += length if did == head_id:
break
did = (did + 1) & desc_id_mask
LxDmesg() LxDmesg()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment