changeset 14107:3d9eb655623f

3922 libkvm/mdb should be able to extract symbols from crash dump Reviewed by: Keith Wesolowski <keith.wesolowski@joyent.com> Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Richard Lowe <richlowe@richlowe.net>
author Bryan Cantrill <bryan@joyent.com>
date Thu, 09 May 2013 18:56:46 +0000
parents 1923bb79231f
children 8d40b8fbaa54
files usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c usr/src/cmd/mdb/common/mdb/mdb_kvm.c usr/src/cmd/mdb/common/mdb/mdb_main.c usr/src/lib/libkvm/common/kvm.c usr/src/lib/libkvm/common/mapfile-vers usr/src/lib/libkvm/kvm.h
diffstat 6 files changed, 142 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c	Fri Aug 02 12:02:33 2013 -0800
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c	Thu May 09 18:56:46 2013 +0000
@@ -23,7 +23,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ */
 
 /*
  * The default KVM backend, which simply calls directly into libkvm for all
@@ -39,9 +41,10 @@
 
 /*ARGSUSED*/
 static mdb_io_t *
-libkvm_sym_io(void *kvm, const char *symfile)
+libkvm_sym_io(void *kvm, const char *ignored)
 {
 	mdb_io_t *io;
+	const char *symfile = kvm_namelist(kvm);
 
 	if ((io = mdb_fdio_create_path(NULL, symfile, O_RDONLY, 0)) == NULL)
 		mdb_warn("failed to open %s", symfile);
--- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c	Fri Aug 02 12:02:33 2013 -0800
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c	Thu May 09 18:56:46 2013 +0000
@@ -23,6 +23,10 @@
  */
 
 /*
+ * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ */
+
+/*
  * Libkvm Kernel Target
  *
  * The libkvm kernel target provides access to both crash dumps and live
@@ -1571,10 +1575,23 @@
 }
 
 int
+mdb_kvm_is_dump(mdb_io_t *io)
+{
+	dumphdr_t h;
+
+	(void) IOP_SEEK(io, (off64_t)0L, SEEK_SET);
+
+	return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) &&
+	    h.dump_magic == DUMP_MAGIC);
+}
+
+int
 mdb_kvm_is_compressed_dump(mdb_io_t *io)
 {
 	dumphdr_t h;
 
+	(void) IOP_SEEK(io, (off64_t)0L, SEEK_SET);
+
 	return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) &&
 	    h.dump_magic == DUMP_MAGIC &&
 	    (h.dump_flags & DF_COMPRESSED) != 0);
--- a/usr/src/cmd/mdb/common/mdb/mdb_main.c	Fri Aug 02 12:02:33 2013 -0800
+++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c	Thu May 09 18:56:46 2013 +0000
@@ -24,6 +24,10 @@
  * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ */
+
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <sys/priocntl.h>
@@ -403,8 +407,9 @@
 main(int argc, char *argv[], char *envp[])
 {
 	extern int mdb_kvm_is_compressed_dump(mdb_io_t *);
+	extern int mdb_kvm_is_dump(mdb_io_t *);
 	mdb_tgt_ctor_f *tgt_ctor = NULL;
-	const char **tgt_argv = alloca(argc * sizeof (char *));
+	const char **tgt_argv = alloca((argc + 2) * sizeof (char *));
 	int tgt_argc = 0;
 	mdb_tgt_t *tgt;
 
@@ -818,8 +823,8 @@
 			size_t len = strlen(tgt_argv[0]) + 8;
 			const char *object = tgt_argv[0];
 
-			tgt_argv[0] = mdb_alloc(len, UM_SLEEP);
-			tgt_argv[1] = mdb_alloc(len, UM_SLEEP);
+			tgt_argv[0] = alloca(len);
+			tgt_argv[1] = alloca(len);
 
 			(void) strcpy((char *)tgt_argv[0], "unix.");
 			(void) strcat((char *)tgt_argv[0], object);
@@ -827,6 +832,14 @@
 			(void) strcat((char *)tgt_argv[1], object);
 
 			if (access(tgt_argv[0], F_OK) == -1 &&
+			    access(tgt_argv[1], F_OK) != -1) {
+				/*
+				 * If we have a vmcore but not a unix file,
+				 * set the symbol table to be the vmcore to
+				 * force libkvm to extract it out of the dump.
+				 */
+				tgt_argv[0] = tgt_argv[1];
+			} else if (access(tgt_argv[0], F_OK) == -1 &&
 			    access(tgt_argv[1], F_OK) == -1) {
 				(void) strcpy((char *)tgt_argv[1], "vmdump.");
 				(void) strcat((char *)tgt_argv[1], object);
@@ -850,17 +863,25 @@
 		    O_RDONLY, 0)) == NULL)
 			die("failed to open %s", tgt_argv[0]);
 
