Re: how to use crash utility to parse the binary memory dump

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> In any case, if you use "htol()" instead of strtoul(), the user won't
> have to enter the "0x", because it will presume it's a hexadecimal
> address.
>
Done.


> However, you cannot put *nothing* as the offset value:
> So that also needs fixing.
Done.

>
> A few other questions/comments...
>
> When 32-bit x86 kdumps are created, they typically default to using
> the 64-bit ELF format.  That is required in case there is physical memory
> above the 4GB threshold, which cannot be described in a 32-bit ELF
> header.  Since 32-bit ARM can also be PAE, would it make more sense
> to *always* use 64-bit ELF headers for both ARM and ARM64?  I don't
> see why not, and it should simplify ramdump.c quite a bit.
That's right. I had to add a case in is_netdump() for EM_ARM.

>
> And thinking this through a bit more, to me it seems really wasteful
> to have to create a whole new dumpfile.  Even if it's only a temporary
> file, it's still seemingly an unnecessary duplication of disk space.
>
I agree.

> Here's an idea, not fully thought through, but seems like it could
> work when the "temporary" dumpfile option is used:
>
>  (1) Create a temporary file that *only* consists of the ELF header.
>  (2) Set a new RAMDUMP flag in pc->flags2.
>  (3) Pass that temporary ELF header file to is_kdump() as you do now.
>  (4) is_kdump() passes it to is_netdump(), and I believe that is_netdump()
>      should parse just the ELF header and accept it as a KDUMP_ELF32 or
>      KDUMP_ELF64 file without even being aware that there's no physical
>      memory data attached.
>  (5) When kdump_init() is called, it passes through to netdump_init()
>      which calls check_dumpfile_size() -- which would need to look
>      at the RAMDUMP flag to do the right thing.
>  (6) And instead of using read_kdump(), copy it to a new read_ramdump()
>      function that reads from the original RAM dump image(s), figuring
>      out the file offsets accordingly.  Keep the new read_ramdump()
>      function in netdump.c so it can continue to use the "nd" vmcore_data
>      structure that is statically defined there.  read_ramdump() may have
>      to interact with function(s) in ramdump.c, or perhaps the ramdump_def
>      structure can be shared with netdump.c somehow.
>
The attached patch contains these changes. Please review. I have tried
to keep read_ramdump()
within ramdump.c.

> There will presumably be a few other glitches that will require checking
> the new RAMDUMP flag, but I don't think that there would be anything that
> couldn't be overcome.  That really would be the ideal way to handle these
> files.
I have done some tests using both arm and arm64 ramdumps, including
the ones you had pointed out in previous emails.
I will do more tests and will let you know if I find any more bugs.
Please let me know if there are any specific tests to be done, which
can bring out these glitches.

Thanks,
Vinayak
From 25fcd20044aac65d049baa493862f53327783aa9 Mon Sep 17 00:00:00 2001
From: Vinayak Menon <vinayakm.list@xxxxxxxxx>
Date: Wed, 23 Jul 2014 11:30:40 +0530
Subject: [PATCH] raw RAM image support

Raw RAM image can be passed to crash utility in the
following format.

> crash [options] image@address... [namelist] [-o out_file]
image@address is an ordered pair where, image is the raw
RAM file representing a memory node, and address is the
physical start of the node. Multiple such ordered pairs can
be provided, which represent different memory nodes.

The raw dump is converted to kdump format.

Optionally -o can be used to specify the ELF output file
to be generated.

Signed-off-by: Vinayak Menon <vinayakm.list@xxxxxxxxx>
---
 Makefile  |   9 +-
 defs.h    |  10 ++
 help.c    |  15 ++-
 main.c    |  30 +++++-
 netdump.c |  11 ++
 ramdump.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 408 insertions(+), 5 deletions(-)
 create mode 100644 ramdump.c

diff --git a/Makefile b/Makefile
index cf3ae44..64616fc 100644
--- a/Makefile
+++ b/Makefile
@@ -68,7 +68,8 @@ CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \
 	netdump.c diskdump.c makedumpfile.c xendump.c unwind.c unwind_decoder.c \
 	unwind_x86_32_64.c unwind_arm.c \
 	xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \
