[Crash-utility] [PATCH 3/4] enhance dev command to extract NT_VMCOREDD from ELF vmcore

Surendra Mobiya surendra at chelsio.com
Thu Apr 18 13:13:30 UTC 2019


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 at chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy at 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




More information about the Crash-utility mailing list