Enhanced dev command to analyze and extract hardware specific
device dumps in ELF vmcore.
-V list all device dumps present in vmcore
-v <index> [-r file] select and display one device dump, either in
human-readable format to the screen by default,
or optionally copy it raw to a file
Signed-off-by: Surendra Mobiya <surendra(a)chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy(a)chelsio.com>
---
rfc:
- Moved logic to extract device dumps from "devdump" to "dev"
command.
- By default, device dumps are output to screen as hexdump, if output
file is not provided.
defs.h | 5 ++++
dev.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
help.c | 23 ++++++++++++++++-
memory.c | 10 ++++++++
netdump.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 180 insertions(+), 3 deletions(-)
diff --git a/defs.h b/defs.h
index 0925a46..a6a0d8a 100644
--- a/defs.h
+++ b/defs.h
@@ -5277,6 +5277,7 @@ char *format_stack_entry(struct bt_info *bt, char *, ulong, ulong);
int in_user_stack(ulong, ulong);
int dump_inode_page(ulong);
ulong valid_section_nr(ulong);
+void display_memory_from_file_offset(ulonglong, long, void *);
/*
@@ -5686,6 +5687,8 @@ enum {
*/
void dev_init(void);
void dump_dev_table(void);
+void devdump_extract(void *, ulonglong, char *, FILE *);
+void devdump_info(void *, ulonglong, FILE *);
/*
* ipcs.c
@@ -6400,6 +6403,8 @@ void *netdump_get_prstatus_percpu(int);
int kdump_kaslr_check(void);
void display_vmcoredd_note(void *ptr, FILE *ofp);
QEMUCPUState *kdump_get_qemucpustate(int);
+void kdump_device_dump_info(FILE *);
+void kdump_device_dump_extract(int, char *, FILE *);
#define PRSTATUS_NOTE (1)
#define QEMU_NOTE (2)
diff --git a/dev.c b/dev.c
index 24efea2..08bb010 100644
--- a/dev.c
+++ b/dev.c
@@ -16,6 +16,7 @@
*/
#include "defs.h"
+#include "vmcore.h"
static void dump_blkdevs(ulong);
static void dump_chrdevs(ulong);
@@ -104,12 +105,13 @@ dev_init(void)
void
cmd_dev(void)
{
- int c;
+ int c, index = -1;
+ char *outputfile = NULL;
ulong flags;
flags = 0;
- while ((c = getopt(argcnt, args, "dDpi")) != EOF) {
+ while ((c = getopt(argcnt, args, "dDpiVv:r:")) != EOF) {
switch(c)
{
case 'd':
@@ -137,6 +139,21 @@ cmd_dev(void)
option_not_supported(c);
return;
+ case 'V':
+ if (KDUMP_DUMPFILE())
+ kdump_device_dump_info(fp);
+ else
+ error(WARNING, "KDUMP flag not found");
+ return;
+
+ case 'v':
+ index = atoi(optarg);
+ break;
+
+ case 'r':
+ outputfile = optarg;
+ break;
+
default:
argerrs++;
break;
@@ -146,6 +163,14 @@ cmd_dev(void)
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
+ if (index >= 0) {
+ if (KDUMP_DUMPFILE())
+ kdump_device_dump_extract(index, outputfile, fp);
+ else
+ error(WARNING, "KDUMP flag not found");
+ return;
+ }
+
dump_chrdevs(flags);
fprintf(fp, "\n");
dump_blkdevs(flags);
@@ -4519,3 +4544,50 @@ diskio_option(ulong flags)
diskio_init();
display_all_diskio(flags);
}
+
+void
+devdump_extract(void *_note, ulonglong offset, char *dump_file, FILE *ofp)
+{
+ struct vmcoredd_header *vh = (struct vmcoredd_header *)_note;
+ ulong dump_size;
+ FILE *tmpfp;
+
+ if (vh->n_type != NT_VMCOREDD) {
+ error(WARNING, "Unsupported note type 0x%x", vh->n_type);
+ return;
+ }
+
+ dump_size = vh->n_descsz - VMCOREDD_MAX_NAME_BYTES;
+
+ if (dump_file) {
+ tmpfp = fopen(dump_file, "w");
+ if (!tmpfp) {
+ error(FATAL, "cannot open output file: %s\n",
+ dump_file);
+ return;
+ }
+ set_tmpfile2(tmpfp);
+ }
+ fprintf(ofp, "Device Dump: %s\n", vh->dump_name);
+ display_memory_from_file_offset(offset + sizeof(struct vmcoredd_header),
+ dump_size, dump_file);
+}
+
+void devdump_info(void *_note, ulonglong offset, FILE *ofp)
+{
+ struct vmcoredd_header *vh = (struct vmcoredd_header *)_note;
+ char buf[BUFSIZE];
+ ulong dump_size;
+
+ if (vh->n_type != NT_VMCOREDD)
+ return;
+
+ dump_size = vh->n_descsz - VMCOREDD_MAX_NAME_BYTES;
+
+ fprintf(ofp, "0x%s ", mkstring(buf, LONG_LONG_PRLEN, LJUST | LONG_HEX,
+ MKSTR(offset + sizeof(struct vmcoredd_header))));
+ fprintf(ofp, "%s ", mkstring(buf, LONG_PRLEN, LJUST | LONG_DEC,
+ MKSTR(dump_size)));
+ fprintf(ofp, "%s\n", mkstring(buf, VMCOREDD_MAX_NAME_BYTES, LJUST,
+ (char *)vh->dump_name));
+}
diff --git a/help.c b/help.c
index 47058ed..e9bf1b9 100644
--- a/help.c
+++ b/help.c
@@ -3207,7 +3207,7 @@ NULL
char *help_dev[] = {
"dev",
"device data",
-"[-i | -p | -d | -D]",
+"[-i | -p | -d | -D | -V ] [-v index [-r file]]",
" If no argument is entered, this command dumps character and block",
" device data.\n",
" -i display I/O port usage; on 2.4 kernels, also display I/O memory
usage.",
@@ -3222,6 +3222,11 @@ char *help_dev[] = {
" If the device driver uses blk-mq interface, this field",
" shows N/A(MQ). If not available, this column is not shown.",
" -D same as -d, but filter out disks with no in-progress I/O requests.",
+" -V list all device dumps present in vmcore",
+" -v <index> [-r file] select and display one device dump, either
in",
+" human-readable format to the screen by default,",
+" or optionally copy it raw to a file",
+
"\nEXAMPLES",
" Display character and block device data:\n",
" %s> dev",
@@ -3353,6 +3358,22 @@ char *help_dev[] = {
" 8 ffff81012dc77000 sdb ffff81012d8b5740 0 0 0
0",
" 8 ffff81012d8d0c00 sdc ffff81012d8ae9c0 0 0 0
0",
+"\n Display the available device dumps:\n",
+" %s> dev -V",
+" INDEX OFFSET SIZE NAME",
+" 0 0x240 33558464 cxgb4_0000:02:00.4",
+" 1 0x2001240 33558464 cxgb4_0000:03:00.4",
+
+"\n Extract device dump at specified index to file:\n",
+" %s> dev -v 0 -r device_dump_0.bin",
+" Device Dump: cxgb4_0000:02:00.4",
+" 33558464 bytes copied from 0x240 to device_dump_0.bin",
+
+"\n Format and display device dump output to screen using rd command:\n",
+" %s> rd -f 0x240 -32 8",
+" 240: 040b69e2 00000038 000e0001 00675fd4 .i..8........_g.",
+" 250: 00000000 21600047 00000000 00000000 ....G.`!........",
+
NULL
};
diff --git a/memory.c b/memory.c
index ab561b3..1395413 100644
--- a/memory.c
+++ b/memory.c
@@ -1793,6 +1793,16 @@ display_memory(ulonglong addr, long count, ulong flag, int memtype,
void *opt)
fprintf(fp,"\n");
}
+void
+display_memory_from_file_offset(ulonglong addr, long count, void *file)
+{
+ if (file)
+ display_memory(addr, count, DISPLAY_RAW, FILEADDR, file);
+ else
+ display_memory(addr, count, DISPLAY_64 | ASCII_ENDLINE | HEXADECIMAL,
+ FILEADDR, file);
+}
+
/*
* cmd_wr() is the sister routine of cmd_rd(), used to modify the contents
* of memory. Like the "rd" command, the starting address may be entered
diff --git a/netdump.c b/netdump.c
index c4e9b3e..cd950e4 100644
--- a/netdump.c
+++ b/netdump.c
@@ -5082,3 +5082,72 @@ kdump_get_qemucpustate(int cpu)
return (QEMUCPUState *)nd->nt_qemu_percpu[cpu];
}
#endif
+
+static void *
+get_kdump_device_dump_offset(void)
+{
+ void *elf_base;
+
+ if (DUMPFILE_FORMAT(nd->flags) == KDUMP_ELF64) {
+ elf_base = (void *)nd->elf64;
+ } else if (DUMPFILE_FORMAT(nd->flags) == KDUMP_ELF32) {
+ elf_base = (void *)nd->elf32;
+ } else {
+ error(WARNING, "Unsupported Dumpfile Format: 0x%x",
+ DUMPFILE_FORMAT(nd->flags));
+ return NULL;
+ }
+
+ return elf_base;
+}
+
+/*
+ * extract hardware specific device dumps from coredump.
+ */
+void
+kdump_device_dump_extract(int index, char *outfile, FILE *ofp)
+{
+ ulonglong offset;
+ void *elf_base;
+
+ if (index >= nd->num_vmcoredd_notes) {
+ error(WARNING, "No device dump found at index: %d", index);
+ return;
+ }
+
+ elf_base = get_kdump_device_dump_offset();
+ if (!elf_base)
+ return;
+
+ offset = nd->nt_vmcoredd_array[index] - elf_base;
+
+ devdump_extract(nd->nt_vmcoredd_array[index], offset, outfile, ofp);
+}
+
+/*
+ * list all hardware specific device dumps present in coredump.
+ */
+void kdump_device_dump_info(FILE *ofp)
+{
+ ulonglong offset;
+ char buf[BUFSIZE];
+ void *elf_base;
+ ulong i;
+
+ fprintf(fp, "%s ", mkstring(buf, INT_PRLEN, LJUST, "INDEX"));
+ fprintf(fp, "%s ", mkstring(buf, LONG_LONG_PRLEN, LJUST,
"OFFSET"));
+ fprintf(fp, " %s ", mkstring(buf, LONG_PRLEN, LJUST, "SIZE"));
+ fprintf(fp, "%s\n", mkstring(buf, VMCOREDD_MAX_NAME_BYTES, LJUST,
+ "NAME"));
+
+ elf_base = get_kdump_device_dump_offset();
+ if (!elf_base)
+ return;
+
+ for (i = 0; i < nd->num_vmcoredd_notes; i++) {
+ fprintf(fp, "%s ", mkstring(buf, INT_PRLEN, LJUST | INT_DEC,
+ MKSTR(i)));
+ offset = nd->nt_vmcoredd_array[i] - elf_base;
+ devdump_info(nd->nt_vmcoredd_array[i], offset, ofp);
+ }
+}
--
2.21.0