-	xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c
+	xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c \
+	ramdump.c
 
 SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \
 	${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \
@@ -85,7 +86,8 @@ OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \
 	lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o \
 	unwind_x86_32_64.o unwind_arm.o \
 	xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \
-	xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o
+	xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o \
+	ramdump.o
 
 MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README
 
@@ -485,6 +487,9 @@ xen_hyper_global_data.o: ${GENERIC_HFILES} xen_hyper_global_data.c
 xen_hyper_dump_tables.o: ${GENERIC_HFILES} xen_hyper_dump_tables.c
 	${CC} -c ${CRASH_CFLAGS} xen_hyper_dump_tables.c ${WARNING_OPTIONS} ${WARNING_ERROR}
 
+ramdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} ramdump.c
+	${CC} -c ${CRASH_CFLAGS} ramdump.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
 ${PROGRAM}: force
 	@make --no-print-directory all
 
diff --git a/defs.h b/defs.h
index 2737b10..b1c3ca8 100644
--- a/defs.h
+++ b/defs.h
@@ -503,6 +503,7 @@ struct program_context {
 #define VMCOREINFO    (0x400ULL)
 #define ALLOW_FP      (0x800ULL)
 #define REM_PAUSED_F (0x1000ULL)
+#define RAMDUMP	(0x2000ULL)
 #define REMOTE_PAUSED() (pc->flags2 & REM_PAUSED_F)
 	char *cleanup;
 	char *namelist_orig;
@@ -5524,6 +5525,15 @@ void dump_registers_for_qemu_mem_dump(void);
 void kdump_backup_region_init(void);
 
 /*
+ * ramdump.c
+ */
+int is_ramdump(char *pattern);
+char *ramdump_to_elf(void);
+void ramdump_elf_output_file(char *opt);
+void ramdump_cleanup(void);
+int read_ramdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr);
+
+/*
  *  diskdump.c
  */
 int is_diskdump(char *);
