summaryrefslogtreecommitdiff
path: root/src/btree/bt_dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/btree/bt_dump.c')
-rw-r--r--src/btree/bt_dump.c472
1 files changed, 472 insertions, 0 deletions
diff --git a/src/btree/bt_dump.c b/src/btree/bt_dump.c
new file mode 100644
index 00000000000..4d46fceff27
--- /dev/null
+++ b/src/btree/bt_dump.c
@@ -0,0 +1,472 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2011 WiredTiger, Inc.
+ * All rights reserved.
+ *
+ * $Id$
+ */
+
+#include "wt_internal.h"
+
+typedef struct {
+ void (*p) /* Print function */
+ (uint8_t *, uint32_t, FILE *);
+ FILE *stream; /* Dump stream */
+
+ void (*f)(const char *, uint64_t); /* Progress callback */
+ uint64_t fcnt; /* Progress counter */
+
+ DBT *dupkey; /* Offpage duplicate tree key */
+} WT_DSTUFF;
+
+static int __wt_dump_page(WT_TOC *, WT_PAGE *, void *);
+static void __wt_dump_page_col_fix(WT_TOC *, WT_PAGE *, WT_DSTUFF *);
+static int __wt_dump_page_col_rle(WT_TOC *, WT_PAGE *, WT_DSTUFF *);
+static int __wt_dump_page_col_var(WT_TOC *, WT_PAGE *, WT_DSTUFF *);
+static int __wt_dump_page_dup_leaf(WT_TOC *, WT_PAGE *, WT_DSTUFF *);
+static int __wt_dump_page_row_leaf(WT_TOC *, WT_PAGE *, WT_DSTUFF *);
+static void __wt_print_byte_string_hex(uint8_t *, uint32_t, FILE *);
+static void __wt_print_byte_string_nl(uint8_t *, uint32_t, FILE *);
+
+/*
+ * __wt_db_dump --
+ * Db.dump method.
+ */
+int
+__wt_db_dump(WT_TOC *toc,
+ FILE *stream, void (*f)(const char *, uint64_t), uint32_t flags)
+{
+ WT_DSTUFF dstuff;
+ int ret;
+
+ if (LF_ISSET(WT_DEBUG)) {
+ /*
+ * We use the verification code to do debugging dumps because
+ * if we're dumping in debugging mode, we want to confirm the
+ * page is OK before blindly reading it.
+ */
+ return (__wt_verify(toc, f, stream));
+ }
+
+ dstuff.p = flags == WT_PRINTABLES ?
+ __wt_print_byte_string_nl : __wt_print_byte_string_hex;
+ dstuff.stream = stream;
+ dstuff.f = f;
+ dstuff.fcnt = 0;
+ dstuff.dupkey = NULL;
+
+ /*
+ * Note we do not have a hazard reference for the root page, and that's
+ * safe -- root pages are pinned into memory when a database is opened,
+ * and never re-written until the database is closed.
+ */
+ fprintf(stream, "VERSION=1\n");
+ fprintf(stream, "HEADER=END\n");
+ ret = __wt_tree_walk(toc, NULL, 0, __wt_dump_page, &dstuff);
+ fprintf(stream, "DATA=END\n");
+
+ /* Wrap up reporting. */
+ if (f != NULL)
+ f(toc->name, dstuff.fcnt);
+
+ return (ret);
+}
+
+/*
+ * __wt_dump_page --
+ * Depth-first recursive walk of a btree.
+ */
+static int
+__wt_dump_page(WT_TOC *toc, WT_PAGE *page, void *arg)
+{
+ DB *db;
+ WT_DSTUFF *dp;
+
+ db = toc->db;
+ dp = arg;
+
+ switch (page->dsk->type) {
+ case WT_PAGE_COL_INT:
+ case WT_PAGE_DUP_INT:
+ case WT_PAGE_ROW_INT:
+ break;
+ case WT_PAGE_COL_FIX:
+ __wt_dump_page_col_fix(toc, page, dp);
+ break;
+ case WT_PAGE_COL_RLE:
+ WT_RET(__wt_dump_page_col_rle(toc, page, dp));
+ break;
+ case WT_PAGE_COL_VAR:
+ WT_RET(__wt_dump_page_col_var(toc, page, dp));
+ break;
+ case WT_PAGE_DUP_LEAF:
+ WT_RET(__wt_dump_page_dup_leaf(toc, page, dp));
+ break;
+ case WT_PAGE_ROW_LEAF:
+ WT_RET(__wt_dump_page_row_leaf(toc, page, dp));
+ break;
+ WT_ILLEGAL_FORMAT(db);
+ }
+
+ /* Report progress every 10 pages. */
+ if (dp->f != NULL && ++dp->fcnt % 10 == 0)
+ dp->f(toc->name, dp->fcnt);
+
+ return (0);
+}
+
+/*
+ * __wt_dump_page_col_fix --
+ * Dump a WT_PAGE_COL_FIX page.
+ */
+static void
+__wt_dump_page_col_fix(WT_TOC *toc, WT_PAGE *page, WT_DSTUFF *dp)
+{
+ DB *db;
+ WT_COL *cip;
+ WT_REPL *repl;
+ uint32_t i;
+
+ db = toc->db;
+
+ /* Walk the page, dumping data items. */
+ WT_INDX_FOREACH(page, cip, i) {
+ if ((repl = WT_COL_REPL(page, cip)) == NULL) {
+ if (!WT_FIX_DELETE_ISSET(cip->data))
+ dp->p(cip->data, db->fixed_len, dp->stream);
+ } else
+ if (!WT_REPL_DELETED_ISSET(repl))
+ dp->p(WT_REPL_DATA(repl),
+ db->fixed_len, dp->stream);
+ }
+}
+
+/*
+ * __wt_dump_page_col_rle --
+ * Dump a WT_PAGE_COL_RLE page.
+ */
+static int
+__wt_dump_page_col_rle(WT_TOC *toc, WT_PAGE *page, WT_DSTUFF *dp)
+{
+ DB *db;
+ ENV *env;
+ WT_COL *cip;
+ WT_RLE_EXPAND *exp, **expsort, **expp;
+ WT_REPL *repl;
+ uint64_t recno;
+ uint32_t i, n_expsort;
+ uint16_t n_repeat;
+
+ db = toc->db;
+ env = toc->env;
+ expsort = NULL;
+ n_expsort = 0;
+
+ recno = page->dsk->start_recno;
+ WT_INDX_FOREACH(page, cip, i) {
+ /*
+ * Get a sorted list of any expansion entries we've created for
+ * this set of records. The sort function returns a NULL-
+ * terminated array of references to WT_RLE_EXPAND structures,
+ * sorted by record number.
+ */
+ WT_RET(__wt_rle_expand_sort(
+ env, page, cip, &expsort, &n_expsort));
+
+ /*
+ * Dump the records. We use the WT_REPL entry for records in
+ * in the WT_RLE_EXPAND array, and original data otherwise.
+ */
+ for (expp = expsort,
+ n_repeat = WT_RLE_REPEAT_COUNT(cip->data);
+ n_repeat > 0; --n_repeat, ++recno)
+ if ((exp = *expp) != NULL && exp->recno == recno) {
+ ++expp;
+ repl = exp->repl;
+ if (WT_REPL_DELETED_ISSET(repl))
+ continue;
+ dp->p(
+ WT_REPL_DATA(repl), repl->size, dp->stream);
+ } else
+ dp->p(WT_RLE_REPEAT_DATA(cip->data),
+ db->fixed_len, dp->stream);
+ }
+ /* Free the sort array. */
+ if (expsort != NULL)
+ __wt_free(env, expsort, n_expsort * sizeof(WT_RLE_EXPAND *));
+
+ return (0);
+}
+
+/*
+ * __wt_dump_page_col_var --
+ * Dump a WT_PAGE_COL_VAR page.
+ */
+static int
+__wt_dump_page_col_var(WT_TOC *toc, WT_PAGE *page, WT_DSTUFF *dp)
+{
+ DB *db;
+ DBT *tmp;
+ WT_COL *cip;
+ WT_ITEM *item;
+ WT_REPL *repl;
+ uint32_t i;
+ int ret;
+ void *huffman;
+
+ db = toc->db;
+ huffman = db->idb->huffman_data;
+ ret = 0;
+
+ WT_RET(__wt_scr_alloc(toc, 0, &tmp));
+ WT_INDX_FOREACH(page, cip, i) {
+ /* Check for replace or deletion. */
+ if ((repl = WT_COL_REPL(page, cip)) != NULL) {
+ if (!WT_REPL_DELETED_ISSET(repl))
+ dp->p(
+ WT_REPL_DATA(repl), repl->size, dp->stream);
+ continue;
+ }
+
+ /* Process the original data. */
+ item = cip->data;
+ switch (WT_ITEM_TYPE(item)) {
+ case WT_ITEM_DATA:
+ if (huffman == NULL) {
+ dp->p(WT_ITEM_BYTE(item),
+ WT_ITEM_LEN(item), dp->stream);
+ break;
+ }
+ /* FALLTHROUGH */
+ case WT_ITEM_DATA_OVFL:
+ WT_ERR(__wt_item_process(toc, item, tmp));
+ dp->p(tmp->data, tmp->size, dp->stream);
+ break;
+ case WT_ITEM_DEL:
+ break;
+ WT_ILLEGAL_FORMAT_ERR(db, ret);
+ }
+ }
+
+err: __wt_scr_release(&tmp);
+ return (ret);
+}
+
+/*
+ * __wt_dump_page_dup_leaf --
+ * Dump a WT_PAGE_DUP_LEAF page.
+ */
+static int
+__wt_dump_page_dup_leaf(WT_TOC *toc, WT_PAGE *page, WT_DSTUFF *dp)
+{
+ DB *db;
+ DBT *dupkey, *tmp;
+ WT_ITEM *item;
+ WT_REPL *repl;
+ WT_ROW *rip;
+ uint32_t i;
+ int ret;
+ void *huffman;
+
+ db = toc->db;
+ dupkey = dp->dupkey;
+ huffman = db->idb->huffman_data;
+ ret = 0;
+
+ WT_ERR(__wt_scr_alloc(toc, 0, &tmp));
+ WT_INDX_FOREACH(page, rip, i) {
+ /* Check for deletion. */
+ if ((repl = WT_ROW_REPL(
+ page, rip)) != NULL && WT_REPL_DELETED_ISSET(repl))
+ continue;
+
+ /* Output the key, we're going to need it. */
+ dp->p(dupkey->data, dupkey->size, dp->stream);
+
+ /* Output the replacement item. */
+ if (repl != NULL) {
+ dp->p(WT_REPL_DATA(repl), repl->size, dp->stream);
+ continue;
+ }
+
+ /* Process the original data. */
+ item = rip->data;
+ switch (WT_ITEM_TYPE(item)) {
+ case WT_ITEM_DATA_DUP:
+ if (huffman == NULL) {
+ dp->p(WT_ITEM_BYTE(item),
+ WT_ITEM_LEN(item), dp->stream);
+ break;
+ }
+ /* FALLTHROUGH */
+ case WT_ITEM_DATA_DUP_OVFL:
+ WT_ERR(__wt_item_process(toc, item, tmp));
+ dp->p(tmp->data, tmp->size, dp->stream);
+ break;
+ WT_ILLEGAL_FORMAT_ERR(db, ret);
+ }
+ }
+
+err: __wt_scr_release(&tmp);
+ return (ret);
+}
+
+/*
+ * __wt_dump_page_row_leaf --
+ * Dump a WT_PAGE_ROW_LEAF page.
+ */
+static int
+__wt_dump_page_row_leaf(WT_TOC *toc, WT_PAGE *page, WT_DSTUFF *dp)
+{
+ DB *db;
+ DBT *key, *data, *key_tmp, *data_tmp, key_local, data_local;
+ WT_ITEM *item;
+ WT_OFF *off;
+ WT_REF *ref;
+ WT_REPL *repl;
+ WT_ROW *rip;
+ uint32_t i;
+ int ret;
+ void *huffman;
+
+ db = toc->db;
+ key = data = key_tmp = data_tmp = NULL;
+ huffman = db->idb->huffman_data;
+ ret = 0;
+
+ WT_ERR(__wt_scr_alloc(toc, 0, &key_tmp));
+ WT_ERR(__wt_scr_alloc(toc, 0, &data_tmp));
+ WT_CLEAR(key_local);
+ WT_CLEAR(data_local);
+
+ WT_INDX_FOREACH(page, rip, i) {
+ /* Check for deletion. */
+ if ((repl = WT_ROW_REPL(
+ page, rip)) != NULL && WT_REPL_DELETED_ISSET(repl))
+ continue;
+
+ /*
+ * The key and data variables reference the DBT's we'll print.
+ * Set the key.
+ */
+ if (__wt_key_process(rip)) {
+ WT_ERR(__wt_item_process(toc, rip->key, key_tmp));
+ key = key_tmp;
+ } else
+ key = (DBT *)rip;
+
+ /*
+ * If the item was ever replaced, we're done: it can't be an
+ * off-page tree, and we don't care what kind of item it was
+ * originally. Dump the data from the replacement entry.
+ *
+ * XXX
+ * This is wrong -- if an off-page dup tree is reconciled,
+ * the off-page reference will change underfoot.
+ */
+ if (repl != NULL) {
+ dp->p(key->data, key->size, dp->stream);
+ dp->p(WT_REPL_DATA(repl), repl->size, dp->stream);
+ continue;
+ }
+
+ /* Set data to reference the data we'll dump. */
+ item = rip->data;
+ switch (WT_ITEM_TYPE(item)) {
+ case WT_ITEM_DATA:
+ case WT_ITEM_DATA_DUP:
+ if (huffman == NULL) {
+ data_local.data = WT_ITEM_BYTE(item);
+ data_local.size = WT_ITEM_LEN(item);
+ data = &data_local;
+ break;
+ }
+ /* FALLTHROUGH */
+ case WT_ITEM_DATA_DUP_OVFL:
+ case WT_ITEM_DATA_OVFL:
+ WT_ERR(__wt_item_process(toc, item, data_tmp));
+ data = data_tmp;
+ break;
+ case WT_ITEM_OFF:
+ /*
+ * Set the key and recursively call the tree-walk code
+ * for any off-page duplicate trees. (Check for any
+ * off-page duplicate trees locally because we already
+ * have to walk the page, so it's faster than walking
+ * the page both here and in the tree-walk function.)
+ */
+ dp->dupkey = key;
+
+ ref = WT_ROW_DUP(page, rip);
+ off = WT_ROW_OFF(rip);
+ WT_RET(__wt_page_in(toc, page, ref, off, 0));
+ ret = __wt_tree_walk(toc, ref, 0, __wt_dump_page, dp);
+ __wt_hazard_clear(toc, ref->page);
+ if (ret != 0)
+ goto err;
+ continue;
+ WT_ILLEGAL_FORMAT_ERR(db, ret);
+ }
+
+ dp->p(key->data, key->size, dp->stream);
+ dp->p(data->data, data->size, dp->stream);
+ }
+
+err: /* Discard any space allocated to hold off-page key/data items. */
+ if (key_tmp != NULL)
+ __wt_scr_release(&key_tmp);
+ if (data_tmp != NULL)
+ __wt_scr_release(&data_tmp);
+
+ return (ret);
+}
+
+static const char hex[] = "0123456789abcdef";
+
+/*
+ * __wt_print_byte_string_nl --
+ * Output a single byte stringin printable characters, where possible.
+ * In addition, terminate with a <newline> character, unless the entry
+ * is itself terminated with a <newline> character.
+ */
+static void
+__wt_print_byte_string_nl(uint8_t *data, uint32_t size, FILE *stream)
+{
+ if (data[size - 1] == '\n')
+ --size;
+ __wt_print_byte_string(data, size, stream);
+ fprintf(stream, "\n");
+}
+
+/*
+ * __wt_print_byte_string --
+ * Output a single byte string in printable characters, where possible.
+ */
+void
+__wt_print_byte_string(uint8_t *data, uint32_t size, FILE *stream)
+{
+ int ch;
+
+ for (; size > 0; --size, ++data) {
+ ch = data[0];
+ if (isprint(ch))
+ fprintf(stream, "%c", ch);
+ else
+ fprintf(stream, "%x%x",
+ hex[(data[0] & 0xf0) >> 4], hex[data[0] & 0x0f]);
+ }
+}
+
+/*
+ * __wt_print_byte_string_hex --
+ * Output a single byte string in hexadecimal characters.
+ */
+static void
+__wt_print_byte_string_hex(uint8_t *data, uint32_t size, FILE *stream)
+{
+ for (; size > 0; --size, ++data)
+ fprintf(stream, "%x%x",
+ hex[(data[0] & 0xf0) >> 4], hex[data[0] & 0x0f]);
+ fprintf(stream, "\n");
+}