summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Watson <rwatson@FreeBSD.org>2008-03-31 21:57:24 +0000
committerRobert Watson <rwatson@FreeBSD.org>2008-03-31 21:57:24 +0000
commitd164d35c014133e0c25eef9184dbc6a44b270cec (patch)
treec1266bb4e240e76237db1068acb8802a202fdafb
parent26efbba75fa773a82cb11628a0a6e384331276f1 (diff)
Notes
-rw-r--r--sys/conf/files1
-rw-r--r--sys/ddb/db_capture.c58
-rw-r--r--sys/ddb/db_command.c1
-rw-r--r--sys/ddb/db_textdump.c28
-rw-r--r--sys/ddb/ddb.h23
-rw-r--r--sys/kern/kern_shutdown.c10
6 files changed, 102 insertions, 19 deletions
diff --git a/sys/conf/files b/sys/conf/files
index b37dc0fe8897..4dbc4539c90c 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -364,6 +364,7 @@ ddb/db_run.c optional ddb
ddb/db_script.c optional ddb
ddb/db_sym.c optional ddb
ddb/db_thread.c optional ddb
+ddb/db_textdump.c optional ddb
ddb/db_variables.c optional ddb
ddb/db_watch.c optional ddb
ddb/db_write_cmd.c optional ddb
diff --git a/sys/ddb/db_capture.c b/sys/ddb/db_capture.c
index 4e6dd7ccdb30..bbc7925fb4c0 100644
--- a/sys/ddb/db_capture.c
+++ b/sys/ddb/db_capture.c
@@ -57,11 +57,13 @@ static MALLOC_DEFINE(M_DB_CAPTURE, "db_capture", "DDB capture buffer");
#define DB_CAPTURE_DEFAULTBUFSIZE 48*1024
#define DB_CAPTURE_MAXBUFSIZE 512*1024
+#define DB_CAPTURE_FILENAME "ddb.txt" /* Captured DDB output. */
static char *db_capture_buf;
static u_int db_capture_bufsize = DB_CAPTURE_DEFAULTBUFSIZE;
static u_int db_capture_maxbufsize = DB_CAPTURE_MAXBUFSIZE; /* Read-only. */
static u_int db_capture_bufoff; /* Next location to write in buffer. */
+static u_int db_capture_bufpadding; /* Amount of zero padding. */
static int db_capture_inpager; /* Suspend capture in pager. */
static int db_capture_inprogress; /* DDB capture currently in progress. */
@@ -79,6 +81,14 @@ SYSCTL_UINT(_debug_ddb_capture, OID_AUTO, maxbufsize, CTLFLAG_RD,
"Maximum value for debug.ddb.capture.bufsize");
/*
+ * Various compile-time assertions: defaults must be even multiples of
+ * textdump block size. We also perform run-time checking of
+ * user-configurable values.
+ */
+CTASSERT(DB_CAPTURE_DEFAULTBUFSIZE % TEXTDUMP_BLOCKSIZE == 0);
+CTASSERT(DB_CAPTURE_MAXBUFSIZE % TEXTDUMP_BLOCKSIZE == 0);
+
+/*
* Boot-time allocation of the DDB capture buffer, if any.
*/
static void
@@ -86,9 +96,9 @@ db_capture_sysinit(__unused void *dummy)
{
TUNABLE_INT_FETCH("debug.ddb.capture.bufsize", &db_capture_bufsize);
+ db_capture_bufsize = roundup(db_capture_bufsize, TEXTDUMP_BLOCKSIZE);
if (db_capture_bufsize > DB_CAPTURE_MAXBUFSIZE)
db_capture_bufsize = DB_CAPTURE_MAXBUFSIZE;
-
if (db_capture_bufsize != 0)
db_capture_buf = malloc(db_capture_bufsize, M_DB_CAPTURE,
M_WAITOK);
@@ -110,9 +120,9 @@ sysctl_debug_ddb_capture_bufsize(SYSCTL_HANDLER_ARGS)
error = sysctl_handle_int(oidp, &size, 0, req);
if (error || req->newptr == NULL)
return (error);
+ size = roundup(size, TEXTDUMP_BLOCKSIZE);
if (size > DB_CAPTURE_MAXBUFSIZE)
return (EINVAL);
-
sx_xlock(&db_capture_sx);
if (size != 0) {
/*
@@ -216,6 +226,23 @@ db_capture_exitpager(void)
}
/*
+ * Zero out any bytes left in the last block of the DDB capture buffer. This
+ * is run shortly before writing the blocks to disk, rather than when output
+ * capture is stopped, in order to avoid injecting nul's into the middle of
+ * output.
+ */
+static void
+db_capture_zeropad(void)
+{
+ u_int len;
+
+ len = min(TEXTDUMP_BLOCKSIZE, (db_capture_bufsize -
+ db_capture_bufoff) % TEXTDUMP_BLOCKSIZE);
+ bzero(db_capture_buf + db_capture_bufoff, len);
+ db_capture_bufpadding = len;
+}
+
+/*
* Reset capture state, which flushes buffers.
*/
static void
@@ -224,6 +251,7 @@ db_capture_reset(void)
db_capture_inprogress = 0;
db_capture_bufoff = 0;
+ db_capture_bufpadding = 0;
}
/*
@@ -242,7 +270,9 @@ db_capture_start(void)
}
/*
- * Terminate DDB output capture.
+ * Terminate DDB output capture--real work is deferred to db_capture_dump,
+ * which executes outside of the DDB context. We don't zero pad here because
+ * capture may be started again before the dump takes place.
*/
static void
db_capture_stop(void)
@@ -255,6 +285,28 @@ db_capture_stop(void)
db_capture_inprogress = 0;
}
+/*
+ * Dump DDB(4) captured output (and resets capture buffers).
+ */
+void
+db_capture_dump(struct dumperinfo *di)
+{
+ u_int offset;
+
+ if (db_capture_bufoff == 0)
+ return;
+
+ db_capture_zeropad();
+ textdump_mkustar(textdump_block_buffer, DB_CAPTURE_FILENAME,
+ db_capture_bufoff);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+ for (offset = 0; offset < db_capture_bufoff + db_capture_bufpadding;
+ offset += TEXTDUMP_BLOCKSIZE)
+ (void)textdump_writenextblock(di, db_capture_buf + offset);
+ db_capture_bufoff = 0;
+ db_capture_bufpadding = 0;
+}
+
/*-
* DDB(4) command to manage capture:
*
diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c
index b4f8ecc9531c..db6dc24774c5 100644
--- a/sys/ddb/db_command.c
+++ b/sys/ddb/db_command.c
@@ -146,6 +146,7 @@ static struct command db_commands[] = {
{ "scripts", db_scripts_cmd, 0, 0 },
{ "unscript", db_unscript_cmd, CS_OWN, 0 },
{ "capture", db_capture_cmd, CS_OWN, 0 },
+ { "textdump", db_textdump_cmd, CS_OWN, 0 },
{ (char *)0, }
};
diff --git a/sys/ddb/db_textdump.c b/sys/ddb/db_textdump.c
index fbfc18f33cb7..237040c60e87 100644
--- a/sys/ddb/db_textdump.c
+++ b/sys/ddb/db_textdump.c
@@ -25,12 +25,12 @@
*/
/*-
- * Kernel text-dump support: write a series of text files to the dump
- * partition for later recovery, including captured DDB output, kernel
- * configuration, message buffer, and panic message. This allows for a more
- * compact representation of critical debugging information than traditional
- * binary dumps, as well as allowing dump information to be used without
- * access to kernel symbols, source code, etc.
+ * Kernel text-dump support: allow a series of text files to be written to
+ * the dump partition for later recovery, including captured DDB output, the
+ * kernel configuration, message buffer, panic message, etc. This allows for
+ * a more compact representation of critical debugging information than
+ * traditional binary dumps, as well as allowing dump information to be used
+ * without access to kernel symbols, source code, etc.
*
* Storage Layout
* --------------
@@ -46,8 +46,9 @@
* know to reverse the order of the blocks in order to produce a readable
* file.
*
- * Data is written out in the ustar file format so that we can write data
- * incrementally as a stream without reference to previous files.
+ * Data is written out in the 'tar' file format, as it provides the facility
+ * to write data incrementally as a stream without reference to previous
+ * files.
*
* TODO
* ----
@@ -200,7 +201,7 @@ mkdumpheader(struct kerneldumpheader *kdh, uint32_t archver,
}
/*
- * Calculate and fill in the checksum for a ustar header.
+ * Calculate and fill in the checksum for a tar header.
*/
static void
ustar_checksum(struct ustar_header *uhp)
@@ -259,8 +260,8 @@ textdump_writeblock(struct dumperinfo *di, off_t offset, char *buffer)
return (EIO);
if (offset < SIZEOF_METADATA)
return (ENOSPC);
- textdump_error = dump_write(di, buffer, 0, offset + di->mediaoffset,
- TEXTDUMP_BLOCKSIZE);
+ textdump_error = di->dumper(di->priv, buffer, 0, offset +
+ di->mediaoffset, TEXTDUMP_BLOCKSIZE);
return (textdump_error);
}
@@ -268,9 +269,6 @@ textdump_writeblock(struct dumperinfo *di, off_t offset, char *buffer)
* Interfaces to save and restore the dump offset, so that printers can go
* back to rewrite a header if required, while avoiding their knowing about
* the global layout of the blocks.
- *
- * If we ever want to support writing textdumps to tape or other
- * stream-oriented target, we'll need to remove this.
*/
void
textdump_saveoff(off_t *offsetp)
@@ -502,7 +500,7 @@ textdump_dumpsys(struct dumperinfo *di)
* Terminate the dump, report any errors, and clear the pending flag.
*/
if (textdump_error == 0)
- (void)dump_write(di, NULL, 0, 0, 0);
+ (void)di->dumper(di->priv, NULL, 0, 0, 0);
if (textdump_error == ENOSPC)
printf("Insufficient space on dump partition\n");
else if (textdump_error != 0)
diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h
index ebec60af58a7..d5599785090a 100644
--- a/sys/ddb/ddb.h
+++ b/sys/ddb/ddb.h
@@ -169,6 +169,7 @@ db_cmdfcn_t db_set_thread;
db_cmdfcn_t db_show_regs;
db_cmdfcn_t db_show_threads;
db_cmdfcn_t db_single_step_cmd;
+db_cmdfcn_t db_textdump_cmd;
db_cmdfcn_t db_trace_until_call_cmd;
db_cmdfcn_t db_trace_until_matching_cmd;
db_cmdfcn_t db_unscript_cmd;
@@ -212,4 +213,26 @@ void db_capture_writech(char ch);
*/
void db_script_kdbenter(const char *eventname); /* KDB enter event. */
+/*
+ * Interface between DDB and the textdump facility.
+ *
+ * Text dump blocks are of a fixed size; textdump_block_buffer is a
+ * statically allocated buffer that code interacting with textdumps can use
+ * to prepare and hold a pending block in when calling writenextblock().
+ */
+#define TEXTDUMP_BLOCKSIZE 512
+extern char textdump_block_buffer[TEXTDUMP_BLOCKSIZE];
+
+void textdump_mkustar(char *block_buffer, const char *filename,
+ u_int size);
+void textdump_restoreoff(off_t offset);
+void textdump_saveoff(off_t *offsetp);
+int textdump_writenextblock(struct dumperinfo *di, char *buffer);
+
+/*
+ * Interface between the kernel and textdumps.
+ */
+extern int textdump_pending; /* Call textdump_dumpsys() instead. */
+void textdump_dumpsys(struct dumperinfo *di);
+
#endif /* !_DDB_DDB_H_ */
diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c
index 45f3c7696bba..1262f1b4d39e 100644
--- a/sys/kern/kern_shutdown.c
+++ b/sys/kern/kern_shutdown.c
@@ -37,6 +37,7 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_ddb.h"
#include "opt_kdb.h"
#include "opt_mac.h"
#include "opt_panic.h"
@@ -64,6 +65,8 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/sysproto.h>
+#include <ddb/ddb.h>
+
#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/smp.h>
@@ -240,7 +243,12 @@ doadump(void)
savectx(&dumppcb);
dumptid = curthread->td_tid;
dumping++;
- dumpsys(&dumper);
+#ifdef DDB
+ if (textdump_pending)
+ textdump_dumpsys(&dumper);
+ else
+#endif
+ dumpsys(&dumper);
}
static int