diff --git a/help.c b/help.c
index e1f265a..a35ba9c 100644
--- a/help.c
+++ b/help.c
@@ -47,8 +47,9 @@ char *program_usage_info[] = {
     "",
     "USAGE:",
     "",
-    "  crash [OPTION]... NAMELIST MEMORY-IMAGE  (dumpfile form)",
-    "  crash [OPTION]... [NAMELIST]             (live system form)",
+    "  crash [OPTION]... NAMELIST MEMORY-IMAGE  		(dumpfile form)",
+    "  crash [OPTION]... NAMELIST MEMORY-IMAGE@ADDRESS...	(dumpfile form)",
+    "  crash [OPTION]... [NAMELIST]             		(live system form)",
     "",
     "OPTIONS:",
     "",
@@ -69,6 +70,11 @@ char *program_usage_info[] = {
     "    /dev/mem will be used; but if the kernel has been configured with ",
     "    CONFIG_STRICT_DEVMEM, then /proc/kcore will be used.  It is permissible",
     "    to explicitly enter /dev/crash, /dev/mem or /proc/kcore.",
+    "    @ADDRESS is used along with MEMORY-IMAGE when the dumpfile is a raw dump",
+    "    which needs a conversion to kernel core dump format. If -o option is not",
+    "    used, the kdump ELF file is created in /var/tmp/ and is unlinked on crash",
+    "    exit. Multiple MEMORY-IMAGE@ADDRESS ordered pairs can be used, each",
+    "    representing a memory node. ADDRESS is the physical start address of the node",
     "",
     "  mapfile",
     "    If the NAMELIST file is not the same kernel that is running",
@@ -139,6 +145,11 @@ char *program_usage_info[] = {
     "    architecture-specific option/pairs should only be required in",
     "    very rare circumstances:",
     "",
+    "  -o file",
+    "  Can be used with the MEMORY-IMAGE@ADDRESS format, to specify",
+    "  the path and name of the kdump ELF generated. The file thus generated",
+    "  will not be unlinked on crash exit, and can used as kdump input to crash.",
+    "",
     "    X86_64:",
     "      phys_base=<physical-address>",
     "      irq_eframe_link=<value>",
diff --git a/main.c b/main.c
index b0524d2..6d47548 100644
--- a/main.c
+++ b/main.c
@@ -86,7 +86,7 @@ main(int argc, char **argv)
 	 */
 	opterr = 0;
 	optind = 0;
-	while((c = getopt_long(argc, argv, "Lkgh::e:i:sSvc:d:tfp:m:x",
+	while((c = getopt_long(argc, argv, "Lkgh::e:i:sSvc:d:tfp:m:xo:",
        		long_options, &option_index)) != -1) {
 		switch (c)
 		{
@@ -387,6 +387,10 @@ main(int argc, char **argv)
 			pc->flags |= PRELOAD_EXTENSIONS;
 			break;
 
+		case 'o':
+			ramdump_elf_output_file(optarg);
+			break;
+
 		default:
 			error(INFO, "invalid option: %s\n",
 				argv[optind-1]);
@@ -402,6 +406,29 @@ main(int argc, char **argv)
 	 */
 	while (argv[optind]) {
 
+		if (is_ramdump(argv[optind])) {
+			if (pc->flags & MEMORY_SOURCES) {
+				error(INFO,
+					"too many dumpfile arguments\n");
+					program_usage(SHORT_FORM);
+			}
+			pc->dumpfile = ramdump_to_elf();
+			if (is_kdump(pc->dumpfile, KDUMP_LOCAL)) {
+				pc->flags |= KDUMP;
+				if (pc->flags2 & RAMDUMP)
+					pc->readmem = read_ramdump;
+				else
+					pc->readmem = read_kdump;
+				pc->writemem = NULL;
+			} else {
+				error(INFO, "malformed ELF file: %s\n",
+					pc->dumpfile);
+				program_usage(SHORT_FORM);
+			}
+			optind++;
+			continue;
+		}
+
 		if (is_remote_daemon(argv[optind])) {
                 	if (pc->flags & DUMPFILE_TYPES) {
 				error(INFO, 
@@ -1699,6 +1726,7 @@ clean_exit(int status)
 	if (pc->cleanup && file_exists(pc->cleanup, NULL))
 		unlink(pc->cleanup);
 
+	ramdump_cleanup();
 	exit(status);
 }
 
diff --git a/netdump.c b/netdump.c
index c86a992..043c7f7 100644
--- a/netdump.c
+++ b/netdump.c
@@ -249,6 +249,12 @@ is_netdump(char *file, ulong source_query)
 				goto bailout;
 			break;
 
+		case EM_ARM:
+			if (machine_type_mismatch(file, "ARM", NULL,
+			    source_query))
+				goto bailout;
+			break;
+
 		case EM_AARCH64:
 			if (machine_type_mismatch(file, "ARM64", NULL,
 			    source_query))
@@ -476,6 +482,11 @@ check_dumpfile_size(char *file)
 	struct pt_load_segment *pls;
 	uint64_t segment_end;
 
+	if (pc->flags2 & RAMDUMP) {
+		error(NOTE, "Skipping dumpfile size check for ramdump\n");
+		return;
+	}
+
 	if (stat64(file, &stat) < 0)
 		return;
 
diff --git a/ramdump.c b/ramdump.c
new file mode 100644
index 0000000..2d5824a
--- /dev/null
+++ b/ramdump.c
@@ -0,0 +1,338 @@
+/*
+ * ramdump.c - core analysis suite
+ *
+ * Copyright (c) 2014 Vinayak Menon <vinayakm.list@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Author: Vinayak Menon <vinayakm.list@xxxxxxxxx>
+ */
+
+#define _LARGEFILE64_SOURCE 1  /* stat64() */
+#include "defs.h"
+#include <libelf.h>
+
+struct ramdump_def {
+	char *path;
+	int rfd;
+	ulong start_paddr;
+	ulong end_paddr;
+};
+
+static struct ramdump_def *ramdump;
+static int nodes;
+static char *user_elf;
+static char elf_default[] = "/var/tmp/ramdump_elfXXXXXX";
+
+static void alloc_elf_header(Elf64_Ehdr *ehdr, ushort e_machine)
+{
+	memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+	ehdr->e_ident[EI_CLASS] = ELFCLASS64;
+	ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+	ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+	ehdr->e_ident[EI_OSABI] = ELFOSABI_LINUX;
+	ehdr->e_ident[EI_ABIVERSION] = 0;
+	memset(ehdr->e_ident+EI_PAD, 0,
+		EI_NIDENT-EI_PAD);
+	ehdr->e_type = ET_CORE;
+	ehdr->e_machine = e_machine;
+	ehdr->e_version = EV_CURRENT;
+	ehdr->e_entry = 0;
+	ehdr->e_phoff = sizeof(Elf64_Ehdr);
+	ehdr->e_shoff = 0;
+	ehdr->e_flags = 0;
+	ehdr->e_ehsize = sizeof(Elf64_Ehdr);
+	ehdr->e_phentsize = sizeof(Elf64_Phdr);
+	ehdr->e_phnum = 1 + nodes;
+	ehdr->e_shentsize = 0;
+	ehdr->e_shnum = 0;
+	ehdr->e_shstrndx = 0;
+}
+
+static int alloc_program_headers(Elf64_Phdr *phdr)
+{
+	unsigned int i;
+	struct stat64 st;
+
+	for (i = 0; i < nodes; i++, phdr++) {
+		phdr[i].p_type = PT_LOAD;
+
+		if (0 > stat64(ramdump[i].path, &st)) {
+			error(INFO, "ramdump stat failed\n");
+			return -1;
+		}
+
+		phdr[i].p_filesz = st.st_size;
+		phdr[i].p_memsz = phdr[i].p_filesz;
+		phdr[i].p_vaddr = 0;
+		phdr[i].p_paddr = ramdump[i].start_paddr;
+		ramdump[i].end_paddr = ramdump[i].start_paddr + st.st_size - 1;
+		phdr[i].p_flags = PF_R | PF_W | PF_X;
+		phdr[i].p_align = 0;
+	}
+
+	return 0;
+}
+
+static char *write_elf(Elf64_Phdr *load, Elf64_Ehdr *e_head, size_t data_offset)
+{
+#define CPY_BUF_SZ 4096
+	int fd1, fd2, i, err = 1;
+	char *buf;
+	char *out_elf;
+	size_t rd, len, offset;
+
+	buf = (char *)GETBUF(CPY_BUF_SZ);
+
+	offset = data_offset;
+
+	if (user_elf) {
+		fd2 = open(user_elf, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
+		if (fd2 < 0) {
+			error(INFO, "%s open error, %s\n",
+				user_elf, strerror(errno));
+			goto end1;
+		}
+		out_elf = user_elf;
+	} else {
+		fd2 = mkstemp(elf_default);
+		if (fd2 < 0) {
+			error(INFO, "%s open error, %s\n",
+				elf_default, strerror(errno));
+			goto end1;
+		}
+		out_elf = elf_default;
+		pc->flags2 |= RAMDUMP;
+	}
+
+	error(NOTE, "Creating kdump ELF %s\n", out_elf);
+
+	while (offset > 0) {
+		len = write(fd2, e_head + (data_offset - offset), offset);
+		if (len < 0) {
+			error(INFO, "ramdump write error, %s\n",
+				strerror(errno));
+			goto end;
+		}
+		offset -= len;
+	}
+
+	if (user_elf) {
+		for (i = 0; i < nodes; i++) {
+			offset = load[i].p_offset;
+
+			fd1 = open(ramdump[i].path, O_RDONLY, S_IRUSR);
+			if (fd1 < 0) {
+				error(INFO, "%s open error, %s\n",
+					ramdump[i].path, strerror(errno));
+				goto end;
+			}
+
+			lseek(fd2, (off_t)offset, SEEK_SET);
+			while ((rd = read(fd1, buf, CPY_BUF_SZ)) > 0) {
+				if (write(fd2, buf, rd) != rd) {
+					error(INFO, "%s write error, %s\n",
+						ramdump[i].path,
+						strerror(errno));
+					close(fd1);
+					goto end;
+				}
+			}
+			close(fd1);
+		}
+	}
+
+	err = 0;
+end:
+	close(fd2);
+end1:
+	FREEBUF(buf);
+	return err ? NULL : out_elf;
+}
+
+static void alloc_notes(Elf64_Phdr *notes)
+{
+	/* Nothing filled in as of now */
+	notes->p_type = PT_NOTE;
+	notes->p_offset = 0;
+	notes->p_vaddr = 0;
+	notes->p_paddr = 0;
+	notes->p_filesz = 0;
+	notes->p_memsz = 0;
+	notes->p_flags = 0;
+	notes->p_align = 0;
+}
+
+char *ramdump_to_elf(void)
+{
+	int i;
+	char *ptr, *e_file = NULL;
+	ushort e_machine = 0;
+	size_t offset, data_offset;
+	size_t l_offset;
+	Elf64_Phdr *notes, *load;
+	Elf64_Ehdr *e_head;
+
+	if (machine_type("ARM")) {
+		e_machine = EM_ARM;
+	} else if (machine_type("ARM64")) {
+		e_machine = EM_AARCH64;
+	} else {
+		error(INFO, "unknown machine type\n");
+		return NULL;
+	}
+
+	e_head = (Elf64_Ehdr *)GETBUF(sizeof(Elf64_Ehdr) +
+		nodes * sizeof(Elf64_Phdr) + PAGESIZE() * 2);
+	ptr = (char *)e_head;
+	offset = 0;
+
+	error(NOTE, "Converting ramdump to kdump\n");
+	alloc_elf_header(e_head, e_machine);
+
+	ptr += sizeof(Elf64_Ehdr);
+	offset += sizeof(Elf64_Ehdr);
+
+	notes = (Elf64_Phdr *)ptr;
+
+	alloc_notes(notes);
+
+	offset += sizeof(Elf64_Phdr);
+	ptr += sizeof(Elf64_Phdr);
+
+	load = (Elf64_Phdr *)ptr;
+
+	if (alloc_program_headers(load))
+		goto end;
+
+	offset += sizeof(Elf64_Phdr) * nodes;
+	ptr += sizeof(Elf64_Phdr) * nodes;
+
+	/* Empty note */
+	notes->p_offset = offset;
+
+	l_offset = offset;
+
+	data_offset = offset;
+
+	for (i = 0; i < nodes; i++) {
+		load[i].p_offset = l_offset;
+		l_offset += load[i].p_filesz;
+	}
+
+	e_file = write_elf(load, e_head, data_offset);
+end:
+	FREEBUF(e_head);
+	return e_file;
+}
+
+int is_ramdump(char *p)
+{
+	char *x = NULL, *y = NULL, *pat;
+	size_t len;
+	char *pattern;
+	int err = 0;
+
+	if (nodes || !strchr(p, '@'))
+		return 0;
+
+	len = strlen(p);
+	pattern = (char *)GETBUF(len + 1);
+	strlcpy(pattern, p, len + 1);
+
+	pat = pattern;
+	while ((pat = strtok_r(pat, ",", &x))) {
+		if ((pat = strtok_r(pat, "@", &y))) {
+			nodes++;
+			ramdump = realloc(ramdump,
+				sizeof(struct ramdump_def) * nodes);
+			if (!ramdump)
+				error(FATAL, "realloc failure\n");
+			ramdump[nodes - 1].path = pat;
+			pat = strtok_r(NULL, "@", &y);
+			ramdump[nodes - 1].start_paddr =
+				htol(pat, RETURN_ON_ERROR, &err);
+			if (err == TRUE)
+				error(FATAL, "Invalid ramdump address\n");
+			if ((ramdump[nodes - 1].rfd =
+				open(ramdump[nodes - 1].path, O_RDONLY)) < 0)
+					error(FATAL,
+						"ramdump %s open failed:%s\n",
+						ramdump[nodes - 1].path,
+						strerror(errno));
+		}
+
+		pat = NULL;
+	}
+
+	return nodes;
+}
+
+void ramdump_elf_output_file(char *opt)
+{
+	user_elf = opt;
+}
+
+void ramdump_cleanup(void)
+{
+	if (!user_elf)
+		unlink(elf_default);
+}
+
+int
+read_ramdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
+{
+	off_t offset;
+	int i;
+	struct ramdump_def *r = &ramdump[0];
+
+	offset = 0;
+
+	if (nodes == 1) {
+		offset = (off_t)paddr - (off_t)ramdump[0].start_paddr;
+	} else {
+
+		for (i = offset = 0; i < nodes; i++) {
+			r = &ramdump[i];
+			if ((paddr >= r->start_paddr) &&
+			    (paddr < r->end_paddr)) {
+				offset = (off_t)paddr - (off_t)r->start_paddr;
+				break;
+			}
+		}
+		if (CRASHDEBUG(8))
+			fprintf(fp, "read_ramdump: READ_ERROR: "
+				    "offset not found for paddr: %llx\n",
+					(ulonglong)paddr);
+		return READ_ERROR;
+	}
+
+	if (CRASHDEBUG(8))
+		fprintf(fp,
+		"read_ramdump: addr: %lx paddr: %llx cnt: %d offset: %llx\n",
+			addr, (ulonglong)paddr, cnt, (ulonglong)offset);
+
+	if (lseek(r->rfd, offset, SEEK_SET) == -1) {
+		if (CRASHDEBUG(8))
+			fprintf(fp, "read_ramdump: SEEK_ERROR: "
+				"offset: %llx\n", (ulonglong)offset);
+		return SEEK_ERROR;
+	}
+
+	if (read(r->rfd, bufptr, cnt) != cnt) {
+		if (CRASHDEBUG(8))
+			fprintf(fp, "read_ramdump: READ_ERROR: "
+				"offset: %llx\n", (ulonglong)offset);
+		return READ_ERROR;
+	}
+
+        return cnt;
+}
-- 
1.9.1

--
Crash-utility mailing list
Crash-utility@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/crash-utility

[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux