diff --git a/sbin/ddb/Makefile b/sbin/ddb/Makefile index 1ae724b55ee3..b9189c1c0d9e 100644 --- a/sbin/ddb/Makefile +++ b/sbin/ddb/Makefile @@ -1,8 +1,11 @@ # $FreeBSD$ PROG= ddb -SRCS= ddb.c ddb_script.c +SRCS= ddb.c ddb_capture.c ddb_script.c MAN= ddb.8 WARNS= 3 +DPADD= ${LIBKVM} +LDADD= -lkvm + .include diff --git a/sbin/ddb/ddb.8 b/sbin/ddb/ddb.8 index 0165f56fbefd..90ceb6300c3d 100644 --- a/sbin/ddb/ddb.8 +++ b/sbin/ddb/ddb.8 @@ -1,5 +1,5 @@ .\"- -.\" Copyright (c) 2007 Robert N. M. Watson +.\" Copyright (c) 2007-2008 Robert N. M. Watson .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 4, 2008 +.Dd April 24, 2008 .Dt DDB 8 .Os .Sh NAME @@ -33,6 +33,16 @@ .Nd "configure DDB kernel debugger properties" .Sh SYNOPSIS .Nm +.Cm capture +.Op Fl M core +.Op Fl N system +.Cm print +.Nm +.Cm capture +.Op Fl M core +.Op Fl N system +.Cm status +.Nm .Cm script .Ar scriptname .Nm @@ -67,6 +77,33 @@ utility. Whitespace at the beginning of lines will be ignored as will lines where the first non-whitespace character is .Ql # . +.Sh OUTPUT CAPTURE +The +.Nm +utility can be used to extract the contents of the +.Xr ddb 4 +output capture buffer of the current live kernel, or from the crash dump of a +kernel on disk. +The following debugger commands are available from the command line: +.Bl -tag -width indent +.It Xo +.Ic Cm capture +.Op Fl M Ar core +.Op Fl N Ar system +.Cm print +.Xc +Print the current contents of the +.Xr ddb 4 +output capture buffer. +.It Xo +.Ic Cm capture +.Op Fl M Ar core +.Op Fl N Ar system +.Cm status +.Xc +Print the current status of the +.Xr ddb 4 +output capture buffer. .Sh SCRIPTING The .Nm diff --git a/sbin/ddb/ddb.c b/sbin/ddb/ddb.c index 5e86efc1e47b..f5c5dd026451 100644 --- a/sbin/ddb/ddb.c +++ b/sbin/ddb/ddb.c @@ -43,7 +43,9 @@ void usage(void) { - fprintf(stderr, "usage: ddb script scriptname\n"); + fprintf(stderr, "usage: ddb capture [-M core] [-N system] print\n"); + fprintf(stderr, " ddb capture [-M core] [-N system] status\n"); + fprintf(stderr, " ddb script scriptname\n"); fprintf(stderr, " ddb script scriptname=script\n"); fprintf(stderr, " ddb scripts\n"); fprintf(stderr, " ddb unscript scriptname\n"); @@ -103,7 +105,9 @@ ddb_main(int argc, char *argv[]) if (argc < 1) usage(); - if (strcmp(argv[0], "script") == 0) + if (strcmp(argv[0], "capture") == 0) + ddb_capture(argc, argv); + else if (strcmp(argv[0], "script") == 0) ddb_script(argc, argv); else if (strcmp(argv[0], "scripts") == 0) ddb_scripts(argc, argv); diff --git a/sbin/ddb/ddb.h b/sbin/ddb/ddb.h index 50128bd43c61..8363bccfd889 100644 --- a/sbin/ddb/ddb.h +++ b/sbin/ddb/ddb.h @@ -29,6 +29,7 @@ #ifndef DDB_H #define DDB_H +void ddb_capture(int argc, char *argv[]); void ddb_script(int argc, char *argv[]); void ddb_scripts(int argc, char *argv[]); void ddb_unscript(int argc, char *argv[]); diff --git a/sbin/ddb/ddb_capture.c b/sbin/ddb/ddb_capture.c new file mode 100644 index 000000000000..9f83acbb03a5 --- /dev/null +++ b/sbin/ddb/ddb_capture.c @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2008 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ddb.h" + +/* + * Interface with the ddb(4) capture buffer of a live kernel using sysctl, or + * for a crash dump using libkvm. + */ +#define SYSCTL_DDB_CAPTURE_BUFOFF "debug.ddb.capture.bufoff" +#define SYSCTL_DDB_CAPTURE_BUFSIZE "debug.ddb.capture.bufsize" +#define SYSCTL_DDB_CAPTURE_MAXBUFSIZE "debug.ddb.capture.maxbufsize" +#define SYSCTL_DDB_CAPTURE_DATA "debug.ddb.capture.data" +#define SYSCTL_DDB_CAPTURE_INPROGRESS "debug.ddb.capture.inprogress" + +static struct nlist namelist[] = { +#define X_DB_CAPTURE_BUF 0 + { .n_name = "_db_capture_buf" }, +#define X_DB_CAPTURE_BUFSIZE 1 + { .n_name = "_db_capture_bufsize" }, +#define X_DB_CAPTURE_MAXBUFSIZE 2 + { .n_name = "_db_capture_maxbufsize" }, +#define X_DB_CAPTURE_BUFOFF 3 + { .n_name = "_db_capture_bufoff" }, +#define X_DB_CAPTURE_INPROGRESS 4 + { .n_name = "_db_capture_inprogress" }, + { .n_name = "" }, +}; + +static int +kread(kvm_t *kvm, void *kvm_pointer, void *address, size_t size, + size_t offset) +{ + ssize_t ret; + + ret = kvm_read(kvm, (unsigned long)kvm_pointer + offset, address, + size); + if (ret < 0 || (size_t)ret != size) + return (-1); + return (0); +} + +static int +kread_symbol(kvm_t *kvm, int index, void *address, size_t size, + size_t offset) +{ + ssize_t ret; + + ret = kvm_read(kvm, namelist[index].n_value + offset, address, size); + if (ret < 0 || (size_t)ret != size) + return (-1); + return (0); +} + +static void +ddb_capture_print_kvm(kvm_t *kvm) +{ + u_int db_capture_bufsize; + char *buffer, *db_capture_buf; + + if (kread_symbol(kvm, X_DB_CAPTURE_BUF, &db_capture_buf, + sizeof(db_capture_buf), 0) < 0) + errx(-1, "kvm: unable to read db_capture_buf"); + + if (kread_symbol(kvm, X_DB_CAPTURE_BUFSIZE, &db_capture_bufsize, + sizeof(db_capture_bufsize), 0) < 0) + errx(-1, "kvm: unable to read db_capture_bufsize"); + + buffer = malloc(db_capture_bufsize + 1); + if (buffer == NULL) + err(-1, "malloc: db_capture_bufsize (%u)", + db_capture_bufsize); + bzero(buffer, db_capture_bufsize + 1); + + if (kread(kvm, db_capture_buf, buffer, db_capture_bufsize, 0) < 0) + errx(-1, "kvm: unable to read buffer"); + + printf("%s\n", buffer); + free(buffer); +} + +static void +ddb_capture_print_sysctl(void) +{ + size_t buflen, len; + char *buffer; + int ret; + +repeat: + if (sysctlbyname(SYSCTL_DDB_CAPTURE_DATA, NULL, &buflen, NULL, 0) < 0) + err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_DATA); + if (buflen == 0) + return; + buffer = malloc(buflen); + if (buffer == NULL) + err(EX_OSERR, "malloc"); + bzero(buffer, buflen); + len = buflen; + ret = sysctlbyname(SYSCTL_DDB_CAPTURE_DATA, buffer, &len, NULL, 0); + if (ret < 0 && errno != ENOMEM) + err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_DATA); + if (ret < 0) { + free(buffer); + goto repeat; + } + + printf("%s\n", buffer); + free(buffer); +} + +static void +ddb_capture_status_kvm(kvm_t *kvm) +{ + u_int db_capture_bufoff, db_capture_bufsize, db_capture_inprogress; + + if (kread_symbol(kvm, X_DB_CAPTURE_BUFOFF, &db_capture_bufoff, + sizeof(db_capture_bufoff), 0) < 0) + errx(-1, "kvm: unable to read db_capture_bufoff"); + if (kread_symbol(kvm, X_DB_CAPTURE_BUFSIZE, &db_capture_bufsize, + sizeof(db_capture_bufsize), 0) < 0) + errx(-1, "kvm: unable to read db_capture_bufsize"); + if (kread_symbol(kvm, X_DB_CAPTURE_INPROGRESS, + &db_capture_inprogress, sizeof(db_capture_inprogress), 0) < 0) + err(-1, "kvm: unable to read db_capture_inpgoress"); + printf("%u/%u bytes used\n", db_capture_bufoff, db_capture_bufsize); + if (db_capture_inprogress) + printf("capture is on\n"); + else + printf("capture is off\n"); + +} + +static void +ddb_capture_status_sysctl(void) +{ + u_int db_capture_bufoff, db_capture_bufsize, db_capture_inprogress; + size_t len; + + len = sizeof(db_capture_bufoff); + if (sysctlbyname(SYSCTL_DDB_CAPTURE_BUFOFF, &db_capture_bufoff, &len, + NULL, 0) < 0) + err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_BUFOFF); + len = sizeof(db_capture_bufoff); + if (sysctlbyname(SYSCTL_DDB_CAPTURE_BUFSIZE, &db_capture_bufsize, + &len, NULL, 0) < 0) + err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_BUFSIZE); + len = sizeof(db_capture_inprogress); + if (sysctlbyname(SYSCTL_DDB_CAPTURE_INPROGRESS, + &db_capture_inprogress, &len, NULL, 0) < 0) + err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_INPROGRESS); + printf("%u/%u bytes used\n", db_capture_bufoff, db_capture_bufsize); + if (db_capture_inprogress) + printf("capture is on\n"); + else + printf("capture is off\n"); +} + +void +ddb_capture(int argc, char *argv[]) +{ + char *mflag, *nflag, errbuf[_POSIX2_LINE_MAX]; + kvm_t *kvm; + int ch; + + mflag = NULL; + nflag = NULL; + while ((ch = getopt(argc, argv, "M:N:")) != -1) { + switch (ch) { + case 'M': + mflag = optarg; + break; + + case 'N': + nflag = optarg; + break; + + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if (mflag != NULL) { + kvm = kvm_openfiles(nflag, mflag, NULL, O_RDONLY, errbuf); + if (kvm == NULL) + errx(-1, "ddb_capture: kvm_openfiles: %s", errbuf); + if (kvm_nlist(kvm, namelist) != 0) + errx(-1, "ddb_capture: kvm_nlist"); + } else if (nflag != NULL) + usage(); + if (strcmp(argv[0], "print") == 0) { + if (kvm != NULL) + ddb_capture_print_kvm(kvm); + else + ddb_capture_print_sysctl(); + } else if (strcmp(argv[0], "status") == 0) { + if (kvm != NULL) + ddb_capture_status_kvm(kvm); + else + ddb_capture_status_sysctl(); + } else + usage(); +}