Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1123 lines
38 KiB

commit 105a3e13167665dde5d3c12bf76ef9c916e82d0e
Author: Dave Anderson <anderson@redhat.com>
Date: Thu Oct 10 14:07:22 2019 -0400
Fix for Linux 5.4-rc1 and later kernels that contain commit
688fcbfc06e4fdfbb7e1d5a942a1460fe6379d2d, titled "mm/vmalloc:
modify struct vmap_area to reduce its size". Without the
patch "kmem -v" will display nothing; other architectures
that utilize the vmap_area_list to determine the base of
mapped/vmalloc address space will fail.
(anderson@redhat.com)
diff --git a/memory.c b/memory.c
index 3a8b998..fe82fac 100644
--- a/memory.c
+++ b/memory.c
@@ -401,9 +401,10 @@ vm_init(void)
STRUCT_SIZE_INIT(vmap_area, "vmap_area");
if (VALID_MEMBER(vmap_area_va_start) &&
VALID_MEMBER(vmap_area_va_end) &&
- VALID_MEMBER(vmap_area_flags) &&
VALID_MEMBER(vmap_area_list) &&
VALID_MEMBER(vmap_area_vm) &&
+ (VALID_MEMBER(vmap_area_flags) ||
+ (OFFSET(vmap_area_vm) == MEMBER_OFFSET("vmap_area", "purge_list"))) &&
kernel_symbol_exists("vmap_area_list"))
vt->flags |= USE_VMAP_AREA;
@@ -8742,7 +8743,7 @@ static void
dump_vmap_area(struct meminfo *vi)
{
int i, cnt;
- ulong start, end, vm_struct, flags;
+ ulong start, end, vm_struct, flags, vm;
struct list_data list_data, *ld;
char *vmap_area_buf;
ulong size, pcheck, count, verified;
@@ -8790,9 +8791,15 @@ dump_vmap_area(struct meminfo *vi)
readmem(ld->list_ptr[i], KVADDR, vmap_area_buf,
SIZE(vmap_area), "vmap_area struct", FAULT_ON_ERROR);
- flags = ULONG(vmap_area_buf + OFFSET(vmap_area_flags));
- if (flags != VM_VM_AREA)
- continue;
+ if (VALID_MEMBER(vmap_area_flags)) {
+ flags = ULONG(vmap_area_buf + OFFSET(vmap_area_flags));
+ if (flags != VM_VM_AREA)
+ continue;
+ } else {
+ vm = ULONG(vmap_area_buf + OFFSET(vmap_area_vm));
+ if (!vm)
+ continue;
+ }
start = ULONG(vmap_area_buf + OFFSET(vmap_area_va_start));
end = ULONG(vmap_area_buf + OFFSET(vmap_area_va_end));
vm_struct = ULONG(vmap_area_buf + OFFSET(vmap_area_vm));
commit 82ce13bceb1082a7c53c1bda71e17ca9c2a5cbc4
Author: Dave Anderson <anderson@redhat.com>
Date: Fri Oct 11 11:14:28 2019 -0400
Fix for Linux 5.4-rc1 and later kernels that contain commit/merge
e0703556644a531e50b5dc61b9f6ea83af5f6604, titled "Merge tag 'modules-
for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux
which introduces symbol namespaces. Without the patch, and depending
upon the architecture:
(1) the kernel module symbol list will contain garbage entries
(2) the session fails during session initialization with a dump of
the internal buffer allocation stats followed by the message
"crash: cannot allocate any more memory!"
(3) the session fails during session initialization with a
segmentation violation.
(anderson@redhat.com)
diff --git a/defs.h b/defs.h
index 32bd147..502e7c2 100644
--- a/defs.h
+++ b/defs.h
@@ -2694,6 +2694,7 @@ struct symbol_table_data {
ulong saved_command_line_vmlinux;
ulong pti_init_vmlinux;
ulong kaiser_init_vmlinux;
+ int kernel_symbol_type;
};
/* flags for st */
diff --git a/symbols.c b/symbols.c
index 3ce8692..7af5e69 100644
--- a/symbols.c
+++ b/symbols.c
@@ -1607,39 +1607,100 @@ union kernel_symbol {
unsigned long value;
const char *name;
} v1;
- /* kernel 4.19 introduced relative symbol positionning */
+ /* kernel 4.19 introduced relative symbol positioning */
struct kernel_symbol_v2 {
int value_offset;
int name_offset;
} v2;
+ /* kernel 5.4 introduced symbol namespaces */
+ struct kernel_symbol_v3 {
+ int value_offset;
+ int name_offset;
+ int namespace_offset;
+ } v3;
+ struct kernel_symbol_v4 {
+ unsigned long value;
+ const char *name;
+ const char *namespace;
+ } v4;
};
+static size_t
+kernel_symbol_type_init(void)
+{
+ if (MEMBER_EXISTS("kernel_symbol", "value") &&
+ MEMBER_EXISTS("kernel_symbol", "name")) {
+ if (MEMBER_EXISTS("kernel_symbol", "namespace")) {
+ st->kernel_symbol_type = 4;
+ return (sizeof(struct kernel_symbol_v4));
+ } else {
+ st->kernel_symbol_type = 1;
+ return (sizeof(struct kernel_symbol_v1));
+ }
+ }
+ if (MEMBER_EXISTS("kernel_symbol", "value_offset") &&
+ MEMBER_EXISTS("kernel_symbol", "name_offset")) {
+ if (MEMBER_EXISTS("kernel_symbol", "namespace_offset")) {
+ st->kernel_symbol_type = 3;
+ return (sizeof(struct kernel_symbol_v3));
+ } else {
+ st->kernel_symbol_type = 2;
+ return (sizeof(struct kernel_symbol_v2));
+ }
+ }
+
+ error(FATAL, "kernel_symbol data structure has changed\n");
+
+ return 0;
+}
+
static ulong
modsym_name(ulong syms, union kernel_symbol *modsym, int i)
{
- if (VALID_MEMBER(kernel_symbol_value))
+ switch (st->kernel_symbol_type)
+ {
+ case 1:
return (ulong)modsym->v1.name;
+ case 2:
+ return (syms + i * sizeof(struct kernel_symbol_v2) +
+ offsetof(struct kernel_symbol_v2, name_offset) +
+ modsym->v2.name_offset);
+ case 3:
+ return (syms + i * sizeof(struct kernel_symbol_v3) +
+ offsetof(struct kernel_symbol_v3, name_offset) +
+ modsym->v3.name_offset);
+ case 4:
+ return (ulong)modsym->v4.name;
+ }
- return syms + i * sizeof(struct kernel_symbol_v2) +
- offsetof(struct kernel_symbol_v2, name_offset) +
- modsym->v2.name_offset;
+ return 0;
}
static ulong
modsym_value(ulong syms, union kernel_symbol *modsym, int i)
{
- if (VALID_MEMBER(kernel_symbol_value))
+ switch (st->kernel_symbol_type)
+ {
+ case 1:
return (ulong)modsym->v1.value;
+ case 2:
+ return (syms + i * sizeof(struct kernel_symbol_v2) +
+ offsetof(struct kernel_symbol_v2, value_offset) +
+ modsym->v2.value_offset);
+ case 3:
+ return (syms + i * sizeof(struct kernel_symbol_v3) +
+ offsetof(struct kernel_symbol_v3, value_offset) +
+ modsym->v3.value_offset);
+ case 4:
+ return (ulong)modsym->v4.value;
+ }
- return syms + i * sizeof(struct kernel_symbol_v2) +
- offsetof(struct kernel_symbol_v2, value_offset) +
- modsym->v2.value_offset;
+ return 0;
}
void
store_module_symbols_v2(ulong total, int mods_installed)
{
-
int i, m;
ulong mod, mod_next;
char *mod_name;
@@ -1675,12 +1736,7 @@ store_module_symbols_v2(ulong total, int mods_installed)
"re-initialization of module symbols not implemented yet!\n");
}
- MEMBER_OFFSET_INIT(kernel_symbol_value, "kernel_symbol", "value");
- if (VALID_MEMBER(kernel_symbol_value)) {
- kernel_symbol_size = sizeof(struct kernel_symbol_v1);
- } else {
- kernel_symbol_size = sizeof(struct kernel_symbol_v2);
- }
+ kernel_symbol_size = kernel_symbol_type_init();
if ((st->ext_module_symtable = (struct syment *)
calloc(total, sizeof(struct syment))) == NULL)
@@ -3418,6 +3474,8 @@ dump_symbol_table(void)
fprintf(fp, "\n");
} else
fprintf(fp, "(none)\n");
+
+ fprintf(fp, " kernel_symbol_type: v%d\n", st->kernel_symbol_type);
}
commit c1ac656508ad064ef0ef222acb73621ae0bf4f00
Author: Dave Anderson <anderson@redhat.com>
Date: Tue Oct 15 11:21:55 2019 -0400
Fix for the "timer -r" option on Linux 5.4-rc1 and later kernels
that contain commit 511885d7061eda3eb1faf3f57dcc936ff75863f1, titled
"lib/timerqueue: Rely on rbtree semantics for next timer". Without
the patch, the option fails with the following error "timer: invalid
structure member offset: timerqueue_head_next".
(k-hagio@ab.jp.nec.com)
diff --git a/defs.h b/defs.h
index 502e7c2..efa40b9 100644
--- a/defs.h
+++ b/defs.h
@@ -2073,6 +2073,8 @@ struct offset_table { /* stash of commonly-used offsets */
long cpu_context_save_r7;
long dentry_d_sb;
long device_private_knode_class;
+ long timerqueue_head_rb_root;
+ long rb_root_cached_rb_leftmost;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/kernel.c b/kernel.c
index 375e1b4..c4cb001 100644
--- a/kernel.c
+++ b/kernel.c
@@ -783,7 +783,13 @@ kernel_init()
MEMBER_OFFSET_INIT(timerqueue_node_expires,
"timerqueue_node", "expires");
MEMBER_OFFSET_INIT(timerqueue_node_node,
- "timerqueue_node_node", "node");
+ "timerqueue_node", "node");
+ if (INVALID_MEMBER(timerqueue_head_next)) {
+ MEMBER_OFFSET_INIT(timerqueue_head_rb_root,
+ "timerqueue_head", "rb_root");
+ MEMBER_OFFSET_INIT(rb_root_cached_rb_leftmost,
+ "rb_root_cached", "rb_leftmost");
+ }
}
MEMBER_OFFSET_INIT(hrtimer_softexpires, "hrtimer", "_softexpires");
MEMBER_OFFSET_INIT(hrtimer_function, "hrtimer", "function");
@@ -7647,11 +7653,17 @@ next_one:
readmem((ulong)(base + OFFSET(hrtimer_clock_base_first)),
KVADDR, &curr, sizeof(curr), "hrtimer_clock_base first",
FAULT_ON_ERROR);
- else
+ else if (VALID_MEMBER(timerqueue_head_next))
readmem((ulong)(base + OFFSET(hrtimer_clock_base_active) +
OFFSET(timerqueue_head_next)),
KVADDR, &curr, sizeof(curr), "hrtimer_clock base",
FAULT_ON_ERROR);
+ else
+ readmem((ulong)(base + OFFSET(hrtimer_clock_base_active) +
+ OFFSET(timerqueue_head_rb_root) +
+ OFFSET(rb_root_cached_rb_leftmost)),
+ KVADDR, &curr, sizeof(curr),
+ "hrtimer_clock_base active", FAULT_ON_ERROR);
while (curr && i < next) {
curr = rb_next(curr);
diff --git a/symbols.c b/symbols.c
index 7af5e69..eb88ca1 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10032,6 +10032,8 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(rb_node_rb_left));
fprintf(fp, " rb_node_rb_right: %ld\n",
OFFSET(rb_node_rb_right));
+ fprintf(fp, " rb_root_cached_rb_leftmost: %ld\n",
+ OFFSET(rb_root_cached_rb_leftmost));
fprintf(fp, " x8664_pda_pcurrent: %ld\n",
OFFSET(x8664_pda_pcurrent));
@@ -10388,6 +10390,8 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(hrtimer_function));
fprintf(fp, " timerqueue_head_next: %ld\n",
OFFSET(timerqueue_head_next));
+ fprintf(fp, " timerqueue_head_rb_root: %ld\n",
+ OFFSET(timerqueue_head_rb_root));
fprintf(fp, " timerqueue_node_expires: %ld\n",
OFFSET(timerqueue_node_expires));
fprintf(fp, " timerqueue_node_node: %ld\n",
commit e13fe8ba5a0b9c54edea103a309e9879784d9b94
Author: Dave Anderson <anderson@redhat.com>
Date: Tue Oct 15 16:29:30 2019 -0400
Fix for a "[-Wstringop-truncation]" compiler warning emitted when
symbols.c is built in a Fedora Rawhide environment with gcc-9.0.1
or later.
(anderson@redhat.com)
diff --git a/symbols.c b/symbols.c
index eb88ca1..55199fc 100644
--- a/symbols.c
+++ b/symbols.c
@@ -8174,8 +8174,10 @@ parse_for_member_extended(struct datatype_member *dm,
*/
if (current && p && (p - p1 < BUFSIZE)) {
- strncpy(current->field_name, p1, p - p1);
+// strncpy(current->field_name, p1, p - p1); (NOTE: gcc-9.0.1 emits [-Wstringop-truncation] warning)
current->field_len = p - p1;
+ memcpy(current->field_name, p1, current->field_len);
+ current->field_name[current->field_len] = '\0';
}
if ( p && (*s_e != '{' || (*s_e == '{' && buf[len] == '}') )) {
commit 9937878cce2fc049283d833685cb939caca462ca
Author: Dave Anderson <anderson@redhat.com>
Date: Thu Oct 17 12:00:23 2019 -0400
Fix for the "kmem -n" option on Linux-5.4-rc1 and later kernels that
contain commit b6c88d3b9d38f9448e0fcf44847a075ea81d5ca2, titled
"drivers/base/memory.c: don't store end_section_nr in memory blocks".
Without the patch, the command option fails with the error message
"kmem: invalid structure member offset: memory_block_end_section_nr".
(msys.mizuma@gmail.com)
diff --git a/help.c b/help.c
index a5218a7..cfd46c3 100644
--- a/help.c
+++ b/help.c
@@ -7177,7 +7177,7 @@ char *help_kmem[] = {
" 6 ffff88003d4d90c0 ffffea0000000000 ffffea0000c00000 PM 196608",
" 7 ffff88003d4d90e0 ffffea0000000000 ffffea0000e00000 PM 229376",
" ",
-" MEM_BLOCK NAME PHYSICAL RANGE STATE SECTIONS",
+" MEM_BLOCK NAME PHYSICAL RANGE STATE START_SECTION_NO",
" ffff88003a707c00 memory0 0 - 7ffffff ONLINE 0",
" ffff88003a6e0000 memory1 8000000 - fffffff ONLINE 1",
" ffff88003a6e1000 memory2 10000000 - 17ffffff ONLINE 2",
diff --git a/memory.c b/memory.c
index fe82fac..0a79838 100644
--- a/memory.c
+++ b/memory.c
@@ -17402,20 +17402,18 @@ fill_memory_block_name(ulong memblock, char *name)
}
static void
-fill_memory_block_srange(ulong start_sec, ulong end_sec, char *srange)
+fill_memory_block_srange(ulong start_sec, char *srange)
{
memset(srange, 0, sizeof(*srange) * BUFSIZE);
- if (start_sec == end_sec)
- sprintf(srange, "%lu", start_sec);
- else
- sprintf(srange, "%lu-%lu", start_sec, end_sec);
+ sprintf(srange, "%lu", start_sec);
}
static void
print_memory_block(ulong memory_block)
{
- ulong start_sec, end_sec, start_pfn, end_pfn, nid;
+ ulong start_sec, end_sec, nid;
+ ulong memblock_size, mbs, start_addr, end_addr;
char statebuf[BUFSIZE];
char srangebuf[BUFSIZE];
char name[BUFSIZE];
@@ -17430,15 +17428,25 @@ print_memory_block(ulong memory_block)
readmem(memory_block + OFFSET(memory_block_start_section_nr), KVADDR,
&start_sec, sizeof(void *), "memory_block start_section_nr",
FAULT_ON_ERROR);
- readmem(memory_block + OFFSET(memory_block_end_section_nr), KVADDR,
- &end_sec, sizeof(void *), "memory_block end_section_nr",
- FAULT_ON_ERROR);
- start_pfn = section_nr_to_pfn(start_sec);
- end_pfn = section_nr_to_pfn(end_sec + 1);
+ start_addr = pfn_to_phys(section_nr_to_pfn(start_sec));
+
+ if (symbol_exists("memory_block_size_probed")) {
+ memblock_size = symbol_value("memory_block_size_probed");
+ readmem(memblock_size, KVADDR,
+ &mbs, sizeof(ulong), "memory_block_size_probed",
+ FAULT_ON_ERROR);
+ end_addr = start_addr + mbs - 1;
+ } else {
+ readmem(memory_block + OFFSET(memory_block_end_section_nr), KVADDR,
+ &end_sec, sizeof(void *), "memory_block end_section_nr",
+ FAULT_ON_ERROR);
+ end_addr = pfn_to_phys(section_nr_to_pfn(end_sec + 1)) - 1;
+ }
+
fill_memory_block_state(memory_block, statebuf);
fill_memory_block_name(memory_block, name);
- fill_memory_block_srange(start_sec, end_sec, srangebuf);
+ fill_memory_block_srange(start_sec, srangebuf);
if (MEMBER_EXISTS("memory_block", "nid")) {
readmem(memory_block + OFFSET(memory_block_nid), KVADDR, &nid,
@@ -17448,9 +17456,9 @@ print_memory_block(ulong memory_block)
MKSTR(memory_block)),
mkstring(buf2, 12, CENTER, name),
mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX,
- MKSTR(pfn_to_phys(start_pfn))),
+ MKSTR(start_addr)),
mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX,
- MKSTR(pfn_to_phys(end_pfn) - 1)),
+ MKSTR(end_addr)),
mkstring(buf5, strlen("NODE"), CENTER|LONG_DEC,
MKSTR(nid)),
mkstring(buf6, strlen("CANCEL_OFFLINE"), LJUST,
@@ -17462,9 +17470,9 @@ print_memory_block(ulong memory_block)
MKSTR(memory_block)),
mkstring(buf2, 10, CENTER, name),
mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX,
- MKSTR(pfn_to_phys(start_pfn))),
+ MKSTR(start_addr)),
mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX,
- MKSTR(pfn_to_phys(end_pfn) - 1)),
+ MKSTR(end_addr)),
mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST,
statebuf),
mkstring(buf6, 12, LJUST, srangebuf));
@@ -17552,14 +17560,14 @@ dump_memory_blocks(int initialize)
mkstring(buf3, PADDR_PRLEN*2 + 2, CENTER, "PHYSICAL RANGE"),
mkstring(buf4, strlen("NODE"), CENTER, "NODE"),
mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST, "STATE"),
- mkstring(buf6, 12, LJUST, "SECTIONS"));
+ mkstring(buf6, 12, LJUST, "START_SECTION_NO"));
else
sprintf(mb_hdr, "\n%s %s %s %s %s\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_BLOCK"),
mkstring(buf2, 10, CENTER, "NAME"),
mkstring(buf3, PADDR_PRLEN*2, CENTER, "PHYSICAL RANGE"),
mkstring(buf4, strlen("CANCEL_OFFLINE"), LJUST, "STATE"),
- mkstring(buf5, 12, LJUST, "SECTIONS"));
+ mkstring(buf5, 12, LJUST, "START_SECTION_NO"));
fprintf(fp, "%s", mb_hdr);
for (i = 0; i < klistcnt; i++) {
commit 1f9e0ac5d0b43004639e304f718177ff4c82343b
Author: Dave Anderson <anderson@redhat.com>
Date: Sat Oct 19 16:43:16 2019 -0400
Fix for Linux 4.19.5 and later 4.19-based x86_64 kernels which
are NOT configured with CONFIG_RANDOMIZE_BASE and have backported
kernel commit d52888aa2753e3063a9d3a0c9f72f94aa9809c15, titled
"x86/mm: Move LDT remap out of KASLR region on 5-level paging",
which modified the 4-level and 5-level paging PAGE_OFFSET values.
Without this patch, the crash session fails during initialization
with the error message "crash: seek error: kernel virtual address:
<address> type: "tss_struct ist array".
(anderson@redhat.com)
diff --git a/x86_64.c b/x86_64.c
index d57b602..c7967bd 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -382,7 +382,7 @@ x86_64_init(int when)
case POST_GDB:
if (!(machdep->flags & RANDOMIZED) &&
- ((THIS_KERNEL_VERSION >= LINUX(4,20,0)) ||
+ ((THIS_KERNEL_VERSION >= LINUX(4,19,5)) ||
((THIS_KERNEL_VERSION >= LINUX(4,14,84)) &&
(THIS_KERNEL_VERSION < LINUX(4,15,0))))) {
machdep->machspec->page_offset = machdep->flags & VM_5LEVEL ?
commit 1d2bc0c65792d15f94ebfd97c22da620b74634fa
Author: Dave Anderson <anderson@redhat.com>
Date: Mon Oct 21 11:46:01 2019 -0400
Additional fix for the "kmem -n" option on Linux-5.4-rc1 and later
kernels that contain commit b6c88d3b9d38f9448e0fcf44847a075ea81d5ca2,
titled "drivers/base/memory.c: don't store end_section_nr in memory
blocks". The initial fix only addressed the x86_64 architecture;
this incremental patch addresses the other architectures.
(msys.mizuma@gmail.com)
diff --git a/help.c b/help.c
index cfd46c3..2b2285b 100644
--- a/help.c
+++ b/help.c
@@ -7177,15 +7177,15 @@ char *help_kmem[] = {
" 6 ffff88003d4d90c0 ffffea0000000000 ffffea0000c00000 PM 196608",
" 7 ffff88003d4d90e0 ffffea0000000000 ffffea0000e00000 PM 229376",
" ",
-" MEM_BLOCK NAME PHYSICAL RANGE STATE START_SECTION_NO",
-" ffff88003a707c00 memory0 0 - 7ffffff ONLINE 0",
-" ffff88003a6e0000 memory1 8000000 - fffffff ONLINE 1",
-" ffff88003a6e1000 memory2 10000000 - 17ffffff ONLINE 2",
-" ffff88003a6e1400 memory3 18000000 - 1fffffff ONLINE 3",
-" ffff88003a6e1800 memory4 20000000 - 27ffffff ONLINE 4",
-" ffff88003a6e0400 memory5 28000000 - 2fffffff ONLINE 5",
-" ffff88003a6e0800 memory6 30000000 - 37ffffff ONLINE 6",
-" ffff88003a6e0c00 memory7 38000000 - 3fffffff ONLINE 7",
+" MEM_BLOCK NAME PHYSICAL RANGE STATE START_SECTION_NO",
+" ffff88003a707c00 memory0 0 - 7ffffff ONLINE 0",
+" ffff88003a6e0000 memory1 8000000 - fffffff ONLINE 1",
+" ffff88003a6e1000 memory2 10000000 - 17ffffff ONLINE 2",
+" ffff88003a6e1400 memory3 18000000 - 1fffffff ONLINE 3",
+" ffff88003a6e1800 memory4 20000000 - 27ffffff ONLINE 4",
+" ffff88003a6e0400 memory5 28000000 - 2fffffff ONLINE 5",
+" ffff88003a6e0800 memory6 30000000 - 37ffffff ONLINE 6",
+" ffff88003a6e0c00 memory7 38000000 - 3fffffff ONLINE 7",
"\n Translate a page structure's flags field contents:\n",
" %s> kmem -g 4080",
diff --git a/memory.c b/memory.c
index 0a79838..f36685b 100644
--- a/memory.c
+++ b/memory.c
@@ -17401,6 +17401,23 @@ fill_memory_block_name(ulong memblock, char *name)
read_string(value, name, BUFSIZE-1);
}
+static void
+fill_memory_block_parange(ulong saddr, ulong eaddr, char *parange)
+{
+ char buf1[BUFSIZE];
+ char buf2[BUFSIZE];
+
+ memset(parange, 0, sizeof(*parange) * BUFSIZE);
+
+ if (eaddr == ULLONG_MAX)
+ sprintf(parange, "%s",
+ mkstring(buf1, PADDR_PRLEN*2 + 3, CENTER|LONG_HEX, MKSTR(saddr)));
+ else
+ sprintf(parange, "%s - %s",
+ mkstring(buf1, PADDR_PRLEN, RJUST|LONG_HEX, MKSTR(saddr)),
+ mkstring(buf2, PADDR_PRLEN, RJUST|LONG_HEX, MKSTR(eaddr)));
+}
+
static void
fill_memory_block_srange(ulong start_sec, char *srange)
{
@@ -17413,14 +17430,13 @@ static void
print_memory_block(ulong memory_block)
{
ulong start_sec, end_sec, nid;
- ulong memblock_size, mbs, start_addr, end_addr;
+ ulong memblock_size, mbs, start_addr, end_addr = ULLONG_MAX;
char statebuf[BUFSIZE];
char srangebuf[BUFSIZE];
+ char parangebuf[BUFSIZE];
char name[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
- char buf3[BUFSIZE];
- char buf4[BUFSIZE];
char buf5[BUFSIZE];
char buf6[BUFSIZE];
char buf7[BUFSIZE];
@@ -17437,7 +17453,7 @@ print_memory_block(ulong memory_block)
&mbs, sizeof(ulong), "memory_block_size_probed",
FAULT_ON_ERROR);
end_addr = start_addr + mbs - 1;
- } else {
+ } else if (MEMBER_EXISTS("memory_block", "end_section_nr")) {
readmem(memory_block + OFFSET(memory_block_end_section_nr), KVADDR,
&end_sec, sizeof(void *), "memory_block end_section_nr",
FAULT_ON_ERROR);
@@ -17446,34 +17462,29 @@ print_memory_block(ulong memory_block)
fill_memory_block_state(memory_block, statebuf);
fill_memory_block_name(memory_block, name);
+ fill_memory_block_parange(start_addr, end_addr, parangebuf);
fill_memory_block_srange(start_sec, srangebuf);
if (MEMBER_EXISTS("memory_block", "nid")) {
readmem(memory_block + OFFSET(memory_block_nid), KVADDR, &nid,
sizeof(void *), "memory_block nid", FAULT_ON_ERROR);
- fprintf(fp, " %s %s %s - %s %s %s %s\n",
+ fprintf(fp, " %s %s %s %s %s %s\n",
mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX,
MKSTR(memory_block)),
mkstring(buf2, 12, CENTER, name),
- mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX,
- MKSTR(start_addr)),
- mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX,
- MKSTR(end_addr)),
+ parangebuf,
mkstring(buf5, strlen("NODE"), CENTER|LONG_DEC,
MKSTR(nid)),
- mkstring(buf6, strlen("CANCEL_OFFLINE"), LJUST,
+ mkstring(buf6, strlen("OFFLINE"), LJUST,
statebuf),
mkstring(buf7, 12, LJUST, srangebuf));
} else
- fprintf(fp, " %s %s %s - %s %s %s\n",
+ fprintf(fp, " %s %s %s %s %s\n",
mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX,
MKSTR(memory_block)),
mkstring(buf2, 10, CENTER, name),
- mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX,
- MKSTR(start_addr)),
- mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX,
- MKSTR(end_addr)),
- mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST,
+ parangebuf,
+ mkstring(buf5, strlen("OFFLINE"), LJUST,
statebuf),
mkstring(buf6, 12, LJUST, srangebuf));
}
@@ -17537,6 +17548,7 @@ dump_memory_blocks(int initialize)
int klistcnt, i;
struct list_data list_data;
char mb_hdr[BUFSIZE];
+ char paddr_hdr[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
@@ -17553,20 +17565,26 @@ dump_memory_blocks(int initialize)
init_memory_block(&list_data, &klistcnt, &klistbuf);
+ if ((symbol_exists("memory_block_size_probed")) ||
+ (MEMBER_EXISTS("memory_block", "end_section_nr")))
+ sprintf(paddr_hdr, "%s", "PHYSICAL RANGE");
+ else
+ sprintf(paddr_hdr, "%s", "PHYSICAL START");
+
if (MEMBER_EXISTS("memory_block", "nid"))
- sprintf(mb_hdr, "\n%s %s %s %s %s %s\n",
+ sprintf(mb_hdr, "\n%s %s %s %s %s %s\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_BLOCK"),
mkstring(buf2, 10, CENTER, "NAME"),
- mkstring(buf3, PADDR_PRLEN*2 + 2, CENTER, "PHYSICAL RANGE"),
+ mkstring(buf3, PADDR_PRLEN*2 + 2, CENTER, paddr_hdr),
mkstring(buf4, strlen("NODE"), CENTER, "NODE"),
- mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST, "STATE"),
+ mkstring(buf5, strlen("OFFLINE"), LJUST, "STATE"),
mkstring(buf6, 12, LJUST, "START_SECTION_NO"));
else
- sprintf(mb_hdr, "\n%s %s %s %s %s\n",
+ sprintf(mb_hdr, "\n%s %s %s %s %s\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_BLOCK"),
mkstring(buf2, 10, CENTER, "NAME"),
- mkstring(buf3, PADDR_PRLEN*2, CENTER, "PHYSICAL RANGE"),
- mkstring(buf4, strlen("CANCEL_OFFLINE"), LJUST, "STATE"),
+ mkstring(buf3, PADDR_PRLEN*2, CENTER, paddr_hdr),
+ mkstring(buf4, strlen("OFFLINE"), LJUST, "STATE"),
mkstring(buf5, 12, LJUST, "START_SECTION_NO"));
fprintf(fp, "%s", mb_hdr);
commit 869f3b24fc3f1dd236b58e1cff86fb4e68da76cf
Author: Dave Anderson <anderson@redhat.com>
Date: Tue Oct 22 16:42:57 2019 -0400
In the unlikely event that the panic task in a dumpfile cannot be
determined by the normal means, scan the kernel log buffer for panic
keywords, and if found, generate the panic task from the CPU number
that is specified following the panic message.
(chenqiwu@xiaomi.com)
diff --git a/task.c b/task.c
index 829c794..8dd2b96 100644
--- a/task.c
+++ b/task.c
@@ -48,6 +48,9 @@ static void show_tgid_list(ulong);
static int compare_start_time(const void *, const void *);
static int start_time_timespec(void);
static ulonglong convert_start_time(ulonglong, ulonglong);
+static ulong search_panic_task_by_cpu(char *);
+static ulong search_panic_task_by_keywords(char *, int *);
+static ulong get_log_panic_task(void);
static ulong get_dumpfile_panic_task(void);
static ulong get_active_set_panic_task(void);
static void populate_panic_threads(void);
@@ -132,6 +135,23 @@ static struct sched_policy_info {
{ ULONG_MAX, NULL }
};
+enum PANIC_TASK_FOUND_RESULT {
+ FOUND_NO_PANIC_KEYWORD,
+ FOUND_PANIC_KEYWORD,
+ FOUND_PANIC_TASK
+};
+
+const char *panic_keywords[] = {
+ "Unable to handle kernel",
+ "BUG: unable to handle kernel",
+ "Kernel BUG at",
+ "kernel BUG at",
+ "Bad mode in",
+ "Oops",
+ "Kernel panic",
+ NULL,
+};
+
/*
* Figure out how much space will be required to hold the task context
* data, malloc() it, and call refresh_task_table() to fill it up.
@@ -6116,8 +6136,8 @@ get_panic_ksp(struct bt_info *bt, ulong *ksp)
/*
* Look for kcore's storage information for the system's panic state.
- * If it's not there (somebody else's dump format?), look through all the
- * stack traces for evidence of panic.
+ * If it's not there (somebody else's dump format?), look through all
+ * the stack traces or the log buffer for evidence of panic.
*/
static ulong
get_panic_context(void)
@@ -6321,6 +6341,13 @@ get_panicmsg(char *buf)
break;
}
}
+ rewind(pc->tmpfile);
+ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) {
+ if (strstr(buf, "Bad mode in ")) {
+ msg_found = TRUE;
+ break;
+ }
+ }
close_tmpfile();
@@ -7401,6 +7428,8 @@ panic_search(void)
close_tmpfile();
+ pc->curcmd = pc->program_name;
+
if (!found && (dietask > (NO_TASK+1)) && task_has_cpu(dietask, NULL)) {
lasttask = dietask;
found = TRUE;
@@ -7410,9 +7439,16 @@ panic_search(void)
error(WARNING, "multiple active tasks have called die\n\n");
if (CRASHDEBUG(1) && found)
- error(INFO, "panic_search: %lx (via foreach bt)\n",
+ error(INFO, "panic_search: %lx (via foreach bt)\n",
lasttask);
+ if (!found) {
+ if (CRASHDEBUG(1))
+ error(INFO, "panic_search: failed (via foreach bt)\n");
+ if ((lasttask = get_log_panic_task()))
+ found = TRUE;
+ }
+
found_panic_task:
populate_panic_threads();
@@ -7430,11 +7466,114 @@ found_panic_task:
}
if (CRASHDEBUG(1))
- error(INFO, "panic_search: failed (via foreach bt)\n");
+ error(INFO, "panic_search: failed\n");
return NULL;
}
+static ulong
+search_panic_task_by_cpu(char *buf)
+{
+ int crashing_cpu;
+ char *p1, *p2;
+ ulong task = NO_TASK;
+
+ p1 = NULL;
+
+ if ((p1 = strstr(buf, "CPU: ")))
+ p1 += strlen("CPU: ");
+ else if (STRNEQ(buf, "CPU "))
+ p1 = buf + strlen("CPU ");
+
+ if (p1) {
+ p2 = p1;
+ while (!whitespace(*p2) && (*p2 != '\n'))
+ p2++;
+ *p2 = NULLCHAR;
+ crashing_cpu = dtol(p1, RETURN_ON_ERROR, NULL);
+ if ((crashing_cpu >= 0) && in_cpu_map(ONLINE_MAP, crashing_cpu)) {
+ task = tt->active_set[crashing_cpu];
+ if (CRASHDEBUG(1))
+ error(WARNING,
+ "get_log_panic_task: active_set[%d]: %lx\n",
+ crashing_cpu, tt->active_set[crashing_cpu]);
+ }
+ }
+ return task;
+}
+
+static ulong
+search_panic_task_by_keywords(char *buf, int *found_flag)
+{
+ char *p;
+ int i = 0;
+ ulong task;
+
+ while (panic_keywords[i]) {
+ if ((p = strstr(buf, panic_keywords[i]))) {
+ if ((task = search_panic_task_by_cpu(p))) {
+ *found_flag = FOUND_PANIC_TASK;
+ return task;
+ } else {
+ *found_flag = FOUND_PANIC_KEYWORD;
+ return NO_TASK;
+ }
+ }
+ i++;
+ }
+ *found_flag = FOUND_NO_PANIC_KEYWORD;
+ return NO_TASK;
+}
+
+/*
+ * Search for the panic task by seeking panic keywords from kernel log buffer.
+ * The panic keyword is generally followed by printing out the stack trace info
+ * of the panicking task. We can determine the panic task by finding the first
+ * instance of "CPU: " or "CPU " following the panic keywords.
+ */
+static ulong
+get_log_panic_task(void)
+{
+ int found_flag = FOUND_NO_PANIC_KEYWORD;
+ int found_panic_keyword = FALSE;
+ ulong task = NO_TASK;
+ char buf[BUFSIZE];
+
+ if (!get_active_set())
+ goto fail;
+
+ BZERO(buf, BUFSIZE);
+ open_tmpfile();
+ dump_log(SHOW_LOG_TEXT);
+ rewind(pc->tmpfile);
+ while (fgets(buf, BUFSIZE, pc->tmpfile)) {
+ if (!found_panic_keyword) {
+ task = search_panic_task_by_keywords(buf, &found_flag);
+ switch (found_flag) {
+ case FOUND_PANIC_TASK:
+ goto found_panic_task;
+ case FOUND_PANIC_KEYWORD:
+ found_panic_keyword = TRUE;
+ continue;
+ default:
+ continue;
+ }
+ } else {
+ task = search_panic_task_by_cpu(buf);
+ if (task)
+ goto found_panic_task;
+ }
+ }
+
+found_panic_task:
+ close_tmpfile();
+fail:
+ if (CRASHDEBUG(1) && !task)
+ error(WARNING, "cannot determine the panic task from kernel log buffer\n");
+
+ return task;
+}
+
/*
* Get the panic task from the appropriate dumpfile handler.
*/
commit 6a466f8afbb0dcdf4ddc5ef37aec6d343c2636c6
Author: Dave Anderson <anderson@redhat.com>
Date: Mon Nov 4 11:56:28 2019 -0500
Adjust a crash-7.1.8 patch for support of /proc/kcore as the live
memory source in Linux 4.8 and later x86_64 kernels configured with
CONFIG_RANDOMIZE_BASE, which randomizes the unity-mapping PAGE_OFFSET
value. Since the problem only arises before the determination of the
randomized PAGE_OFFSET value, restrict the patch such that it only
takes effect during session initialization.
(anderson@redhat.com)
diff --git a/netdump.c b/netdump.c
index 55b64e6..fdaecf3 100644
--- a/netdump.c
+++ b/netdump.c
@@ -4269,7 +4269,8 @@ read_proc_kcore(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
* If KASLR, the PAGE_OFFSET may be unknown early on, so try
* the (hopefully) mapped kernel address first.
*/
- if ((pc->curcmd_flags & MEMTYPE_KVADDR) && (kvaddr != addr)) {
+ if (!(pc->flags & RUNTIME) &&
+ (pc->curcmd_flags & MEMTYPE_KVADDR) && (kvaddr != addr)) {
pc->curcmd_flags &= ~MEMTYPE_KVADDR;
for (i = 0; i < pkd->segments; i++) {
lp64 = pkd->load64 + i;
commit c0bbd8fae4271159aee9e643350781909484c92f
Author: Dave Anderson <anderson@redhat.com>
Date: Fri Nov 8 14:00:56 2019 -0500
Add support for extended numbering support in ELF dumpfiles to handle
more than PN_XNUM (0xffff) program headers. If the real number of
program header table entries is equal to or greater than PN_XNUM, the
e_phnum field of the ELF header is set to PN_XNUM, and the actual
number is set in the sh_info field of the section header at index 0.
(k-hagio@ab.jp.nec.com)
diff --git a/netdump.c b/netdump.c
index fdaecf3..3ced87c 100644
--- a/netdump.c
+++ b/netdump.c
@@ -28,12 +28,13 @@ static struct vmcore_data *nd = &vmcore_data;
static struct proc_kcore_data proc_kcore_data = { 0 };
static struct proc_kcore_data *pkd = &proc_kcore_data;
static void netdump_print(char *, ...);
-static size_t resize_elf_header(int, char *, char **, ulong);
+static size_t resize_elf_header(int, char *, char **, char **, ulong);
static void dump_Elf32_Ehdr(Elf32_Ehdr *);
static void dump_Elf32_Phdr(Elf32_Phdr *, int);
static size_t dump_Elf32_Nhdr(Elf32_Off offset, int);
static void dump_Elf64_Ehdr(Elf64_Ehdr *);
static void dump_Elf64_Phdr(Elf64_Phdr *, int);
+static void dump_Elf64_Shdr(Elf64_Shdr *shdr);
static size_t dump_Elf64_Nhdr(Elf64_Off offset, int);
static void get_netdump_regs_32(struct bt_info *, ulong *, ulong *);
static void get_netdump_regs_ppc(struct bt_info *, ulong *, ulong *);
@@ -116,7 +117,7 @@ is_netdump(char *file, ulong source_query)
Elf32_Phdr *load32;
Elf64_Ehdr *elf64;
Elf64_Phdr *load64;
- char *eheader;
+ char *eheader, *sect0;
char buf[BUFSIZE];
size_t size, len, tot;
Elf32_Off offset32;
@@ -330,7 +331,8 @@ is_netdump(char *file, ulong source_query)
goto bailout;
}
- if (!(size = resize_elf_header(fd, file, &eheader, format)))
+ sect0 = NULL;
+ if (!(size = resize_elf_header(fd, file, &eheader, &sect0, format)))
goto bailout;
nd->ndfd = fd;
@@ -372,7 +374,17 @@ is_netdump(char *file, ulong source_query)
case KDUMP_ELF64:
nd->header_size = size;
nd->elf64 = (Elf64_Ehdr *)&nd->elf_header[0];
- nd->num_pt_load_segments = nd->elf64->e_phnum - 1;
+
+ /*
+ * Extended Numbering support
+ * See include/uapi/linux/elf.h and elf(5) for more information
+ */
+ if (nd->elf64->e_phnum == PN_XNUM) {
+ nd->sect0_64 = (Elf64_Shdr *)sect0;
+ nd->num_pt_load_segments = nd->sect0_64->sh_info - 1;
+ } else
+ nd->num_pt_load_segments = nd->elf64->e_phnum - 1;
+
if ((nd->pt_load_segments = (struct pt_load_segment *)
malloc(sizeof(struct pt_load_segment) *
nd->num_pt_load_segments)) == NULL) {
@@ -432,7 +444,8 @@ bailout:
*/
static size_t
-resize_elf_header(int fd, char *file, char **eheader_ptr, ulong format)
+resize_elf_header(int fd, char *file, char **eheader_ptr, char **sect0_ptr,
+ ulong format)
{
int i;
char buf[BUFSIZE];
@@ -462,7 +475,44 @@ resize_elf_header(int fd, char *file, char **eheader_ptr, ulong format)
case NETDUMP_ELF64:
case KDUMP_ELF64:
- num_pt_load_segments = elf64->e_phnum - 1;
+ /*
+ * Extended Numbering support
+ * See include/uapi/linux/elf.h and elf(5) for more information
+ */
+ if (elf64->e_phnum == PN_XNUM) {
+ Elf64_Shdr *shdr64;
+
+ shdr64 = (Elf64_Shdr *)malloc(sizeof(*shdr64));
+ if (!shdr64) {
+ fprintf(stderr,
+ "cannot malloc a section header buffer\n");
+ return 0;
+ }
+ if (FLAT_FORMAT()) {
+ if (!read_flattened_format(fd, elf64->e_shoff,
+ shdr64, elf64->e_shentsize))
+ return 0;
+ } else {
+ if (lseek(fd, elf64->e_shoff, SEEK_SET) !=
+ elf64->e_shoff) {
+ sprintf(buf, "%s: section header lseek",
+ file);
+ perror(buf);
+ return 0;
+ }
+ if (read(fd, shdr64, elf64->e_shentsize) !=
+ elf64->e_shentsize) {
+ sprintf(buf, "%s: section header read",
+ file);
+ perror(buf);
+ return 0;
+ }
+ }
+ num_pt_load_segments = shdr64->sh_info - 1;
+ *sect0_ptr = (char *)shdr64;
+ } else
+ num_pt_load_segments = elf64->e_phnum - 1;
+
header_size = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) +
(sizeof(Elf64_Phdr) * num_pt_load_segments);
break;
@@ -1168,6 +1218,7 @@ netdump_memory_dump(FILE *fp)
netdump_print(" elf64: %lx\n", nd->elf64);
netdump_print(" notes64: %lx\n", nd->notes64);
netdump_print(" load64: %lx\n", nd->load64);
+ netdump_print(" sect0_64: %lx\n", nd->sect0_64);
netdump_print(" nt_prstatus: %lx\n", nd->nt_prstatus);
netdump_print(" nt_prpsinfo: %lx\n", nd->nt_prpsinfo);
netdump_print(" nt_taskstruct: %lx\n", nd->nt_taskstruct);
@@ -1252,6 +1303,8 @@ netdump_memory_dump(FILE *fp)
dump_Elf64_Phdr(nd->notes64, ELFREAD);
for (i = 0; i < nd->num_pt_load_segments; i++)
dump_Elf64_Phdr(nd->load64 + i, ELFREAD);
+ if (nd->sect0_64)
+ dump_Elf64_Shdr(nd->sect0_64);
offset64 = nd->notes64->p_offset;
for (tot = 0; tot < nd->notes64->p_filesz; tot += len) {
if (!(len = dump_Elf64_Nhdr(offset64, ELFREAD)))
@@ -1763,6 +1816,32 @@ dump_Elf64_Phdr(Elf64_Phdr *prog, int store_pt_load_data)
netdump_print(" p_align: %lld\n", prog->p_align);
}
+static void
+dump_Elf64_Shdr(Elf64_Shdr *shdr)
+{
+ netdump_print("Elf64_Shdr:\n");
+ netdump_print(" sh_name: %x\n", shdr->sh_name);
+ netdump_print(" sh_type: %x ", shdr->sh_type);
+ switch (shdr->sh_type)
+ {
+ case SHT_NULL:
+ netdump_print("(SHT_NULL)\n");
+ break;
+ default:
+ netdump_print("\n");
+ break;
+ }
+ netdump_print(" sh_flags: %lx\n", shdr->sh_flags);
+ netdump_print(" sh_addr: %lx\n", shdr->sh_addr);
+ netdump_print(" sh_offset: %lx\n", shdr->sh_offset);
+ netdump_print(" sh_size: %lx\n", shdr->sh_size);
+ netdump_print(" sh_link: %x\n", shdr->sh_link);
+ netdump_print(" sh_info: %x (%u)\n", shdr->sh_info,
+ shdr->sh_info);
+ netdump_print(" sh_addralign: %lx\n", shdr->sh_addralign);
+ netdump_print(" sh_entsize: %lx\n", shdr->sh_entsize);
+}
+
/*
* VMCOREINFO
*
diff --git a/netdump.h b/netdump.h
index ad1fc77..7fa04f7 100644
--- a/netdump.h
+++ b/netdump.h
@@ -61,6 +61,7 @@ struct vmcore_data {
Elf64_Ehdr *elf64;
Elf64_Phdr *notes64;
Elf64_Phdr *load64;
+ Elf64_Shdr *sect0_64;
void *nt_prstatus;
void *nt_prpsinfo;
void *nt_taskstruct;
commit b5c2359f9f7347a2efa4896fa134dbf128601ca8
Author: Dave Anderson <anderson@redhat.com>
Date: Fri Nov 8 14:32:53 2019 -0500
Fix for a "warning: large integer implicitly truncated to unsigned
type [-Woverflow]" compiler message generated on 32-bit architectures
as a result of the "Additional fix for the kmem -n option" patch
above.
(anderson@redhat.com)
diff --git a/memory.c b/memory.c
index f36685b..4f7b6a0 100644
--- a/memory.c
+++ b/memory.c
@@ -17430,7 +17430,7 @@ static void
print_memory_block(ulong memory_block)
{
ulong start_sec, end_sec, nid;
- ulong memblock_size, mbs, start_addr, end_addr = ULLONG_MAX;
+ ulong memblock_size, mbs, start_addr, end_addr = (ulong)ULLONG_MAX;
char statebuf[BUFSIZE];
char srangebuf[BUFSIZE];
char parangebuf[BUFSIZE];