-		/*
-		 * Check for a single vmdump.N compressed dump file,
-		 * and give a helpful message.
-		 */
 		if (tgt_argc == 1) {
 			if (mdb_kvm_is_compressed_dump(io)) {
+				/*
+				 * We have a single vmdump.N compressed dump
+				 * file; give a helpful message.
+				 */
 				mdb_iob_printf(mdb.m_err,
 				    "cannot open compressed dump; "
 				    "decompress using savecore -f %s\n",
 				    tgt_argv[0]);
 				terminate(0);
+			} else if (mdb_kvm_is_dump(io)) {
+				/*
+				 * We have an uncompressed dump as our only
+				 * argument; specify the dump as the symbol
+				 * table to force libkvm to dig it out of the
+				 * dump.
+				 */
+				tgt_argv[tgt_argc++] = tgt_argv[0];
 			}
 		}
 
--- a/usr/src/lib/libkvm/common/kvm.c	Fri Aug 02 12:02:33 2013 -0800
+++ b/usr/src/lib/libkvm/common/kvm.c	Thu May 09 18:56:46 2013 +0000
@@ -24,7 +24,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ */
 
 #include <kvm.h>
 #include <stdio.h>
@@ -34,6 +36,7 @@
 #include <limits.h>
 #include <fcntl.h>
 #include <strings.h>
+#include <errno.h>
 #include <sys/mem.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
@@ -55,12 +58,15 @@
 	proc_t		*kvm_practive;
 	pid_t		kvm_pid;
 	char		kvm_namelist[MAXNAMELEN + 1];
+	boolean_t	kvm_namelist_core;
 	proc_t		kvm_proc;
 };
 
 #define	PREAD	(ssize_t (*)(int, void *, size_t, offset_t))pread64
 #define	PWRITE	(ssize_t (*)(int, void *, size_t, offset_t))pwrite64
 
+static int kvm_nlist_core(kvm_t *kd, struct nlist nl[], const char *err);
+
 static kvm_t *
 fail(kvm_t *kd, const char *err, const char *message, ...)
 {
@@ -164,9 +170,15 @@
 
 	(void) strncpy(kd->kvm_namelist, namelist, MAXNAMELEN);
 
-	if (kvm_nlist(kd, nl) == -1)
-		return (fail(kd, err, "%s is not a %d-bit kernel namelist",
-		    namelist, DUMP_WORDSIZE));
+	if (kvm_nlist(kd, nl) == -1) {
+		if (kd->kvm_corefd == -1) {
+			return (fail(kd, err, "%s is not a %d-bit "
+			    "kernel namelist", namelist, DUMP_WORDSIZE));
+		}
+
+		if (kvm_nlist_core(kd, nl, err) == -1)
+			return (NULL);		/* fail() already called */
+	}
 
 	kd->kvm_kas = (struct as *)nl[0].n_value;
 	kd->kvm_practive = (proc_t *)nl[1].n_value;
@@ -186,16 +198,85 @@
 		(void) close(kd->kvm_kmemfd);
 	if (kd->kvm_memfd != -1)
 		(void) close(kd->kvm_memfd);
+	if (kd->kvm_namelist_core)
+		(void) unlink(kd->kvm_namelist);
 	free(kd);
 	return (0);
 }
 
