Index: Makefile
===================================================================
RCS file: /home/ncvs/src/sbin/ddb/Makefile,v
retrieving revision 1.1
diff -u -r1.1 Makefile
--- Makefile	26 Dec 2007 09:38:22 -0000	1.1
+++ Makefile	24 Apr 2008 23:55:16 -0000
@@ -1,8 +1,11 @@
 # $FreeBSD: src/sbin/ddb/Makefile,v 1.1 2007/12/26 09:38:22 rwatson Exp $
 
 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>
Index: ddb.8
===================================================================
RCS file: /home/ncvs/src/sbin/ddb/ddb.8,v
retrieving revision 1.4
diff -u -r1.4 ddb.8
--- ddb.8	4 Apr 2008 07:31:43 -0000	1.4
+++ ddb.8	25 Apr 2008 13:24:34 -0000
@@ -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: src/sbin/ddb/ddb.8,v 1.4 2008/04/04 07:31:43 ru Exp $
 .\"
-.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,23 @@
 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 Cm capture Op Fl M core Op Fl N system Cm print
+Print the current contents of the
+.Xr ddb 4
+output capture buffer.
+.It Cm capture Op Fl M core Op Fl N system Cm status
+Print the current status of the
+.Xr ddb 4
+output capture buffer.
 .Sh SCRIPTING
 The
 .Nm
Index: ddb.c
===================================================================
RCS file: /home/ncvs/src/sbin/ddb/ddb.c,v
retrieving revision 1.3
diff -u -r1.3 ddb.c
--- ddb.c	4 Apr 2008 07:31:43 -0000	1.3
+++ ddb.c	24 Apr 2008 23:55:16 -0000
@@ -43,7 +43,9 @@
 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 @@
 	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);
Index: ddb.h
===================================================================
RCS file: /home/ncvs/src/sbin/ddb/ddb.h,v
retrieving revision 1.1
diff -u -r1.1 ddb.h
--- ddb.h	26 Dec 2007 09:38:22 -0000	1.1
+++ ddb.h	24 Apr 2008 23:55:16 -0000
@@ -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[]);
Index: ddb_capture.c
===================================================================
RCS file: ddb_capture.c
diff -N ddb_capture.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ddb_capture.c	25 Apr 2008 13:19:21 -0000
@@ -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();
+}
