Mercurial > illumos > illumos-gate
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