Commit d594d8f4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'printk-for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux

Pull printk updates from Petr Mladek:
 "The big new thing is the fully lockless ringbuffer implementation,
  including the support for continuous lines. It will allow to store and
  read messages in any situation wihtout the risk of deadlocks and
  without the need of temporary per-CPU buffers.

  The access is still serialized by logbuf_lock. It synchronizes few
  more operations, for example, temporary buffer for formatting the
  message, syslog and kmsg_dump operations. The lock removal is being
  discussed and should be ready for the next release.

  The continuous lines are handled exactly the same way as before to
  avoid regressions in user space. It means that they are appended to
  the last message when the caller is the same. Only the last message
  can be extended.

  The data ring includes plain text of the messages. Except for an
  integer at the beginning of each message that points back to the
  descriptor ring with other metadata.

  The dictionary has to stay. journalctl uses it to filter the log. It
  allows to show messages related to a given device. The dictionary
  values are stored in the descriptor ring with the other metadata.

  This is the first part of the printk rework as discussed at Plumbers
  2019, see https://lore.kernel.org/r/87k1acz5rx.fsf@linutronix.de. The
  next big step will be handling consoles by kthreads during the normal
  system operation. It will require special handling of situations when
  the kthreads could not get scheduled, for example, early boot,
  suspend, panic.

  Other changes:

   - Add John Ogness as a reviewer for printk subsystem. He is author of
     the rework and is familiar with the code and history.

   - Fix locking in serial8250_do_startup() to prevent lockdep report.

   - Few code cleanups"

* tag 'printk-for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux: (27 commits)
  printk: Use fallthrough pseudo-keyword
  printk: reduce setup_text_buf size to LOG_LINE_MAX
  printk: avoid and/or handle record truncation
  printk: remove dict ring
  printk: move dictionary keys to dev_printk_info
  printk: move printk_info into separate array
  printk: reimplement log_cont using record extension
  printk: ringbuffer: add finalization/extension support
  printk: ringbuffer: change representation of states
  printk: ringbuffer: clear initial reserved fields
  printk: ringbuffer: add BLK_DATALESS() macro
  printk: ringbuffer: relocate get_data()
  printk: ringbuffer: avoid memcpy() on state_var
  printk: ringbuffer: fix setting state in desc_read()
  kernel.h: Move oops_in_progress to printk.h
  scripts/gdb: update for lockless printk ringbuffer
  scripts/gdb: add utils.read_ulong()
  docs: vmcoreinfo: add lockless printk ringbuffer vmcoreinfo
  printk: reduce LOG_BUF_SHIFT range for H8300
  printk: ringbuffer: support dataless records
  ...
