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: 896fbe20b4 ("printk: use the lockless ringbuffer")
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Reported-by: Nick Desaulniers <ndesaulniers@google.com>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Tested-by: Petr Mladek <pmladek@suse.com>
[akpm@linux-foundation.org: A typo fix.]
Signed-off-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20200814212525.6118-3-john.ogness@linutronix.de
This commit is contained in:
John Ogness 2020-08-14 23:31:25 +02:06 committed by Petr Mladek
parent 3e0d075cb0
commit e60768311a
2 changed files with 208 additions and 82 deletions

View file

@ -16,8 +16,13 @@ import sys
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):
"""Print Linux kernel log buffer."""
@ -26,44 +31,102 @@ class LxDmesg(gdb.Command):
super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA)
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]
start = log_buf_addr + log_first_idx
if log_first_idx < log_next_idx:
log_buf_2nd_half = -1
length = log_next_idx - log_first_idx
log_buf = utils.read_memoryview(inf, start, length).tobytes()
else:
log_buf_2nd_half = log_buf_len - log_first_idx
a = utils.read_memoryview(inf, start, log_buf_2nd_half)
b = utils.read_memoryview(inf, log_buf_addr, log_next_idx)
log_buf = a.tobytes() + b.tobytes()
length_offset = printk_log_type.get_type()['len'].bitpos // 8
text_len_offset = printk_log_type.get_type()['text_len'].bitpos // 8
time_stamp_offset = printk_log_type.get_type()['ts_nsec'].bitpos // 8
text_offset = printk_log_type.get_type().sizeof
# read in prb structure
prb_addr = int(str(gdb.parse_and_eval("(void *)'printk.c'::prb")).split()[0], 16)
sz = printk_ringbuffer_type.get_type().sizeof
prb = utils.read_memoryview(inf, prb_addr, sz).tobytes()
pos = 0
while pos < log_buf.__len__():
length = utils.read_u16(log_buf, pos + length_offset)
if length == 0:
if log_buf_2nd_half == -1:
gdb.write("Corrupted log buffer!\n")
# read in descriptor ring structure
off = printk_ringbuffer_type.get_type()['desc_ring'].bitpos // 8
addr = prb_addr + off
sz = prb_desc_ring_type.get_type().sizeof
desc_ring = utils.read_memoryview(inf, addr, sz).tobytes()
# read in descriptor array
off = prb_desc_ring_type.get_type()['count_bits'].bitpos // 8
desc_ring_count = 1 << utils.read_u32(desc_ring, off)
desc_sz = prb_desc_type.get_type().sizeof
off = prb_desc_ring_type.get_type()['descs'].bitpos // 8
addr = utils.read_ulong(desc_ring, off)
descs = utils.read_memoryview(inf, addr, desc_sz * desc_ring_count).tobytes()
# read in text data ring structure
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
pos = log_buf_2nd_half
did = (did + 1) & desc_id_mask
continue
text_len = utils.read_u16(log_buf, pos + text_len_offset)
text_start = pos + text_offset
text = log_buf[text_start:text_start + text_len].decode(
encoding='utf8', errors='replace')
time_stamp = utils.read_u64(log_buf, pos + time_stamp_offset)
begin = utils.read_ulong(descs, desc_off + begin_off) % text_data_sz
end = utils.read_ulong(descs, desc_off + next_off) % text_data_sz
# handle data-less record
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():
msg = u"[{time:12.6f}] {line}\n".format(
@ -75,7 +138,9 @@ class LxDmesg(gdb.Command):
msg = msg.encode(encoding='utf8', errors='replace')
gdb.write(msg)
pos += length
if did == head_id:
break
did = (did + 1) & desc_id_mask
LxDmesg()