forked from luck/tmp_suning_uos_patched
Merge branch 'printk-rework' into for-linus
This commit is contained in:
commit
70333f4ff9
|
@ -170,57 +170,82 @@ 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
|
|
||||||
set $msg = ((struct printk_log *) (log_buf + $idx))
|
|
||||||
set $prefix = 1
|
|
||||||
set $newline = 1
|
|
||||||
set $log = log_buf + $idx + sizeof(*$msg)
|
|
||||||
|
|
||||||
# prev & LOG_CONT && !(msg->flags & LOG_PREIX)
|
|
||||||
if (($prev_flags & 8) && !($msg->flags & 4))
|
|
||||||
set $prefix = 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# msg->flags & LOG_CONT
|
set var $prefix = 1
|
||||||
if ($msg->flags & 8)
|
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))
|
# (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
|
||||||
end
|
while ($idx < $dict_len)
|
||||||
set $c = $dict[$idx]
|
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 +253,67 @@ define dump_log_idx
|
||||||
printf "%c", $c
|
printf "%c", $c
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
set $idx = $idx + 1
|
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
|
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.
|
||||||
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
|
||||||
-------------
|
---
|
||||||
|
|
||||||
Index of the first record stored in the buffer log_buf. Used by
|
A pointer to the printk ringbuffer (struct printk_ringbuffer). This
|
||||||
user-space tools to read the strings in the log_buf.
|
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.
|
||||||
|
|
||||||
log_buf
|
printk_rb_static
|
||||||
-------
|
----------------
|
||||||
|
|
||||||
Console output is written to the ring buffer log_buf at index
|
A pointer to the static boot printk ringbuffer. If @prb has a
|
||||||
log_first_idx. Used to get the kernel log.
|
different value, this is useful for viewing the initial boot messages,
|
||||||
|
which may have been overwritten in the dynamically allocated
|
||||||
|
ringbuffer.
|
||||||
|
|
||||||
log_buf_len
|
clear_seq
|
||||||
-----------
|
|
||||||
|
|
||||||
log_buf's length.
|
|
||||||
|
|
||||||
clear_idx
|
|
||||||
---------
|
---------
|
||||||
|
|
||||||
The index that the next printk() record to read after the last clear
|
The sequence number of the printk() record after the last clear
|
||||||
command. It indicates the first record after the last SYSLOG_ACTION
|
command. It indicates the first record after the last
|
||||||
_CLEAR, like issued by 'dmesg -c'. Used by user-space tools to dump
|
SYSLOG_ACTION_CLEAR, like issued by 'dmesg -c'. Used by user-space
|
||||||
the dmesg log.
|
tools to dump a subset of the dmesg log.
|
||||||
|
|
||||||
log_next_idx
|
printk_ringbuffer
|
||||||
------------
|
-----------------
|
||||||
|
|
||||||
The index of the next record to store in the buffer log_buf. Used to
|
The size of a printk_ringbuffer structure. This structure contains all
|
||||||
compute the index of the current buffer position.
|
information required for accessing the various components of the
|
||||||
|
kernel log buffer.
|
||||||
|
|
||||||
printk_log
|
(printk_ringbuffer, desc_ring|text_data_ring|dict_data_ring|fail)
|
||||||
----------
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
The size of a structure printk_log. Used to compute the size of
|
Offsets for the various components of the printk ringbuffer. Used by
|
||||||
messages, and extract dmesg log. It encapsulates header information for
|
user-space tools to view the kernel log buffer without requiring the
|
||||||
log_buf, such as timestamp, syslog level, etc.
|
declaration of the structure.
|
||||||
|
|
||||||
(printk_log, ts_nsec|len|text_len|dict_len)
|
prb_desc_ring
|
||||||
-------------------------------------------
|
-------------
|
||||||
|
|
||||||
It represents field offsets in struct printk_log. User space tools
|
The size of the prb_desc_ring structure. This structure contains
|
||||||
parse it and check whether the values of printk_log's members have been
|
information about the set of record descriptors.
|
||||||
changed.
|
|
||||||
|
(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)
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Offsets for the fields describing the location of a data block. Used
|
||||||
|
by user-space tools to be able to locate data blocks without
|
||||||
|
requiring the declaration of the structure.
|
||||||
|
|
||||||
|
printk_info
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The size of the printk_info structure. This structure contains all
|
||||||
|
the meta-data for a record.
|
||||||
|
|
||||||
|
(printk_info, seq|ts_nsec|text_len|dict_len|caller_id)
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
Offsets for the fields providing the meta-data for a record. Used by
|
||||||
|
user-space tools to be able to read the information without requiring
|
||||||
|
the declaration of the structure.
|
||||||
|
|
||||||
|
prb_data_ring
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The size of the prb_data_ring structure. This structure contains
|
||||||
|
information about a set of data blocks.
|
||||||
|
|
||||||
|
(prb_data_ring, size_bits|data|head_lpos|tail_lpos)
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
Offsets for the fields describing a set of data blocks. Used by
|
||||||
|
user-space tools to be able to access the data blocks without
|
||||||
|
requiring the declaration of the structure.
|
||||||
|
|
||||||
|
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)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Offset for the long value of an atomic_long_t variable. Used by
|
||||||
|
user-space tools to access the long value without requiring the
|
||||||
|
architecture-specific declaration.
|
||||||
|
|
||||||
(free_area.free_list, MIGRATE_TYPES)
|
(free_area.free_list, MIGRATE_TYPES)
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
|
@ -13838,6 +13838,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/
|
||||||
|
|
|
@ -3835,22 +3835,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=:
|
||||||
|
@ -3866,41 +3865,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);
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,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) \
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -161,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
|
||||||
|
|
||||||
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 @@
|
||||||
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 @@
|
||||||
|
|
||||||
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);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
2083
kernel/printk/printk_ringbuffer.c
Normal file
2083
kernel/printk/printk_ringbuffer.c
Normal file
File diff suppressed because it is too large
Load Diff
382
kernel/printk/printk_ringbuffer.h
Normal file
382
kernel/printk/printk_ringbuffer.h
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
|
||||||
|
#ifndef _KERNEL_PRINTK_RINGBUFFER_H
|
||||||
|
#define _KERNEL_PRINTK_RINGBUFFER_H
|
||||||
|
|
||||||
|
#include <linux/atomic.h>
|
||||||
|
#include <linux/dev_printk.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Meta information about each stored message.
|
||||||
|
*
|
||||||
|
* All fields are set by the printk code except for @seq, which is
|
||||||
|
* set by the ringbuffer code.
|
||||||
|
*/
|
||||||
|
struct printk_info {
|
||||||
|
u64 seq; /* sequence number */
|
||||||
|
u64 ts_nsec; /* timestamp in nanoseconds */
|
||||||
|
u16 text_len; /* length of text message */
|
||||||
|
u8 facility; /* syslog facility */
|
||||||
|
u8 flags:5; /* internal record flags */
|
||||||
|
u8 level:3; /* syslog level */
|
||||||
|
u32 caller_id; /* thread id or processor id */
|
||||||
|
|
||||||
|
struct dev_printk_info dev_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A structure providing the buffers, used by writers and readers.
|
||||||
|
*
|
||||||
|
* Writers:
|
||||||
|
* Using prb_rec_init_wr(), a writer sets @text_buf_size before calling
|
||||||
|
* prb_reserve(). On success, prb_reserve() sets @info and @text_buf to
|
||||||
|
* buffers reserved for that writer.
|
||||||
|
*
|
||||||
|
* Readers:
|
||||||
|
* Using prb_rec_init_rd(), a reader sets all fields before calling
|
||||||
|
* prb_read_valid(). Note that the reader provides the @info and @text_buf,
|
||||||
|
* buffers. On success, the struct pointed to by @info will be filled and
|
||||||
|
* the char array pointed to by @text_buf will be filled with text data.
|
||||||
|
*/
|
||||||
|
struct printk_record {
|
||||||
|
struct printk_info *info;
|
||||||
|
char *text_buf;
|
||||||
|
unsigned int text_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Specifies the logical position and span of a data block. */
|
||||||
|
struct prb_data_blk_lpos {
|
||||||
|
unsigned long begin;
|
||||||
|
unsigned long next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A descriptor: the complete meta-data for a record.
|
||||||
|
*
|
||||||
|
* @state_var: A bitwise combination of descriptor ID and descriptor state.
|
||||||
|
*/
|
||||||
|
struct prb_desc {
|
||||||
|
atomic_long_t state_var;
|
||||||
|
struct prb_data_blk_lpos text_blk_lpos;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A ringbuffer of "ID + data" elements. */
|
||||||
|
struct prb_data_ring {
|
||||||
|
unsigned int size_bits;
|
||||||
|
char *data;
|
||||||
|
atomic_long_t head_lpos;
|
||||||
|
atomic_long_t tail_lpos;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A ringbuffer of "struct prb_desc" elements. */
|
||||||
|
struct prb_desc_ring {
|
||||||
|
unsigned int count_bits;
|
||||||
|
struct prb_desc *descs;
|
||||||
|
struct printk_info *infos;
|
||||||
|
atomic_long_t head_id;
|
||||||
|
atomic_long_t tail_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The high level structure representing the printk ringbuffer.
|
||||||
|
*
|
||||||
|
* @fail: Count of failed prb_reserve() calls where not even a data-less
|
||||||
|
* record was created.
|
||||||
|
*/
|
||||||
|
struct printk_ringbuffer {
|
||||||
|
struct prb_desc_ring desc_ring;
|
||||||
|
struct prb_data_ring text_data_ring;
|
||||||
|
atomic_long_t fail;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used by writers as a reserve/commit handle.
|
||||||
|
*
|
||||||
|
* @rb: Ringbuffer where the entry is reserved.
|
||||||
|
* @irqflags: Saved irq flags to restore on entry commit.
|
||||||
|
* @id: ID of the reserved descriptor.
|
||||||
|
* @text_space: Total occupied buffer space in the text data ring, including
|
||||||
|
* ID, alignment padding, and wrapping data blocks.
|
||||||
|
*
|
||||||
|
* This structure is an opaque handle for writers. Its contents are only
|
||||||
|
* to be used by the ringbuffer implementation.
|
||||||
|
*/
|
||||||
|
struct prb_reserved_entry {
|
||||||
|
struct printk_ringbuffer *rb;
|
||||||
|
unsigned long irqflags;
|
||||||
|
unsigned long id;
|
||||||
|
unsigned int text_space;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The possible responses of a descriptor state-query. */
|
||||||
|
enum desc_state {
|
||||||
|
desc_miss = -1, /* ID mismatch (pseudo state) */
|
||||||
|
desc_reserved = 0x0, /* reserved, in use by writer */
|
||||||
|
desc_committed = 0x1, /* committed by writer, could get reopened */
|
||||||
|
desc_finalized = 0x2, /* committed, no further modification allowed */
|
||||||
|
desc_reusable = 0x3, /* free, not yet used by any writer */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _DATA_SIZE(sz_bits) (1UL << (sz_bits))
|
||||||
|
#define _DESCS_COUNT(ct_bits) (1U << (ct_bits))
|
||||||
|
#define DESC_SV_BITS (sizeof(unsigned long) * 8)
|
||||||
|
#define DESC_FLAGS_SHIFT (DESC_SV_BITS - 2)
|
||||||
|
#define DESC_FLAGS_MASK (3UL << DESC_FLAGS_SHIFT)
|
||||||
|
#define DESC_STATE(sv) (3UL & (sv >> DESC_FLAGS_SHIFT))
|
||||||
|
#define DESC_SV(id, state) (((unsigned long)state << DESC_FLAGS_SHIFT) | id)
|
||||||
|
#define DESC_ID_MASK (~DESC_FLAGS_MASK)
|
||||||
|
#define DESC_ID(sv) ((sv) & DESC_ID_MASK)
|
||||||
|
#define FAILED_LPOS 0x1
|
||||||
|
#define NO_LPOS 0x3
|
||||||
|
|
||||||
|
#define FAILED_BLK_LPOS \
|
||||||
|
{ \
|
||||||
|
.begin = FAILED_LPOS, \
|
||||||
|
.next = FAILED_LPOS, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Descriptor Bootstrap
|
||||||
|
*
|
||||||
|
* The descriptor array is minimally initialized to allow immediate usage
|
||||||
|
* by readers and writers. The requirements that the descriptor array
|
||||||
|
* initialization must satisfy:
|
||||||
|
*
|
||||||
|
* Req1
|
||||||
|
* The tail must point to an existing (committed or reusable) descriptor.
|
||||||
|
* This is required by the implementation of prb_first_seq().
|
||||||
|
*
|
||||||
|
* Req2
|
||||||
|
* Readers must see that the ringbuffer is initially empty.
|
||||||
|
*
|
||||||
|
* Req3
|
||||||
|
* The first record reserved by a writer is assigned sequence number 0.
|
||||||
|
*
|
||||||
|
* To satisfy Req1, the tail initially points to a descriptor that is
|
||||||
|
* minimally initialized (having no data block, i.e. data-less with the
|
||||||
|
* data block's lpos @begin and @next values set to FAILED_LPOS).
|
||||||
|
*
|
||||||
|
* To satisfy Req2, the initial tail descriptor is initialized to the
|
||||||
|
* reusable state. Readers recognize reusable descriptors as existing
|
||||||
|
* records, but skip over them.
|
||||||
|
*
|
||||||
|
* To satisfy Req3, the last descriptor in the array is used as the initial
|
||||||
|
* head (and tail) descriptor. This allows the first record reserved by a
|
||||||
|
* writer (head + 1) to be the first descriptor in the array. (Only the first
|
||||||
|
* descriptor in the array could have a valid sequence number of 0.)
|
||||||
|
*
|
||||||
|
* The first time a descriptor is reserved, it is assigned a sequence number
|
||||||
|
* with the value of the array index. A "first time reserved" descriptor can
|
||||||
|
* be recognized because it has a sequence number of 0 but does not have an
|
||||||
|
* index of 0. (Only the first descriptor in the array could have a valid
|
||||||
|
* sequence number of 0.) After the first reservation, all future reservations
|
||||||
|
* (recycling) simply involve incrementing the sequence number by the array
|
||||||
|
* count.
|
||||||
|
*
|
||||||
|
* Hack #1
|
||||||
|
* Only the first descriptor in the array is allowed to have the sequence
|
||||||
|
* number 0. In this case it is not possible to recognize if it is being
|
||||||
|
* reserved the first time (set to index value) or has been reserved
|
||||||
|
* previously (increment by the array count). This is handled by _always_
|
||||||
|
* incrementing the sequence number by the array count when reserving the
|
||||||
|
* first descriptor in the array. In order to satisfy Req3, the sequence
|
||||||
|
* number of the first descriptor in the array is initialized to minus
|
||||||
|
* the array count. Then, upon the first reservation, it is incremented
|
||||||
|
* to 0, thus satisfying Req3.
|
||||||
|
*
|
||||||
|
* Hack #2
|
||||||
|
* prb_first_seq() can be called at any time by readers to retrieve the
|
||||||
|
* sequence number of the tail descriptor. However, due to Req2 and Req3,
|
||||||
|
* initially there are no records to report the sequence number of
|
||||||
|
* (sequence numbers are u64 and there is nothing less than 0). To handle
|
||||||
|
* this, the sequence number of the initial tail descriptor is initialized
|
||||||
|
* to 0. Technically this is incorrect, because there is no record with
|
||||||
|
* sequence number 0 (yet) and the tail descriptor is not the first
|
||||||
|
* descriptor in the array. But it allows prb_read_valid() to correctly
|
||||||
|
* report the existence of a record for _any_ given sequence number at all
|
||||||
|
* times. Bootstrapping is complete when the tail is pushed the first
|
||||||
|
* time, thus finally pointing to the first descriptor reserved by a
|
||||||
|
* writer, which has the assigned sequence number 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiating Logical Value Overflows
|
||||||
|
*
|
||||||
|
* Both logical position (lpos) and ID values can be mapped to array indexes
|
||||||
|
* but may experience overflows during the lifetime of the system. To ensure
|
||||||
|
* that printk_ringbuffer can handle the overflows for these types, initial
|
||||||
|
* values are chosen that map to the correct initial array indexes, but will
|
||||||
|
* result in overflows soon.
|
||||||
|
*
|
||||||
|
* BLK0_LPOS
|
||||||
|
* The initial @head_lpos and @tail_lpos for data rings. It is at index
|
||||||
|
* 0 and the lpos value is such that it will overflow on the first wrap.
|
||||||
|
*
|
||||||
|
* DESC0_ID
|
||||||
|
* The initial @head_id and @tail_id for the desc ring. It is at the last
|
||||||
|
* index of the descriptor array (see Req3 above) and the ID value is such
|
||||||
|
* that it will overflow on the second wrap.
|
||||||
|
*/
|
||||||
|
#define BLK0_LPOS(sz_bits) (-(_DATA_SIZE(sz_bits)))
|
||||||
|
#define DESC0_ID(ct_bits) DESC_ID(-(_DESCS_COUNT(ct_bits) + 1))
|
||||||
|
#define DESC0_SV(ct_bits) DESC_SV(DESC0_ID(ct_bits), desc_reusable)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define a ringbuffer with an external text data buffer. The same as
|
||||||
|
* DEFINE_PRINTKRB() but requires specifying an external buffer for the
|
||||||
|
* text data.
|
||||||
|
*
|
||||||
|
* Note: The specified external buffer must be of the size:
|
||||||
|
* 2 ^ (descbits + avgtextbits)
|
||||||
|
*/
|
||||||
|
#define _DEFINE_PRINTKRB(name, descbits, avgtextbits, text_buf) \
|
||||||
|
static struct prb_desc _##name##_descs[_DESCS_COUNT(descbits)] = { \
|
||||||
|
/* the initial head and tail */ \
|
||||||
|
[_DESCS_COUNT(descbits) - 1] = { \
|
||||||
|
/* reusable */ \
|
||||||
|
.state_var = ATOMIC_INIT(DESC0_SV(descbits)), \
|
||||||
|
/* no associated data block */ \
|
||||||
|
.text_blk_lpos = FAILED_BLK_LPOS, \
|
||||||
|
}, \
|
||||||
|
}; \
|
||||||
|
static struct printk_info _##name##_infos[_DESCS_COUNT(descbits)] = { \
|
||||||
|
/* this will be the first record reserved by a writer */ \
|
||||||
|
[0] = { \
|
||||||
|
/* will be incremented to 0 on the first reservation */ \
|
||||||
|
.seq = -(u64)_DESCS_COUNT(descbits), \
|
||||||
|
}, \
|
||||||
|
/* the initial head and tail */ \
|
||||||
|
[_DESCS_COUNT(descbits) - 1] = { \
|
||||||
|
/* reports the first seq value during the bootstrap phase */ \
|
||||||
|
.seq = 0, \
|
||||||
|
}, \
|
||||||
|
}; \
|
||||||
|
static struct printk_ringbuffer name = { \
|
||||||
|
.desc_ring = { \
|
||||||
|
.count_bits = descbits, \
|
||||||
|
.descs = &_##name##_descs[0], \
|
||||||
|
.infos = &_##name##_infos[0], \
|
||||||
|
.head_id = ATOMIC_INIT(DESC0_ID(descbits)), \
|
||||||
|
.tail_id = ATOMIC_INIT(DESC0_ID(descbits)), \
|
||||||
|
}, \
|
||||||
|
.text_data_ring = { \
|
||||||
|
.size_bits = (avgtextbits) + (descbits), \
|
||||||
|
.data = text_buf, \
|
||||||
|
.head_lpos = ATOMIC_LONG_INIT(BLK0_LPOS((avgtextbits) + (descbits))), \
|
||||||
|
.tail_lpos = ATOMIC_LONG_INIT(BLK0_LPOS((avgtextbits) + (descbits))), \
|
||||||
|
}, \
|
||||||
|
.fail = ATOMIC_LONG_INIT(0), \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEFINE_PRINTKRB() - Define a ringbuffer.
|
||||||
|
*
|
||||||
|
* @name: The name of the ringbuffer variable.
|
||||||
|
* @descbits: The number of descriptors as a power-of-2 value.
|
||||||
|
* @avgtextbits: The average text data size per record as a power-of-2 value.
|
||||||
|
*
|
||||||
|
* This is a macro for defining a ringbuffer and all internal structures
|
||||||
|
* such that it is ready for immediate use. See _DEFINE_PRINTKRB() for a
|
||||||
|
* variant where the text data buffer can be specified externally.
|
||||||
|
*/
|
||||||
|
#define DEFINE_PRINTKRB(name, descbits, avgtextbits) \
|
||||||
|
static char _##name##_text[1U << ((avgtextbits) + (descbits))] \
|
||||||
|
__aligned(__alignof__(unsigned long)); \
|
||||||
|
_DEFINE_PRINTKRB(name, descbits, avgtextbits, &_##name##_text[0])
|
||||||
|
|
||||||
|
/* Writer Interface */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prb_rec_init_wd() - Initialize a buffer for writing records.
|
||||||
|
*
|
||||||
|
* @r: The record to initialize.
|
||||||
|
* @text_buf_size: The needed text buffer size.
|
||||||
|
*/
|
||||||
|
static inline void prb_rec_init_wr(struct printk_record *r,
|
||||||
|
unsigned int text_buf_size)
|
||||||
|
{
|
||||||
|
r->info = NULL;
|
||||||
|
r->text_buf = NULL;
|
||||||
|
r->text_buf_size = text_buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool prb_reserve(struct prb_reserved_entry *e, struct printk_ringbuffer *rb,
|
||||||
|
struct printk_record *r);
|
||||||
|
bool prb_reserve_in_last(struct prb_reserved_entry *e, struct printk_ringbuffer *rb,
|
||||||
|
struct printk_record *r, u32 caller_id, unsigned int max_size);
|
||||||
|
void prb_commit(struct prb_reserved_entry *e);
|
||||||
|
void prb_final_commit(struct prb_reserved_entry *e);
|
||||||
|
|
||||||
|
void prb_init(struct printk_ringbuffer *rb,
|
||||||
|
char *text_buf, unsigned int text_buf_size,
|
||||||
|
struct prb_desc *descs, unsigned int descs_count_bits,
|
||||||
|
struct printk_info *infos);
|
||||||
|
unsigned int prb_record_text_space(struct prb_reserved_entry *e);
|
||||||
|
|
||||||
|
/* Reader Interface */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prb_rec_init_rd() - Initialize a buffer for reading records.
|
||||||
|
*
|
||||||
|
* @r: The record to initialize.
|
||||||
|
* @info: A buffer to store record meta-data.
|
||||||
|
* @text_buf: A buffer to store text data.
|
||||||
|
* @text_buf_size: The size of @text_buf.
|
||||||
|
*
|
||||||
|
* Initialize all the fields that a reader is interested in. All arguments
|
||||||
|
* (except @r) are optional. Only record data for arguments that are
|
||||||
|
* non-NULL or non-zero will be read.
|
||||||
|
*/
|
||||||
|
static inline void prb_rec_init_rd(struct printk_record *r,
|
||||||
|
struct printk_info *info,
|
||||||
|
char *text_buf, unsigned int text_buf_size)
|
||||||
|
{
|
||||||
|
r->info = info;
|
||||||
|
r->text_buf = text_buf;
|
||||||
|
r->text_buf_size = text_buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prb_for_each_record() - Iterate over the records of a ringbuffer.
|
||||||
|
*
|
||||||
|
* @from: The sequence number to begin with.
|
||||||
|
* @rb: The ringbuffer to iterate over.
|
||||||
|
* @s: A u64 to store the sequence number on each iteration.
|
||||||
|
* @r: A printk_record to store the record on each iteration.
|
||||||
|
*
|
||||||
|
* This is a macro for conveniently iterating over a ringbuffer.
|
||||||
|
* Note that @s may not be the sequence number of the record on each
|
||||||
|
* iteration. For the sequence number, @r->info->seq should be checked.
|
||||||
|
*
|
||||||
|
* Context: Any context.
|
||||||
|
*/
|
||||||
|
#define prb_for_each_record(from, rb, s, r) \
|
||||||
|
for ((s) = from; prb_read_valid(rb, s, r); (s) = (r)->info->seq + 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prb_for_each_info() - Iterate over the meta data of a ringbuffer.
|
||||||
|
*
|
||||||
|
* @from: The sequence number to begin with.
|
||||||
|
* @rb: The ringbuffer to iterate over.
|
||||||
|
* @s: A u64 to store the sequence number on each iteration.
|
||||||
|
* @i: A printk_info to store the record meta data on each iteration.
|
||||||
|
* @lc: An unsigned int to store the text line count of each record.
|
||||||
|
*
|
||||||
|
* This is a macro for conveniently iterating over a ringbuffer.
|
||||||
|
* Note that @s may not be the sequence number of the record on each
|
||||||
|
* iteration. For the sequence number, @r->info->seq should be checked.
|
||||||
|
*
|
||||||
|
* Context: Any context.
|
||||||
|
*/
|
||||||
|
#define prb_for_each_info(from, rb, s, i, lc) \
|
||||||
|
for ((s) = from; prb_read_valid_info(rb, s, i, lc); (s) = (i)->seq + 1)
|
||||||
|
|
||||||
|
bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq,
|
||||||
|
struct printk_record *r);
|
||||||
|
bool prb_read_valid_info(struct printk_ringbuffer *rb, u64 seq,
|
||||||
|
struct printk_info *info, unsigned int *line_count);
|
||||||
|
|
||||||
|
u64 prb_first_valid_seq(struct printk_ringbuffer *rb);
|
||||||
|
u64 prb_next_seq(struct printk_ringbuffer *rb);
|
||||||
|
|
||||||
|
#endif /* _KERNEL_PRINTK_RINGBUFFER_H */
|
|
@ -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
|
||||||
|
|
||||||
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):
|
||||||
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:
|
|
||||||
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
|
# read in prb structure
|
||||||
text_len_offset = printk_log_type.get_type()['text_len'].bitpos // 8
|
prb_addr = int(str(gdb.parse_and_eval("(void *)'printk.c'::prb")).split()[0], 16)
|
||||||
time_stamp_offset = printk_log_type.get_type()['ts_nsec'].bitpos // 8
|
sz = printk_ringbuffer_type.get_type().sizeof
|
||||||
text_offset = printk_log_type.get_type().sizeof
|
prb = utils.read_memoryview(inf, prb_addr, sz).tobytes()
|
||||||
|
|
||||||
pos = 0
|
# read in descriptor ring structure
|
||||||
while pos < log_buf.__len__():
|
off = printk_ringbuffer_type.get_type()['desc_ring'].bitpos // 8
|
||||||
length = utils.read_u16(log_buf, pos + length_offset)
|
addr = prb_addr + off
|
||||||
if length == 0:
|
sz = prb_desc_ring_type.get_type().sizeof
|
||||||
if log_buf_2nd_half == -1:
|
desc_ring = utils.read_memoryview(inf, addr, sz).tobytes()
|
||||||
gdb.write("Corrupted log buffer!\n")
|
|
||||||
|
# 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 info array
|
||||||
|
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):
|
||||||
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):
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user