parents 029f56db 70333f4f
...@@ -170,57 +170,103 @@ document trapinfo ...@@ -170,57 +170,103 @@ 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) set var $info = $arg1
set $prev_flags = $arg1 if ($argc > 2)
set var $prev_flags = $arg2
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 $prefix = 1
set $newline = 1 set var $newline = 1
set $log = log_buf + $idx + sizeof(*$msg)
set var $begin = $desc->text_blk_lpos.begin % (1U << prb->text_data_ring.size_bits)
# prev & LOG_CONT && !(msg->flags & LOG_PREIX) set var $next = $desc->text_blk_lpos.next % (1U << prb->text_data_ring.size_bits)
if (($prev_flags & 8) && !($msg->flags & 4))
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 $dict = &$info->dev_info.subsystem[0]
while ($idx < $msg->dict_len) set var $dict_len = sizeof($info->dev_info.subsystem)
if ($line) if ($dict[0] != '\0')
printf " " printf " SUBSYSTEM="
set $line = 0 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 end
set $c = $dict[$idx] 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') if ($c == '\0')
printf "\n" loop_break
set $line = 1
else else
if ($c < ' ' || $c >= 127 || $c == '\\') if ($c < ' ' || $c >= 127 || $c == '\\')
printf "\\x%02x", $c printf "\\x%02x", $c
...@@ -228,33 +274,46 @@ define dump_log_idx ...@@ -228,33 +274,46 @@ 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 the second parameter is the info, the third parameter is
specified the previous log buffer's flags, used for properly optional and specifies the previous record's flags, used for
formatting continued lines. properly formatting continued lines.
end end
define dmesg define dmesg
set $i = log_first_idx # definitions from kernel/printk/printk_ringbuffer.h
set $end_idx = log_first_idx set var $desc_committed = 1
set $prev_flags = 0 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) 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 var $info = &prb->desc_ring.infos[$id % $desc_count]
set $i = 0
else # skip non-committed record
dump_log_idx $i $prev_flags set var $state = 3 & ($desc->state_var.counter >> $desc_flags_shift)
set $i = $i + $msg->len if ($state == $desc_committed || $state == $desc_finalized)
set $prev_flags = $msg->flags dump_record $desc $info $prev_flags
set var $prev_flags = $info->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
......
...@@ -189,50 +189,123 @@ from this. ...@@ -189,50 +189,123 @@ from this.
Free areas descriptor. User-space tools use this value to iterate the Free areas descriptor. User-space tools use this value to iterate the
free_area ranges. MAX_ORDER is used by the zone buddy allocator. free_area ranges. MAX_ORDER is used by the zone buddy allocator.
log_first_idx prb
---
A pointer to the printk ringbuffer (struct printk_ringbuffer). This
may be pointing to the static boot ringbuffer or the dynamically
allocated ringbuffer, depending on when the the core dump occurred.
Used by user-space tools to read the active kernel log buffer.
printk_rb_static
----------------
A pointer to the static boot printk ringbuffer. If @prb has a
different value, this is useful for viewing the initial boot messages,
which may have been overwritten in the dynamically allocated
ringbuffer.
clear_seq
---------
The sequence number of the printk() record after the last clear
command. It indicates the first record after the last
SYSLOG_ACTION_CLEAR, like issued by 'dmesg -c'. Used by user-space
tools to dump a subset of the dmesg log.
printk_ringbuffer
-----------------
The size of a printk_ringbuffer structure. This structure contains all
information required for accessing the various components of the
kernel log buffer.
(printk_ringbuffer, desc_ring|text_data_ring|dict_data_ring|fail)
-----------------------------------------------------------------
Offsets for the various components of the printk ringbuffer. Used by
user-space tools to view the kernel log buffer without requiring the
declaration of the structure.
prb_desc_ring
------------- -------------
Index of the first record stored in the buffer log_buf. Used by The size of the prb_desc_ring structure. This structure contains
user-space tools to read the strings in the log_buf. information about the set of record descriptors.
log_buf (prb_desc_ring, count_bits|descs|head_id|tail_id)
------- -------------------------------------------------
Offsets for the fields describing the set of record descriptors. Used
by user-space tools to be able to traverse the descriptors without
requiring the declaration of the structure.
prb_desc
--------
The size of the prb_desc structure. This structure contains
information about a single record descriptor.
(prb_desc, info|state_var|text_blk_lpos|dict_blk_lpos)
------------------------------------------------------
Offsets for the fields describing a record descriptors. Used by
user-space tools to be able to read descriptors without requiring
the declaration of the structure.
prb_data_blk_lpos
-----------------
The size of the prb_data_blk_lpos structure. This structure contains
information about where the text or dictionary data (data block) is
located within the respective data ring.
(prb_data_blk_lpos, begin|next)
-------------------------------
Console output is written to the ring buffer log_buf at index Offsets for the fields describing the location of a data block. Used
log_first_idx. Used to get the kernel log. by user-space tools to be able to locate data blocks without
requiring the declaration of the structure.
log_buf_len printk_info
----------- -----------
log_buf's length. The size of the printk_info structure. This structure contains all
the meta-data for a record.
clear_idx (printk_info, seq|ts_nsec|text_len|dict_len|caller_id)
--------- ------------------------------------------------------
The index that the next printk() record to read after the last clear Offsets for the fields providing the meta-data for a record. Used by
command. It indicates the first record after the last SYSLOG_ACTION user-space tools to be able to read the information without requiring
_CLEAR, like issued by 'dmesg -c'. Used by user-space tools to dump the declaration of the structure.
the dmesg log.
log_next_idx prb_data_ring
------------ -------------
The index of the next record to store in the buffer log_buf. Used to The size of the prb_data_ring structure. This structure contains
compute the index of the current buffer position. information about a set of data blocks.
printk_log (prb_data_ring, size_bits|data|head_lpos|tail_lpos)
---------- ---------------------------------------------------
The size of a structure printk_log. Used to compute the size of Offsets for the fields describing a set of data blocks. Used by
messages, and extract dmesg log. It encapsulates header information for user-space tools to be able to access the data blocks without
log_buf, such as timestamp, syslog level, etc. requiring the declaration of the structure.
(printk_log, ts_nsec|len|text_len|dict_len) atomic_long_t
------------------------------------------- -------------
The size of the atomic_long_t structure. Used by user-space tools to
be able to copy the full structure, regardless of its
architecture-specific implementation.
(atomic_long_t, counter)
------------------------
It represents field offsets in struct printk_log. User space tools Offset for the long value of an atomic_long_t variable. Used by
parse it and check whether the values of printk_log's members have been user-space tools to access the long value without requiring the
changed. architecture-specific declaration.
(free_area.free_list, MIGRATE_TYPES) (free_area.free_list, MIGRATE_TYPES)
------------------------------------ ------------------------------------
......
...@@ -13970,6 +13970,7 @@ PRINTK ...@@ -13970,6 +13970,7 @@ PRINTK
M: Petr Mladek <pmladek@suse.com> M: Petr Mladek <pmladek@suse.com>
M: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> M: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
R: Steven Rostedt <rostedt@goodmis.org> R: Steven Rostedt <rostedt@goodmis.org>
R: John Ogness <john.ogness@linutronix.de>
S: Maintained S: Maintained
F: include/linux/printk.h F: include/linux/printk.h
F: kernel/printk/ F: kernel/printk/
......
...@@ -4061,22 +4061,21 @@ void device_shutdown(void) ...@@ -4061,22 +4061,21 @@ void device_shutdown(void)
*/ */
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
static int static void
create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) set_dev_info(const struct device *dev, struct dev_printk_info *dev_info)
{ {
const char *subsys; const char *subsys;
size_t pos = 0;
memset(dev_info, 0, sizeof(*dev_info));
if (dev->class) if (dev->class)
subsys = dev->class->name; subsys = dev->class->name;
else if (dev->bus) else if (dev->bus)
subsys = dev->bus->name; subsys = dev->bus->name;
else else
return 0; return;
pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys); strscpy(dev_info->subsystem, subsys, sizeof(dev_info->subsystem));
if (pos >= hdrlen)
goto overflow;
/* /*
* Add device identifier DEVICE=: * Add device identifier DEVICE=:
...@@ -4092,41 +4091,28 @@ create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) ...@@ -4092,41 +4091,28 @@ create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
c = 'b'; c = 'b';
else else
c = 'c'; c = 'c';
pos++;
pos += snprintf(hdr + pos, hdrlen - pos, snprintf(dev_info->device, sizeof(dev_info->device),
"DEVICE=%c%u:%u", "%c%u:%u", c, MAJOR(dev->devt), MINOR(dev->devt));
c, MAJOR(dev->devt), MINOR(dev->devt));
} else if (strcmp(subsys, "net") == 0) { } else if (strcmp(subsys, "net") == 0) {
struct net_device *net = to_net_dev(dev); struct net_device *net = to_net_dev(dev);
pos++; snprintf(dev_info->device, sizeof(dev_info->device),
pos += snprintf(hdr + pos, hdrlen - pos, "n%u", net->ifindex);
"DEVICE=n%u", net->ifindex);
} else { } else {
pos++; snprintf(dev_info->device, sizeof(dev_info->device),
pos += snprintf(hdr + pos, hdrlen - pos, "+%s:%s", subsys, dev_name(dev));
"DEVICE=+%s:%s", subsys, dev_name(dev));
} }
if (pos >= hdrlen)
goto overflow;
return pos;
overflow:
dev_WARN(dev, "device/subsystem name too long");
return 0;
} }
int dev_vprintk_emit(int level, const struct device *dev, int dev_vprintk_emit(int level, const struct device *dev,
const char *fmt, va_list args) const char *fmt, va_list args)
{ {
char hdr[128]; struct dev_printk_info dev_info;
size_t hdrlen;
hdrlen = create_syslog_header(dev, hdr, sizeof(hdr)); set_dev_info(dev, &dev_info);
return vprintk_emit(0, level, hdrlen ? hdr : NULL, hdrlen, fmt, args); return vprintk_emit(0, level, &dev_info, fmt, args);
} }
EXPORT_SYMBOL(dev_vprintk_emit); EXPORT_SYMBOL(dev_vprintk_emit);
......
...@@ -55,6 +55,9 @@ phys_addr_t paddr_vmcoreinfo_note(void); ...@@ -55,6 +55,9 @@ phys_addr_t paddr_vmcoreinfo_note(void);
#define VMCOREINFO_OFFSET(name, field) \ #define VMCOREINFO_OFFSET(name, field) \
vmcoreinfo_append_str("OFFSET(%s.%s)=%lu\n", #name, #field, \ vmcoreinfo_append_str("OFFSET(%s.%s)=%lu\n", #name, #field, \
(unsigned long)offsetof(struct name, field)) (unsigned long)offsetof(struct name, field))
#define VMCOREINFO_TYPE_OFFSET(name, field) \
vmcoreinfo_append_str("OFFSET(%s.%s)=%lu\n", #name, #field, \
(unsigned long)offsetof(name, field))
#define VMCOREINFO_LENGTH(name, value) \ #define VMCOREINFO_LENGTH(name, value) \
vmcoreinfo_append_str("LENGTH(%s)=%lu\n", #name, (unsigned long)value) vmcoreinfo_append_str("LENGTH(%s)=%lu\n", #name, (unsigned long)value)
#define VMCOREINFO_NUMBER(name) \ #define VMCOREINFO_NUMBER(name) \
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
#ifndef __LINUX_DEBUG_LOCKING_H #ifndef __LINUX_DEBUG_LOCKING_H
#define __LINUX_DEBUG_LOCKING_H #define __LINUX_DEBUG_LOCKING_H
#include <linux/kernel.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/printk.h>
struct task_struct; struct task_struct;
......
...@@ -21,6 +21,14 @@ ...@@ -21,6 +21,14 @@
struct device; struct device;
#define PRINTK_INFO_SUBSYSTEM_LEN 16
#define PRINTK_INFO_DEVICE_LEN 48
struct dev_printk_info {
char subsystem[PRINTK_INFO_SUBSYSTEM_LEN];
char device[PRINTK_INFO_DEVICE_LEN];
};
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
__printf(3, 0) __cold __printf(3, 0) __cold
......
...@@ -526,7 +526,6 @@ extern unsigned int sysctl_oops_all_cpu_backtrace; ...@@ -526,7 +526,6 @@ extern unsigned int sysctl_oops_all_cpu_backtrace;
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
extern void bust_spinlocks(int yes); extern void bust_spinlocks(int yes);
extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in progress */
extern int panic_timeout; extern int panic_timeout;
extern unsigned long panic_print; extern unsigned long panic_print;
extern int panic_on_oops; extern int panic_on_oops;
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
extern const char linux_banner[]; extern const char linux_banner[];
extern const char linux_proc_banner[]; extern const char linux_proc_banner[];
extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in progress */
#define PRINTK_MAX_SINGLE_HEADER_LEN 2 #define PRINTK_MAX_SINGLE_HEADER_LEN 2
static inline int printk_get_level(const char *buffer) static inline int printk_get_level(const char *buffer)
...@@ -159,10 +161,12 @@ static inline void printk_nmi_direct_enter(void) { } ...@@ -159,10 +161,12 @@ static inline void printk_nmi_direct_enter(void) { }
static inline void printk_nmi_direct_exit(void) { } static inline void printk_nmi_direct_exit(void) { }
#endif /* PRINTK_NMI */ #endif /* PRINTK_NMI */
struct dev_printk_info;
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
asmlinkage __printf(5, 0) asmlinkage __printf(4, 0)
int vprintk_emit(int facility, int level, int vprintk_emit(int facility, int level,
const char *dict, size_t dictlen, const struct dev_printk_info *dev_info,
const char *fmt, va_list args); const char *fmt, va_list args);
asmlinkage __printf(1, 0) asmlinkage __printf(1, 0)
......
...@@ -682,7 +682,8 @@ config IKHEADERS ...@@ -682,7 +682,8 @@ config IKHEADERS
config LOG_BUF_SHIFT config LOG_BUF_SHIFT
int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
range 12 25 range 12 25 if !H8300
range 12 19 if H8300
default 17 default 17
depends on PRINTK depends on PRINTK
help help
......
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
obj-y = printk.o obj-y = printk.o
obj-$(CONFIG_PRINTK) += printk_safe.o obj-$(CONFIG_PRINTK) += printk_safe.o
obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o
obj-$(CONFIG_PRINTK) += printk_ringbuffer.o
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
extern raw_spinlock_t logbuf_lock; extern raw_spinlock_t logbuf_lock;
__printf(5, 0) __printf(4, 0)
int vprintk_store(int facility, int level, int vprintk_store(int facility, int level,
const char *dict, size_t dictlen, const struct dev_printk_info *dev_info,
const char *fmt, va_list args); const char *fmt, va_list args);
__printf(1, 0) int vprintk_default(const char *fmt, va_list args); __printf(1, 0) int vprintk_default(const char *fmt, va_list args);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -375,7 +375,7 @@ __printf(1, 0) int vprintk_func(const char *fmt, va_list args) ...@@ -375,7 +375,7 @@ __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
raw_spin_trylock(&logbuf_lock)) { raw_spin_trylock(&logbuf_lock)) {
int len; int len;
len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args); len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, fmt, args);
raw_spin_unlock(&logbuf_lock); raw_spin_unlock(&logbuf_lock);
defer_console_output(); defer_console_output();
return len; return len;
......
...@@ -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,110 @@ class LxDmesg(gdb.Command): ...@@ -26,44 +31,110 @@ 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 info array
gdb.write("Corrupted log buffer!\n") info_sz = printk_info_type.get_type().sizeof
off = prb_desc_ring_type.get_type()['infos'].bitpos // 8
addr = utils.read_ulong(desc_ring, off)
infos = utils.read_memoryview(inf, addr, info_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)
ts_off = printk_info_type.get_type()['ts_nsec'].bitpos // 8
len_off = printk_info_type.get_type()['text_len'].bitpos // 8
# definitions from kernel/printk/printk_ringbuffer.h
desc_committed = 1
desc_finalized = 2
desc_sv_bits = utils.get_long_type().sizeof * 8
desc_flags_shift = desc_sv_bits - 2
desc_flags_mask = 3 << desc_flags_shift
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
info_off = info_sz * ind
# skip non-committed record
state = 3 & (utils.read_u64(descs, desc_off + sv_off +
counter_off) >> desc_flags_shift)
if state != desc_committed and state != desc_finalized:
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(infos, info_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(infos, info_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 +146,9 @@ class LxDmesg(gdb.Command): ...@@ -75,7 +146,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()
...@@ -123,6 +123,13 @@ def read_u64(buffer, offset): ...@@ -123,6 +123,13 @@ def read_u64(buffer, offset):
return read_u32(buffer, offset + 4) + (read_u32(buffer, offset) << 32) return read_u32(buffer, offset + 4) + (read_u32(buffer, offset) << 32)
def read_ulong(buffer, offset):
if get_long_type().sizeof == 8:
return read_u64(buffer, offset)
else:
return read_u32(buffer, offset)
target_arch = None target_arch = None
......
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