summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Cantrill <bryan@joyent.com>2013-05-09 18:56:46 +0000
committerBryan Cantrill <bryan@joyent.com>2013-05-09 18:56:46 +0000
commite083a554cd81f87a93a0b654bcbe3ffa29437ef2 (patch)
treeea4e01ab226b18c9c955f985fafdd639df591568
parent43840e901cacf50a9bb41148069e34ca307524e8 (diff)
downloadillumos-joyent-e083a554cd81f87a93a0b654bcbe3ffa29437ef2.tar.gz
OS-2190 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>
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c7
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kvm.c17
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_main.c35
-rw-r--r--usr/src/lib/libkvm/common/kvm.c89
-rw-r--r--usr/src/lib/libkvm/common/mapfile-vers2
-rw-r--r--usr/src/lib/libkvm/kvm.h7
6 files changed, 142 insertions, 15 deletions
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
index d3cac91c5c..48b6e1ec93 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
@@ -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);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
index 4b368e357b..8e96c04350 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
@@ -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 @@ err:
}
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);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c
index 4ca42e83cb..90e048bb82 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_main.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c
@@ -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>
@@ -405,8 +409,9 @@ int
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;
@@ -834,8 +839,8 @@ main(int argc, char *argv[], char *envp[])
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);
@@ -843,6 +848,14 @@ main(int argc, char *argv[], char *envp[])
(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);
@@ -866,17 +879,25 @@ main(int argc, char *argv[], char *envp[])
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];
}
}
diff --git a/usr/src/lib/libkvm/common/kvm.c b/usr/src/lib/libkvm/common/kvm.c
index d3a5dbd81c..c71c4db31b 100644
--- a/usr/src/lib/libkvm/common/kvm.c
+++ b/usr/src/lib/libkvm/common/kvm.c
@@ -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 @@ struct _kvmd {
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 @@ kvm_open(const char *namelist, const char *corefile, const char *swapfile,
(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 @@ kvm_close(kvm_t *kd)
(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)
{
diff --git a/usr/src/lib/libkvm/common/mapfile-vers b/usr/src/lib/libkvm/common/mapfile-vers
index 34ccee8093..091c1cbd08 100644
--- a/usr/src/lib/libkvm/common/mapfile-vers
+++ b/usr/src/lib/libkvm/common/mapfile-vers
@@ -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 @@ SYMBOL_VERSION SUNWprivate_1.1 {
global:
kvm_aread;
kvm_awrite;
+ kvm_namelist;
kvm_physaddr;
kvm_pread;
kvm_pwrite;
diff --git a/usr/src/lib/libkvm/kvm.h b/usr/src/lib/libkvm/kvm.h
index ec2bd2a7c2..348b0c2e38 100644
--- a/usr/src/lib/libkvm/kvm.h
+++ b/usr/src/lib/libkvm/kvm.h
@@ -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 proc_t *kvm_nextproc(kvm_t *);
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