summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ddb/Makefile5
-rw-r--r--sbin/ddb/ddb.841
-rw-r--r--sbin/ddb/ddb.c8
-rw-r--r--sbin/ddb/ddb.h1
-rw-r--r--sbin/ddb/ddb_capture.c247
5 files changed, 297 insertions, 5 deletions
diff --git a/sbin/ddb/Makefile b/sbin/ddb/Makefile
index 1ae724b..b9189c1 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 <bsd.prog.mk>
diff --git a/sbin/ddb/ddb.8 b/sbin/ddb/ddb.8
index 0165f56..90ceb63 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 5e86efc..f5c5dd0 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 50128bd..8363bcc 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 0000000..9f83acb
--- /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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#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();
+}
OpenPOWER on IntegriCloud