+const char *
+kvm_namelist(kvm_t *kd)
+{
+	return (kd->kvm_namelist);
+}
+
 int
 kvm_nlist(kvm_t *kd, struct nlist nl[])
 {
 	return (nlist(kd->kvm_namelist, nl));
 }
 
+/*
+ * If we don't have a name list, try to dig it out of the kernel crash dump.
+ * (The symbols have been present in the dump, uncompressed, for nearly a
+ * decade as of this writing -- and it is frankly surprising that the archaic
+ * notion of a disjoint symbol table managed to survive that change.)
+ */
+static int
+kvm_nlist_core(kvm_t *kd, struct nlist nl[], const char *err)
+{
+	dumphdr_t *dump = &kd->kvm_dump;
+	char *msg = "couldn't extract symbols from dump";
+	char *template = "/tmp/.libkvm.kvm_nlist_core.pid%d.XXXXXX";
+	int fd, rval;
+
+	if (dump->dump_ksyms_size != dump->dump_ksyms_csize) {
+		(void) fail(kd, err, "%s: kernel symbols are compressed", msg);
+		return (-1);
+	}
+
+	if (dump->dump_ksyms + dump->dump_ksyms_size > kd->kvm_coremapsize) {
+		(void) fail(kd, err, "%s: kernel symbols not mapped", msg);
+		return (-1);
+	}
+
+	/*
+	 * Beause this temporary file may be left as a turd if the caller
+	 * does not properly call kvm_close(), we make sure that it clearly
+	 * indicates its origins.
+	 */
+	(void) snprintf(kd->kvm_namelist, MAXNAMELEN, template, getpid());
+
+	if ((fd = mkstemp(kd->kvm_namelist)) == -1) {
+		(void) fail(kd, err, "%s: couldn't create temporary "
+		    "symbols file: %s", msg, strerror(errno));
+		return (-1);
+	}
+
+	kd->kvm_namelist_core = B_TRUE;
+
+	do {
+		rval = write(fd, (caddr_t)((uintptr_t)kd->kvm_core +
+		    (uintptr_t)dump->dump_ksyms), dump->dump_ksyms_size);
+	} while (rval < dump->dump_ksyms_size && errno == EINTR);
+
+	if (rval < dump->dump_ksyms_size) {
+		(void) fail(kd, err, "%s: couldn't write to temporary "
+		    "symbols file: %s", msg, strerror(errno));
+		(void) close(fd);
+		return (-1);
+	}
+
+	(void) close(fd);
+
+	if (kvm_nlist(kd, nl) == -1) {
+		(void) fail(kd, err, "%s: symbols not valid", msg);
+		return (-1);
+	}
+
+	return (0);
+}
+
 static offset_t
 kvm_lookup(kvm_t *kd, struct as *as, uint64_t addr)
 {
--- a/usr/src/lib/libkvm/common/mapfile-vers	Fri Aug 02 12:02:33 2013 -0800
+++ b/usr/src/lib/libkvm/common/mapfile-vers	Thu May 09 18:56:46 2013 +0000
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, Joyent, Inc.  All rights reserved.
 #
 
 #
@@ -60,6 +61,7 @@
     global:
 	kvm_aread;
 	kvm_awrite;
+	kvm_namelist;
 	kvm_physaddr;
 	kvm_pread;
 	kvm_pwrite;
--- a/usr/src/lib/libkvm/kvm.h	Fri Aug 02 12:02:33 2013 -0800
+++ b/usr/src/lib/libkvm/kvm.h	Thu May 09 18:56:46 2013 +0000
@@ -24,11 +24,13 @@
  * All rights reserved.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ */
+
 #ifndef	_KVM_H
 #define	_KVM_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <nlist.h>
 #include <sys/user.h>
@@ -67,6 +69,7 @@
 extern int	kvm_setproc(kvm_t *);
 extern user_t	*kvm_getu(kvm_t *, struct proc *);
 extern int	kvm_getcmd(kvm_t *, proc_t *, user_t *, char ***, char ***);
+extern const char *kvm_namelist(kvm_t *);
 
 #else