All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/1] xfsprogs/db: add a command for dumping the btree blocks
@ 2017-04-06  1:44 Shan Hai
  2017-04-06  1:44 ` [RFC PATCH 1/1] " Shan Hai
  0 siblings, 1 reply; 6+ messages in thread
From: Shan Hai @ 2017-04-06  1:44 UTC (permalink / raw)
  To: linux-xfs


This patch adds a command named "treedump" to the xfs_db to dump the
blocks of the btree in batch, below is an example usage:

sudo xfs_db -r -c "agf 0" -c "treedump -b" /dev/sda1

The patch calls the "print" command from code to dump the contents
of the blocks instead of reinventing the wheel for printing.

db/Makefile   |   4 +-
 db/command.c  |   1 +
 db/treedump.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 db/treedump.h |  20 ++++
 4 files changed, 329 insertions(+), 2 deletions(-)
 create mode 100644 db/treedump.c
 create mode 100644 db/treedump.h


Thanks
Shan Hai

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH 1/1] xfsprogs/db: add a command for dumping the btree blocks
  2017-04-06  1:44 [RFC PATCH 0/1] xfsprogs/db: add a command for dumping the btree blocks Shan Hai
@ 2017-04-06  1:44 ` Shan Hai
  2017-04-06  6:59   ` Darrick J. Wong
  0 siblings, 1 reply; 6+ messages in thread
From: Shan Hai @ 2017-04-06  1:44 UTC (permalink / raw)
  To: linux-xfs

Currently there is no way to dump the whole blocks of btrees in the
xfs_db except manually step through the btree nodes, dumping the blocks
of the whole btree by a command is more convenient than interactive
walking of the tree in some circumstances.

This patch adds a new command to the xfs_db utility called 'treedump',
which can dump the specific btree or dump all btrees of the xfs, below
is an example usage of the command:

sudo xfs_db -r -c "agf 0" -c "treedump -b" /dev/sda1

The blocks of the bnobt tree are dumped to the stdout.

Signed-off-by: Shan Hai <shan.hai@oracle.com>
---
 db/Makefile   |   4 +-
 db/command.c  |   1 +
 db/treedump.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 db/treedump.h |  20 ++++
 4 files changed, 329 insertions(+), 2 deletions(-)
 create mode 100644 db/treedump.c
 create mode 100644 db/treedump.h

diff --git a/db/Makefile b/db/Makefile
index cdc0b99..03a2283 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -8,8 +8,8 @@ include $(TOPDIR)/include/builddefs
 LTCOMMAND = xfs_db
 
 HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
-	btblock.h bmroot.h check.h command.h convert.h crc.h debug.h \
-	dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
+	btblock.h bmroot.h treedump.h check.h command.h convert.h crc.h \
+	debug.h dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
 	flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
 	io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
 	 sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h
diff --git a/db/command.c b/db/command.c
index 3d7cfd7..060ebed 100644
--- a/db/command.c
+++ b/db/command.c
@@ -143,6 +143,7 @@ init_commands(void)
 	print_init();
 	quit_init();
 	sb_init();
+	treedump_init();
 	type_init();
 	write_init();
 	dquot_init();
diff --git a/db/treedump.c b/db/treedump.c
new file mode 100644
index 0000000..8d5a33e
--- /dev/null
+++ b/db/treedump.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2017 Oracle.
+ * All Rights Reserved.
+ *
+ * Author: Shan Hai <shan.hai@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include <math.h>
+#include <sys/time.h>
+#include "command.h"
+#include "io.h"
+#include "type.h"
+#include "fprint.h"
+#include "faddr.h"
+#include "field.h"
+#include "sb.h"
+#include "input.h"
+#include "output.h"
+#include "init.h"
+#include "malloc.h"
+
+#define TYP_MASK ( \
+	TYP_AGF | TYP_AGFL |  TYP_AGI | TYP_ATTR \
+	| TYP_BMAPBTA | TYP_BMAPBTD | TYP_BNOBT | TYP_CNTBT \
+	| TYP_RMAPBT | TYP_REFCBT | TYP_DATA | TYP_DIR2 \
+	| TYP_DQBLK | TYP_INOBT | TYP_INODATA | TYP_INODE \
+	| TYP_LOG | TYP_RTBITMAP | TYP_RTSUMMARY | TYP_SB \
+	| TYP_SYMLINK | TYP_TEXT | TYP_FINOBT | TYP_NONE)
+
+static struct btree_types {
+	xfs_btnum_t	btnum;
+	uint32_t	magic;
+	uint32_t	crc_magic;
+} const type_to_btree_table[TYP_NONE] = {
+	[0 ... TYP_NONE - 1]	= { XFS_BTNUM_MAX,  -1, -1 },
+
+	[TYP_BNOBT]		= { XFS_BTNUM_BNO,
+				    XFS_ABTB_MAGIC, XFS_ABTB_CRC_MAGIC },
+	[TYP_CNTBT]		= { XFS_BTNUM_CNT,
+				    XFS_ABTC_MAGIC, XFS_ABTC_CRC_MAGIC },
+	[TYP_RMAPBT]		= { XFS_BTNUM_RMAP,
+				    XFS_RMAP_CRC_MAGIC, XFS_RMAP_CRC_MAGIC },
+	[TYP_REFCBT]		= { XFS_BTNUM_REFC,
+				    XFS_REFC_CRC_MAGIC, XFS_REFC_CRC_MAGIC },
+};
+
+static typnm_t	btype_map[TYP_NONE];
+
+#define TYP_MAP_SIZE (sizeof (btype_map) / sizeof (typnm_t))
+
+typedef void	(*scan_sbtree_f_t)(struct xfs_btree_block *block,
+				   int level, xfs_agf_t *agf,
+				   xfs_agblock_t bno, int isroot,
+				   typnm_t btype);
+static int	treedump_f(int argc, char **argv);
+static void	scan_ag(xfs_agnumber_t agno);
+static void	scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
+			    int nlevels, int isroot,
+			    scan_sbtree_f_t func, typnm_t btype);
+static void	scanfunc_btree(struct xfs_btree_block *block, int level,
+			       xfs_agf_t *agf, xfs_agblock_t bno,
+			       int isroot, typnm_t btype);
+/*
+ * [-b]: bnobt, [-c]: cntbt, [-f]: refcntbt, [-r]: rmapbt
+ */
+static const cmdinfo_t	treedump_cmd =
+	{ "treedump", NULL, treedump_f, 0, 4, 0,
+	 N_("[-b] [-c] [-f] [-r]"),
+	 N_("dump the metadata of the btrees"), NULL };
+
+static void set_btree_type(
+	typnm_t	type)
+{
+	btype_map[type] = type;
+}
+
+/* ARGSUSED */
+static int
+treedump_f(
+	int	argc,
+	char	**argv)
+{	
+	int c;
+
+	if (cur_agno == NULLAGNUMBER)
+                cur_agno = 0;
+
+	/* Dump the BNOBT if none is specified */
+	set_btree_type(TYP_BNOBT);
+
+	while ((c = getopt(argc, argv, "bcfr")) != EOF) {
+                switch (c) {
+                case 'b':
+                        set_btree_type(TYP_BNOBT);
+                        break;
+                case 'c':
+                        set_btree_type(TYP_CNTBT);
+                        break;
+		case 'f':
+			set_btree_type(TYP_REFCBT);
+			break;
+		case 'r':
+			set_btree_type(TYP_RMAPBT);
+			break;
+		default:
+			set_btree_type(TYP_BNOBT);
+			break;
+		}
+	}
+
+	scan_ag(cur_agno);
+
+	return 0;
+}
+
+void
+treedump_init(void)
+{
+	add_command(&treedump_cmd);
+}
+
+static void
+scan_ag(
+	xfs_agnumber_t	agno)
+{
+	xfs_agf_t	*agf;
+	xfs_sb_t	tsb;
+	xfs_sb_t	*sb = &tsb;
+	typnm_t		btype;
+	xfs_btnum_t	btree;
+	int i, c;
+	char **argv;
+
+	push_cur();	/* 1 pushed */
+	set_cur(&typtab[TYP_SB],
+		XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+		XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+
+	if (!iocur_top->data) {
+		dbprintf(_("can't read superblock for ag %u\n"), agno);
+		goto pop1_out;
+	}
+
+	libxfs_sb_from_disk(sb, iocur_top->data);
+
+	if (sb->sb_magicnum != XFS_SB_MAGIC) {
+		dbprintf(_("bad sb magic # %#x in ag %u\n"),
+			sb->sb_magicnum, agno);
+	}
+
+	if (!xfs_sb_good_version(sb)) {
+		dbprintf(_("bad sb version # %#x in ag %u\n"),
+			sb->sb_versionnum, agno);
+	}
+
+	if (agno == 0 && sb->sb_inprogress != 0) {
+		dbprintf(_("mkfs not completed successfully\n"));
+	}
+
+	push_cur();     /* 2 pushed */
+        set_cur(&typtab[TYP_AGF],
+                XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+                XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+
+	if ((agf = iocur_top->data) == NULL) {
+                dbprintf(_("can't read agf block for ag %u\n"), agno);
+                goto pop2_out;
+        }
+        if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
+		dbprintf(_("bad agf magic # %#x in ag %u\n"),
+		be32_to_cpu(agf->agf_magicnum), agno);
+        }
+        if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
+		dbprintf(_("bad agf version # %#x in ag %u\n"),
+			be32_to_cpu(agf->agf_versionnum), agno);
+        }
+
+	dbprintf(_("\nAGF %d\n\n"), agno);
+	argv = breakline("print", &c);
+	command(c, argv);
+
+	for (i = 0; i < TYP_MAP_SIZE; i++) {
+		btype = btype_map[i];
+		if (btype & TYP_MASK) {
+			btree = type_to_btree_table[btype].btnum;
+			scan_sbtree(agf, be32_to_cpu(agf->agf_roots[btree]),
+					be32_to_cpu(agf->agf_levels[btree]),
+					1, scanfunc_btree, btype);
+		}
+	}
+
+pop2_out:
+	pop_cur();
+pop1_out:
+	pop_cur();
+}
+
+
+static void
+scan_sbtree(
+	xfs_agf_t	*agf,
+	xfs_agblock_t	root,
+	int		nlevels,
+	int		isroot,
+	scan_sbtree_f_t	func,
+	typnm_t		btype)
+{
+	xfs_agnumber_t	seqno = be32_to_cpu(agf->agf_seqno);
+	push_cur();
+
+	set_cur(&typtab[btype],
+		XFS_AGB_TO_DADDR(mp, seqno, root), blkbb, DB_RING_IGN, NULL);
+	if (iocur_top->data == NULL) {
+		dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
+		pop_cur();
+		return;
+	}
+	(*func)(iocur_top->data, nlevels - 1, agf, root, isroot, btype);
+	pop_cur();
+}
+
+static int check_magic(
+	struct xfs_btree_block	*block,
+	xfs_agnumber_t		seqno,
+	xfs_agblock_t		bno,
+	typnm_t			btype)
+{
+	uint32_t magic = type_to_btree_table[btype].magic;
+	uint32_t crc_magic = type_to_btree_table[btype].crc_magic;
+	char *btree_name = typtab[btype].name;
+	
+	if (be32_to_cpu(block->bb_magic) != magic &&
+		be32_to_cpu(block->bb_magic) != crc_magic) {
+		dbprintf(_("bad magic # %#x in %s block %u/%u\n"),
+			be32_to_cpu(block->bb_magic), btree_name, seqno, bno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+scanfunc_btree(
+	struct xfs_btree_block	*block,
+	int			level,
+	xfs_agf_t		*agf,
+	xfs_agblock_t		bno,
+	int			isroot,
+	typnm_t			btype)
+{
+	int			i, c;
+	char			**argv;
+	xfs_alloc_ptr_t         *pp;
+	xfs_agnumber_t		seqno = be32_to_cpu(agf->agf_seqno);
+
+	if (check_magic(block, seqno, bno, btype))
+		return;
+
+	dbprintf(_("\ncurrent blkno %d\n\n"), bno);
+	if (level == 0) {
+		if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
+		    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
+			dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
+				 "btbno block %u/%u\n"),
+				be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
+				mp->m_alloc_mxr[0], seqno, bno);
+			return;
+		}
+
+		argv = breakline("print", &c);
+		command(c, argv);
+
+		return;
+	} else {
+		argv = breakline("print", &c);
+		command(c, argv);
+	}
+
+	if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
+	    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
+		dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
+			 "%u/%u\n"),
+			be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
+			mp->m_alloc_mxr[1], seqno, bno);
+		return;
+	}
+
+	pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
+	for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
+		scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_btree, btype);
+}
diff --git a/db/treedump.h b/db/treedump.h
new file mode 100644
index 0000000..c9dbffc
--- /dev/null
+++ b/db/treedump.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Shan Hai <shan.hai@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+extern void	treedump_init(void);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH 1/1] xfsprogs/db: add a command for dumping the btree blocks
  2017-04-06  1:44 ` [RFC PATCH 1/1] " Shan Hai
@ 2017-04-06  6:59   ` Darrick J. Wong
  2017-04-06  7:54     ` Shan Hai
  2017-04-06  9:02     ` Shan Hai
  0 siblings, 2 replies; 6+ messages in thread
From: Darrick J. Wong @ 2017-04-06  6:59 UTC (permalink / raw)
  To: Shan Hai; +Cc: linux-xfs

On Thu, Apr 06, 2017 at 09:44:43AM +0800, Shan Hai wrote:
> Currently there is no way to dump the whole blocks of btrees in the
> xfs_db except manually step through the btree nodes, dumping the blocks
> of the whole btree by a command is more convenient than interactive
> walking of the tree in some circumstances.
> 
> This patch adds a new command to the xfs_db utility called 'treedump',
> which can dump the specific btree or dump all btrees of the xfs, below
> is an example usage of the command:
> 
> sudo xfs_db -r -c "agf 0" -c "treedump -b" /dev/sda1
> 
> The blocks of the bnobt tree are dumped to the stdout.
> 
> Signed-off-by: Shan Hai <shan.hai@oracle.com>
> ---
>  db/Makefile   |   4 +-
>  db/command.c  |   1 +
>  db/treedump.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  db/treedump.h |  20 ++++
>  4 files changed, 329 insertions(+), 2 deletions(-)
>  create mode 100644 db/treedump.c
>  create mode 100644 db/treedump.h
> 
> diff --git a/db/Makefile b/db/Makefile
> index cdc0b99..03a2283 100644
> --- a/db/Makefile
> +++ b/db/Makefile
> @@ -8,8 +8,8 @@ include $(TOPDIR)/include/builddefs
>  LTCOMMAND = xfs_db
>  
>  HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
> -	btblock.h bmroot.h check.h command.h convert.h crc.h debug.h \
> -	dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
> +	btblock.h bmroot.h treedump.h check.h command.h convert.h crc.h \
> +	debug.h dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
>  	flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
>  	io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
>  	 sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h
> diff --git a/db/command.c b/db/command.c
> index 3d7cfd7..060ebed 100644
> --- a/db/command.c
> +++ b/db/command.c
> @@ -143,6 +143,7 @@ init_commands(void)
>  	print_init();
>  	quit_init();
>  	sb_init();
> +	treedump_init();
>  	type_init();
>  	write_init();
>  	dquot_init();
> diff --git a/db/treedump.c b/db/treedump.c
> new file mode 100644
> index 0000000..8d5a33e
> --- /dev/null
> +++ b/db/treedump.c
> @@ -0,0 +1,306 @@
> +/*
> + * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
> + * Copyright (c) 2017 Oracle.
> + * All Rights Reserved.
> + *
> + * Author: Shan Hai <shan.hai@oracle.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#include "libxfs.h"
> +#include <math.h>
> +#include <sys/time.h>
> +#include "command.h"
> +#include "io.h"
> +#include "type.h"
> +#include "fprint.h"
> +#include "faddr.h"
> +#include "field.h"
> +#include "sb.h"
> +#include "input.h"
> +#include "output.h"
> +#include "init.h"
> +#include "malloc.h"
> +
> +#define TYP_MASK ( \
> +	TYP_AGF | TYP_AGFL |  TYP_AGI | TYP_ATTR \
> +	| TYP_BMAPBTA | TYP_BMAPBTD | TYP_BNOBT | TYP_CNTBT \
> +	| TYP_RMAPBT | TYP_REFCBT | TYP_DATA | TYP_DIR2 \
> +	| TYP_DQBLK | TYP_INOBT | TYP_INODATA | TYP_INODE \
> +	| TYP_LOG | TYP_RTBITMAP | TYP_RTSUMMARY | TYP_SB \
> +	| TYP_SYMLINK | TYP_TEXT | TYP_FINOBT | TYP_NONE)
> +
> +static struct btree_types {
> +	xfs_btnum_t	btnum;
> +	uint32_t	magic;
> +	uint32_t	crc_magic;
> +} const type_to_btree_table[TYP_NONE] = {
> +	[0 ... TYP_NONE - 1]	= { XFS_BTNUM_MAX,  -1, -1 },
> +
> +	[TYP_BNOBT]		= { XFS_BTNUM_BNO,
> +				    XFS_ABTB_MAGIC, XFS_ABTB_CRC_MAGIC },
> +	[TYP_CNTBT]		= { XFS_BTNUM_CNT,
> +				    XFS_ABTC_MAGIC, XFS_ABTC_CRC_MAGIC },
> +	[TYP_RMAPBT]		= { XFS_BTNUM_RMAP,
> +				    XFS_RMAP_CRC_MAGIC, XFS_RMAP_CRC_MAGIC },
> +	[TYP_REFCBT]		= { XFS_BTNUM_REFC,
> +				    XFS_REFC_CRC_MAGIC, XFS_REFC_CRC_MAGIC },

Uh.... what if I want to dump one of the other btrees?  There's at least
four more types. ;)

TBH I was wondering why not let the user navigate to the root node of
whichever tree they wish to dump, and then the command simply has to
travel down the left side of the tree to level 0 and then dump each leaf
and use rightsib to move on to the next leaf?

i.e.
xfs_db> agi 3
xfs_db> addr free_root
xfs_db> treedump
0: [...]
1: [...]
xfs_db>

--D

> +};
> +
> +static typnm_t	btype_map[TYP_NONE];
> +
> +#define TYP_MAP_SIZE (sizeof (btype_map) / sizeof (typnm_t))
> +
> +typedef void	(*scan_sbtree_f_t)(struct xfs_btree_block *block,
> +				   int level, xfs_agf_t *agf,
> +				   xfs_agblock_t bno, int isroot,
> +				   typnm_t btype);
> +static int	treedump_f(int argc, char **argv);
> +static void	scan_ag(xfs_agnumber_t agno);
> +static void	scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
> +			    int nlevels, int isroot,
> +			    scan_sbtree_f_t func, typnm_t btype);
> +static void	scanfunc_btree(struct xfs_btree_block *block, int level,
> +			       xfs_agf_t *agf, xfs_agblock_t bno,
> +			       int isroot, typnm_t btype);
> +/*
> + * [-b]: bnobt, [-c]: cntbt, [-f]: refcntbt, [-r]: rmapbt
> + */
> +static const cmdinfo_t	treedump_cmd =
> +	{ "treedump", NULL, treedump_f, 0, 4, 0,
> +	 N_("[-b] [-c] [-f] [-r]"),
> +	 N_("dump the metadata of the btrees"), NULL };
> +
> +static void set_btree_type(
> +	typnm_t	type)
> +{
> +	btype_map[type] = type;
> +}
> +
> +/* ARGSUSED */
> +static int
> +treedump_f(
> +	int	argc,
> +	char	**argv)
> +{	
> +	int c;
> +
> +	if (cur_agno == NULLAGNUMBER)
> +                cur_agno = 0;
> +
> +	/* Dump the BNOBT if none is specified */
> +	set_btree_type(TYP_BNOBT);
> +
> +	while ((c = getopt(argc, argv, "bcfr")) != EOF) {
> +                switch (c) {
> +                case 'b':
> +                        set_btree_type(TYP_BNOBT);
> +                        break;
> +                case 'c':
> +                        set_btree_type(TYP_CNTBT);
> +                        break;
> +		case 'f':
> +			set_btree_type(TYP_REFCBT);
> +			break;
> +		case 'r':
> +			set_btree_type(TYP_RMAPBT);
> +			break;
> +		default:
> +			set_btree_type(TYP_BNOBT);
> +			break;
> +		}
> +	}
> +
> +	scan_ag(cur_agno);
> +
> +	return 0;
> +}
> +
> +void
> +treedump_init(void)
> +{
> +	add_command(&treedump_cmd);
> +}
> +
> +static void
> +scan_ag(
> +	xfs_agnumber_t	agno)
> +{
> +	xfs_agf_t	*agf;
> +	xfs_sb_t	tsb;
> +	xfs_sb_t	*sb = &tsb;
> +	typnm_t		btype;
> +	xfs_btnum_t	btree;
> +	int i, c;
> +	char **argv;
> +
> +	push_cur();	/* 1 pushed */
> +	set_cur(&typtab[TYP_SB],
> +		XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
> +		XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
> +
> +	if (!iocur_top->data) {
> +		dbprintf(_("can't read superblock for ag %u\n"), agno);
> +		goto pop1_out;
> +	}
> +
> +	libxfs_sb_from_disk(sb, iocur_top->data);
> +
> +	if (sb->sb_magicnum != XFS_SB_MAGIC) {
> +		dbprintf(_("bad sb magic # %#x in ag %u\n"),
> +			sb->sb_magicnum, agno);
> +	}
> +
> +	if (!xfs_sb_good_version(sb)) {
> +		dbprintf(_("bad sb version # %#x in ag %u\n"),
> +			sb->sb_versionnum, agno);
> +	}
> +
> +	if (agno == 0 && sb->sb_inprogress != 0) {
> +		dbprintf(_("mkfs not completed successfully\n"));
> +	}
> +
> +	push_cur();     /* 2 pushed */
> +        set_cur(&typtab[TYP_AGF],
> +                XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
> +                XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
> +
> +	if ((agf = iocur_top->data) == NULL) {
> +                dbprintf(_("can't read agf block for ag %u\n"), agno);
> +                goto pop2_out;
> +        }
> +        if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
> +		dbprintf(_("bad agf magic # %#x in ag %u\n"),
> +		be32_to_cpu(agf->agf_magicnum), agno);
> +        }
> +        if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
> +		dbprintf(_("bad agf version # %#x in ag %u\n"),
> +			be32_to_cpu(agf->agf_versionnum), agno);
> +        }
> +
> +	dbprintf(_("\nAGF %d\n\n"), agno);
> +	argv = breakline("print", &c);
> +	command(c, argv);
> +
> +	for (i = 0; i < TYP_MAP_SIZE; i++) {
> +		btype = btype_map[i];
> +		if (btype & TYP_MASK) {
> +			btree = type_to_btree_table[btype].btnum;
> +			scan_sbtree(agf, be32_to_cpu(agf->agf_roots[btree]),
> +					be32_to_cpu(agf->agf_levels[btree]),
> +					1, scanfunc_btree, btype);
> +		}
> +	}
> +
> +pop2_out:
> +	pop_cur();
> +pop1_out:
> +	pop_cur();
> +}
> +
> +
> +static void
> +scan_sbtree(
> +	xfs_agf_t	*agf,
> +	xfs_agblock_t	root,
> +	int		nlevels,
> +	int		isroot,
> +	scan_sbtree_f_t	func,
> +	typnm_t		btype)
> +{
> +	xfs_agnumber_t	seqno = be32_to_cpu(agf->agf_seqno);
> +	push_cur();
> +
> +	set_cur(&typtab[btype],
> +		XFS_AGB_TO_DADDR(mp, seqno, root), blkbb, DB_RING_IGN, NULL);
> +	if (iocur_top->data == NULL) {
> +		dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
> +		pop_cur();
> +		return;
> +	}
> +	(*func)(iocur_top->data, nlevels - 1, agf, root, isroot, btype);
> +	pop_cur();
> +}
> +
> +static int check_magic(
> +	struct xfs_btree_block	*block,
> +	xfs_agnumber_t		seqno,
> +	xfs_agblock_t		bno,
> +	typnm_t			btype)
> +{
> +	uint32_t magic = type_to_btree_table[btype].magic;
> +	uint32_t crc_magic = type_to_btree_table[btype].crc_magic;
> +	char *btree_name = typtab[btype].name;
> +	
> +	if (be32_to_cpu(block->bb_magic) != magic &&
> +		be32_to_cpu(block->bb_magic) != crc_magic) {
> +		dbprintf(_("bad magic # %#x in %s block %u/%u\n"),
> +			be32_to_cpu(block->bb_magic), btree_name, seqno, bno);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +scanfunc_btree(
> +	struct xfs_btree_block	*block,
> +	int			level,
> +	xfs_agf_t		*agf,
> +	xfs_agblock_t		bno,
> +	int			isroot,
> +	typnm_t			btype)
> +{
> +	int			i, c;
> +	char			**argv;
> +	xfs_alloc_ptr_t         *pp;
> +	xfs_agnumber_t		seqno = be32_to_cpu(agf->agf_seqno);
> +
> +	if (check_magic(block, seqno, bno, btype))
> +		return;
> +
> +	dbprintf(_("\ncurrent blkno %d\n\n"), bno);
> +	if (level == 0) {
> +		if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
> +		    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
> +			dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
> +				 "btbno block %u/%u\n"),
> +				be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
> +				mp->m_alloc_mxr[0], seqno, bno);
> +			return;
> +		}
> +
> +		argv = breakline("print", &c);
> +		command(c, argv);
> +
> +		return;
> +	} else {
> +		argv = breakline("print", &c);
> +		command(c, argv);
> +	}
> +
> +	if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
> +	    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
> +		dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
> +			 "%u/%u\n"),
> +			be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
> +			mp->m_alloc_mxr[1], seqno, bno);
> +		return;
> +	}
> +
> +	pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
> +	for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
> +		scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_btree, btype);
> +}
> diff --git a/db/treedump.h b/db/treedump.h
> new file mode 100644
> index 0000000..c9dbffc
> --- /dev/null
> +++ b/db/treedump.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright (c) 2017 Oracle.  All Rights Reserved.
> + *
> + * Author: Shan Hai <shan.hai@oracle.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +extern void	treedump_init(void);
> -- 
> 2.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH 1/1] xfsprogs/db: add a command for dumping the btree blocks
  2017-04-06  6:59   ` Darrick J. Wong
@ 2017-04-06  7:54     ` Shan Hai
  2017-04-06  9:02     ` Shan Hai
  1 sibling, 0 replies; 6+ messages in thread
From: Shan Hai @ 2017-04-06  7:54 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2017年04月06日 14:59, Darrick J. Wong wrote:
> On Thu, Apr 06, 2017 at 09:44:43AM +0800, Shan Hai wrote:
>> Currently there is no way to dump the whole blocks of btrees in the
>> xfs_db except manually step through the btree nodes, dumping the blocks
>> of the whole btree by a command is more convenient than interactive
>> walking of the tree in some circumstances.
>>
>> This patch adds a new command to the xfs_db utility called 'treedump',
>> which can dump the specific btree or dump all btrees of the xfs, below
>> is an example usage of the command:
>>
>> sudo xfs_db -r -c "agf 0" -c "treedump -b" /dev/sda1
>>
>> The blocks of the bnobt tree are dumped to the stdout.
>>
>> Signed-off-by: Shan Hai <shan.hai@oracle.com>
>> ---
>>   db/Makefile   |   4 +-
>>   db/command.c  |   1 +
>>   db/treedump.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   db/treedump.h |  20 ++++
>>   4 files changed, 329 insertions(+), 2 deletions(-)
>>   create mode 100644 db/treedump.c
>>   create mode 100644 db/treedump.h
>>
>> diff --git a/db/Makefile b/db/Makefile
>> index cdc0b99..03a2283 100644
>> --- a/db/Makefile
>> +++ b/db/Makefile
>> @@ -8,8 +8,8 @@ include $(TOPDIR)/include/builddefs
>>   LTCOMMAND = xfs_db
>>   
>>   HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
>> -	btblock.h bmroot.h check.h command.h convert.h crc.h debug.h \
>> -	dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
>> +	btblock.h bmroot.h treedump.h check.h command.h convert.h crc.h \
>> +	debug.h dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
>>   	flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
>>   	io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
>>   	 sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h
>> diff --git a/db/command.c b/db/command.c
>> index 3d7cfd7..060ebed 100644
>> --- a/db/command.c
>> +++ b/db/command.c
>> @@ -143,6 +143,7 @@ init_commands(void)
>>   	print_init();
>>   	quit_init();
>>   	sb_init();
>> +	treedump_init();
>>   	type_init();
>>   	write_init();
>>   	dquot_init();
>> diff --git a/db/treedump.c b/db/treedump.c
>> new file mode 100644
>> index 0000000..8d5a33e
>> --- /dev/null
>> +++ b/db/treedump.c
>> @@ -0,0 +1,306 @@
>> +/*
>> + * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
>> + * Copyright (c) 2017 Oracle.
>> + * All Rights Reserved.
>> + *
>> + * Author: Shan Hai <shan.hai@oracle.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it would be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write the Free Software Foundation,
>> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
>> + */
>> +
>> +#include "libxfs.h"
>> +#include <math.h>
>> +#include <sys/time.h>
>> +#include "command.h"
>> +#include "io.h"
>> +#include "type.h"
>> +#include "fprint.h"
>> +#include "faddr.h"
>> +#include "field.h"
>> +#include "sb.h"
>> +#include "input.h"
>> +#include "output.h"
>> +#include "init.h"
>> +#include "malloc.h"
>> +
>> +#define TYP_MASK ( \
>> +	TYP_AGF | TYP_AGFL |  TYP_AGI | TYP_ATTR \
>> +	| TYP_BMAPBTA | TYP_BMAPBTD | TYP_BNOBT | TYP_CNTBT \
>> +	| TYP_RMAPBT | TYP_REFCBT | TYP_DATA | TYP_DIR2 \
>> +	| TYP_DQBLK | TYP_INOBT | TYP_INODATA | TYP_INODE \
>> +	| TYP_LOG | TYP_RTBITMAP | TYP_RTSUMMARY | TYP_SB \
>> +	| TYP_SYMLINK | TYP_TEXT | TYP_FINOBT | TYP_NONE)
>> +
>> +static struct btree_types {
>> +	xfs_btnum_t	btnum;
>> +	uint32_t	magic;
>> +	uint32_t	crc_magic;
>> +} const type_to_btree_table[TYP_NONE] = {
>> +	[0 ... TYP_NONE - 1]	= { XFS_BTNUM_MAX,  -1, -1 },
>> +
>> +	[TYP_BNOBT]		= { XFS_BTNUM_BNO,
>> +				    XFS_ABTB_MAGIC, XFS_ABTB_CRC_MAGIC },
>> +	[TYP_CNTBT]		= { XFS_BTNUM_CNT,
>> +				    XFS_ABTC_MAGIC, XFS_ABTC_CRC_MAGIC },
>> +	[TYP_RMAPBT]		= { XFS_BTNUM_RMAP,
>> +				    XFS_RMAP_CRC_MAGIC, XFS_RMAP_CRC_MAGIC },
>> +	[TYP_REFCBT]		= { XFS_BTNUM_REFC,
>> +				    XFS_REFC_CRC_MAGIC, XFS_REFC_CRC_MAGIC },
> Uh.... what if I want to dump one of the other btrees?  There's at least
> four more types. ;)
>
> TBH I was wondering why not let the user navigate to the root node of
> whichever tree they wish to dump, and then the command simply has to
> travel down the left side of the tree to level 0 and then dump each leaf
> and use rightsib to move on to the next leaf?

Agreed.

If we choose the root of the tree first and then dump the tree the above 
table definition is
not necessary.

Dump the tree by following the rightsib pointer can eliminate the 
recursion in the code.

I am going to do that in the v2 patch, thanks for the suggestions.

Regards
Shan Hai
> i.e.
> xfs_db> agi 3
> xfs_db> addr free_root
> xfs_db> treedump
> 0: [...]
> 1: [...]
> xfs_db>
>
> --D
>
>> +};
>> +
>> +static typnm_t	btype_map[TYP_NONE];
>> +
>> +#define TYP_MAP_SIZE (sizeof (btype_map) / sizeof (typnm_t))
>> +
>> +typedef void	(*scan_sbtree_f_t)(struct xfs_btree_block *block,
>> +				   int level, xfs_agf_t *agf,
>> +				   xfs_agblock_t bno, int isroot,
>> +				   typnm_t btype);
>> +static int	treedump_f(int argc, char **argv);
>> +static void	scan_ag(xfs_agnumber_t agno);
>> +static void	scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
>> +			    int nlevels, int isroot,
>> +			    scan_sbtree_f_t func, typnm_t btype);
>> +static void	scanfunc_btree(struct xfs_btree_block *block, int level,
>> +			       xfs_agf_t *agf, xfs_agblock_t bno,
>> +			       int isroot, typnm_t btype);
>> +/*
>> + * [-b]: bnobt, [-c]: cntbt, [-f]: refcntbt, [-r]: rmapbt
>> + */
>> +static const cmdinfo_t	treedump_cmd =
>> +	{ "treedump", NULL, treedump_f, 0, 4, 0,
>> +	 N_("[-b] [-c] [-f] [-r]"),
>> +	 N_("dump the metadata of the btrees"), NULL };
>> +
>> +static void set_btree_type(
>> +	typnm_t	type)
>> +{
>> +	btype_map[type] = type;
>> +}
>> +
>> +/* ARGSUSED */
>> +static int
>> +treedump_f(
>> +	int	argc,
>> +	char	**argv)
>> +{	
>> +	int c;
>> +
>> +	if (cur_agno == NULLAGNUMBER)
>> +                cur_agno = 0;
>> +
>> +	/* Dump the BNOBT if none is specified */
>> +	set_btree_type(TYP_BNOBT);
>> +
>> +	while ((c = getopt(argc, argv, "bcfr")) != EOF) {
>> +                switch (c) {
>> +                case 'b':
>> +                        set_btree_type(TYP_BNOBT);
>> +                        break;
>> +                case 'c':
>> +                        set_btree_type(TYP_CNTBT);
>> +                        break;
>> +		case 'f':
>> +			set_btree_type(TYP_REFCBT);
>> +			break;
>> +		case 'r':
>> +			set_btree_type(TYP_RMAPBT);
>> +			break;
>> +		default:
>> +			set_btree_type(TYP_BNOBT);
>> +			break;
>> +		}
>> +	}
>> +
>> +	scan_ag(cur_agno);
>> +
>> +	return 0;
>> +}
>> +
>> +void
>> +treedump_init(void)
>> +{
>> +	add_command(&treedump_cmd);
>> +}
>> +
>> +static void
>> +scan_ag(
>> +	xfs_agnumber_t	agno)
>> +{
>> +	xfs_agf_t	*agf;
>> +	xfs_sb_t	tsb;
>> +	xfs_sb_t	*sb = &tsb;
>> +	typnm_t		btype;
>> +	xfs_btnum_t	btree;
>> +	int i, c;
>> +	char **argv;
>> +
>> +	push_cur();	/* 1 pushed */
>> +	set_cur(&typtab[TYP_SB],
>> +		XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
>> +		XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
>> +
>> +	if (!iocur_top->data) {
>> +		dbprintf(_("can't read superblock for ag %u\n"), agno);
>> +		goto pop1_out;
>> +	}
>> +
>> +	libxfs_sb_from_disk(sb, iocur_top->data);
>> +
>> +	if (sb->sb_magicnum != XFS_SB_MAGIC) {
>> +		dbprintf(_("bad sb magic # %#x in ag %u\n"),
>> +			sb->sb_magicnum, agno);
>> +	}
>> +
>> +	if (!xfs_sb_good_version(sb)) {
>> +		dbprintf(_("bad sb version # %#x in ag %u\n"),
>> +			sb->sb_versionnum, agno);
>> +	}
>> +
>> +	if (agno == 0 && sb->sb_inprogress != 0) {
>> +		dbprintf(_("mkfs not completed successfully\n"));
>> +	}
>> +
>> +	push_cur();     /* 2 pushed */
>> +        set_cur(&typtab[TYP_AGF],
>> +                XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
>> +                XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
>> +
>> +	if ((agf = iocur_top->data) == NULL) {
>> +                dbprintf(_("can't read agf block for ag %u\n"), agno);
>> +                goto pop2_out;
>> +        }
>> +        if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
>> +		dbprintf(_("bad agf magic # %#x in ag %u\n"),
>> +		be32_to_cpu(agf->agf_magicnum), agno);
>> +        }
>> +        if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
>> +		dbprintf(_("bad agf version # %#x in ag %u\n"),
>> +			be32_to_cpu(agf->agf_versionnum), agno);
>> +        }
>> +
>> +	dbprintf(_("\nAGF %d\n\n"), agno);
>> +	argv = breakline("print", &c);
>> +	command(c, argv);
>> +
>> +	for (i = 0; i < TYP_MAP_SIZE; i++) {
>> +		btype = btype_map[i];
>> +		if (btype & TYP_MASK) {
>> +			btree = type_to_btree_table[btype].btnum;
>> +			scan_sbtree(agf, be32_to_cpu(agf->agf_roots[btree]),
>> +					be32_to_cpu(agf->agf_levels[btree]),
>> +					1, scanfunc_btree, btype);
>> +		}
>> +	}
>> +
>> +pop2_out:
>> +	pop_cur();
>> +pop1_out:
>> +	pop_cur();
>> +}
>> +
>> +
>> +static void
>> +scan_sbtree(
>> +	xfs_agf_t	*agf,
>> +	xfs_agblock_t	root,
>> +	int		nlevels,
>> +	int		isroot,
>> +	scan_sbtree_f_t	func,
>> +	typnm_t		btype)
>> +{
>> +	xfs_agnumber_t	seqno = be32_to_cpu(agf->agf_seqno);
>> +	push_cur();
>> +
>> +	set_cur(&typtab[btype],
>> +		XFS_AGB_TO_DADDR(mp, seqno, root), blkbb, DB_RING_IGN, NULL);
>> +	if (iocur_top->data == NULL) {
>> +		dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
>> +		pop_cur();
>> +		return;
>> +	}
>> +	(*func)(iocur_top->data, nlevels - 1, agf, root, isroot, btype);
>> +	pop_cur();
>> +}
>> +
>> +static int check_magic(
>> +	struct xfs_btree_block	*block,
>> +	xfs_agnumber_t		seqno,
>> +	xfs_agblock_t		bno,
>> +	typnm_t			btype)
>> +{
>> +	uint32_t magic = type_to_btree_table[btype].magic;
>> +	uint32_t crc_magic = type_to_btree_table[btype].crc_magic;
>> +	char *btree_name = typtab[btype].name;
>> +	
>> +	if (be32_to_cpu(block->bb_magic) != magic &&
>> +		be32_to_cpu(block->bb_magic) != crc_magic) {
>> +		dbprintf(_("bad magic # %#x in %s block %u/%u\n"),
>> +			be32_to_cpu(block->bb_magic), btree_name, seqno, bno);
>> +		return -1;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void
>> +scanfunc_btree(
>> +	struct xfs_btree_block	*block,
>> +	int			level,
>> +	xfs_agf_t		*agf,
>> +	xfs_agblock_t		bno,
>> +	int			isroot,
>> +	typnm_t			btype)
>> +{
>> +	int			i, c;
>> +	char			**argv;
>> +	xfs_alloc_ptr_t         *pp;
>> +	xfs_agnumber_t		seqno = be32_to_cpu(agf->agf_seqno);
>> +
>> +	if (check_magic(block, seqno, bno, btype))
>> +		return;
>> +
>> +	dbprintf(_("\ncurrent blkno %d\n\n"), bno);
>> +	if (level == 0) {
>> +		if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
>> +		    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
>> +			dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
>> +				 "btbno block %u/%u\n"),
>> +				be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
>> +				mp->m_alloc_mxr[0], seqno, bno);
>> +			return;
>> +		}
>> +
>> +		argv = breakline("print", &c);
>> +		command(c, argv);
>> +
>> +		return;
>> +	} else {
>> +		argv = breakline("print", &c);
>> +		command(c, argv);
>> +	}
>> +
>> +	if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
>> +	    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
>> +		dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
>> +			 "%u/%u\n"),
>> +			be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
>> +			mp->m_alloc_mxr[1], seqno, bno);
>> +		return;
>> +	}
>> +
>> +	pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
>> +	for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
>> +		scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_btree, btype);
>> +}
>> diff --git a/db/treedump.h b/db/treedump.h
>> new file mode 100644
>> index 0000000..c9dbffc
>> --- /dev/null
>> +++ b/db/treedump.h
>> @@ -0,0 +1,20 @@
>> +/*
>> + * Copyright (c) 2017 Oracle.  All Rights Reserved.
>> + *
>> + * Author: Shan Hai <shan.hai@oracle.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it would be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write the Free Software Foundation,
>> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
>> + */
>> +
>> +extern void	treedump_init(void);
>> -- 
>> 2.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH 1/1] xfsprogs/db: add a command for dumping the btree blocks
  2017-04-06  6:59   ` Darrick J. Wong
  2017-04-06  7:54     ` Shan Hai
@ 2017-04-06  9:02     ` Shan Hai
  2017-04-06 23:57       ` Darrick J. Wong
  1 sibling, 1 reply; 6+ messages in thread
From: Shan Hai @ 2017-04-06  9:02 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2017年04月06日 14:59, Darrick J. Wong wrote:
> On Thu, Apr 06, 2017 at 09:44:43AM +0800, Shan Hai wrote:
>> Currently there is no way to dump the whole blocks of btrees in the
>> xfs_db except manually step through the btree nodes, dumping the blocks
>> of the whole btree by a command is more convenient than interactive
>> walking of the tree in some circumstances.
>>
>> This patch adds a new command to the xfs_db utility called 'treedump',
>> which can dump the specific btree or dump all btrees of the xfs, below
>> is an example usage of the command:
>>
>> sudo xfs_db -r -c "agf 0" -c "treedump -b" /dev/sda1
>>
>> The blocks of the bnobt tree are dumped to the stdout.
>>
>> Signed-off-by: Shan Hai <shan.hai@oracle.com>
>> ---
>>   db/Makefile   |   4 +-
>>   db/command.c  |   1 +
>>   db/treedump.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   db/treedump.h |  20 ++++
>>   4 files changed, 329 insertions(+), 2 deletions(-)
>>   create mode 100644 db/treedump.c
>>   create mode 100644 db/treedump.h
>>
>> diff --git a/db/Makefile b/db/Makefile
>> index cdc0b99..03a2283 100644
>> --- a/db/Makefile
>> +++ b/db/Makefile
>> @@ -8,8 +8,8 @@ include $(TOPDIR)/include/builddefs
>>   LTCOMMAND = xfs_db
>>   
>>   HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
>> -	btblock.h bmroot.h check.h command.h convert.h crc.h debug.h \
>> -	dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
>> +	btblock.h bmroot.h treedump.h check.h command.h convert.h crc.h \
>> +	debug.h dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
>>   	flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
>>   	io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
>>   	 sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h
>> diff --git a/db/command.c b/db/command.c
>> index 3d7cfd7..060ebed 100644
>> --- a/db/command.c
>> +++ b/db/command.c
>> @@ -143,6 +143,7 @@ init_commands(void)
>>   	print_init();
>>   	quit_init();
>>   	sb_init();
>> +	treedump_init();
>>   	type_init();
>>   	write_init();
>>   	dquot_init();
>> diff --git a/db/treedump.c b/db/treedump.c
>> new file mode 100644
>> index 0000000..8d5a33e
>> --- /dev/null
>> +++ b/db/treedump.c
>> @@ -0,0 +1,306 @@
>> +/*
>> + * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
>> + * Copyright (c) 2017 Oracle.
>> + * All Rights Reserved.
>> + *
>> + * Author: Shan Hai <shan.hai@oracle.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it would be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write the Free Software Foundation,
>> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
>> + */
>> +
>> +#include "libxfs.h"
>> +#include <math.h>
>> +#include <sys/time.h>
>> +#include "command.h"
>> +#include "io.h"
>> +#include "type.h"
>> +#include "fprint.h"
>> +#include "faddr.h"
>> +#include "field.h"
>> +#include "sb.h"
>> +#include "input.h"
>> +#include "output.h"
>> +#include "init.h"
>> +#include "malloc.h"
>> +
>> +#define TYP_MASK ( \
>> +	TYP_AGF | TYP_AGFL |  TYP_AGI | TYP_ATTR \
>> +	| TYP_BMAPBTA | TYP_BMAPBTD | TYP_BNOBT | TYP_CNTBT \
>> +	| TYP_RMAPBT | TYP_REFCBT | TYP_DATA | TYP_DIR2 \
>> +	| TYP_DQBLK | TYP_INOBT | TYP_INODATA | TYP_INODE \
>> +	| TYP_LOG | TYP_RTBITMAP | TYP_RTSUMMARY | TYP_SB \
>> +	| TYP_SYMLINK | TYP_TEXT | TYP_FINOBT | TYP_NONE)
>> +
>> +static struct btree_types {
>> +	xfs_btnum_t	btnum;
>> +	uint32_t	magic;
>> +	uint32_t	crc_magic;
>> +} const type_to_btree_table[TYP_NONE] = {
>> +	[0 ... TYP_NONE - 1]	= { XFS_BTNUM_MAX,  -1, -1 },
>> +
>> +	[TYP_BNOBT]		= { XFS_BTNUM_BNO,
>> +				    XFS_ABTB_MAGIC, XFS_ABTB_CRC_MAGIC },
>> +	[TYP_CNTBT]		= { XFS_BTNUM_CNT,
>> +				    XFS_ABTC_MAGIC, XFS_ABTC_CRC_MAGIC },
>> +	[TYP_RMAPBT]		= { XFS_BTNUM_RMAP,
>> +				    XFS_RMAP_CRC_MAGIC, XFS_RMAP_CRC_MAGIC },
>> +	[TYP_REFCBT]		= { XFS_BTNUM_REFC,
>> +				    XFS_REFC_CRC_MAGIC, XFS_REFC_CRC_MAGIC },
> Uh.... what if I want to dump one of the other btrees?  There's at least
> four more types. ;)
>
> TBH I was wondering why not let the user navigate to the root node of
> whichever tree they wish to dump, and then the command simply has to
> travel down the left side of the tree to level 0 and then dump each leaf
> and use rightsib to move on to the next leaf?

After giving it another thought reveals that the above mentioned method 
can only
dump the leaf nodes but not internal nodes, the purpose of the original 
patch is
dump the whole tree but not just leaves.

How about reserving the current recursive traversal and drop the tree 
type selecting
code and use the 'addr free_root' method?

Thanks
Shan Hai

> i.e.
> xfs_db> agi 3
> xfs_db> addr free_root
> xfs_db> treedump
> 0: [...]
> 1: [...]
> xfs_db>
>
> --D
>
>> +};
>> +
>> +static typnm_t	btype_map[TYP_NONE];
>> +
>> +#define TYP_MAP_SIZE (sizeof (btype_map) / sizeof (typnm_t))
>> +
>> +typedef void	(*scan_sbtree_f_t)(struct xfs_btree_block *block,
>> +				   int level, xfs_agf_t *agf,
>> +				   xfs_agblock_t bno, int isroot,
>> +				   typnm_t btype);
>> +static int	treedump_f(int argc, char **argv);
>> +static void	scan_ag(xfs_agnumber_t agno);
>> +static void	scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
>> +			    int nlevels, int isroot,
>> +			    scan_sbtree_f_t func, typnm_t btype);
>> +static void	scanfunc_btree(struct xfs_btree_block *block, int level,
>> +			       xfs_agf_t *agf, xfs_agblock_t bno,
>> +			       int isroot, typnm_t btype);
>> +/*
>> + * [-b]: bnobt, [-c]: cntbt, [-f]: refcntbt, [-r]: rmapbt
>> + */
>> +static const cmdinfo_t	treedump_cmd =
>> +	{ "treedump", NULL, treedump_f, 0, 4, 0,
>> +	 N_("[-b] [-c] [-f] [-r]"),
>> +	 N_("dump the metadata of the btrees"), NULL };
>> +
>> +static void set_btree_type(
>> +	typnm_t	type)
>> +{
>> +	btype_map[type] = type;
>> +}
>> +
>> +/* ARGSUSED */
>> +static int
>> +treedump_f(
>> +	int	argc,
>> +	char	**argv)
>> +{	
>> +	int c;
>> +
>> +	if (cur_agno == NULLAGNUMBER)
>> +                cur_agno = 0;
>> +
>> +	/* Dump the BNOBT if none is specified */
>> +	set_btree_type(TYP_BNOBT);
>> +
>> +	while ((c = getopt(argc, argv, "bcfr")) != EOF) {
>> +                switch (c) {
>> +                case 'b':
>> +                        set_btree_type(TYP_BNOBT);
>> +                        break;
>> +                case 'c':
>> +                        set_btree_type(TYP_CNTBT);
>> +                        break;
>> +		case 'f':
>> +			set_btree_type(TYP_REFCBT);
>> +			break;
>> +		case 'r':
>> +			set_btree_type(TYP_RMAPBT);
>> +			break;
>> +		default:
>> +			set_btree_type(TYP_BNOBT);
>> +			break;
>> +		}
>> +	}
>> +
>> +	scan_ag(cur_agno);
>> +
>> +	return 0;
>> +}
>> +
>> +void
>> +treedump_init(void)
>> +{
>> +	add_command(&treedump_cmd);
>> +}
>> +
>> +static void
>> +scan_ag(
>> +	xfs_agnumber_t	agno)
>> +{
>> +	xfs_agf_t	*agf;
>> +	xfs_sb_t	tsb;
>> +	xfs_sb_t	*sb = &tsb;
>> +	typnm_t		btype;
>> +	xfs_btnum_t	btree;
>> +	int i, c;
>> +	char **argv;
>> +
>> +	push_cur();	/* 1 pushed */
>> +	set_cur(&typtab[TYP_SB],
>> +		XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
>> +		XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
>> +
>> +	if (!iocur_top->data) {
>> +		dbprintf(_("can't read superblock for ag %u\n"), agno);
>> +		goto pop1_out;
>> +	}
>> +
>> +	libxfs_sb_from_disk(sb, iocur_top->data);
>> +
>> +	if (sb->sb_magicnum != XFS_SB_MAGIC) {
>> +		dbprintf(_("bad sb magic # %#x in ag %u\n"),
>> +			sb->sb_magicnum, agno);
>> +	}
>> +
>> +	if (!xfs_sb_good_version(sb)) {
>> +		dbprintf(_("bad sb version # %#x in ag %u\n"),
>> +			sb->sb_versionnum, agno);
>> +	}
>> +
>> +	if (agno == 0 && sb->sb_inprogress != 0) {
>> +		dbprintf(_("mkfs not completed successfully\n"));
>> +	}
>> +
>> +	push_cur();     /* 2 pushed */
>> +        set_cur(&typtab[TYP_AGF],
>> +                XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
>> +                XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
>> +
>> +	if ((agf = iocur_top->data) == NULL) {
>> +                dbprintf(_("can't read agf block for ag %u\n"), agno);
>> +                goto pop2_out;
>> +        }
>> +        if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
>> +		dbprintf(_("bad agf magic # %#x in ag %u\n"),
>> +		be32_to_cpu(agf->agf_magicnum), agno);
>> +        }
>> +        if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
>> +		dbprintf(_("bad agf version # %#x in ag %u\n"),
>> +			be32_to_cpu(agf->agf_versionnum), agno);
>> +        }
>> +
>> +	dbprintf(_("\nAGF %d\n\n"), agno);
>> +	argv = breakline("print", &c);
>> +	command(c, argv);
>> +
>> +	for (i = 0; i < TYP_MAP_SIZE; i++) {
>> +		btype = btype_map[i];
>> +		if (btype & TYP_MASK) {
>> +			btree = type_to_btree_table[btype].btnum;
>> +			scan_sbtree(agf, be32_to_cpu(agf->agf_roots[btree]),
>> +					be32_to_cpu(agf->agf_levels[btree]),
>> +					1, scanfunc_btree, btype);
>> +		}
>> +	}
>> +
>> +pop2_out:
>> +	pop_cur();
>> +pop1_out:
>> +	pop_cur();
>> +}
>> +
>> +
>> +static void
>> +scan_sbtree(
>> +	xfs_agf_t	*agf,
>> +	xfs_agblock_t	root,
>> +	int		nlevels,
>> +	int		isroot,
>> +	scan_sbtree_f_t	func,
>> +	typnm_t		btype)
>> +{
>> +	xfs_agnumber_t	seqno = be32_to_cpu(agf->agf_seqno);
>> +	push_cur();
>> +
>> +	set_cur(&typtab[btype],
>> +		XFS_AGB_TO_DADDR(mp, seqno, root), blkbb, DB_RING_IGN, NULL);
>> +	if (iocur_top->data == NULL) {
>> +		dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
>> +		pop_cur();
>> +		return;
>> +	}
>> +	(*func)(iocur_top->data, nlevels - 1, agf, root, isroot, btype);
>> +	pop_cur();
>> +}
>> +
>> +static int check_magic(
>> +	struct xfs_btree_block	*block,
>> +	xfs_agnumber_t		seqno,
>> +	xfs_agblock_t		bno,
>> +	typnm_t			btype)
>> +{
>> +	uint32_t magic = type_to_btree_table[btype].magic;
>> +	uint32_t crc_magic = type_to_btree_table[btype].crc_magic;
>> +	char *btree_name = typtab[btype].name;
>> +	
>> +	if (be32_to_cpu(block->bb_magic) != magic &&
>> +		be32_to_cpu(block->bb_magic) != crc_magic) {
>> +		dbprintf(_("bad magic # %#x in %s block %u/%u\n"),
>> +			be32_to_cpu(block->bb_magic), btree_name, seqno, bno);
>> +		return -1;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void
>> +scanfunc_btree(
>> +	struct xfs_btree_block	*block,
>> +	int			level,
>> +	xfs_agf_t		*agf,
>> +	xfs_agblock_t		bno,
>> +	int			isroot,
>> +	typnm_t			btype)
>> +{
>> +	int			i, c;
>> +	char			**argv;
>> +	xfs_alloc_ptr_t         *pp;
>> +	xfs_agnumber_t		seqno = be32_to_cpu(agf->agf_seqno);
>> +
>> +	if (check_magic(block, seqno, bno, btype))
>> +		return;
>> +
>> +	dbprintf(_("\ncurrent blkno %d\n\n"), bno);
>> +	if (level == 0) {
>> +		if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
>> +		    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
>> +			dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
>> +				 "btbno block %u/%u\n"),
>> +				be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
>> +				mp->m_alloc_mxr[0], seqno, bno);
>> +			return;
>> +		}
>> +
>> +		argv = breakline("print", &c);
>> +		command(c, argv);
>> +
>> +		return;
>> +	} else {
>> +		argv = breakline("print", &c);
>> +		command(c, argv);
>> +	}
>> +
>> +	if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
>> +	    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
>> +		dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
>> +			 "%u/%u\n"),
>> +			be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
>> +			mp->m_alloc_mxr[1], seqno, bno);
>> +		return;
>> +	}
>> +
>> +	pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
>> +	for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
>> +		scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_btree, btype);
>> +}
>> diff --git a/db/treedump.h b/db/treedump.h
>> new file mode 100644
>> index 0000000..c9dbffc
>> --- /dev/null
>> +++ b/db/treedump.h
>> @@ -0,0 +1,20 @@
>> +/*
>> + * Copyright (c) 2017 Oracle.  All Rights Reserved.
>> + *
>> + * Author: Shan Hai <shan.hai@oracle.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it would be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write the Free Software Foundation,
>> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
>> + */
>> +
>> +extern void	treedump_init(void);
>> -- 
>> 2.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH 1/1] xfsprogs/db: add a command for dumping the btree blocks
  2017-04-06  9:02     ` Shan Hai
@ 2017-04-06 23:57       ` Darrick J. Wong
  0 siblings, 0 replies; 6+ messages in thread
From: Darrick J. Wong @ 2017-04-06 23:57 UTC (permalink / raw)
  To: Shan Hai; +Cc: linux-xfs

On Thu, Apr 06, 2017 at 05:02:44PM +0800, Shan Hai wrote:
> 
> 
> On 2017年04月06日 14:59, Darrick J. Wong wrote:
> >On Thu, Apr 06, 2017 at 09:44:43AM +0800, Shan Hai wrote:
> >>Currently there is no way to dump the whole blocks of btrees in the
> >>xfs_db except manually step through the btree nodes, dumping the blocks
> >>of the whole btree by a command is more convenient than interactive
> >>walking of the tree in some circumstances.
> >>
> >>This patch adds a new command to the xfs_db utility called 'treedump',
> >>which can dump the specific btree or dump all btrees of the xfs, below
> >>is an example usage of the command:
> >>
> >>sudo xfs_db -r -c "agf 0" -c "treedump -b" /dev/sda1
> >>
> >>The blocks of the bnobt tree are dumped to the stdout.
> >>
> >>Signed-off-by: Shan Hai <shan.hai@oracle.com>
> >>---
> >>  db/Makefile   |   4 +-
> >>  db/command.c  |   1 +
> >>  db/treedump.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>  db/treedump.h |  20 ++++
> >>  4 files changed, 329 insertions(+), 2 deletions(-)
> >>  create mode 100644 db/treedump.c
> >>  create mode 100644 db/treedump.h
> >>
> >>diff --git a/db/Makefile b/db/Makefile
> >>index cdc0b99..03a2283 100644
> >>--- a/db/Makefile
> >>+++ b/db/Makefile
> >>@@ -8,8 +8,8 @@ include $(TOPDIR)/include/builddefs
> >>  LTCOMMAND = xfs_db
> >>  HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
> >>-	btblock.h bmroot.h check.h command.h convert.h crc.h debug.h \
> >>-	dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
> >>+	btblock.h bmroot.h treedump.h check.h command.h convert.h crc.h \
> >>+	debug.h dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
> >>  	flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
> >>  	io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
> >>  	 sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h
> >>diff --git a/db/command.c b/db/command.c
> >>index 3d7cfd7..060ebed 100644
> >>--- a/db/command.c
> >>+++ b/db/command.c
> >>@@ -143,6 +143,7 @@ init_commands(void)
> >>  	print_init();
> >>  	quit_init();
> >>  	sb_init();
> >>+	treedump_init();
> >>  	type_init();
> >>  	write_init();
> >>  	dquot_init();
> >>diff --git a/db/treedump.c b/db/treedump.c
> >>new file mode 100644
> >>index 0000000..8d5a33e
> >>--- /dev/null
> >>+++ b/db/treedump.c
> >>@@ -0,0 +1,306 @@
> >>+/*
> >>+ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
> >>+ * Copyright (c) 2017 Oracle.
> >>+ * All Rights Reserved.
> >>+ *
> >>+ * Author: Shan Hai <shan.hai@oracle.com>
> >>+ *
> >>+ * This program is free software; you can redistribute it and/or
> >>+ * modify it under the terms of the GNU General Public License as
> >>+ * published by the Free Software Foundation.
> >>+ *
> >>+ * This program is distributed in the hope that it would be useful,
> >>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>+ * GNU General Public License for more details.
> >>+ *
> >>+ * You should have received a copy of the GNU General Public License
> >>+ * along with this program; if not, write the Free Software Foundation,
> >>+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> >>+ */
> >>+
> >>+#include "libxfs.h"
> >>+#include <math.h>
> >>+#include <sys/time.h>
> >>+#include "command.h"
> >>+#include "io.h"
> >>+#include "type.h"
> >>+#include "fprint.h"
> >>+#include "faddr.h"
> >>+#include "field.h"
> >>+#include "sb.h"
> >>+#include "input.h"
> >>+#include "output.h"
> >>+#include "init.h"
> >>+#include "malloc.h"
> >>+
> >>+#define TYP_MASK ( \
> >>+	TYP_AGF | TYP_AGFL |  TYP_AGI | TYP_ATTR \
> >>+	| TYP_BMAPBTA | TYP_BMAPBTD | TYP_BNOBT | TYP_CNTBT \
> >>+	| TYP_RMAPBT | TYP_REFCBT | TYP_DATA | TYP_DIR2 \
> >>+	| TYP_DQBLK | TYP_INOBT | TYP_INODATA | TYP_INODE \
> >>+	| TYP_LOG | TYP_RTBITMAP | TYP_RTSUMMARY | TYP_SB \
> >>+	| TYP_SYMLINK | TYP_TEXT | TYP_FINOBT | TYP_NONE)
> >>+
> >>+static struct btree_types {
> >>+	xfs_btnum_t	btnum;
> >>+	uint32_t	magic;
> >>+	uint32_t	crc_magic;
> >>+} const type_to_btree_table[TYP_NONE] = {
> >>+	[0 ... TYP_NONE - 1]	= { XFS_BTNUM_MAX,  -1, -1 },
> >>+
> >>+	[TYP_BNOBT]		= { XFS_BTNUM_BNO,
> >>+				    XFS_ABTB_MAGIC, XFS_ABTB_CRC_MAGIC },
> >>+	[TYP_CNTBT]		= { XFS_BTNUM_CNT,
> >>+				    XFS_ABTC_MAGIC, XFS_ABTC_CRC_MAGIC },
> >>+	[TYP_RMAPBT]		= { XFS_BTNUM_RMAP,
> >>+				    XFS_RMAP_CRC_MAGIC, XFS_RMAP_CRC_MAGIC },
> >>+	[TYP_REFCBT]		= { XFS_BTNUM_REFC,
> >>+				    XFS_REFC_CRC_MAGIC, XFS_REFC_CRC_MAGIC },
> >Uh.... what if I want to dump one of the other btrees?  There's at least
> >four more types. ;)
> >
> >TBH I was wondering why not let the user navigate to the root node of
> >whichever tree they wish to dump, and then the command simply has to
> >travel down the left side of the tree to level 0 and then dump each leaf
> >and use rightsib to move on to the next leaf?
> 
> After giving it another thought reveals that the above mentioned method can
> only
> dump the leaf nodes but not internal nodes, the purpose of the original
> patch is
> dump the whole tree but not just leaves.

Yes.  I suppose you could do something like:

dump_level() {
	push
	do {
		print keys
		print recs
	} while (addr rightsib);
	pop
}

dump_btree() {
	for (i = level; i > 0; i--) {
		dump_level()	# dumps internal nodes
		addr ptrs[1]
	}
	dump_level()	# dumps leaves
}

> How about reserving the current recursive traversal and drop the tree type
> selecting
> code and use the 'addr free_root' method?

I'll punt to Eric Sandeen on that, but I think it makes most sense to
programmatically call the existing xfs_db functions to navigate around
the tree rather than rolling your own.  The only tricky part is
extracting the level field from whichever btree you're already pointed
at.  The names should be consistent across btrees.

Directory/attr btrees are a little different though.

--D

> 
> Thanks
> Shan Hai
> 
> >i.e.
> >xfs_db> agi 3
> >xfs_db> addr free_root
> >xfs_db> treedump
> >0: [...]
> >1: [...]
> >xfs_db>
> >
> >--D
> >
> >>+};
> >>+
> >>+static typnm_t	btype_map[TYP_NONE];
> >>+
> >>+#define TYP_MAP_SIZE (sizeof (btype_map) / sizeof (typnm_t))
> >>+
> >>+typedef void	(*scan_sbtree_f_t)(struct xfs_btree_block *block,
> >>+				   int level, xfs_agf_t *agf,
> >>+				   xfs_agblock_t bno, int isroot,
> >>+				   typnm_t btype);
> >>+static int	treedump_f(int argc, char **argv);
> >>+static void	scan_ag(xfs_agnumber_t agno);
> >>+static void	scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
> >>+			    int nlevels, int isroot,
> >>+			    scan_sbtree_f_t func, typnm_t btype);
> >>+static void	scanfunc_btree(struct xfs_btree_block *block, int level,
> >>+			       xfs_agf_t *agf, xfs_agblock_t bno,
> >>+			       int isroot, typnm_t btype);
> >>+/*
> >>+ * [-b]: bnobt, [-c]: cntbt, [-f]: refcntbt, [-r]: rmapbt
> >>+ */
> >>+static const cmdinfo_t	treedump_cmd =
> >>+	{ "treedump", NULL, treedump_f, 0, 4, 0,
> >>+	 N_("[-b] [-c] [-f] [-r]"),
> >>+	 N_("dump the metadata of the btrees"), NULL };
> >>+
> >>+static void set_btree_type(
> >>+	typnm_t	type)
> >>+{
> >>+	btype_map[type] = type;
> >>+}
> >>+
> >>+/* ARGSUSED */
> >>+static int
> >>+treedump_f(
> >>+	int	argc,
> >>+	char	**argv)
> >>+{	
> >>+	int c;
> >>+
> >>+	if (cur_agno == NULLAGNUMBER)
> >>+                cur_agno = 0;
> >>+
> >>+	/* Dump the BNOBT if none is specified */
> >>+	set_btree_type(TYP_BNOBT);
> >>+
> >>+	while ((c = getopt(argc, argv, "bcfr")) != EOF) {
> >>+                switch (c) {
> >>+                case 'b':
> >>+                        set_btree_type(TYP_BNOBT);
> >>+                        break;
> >>+                case 'c':
> >>+                        set_btree_type(TYP_CNTBT);
> >>+                        break;
> >>+		case 'f':
> >>+			set_btree_type(TYP_REFCBT);
> >>+			break;
> >>+		case 'r':
> >>+			set_btree_type(TYP_RMAPBT);
> >>+			break;
> >>+		default:
> >>+			set_btree_type(TYP_BNOBT);
> >>+			break;
> >>+		}
> >>+	}
> >>+
> >>+	scan_ag(cur_agno);
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+void
> >>+treedump_init(void)
> >>+{
> >>+	add_command(&treedump_cmd);
> >>+}
> >>+
> >>+static void
> >>+scan_ag(
> >>+	xfs_agnumber_t	agno)
> >>+{
> >>+	xfs_agf_t	*agf;
> >>+	xfs_sb_t	tsb;
> >>+	xfs_sb_t	*sb = &tsb;
> >>+	typnm_t		btype;
> >>+	xfs_btnum_t	btree;
> >>+	int i, c;
> >>+	char **argv;
> >>+
> >>+	push_cur();	/* 1 pushed */
> >>+	set_cur(&typtab[TYP_SB],
> >>+		XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
> >>+		XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
> >>+
> >>+	if (!iocur_top->data) {
> >>+		dbprintf(_("can't read superblock for ag %u\n"), agno);
> >>+		goto pop1_out;
> >>+	}
> >>+
> >>+	libxfs_sb_from_disk(sb, iocur_top->data);
> >>+
> >>+	if (sb->sb_magicnum != XFS_SB_MAGIC) {
> >>+		dbprintf(_("bad sb magic # %#x in ag %u\n"),
> >>+			sb->sb_magicnum, agno);
> >>+	}
> >>+
> >>+	if (!xfs_sb_good_version(sb)) {
> >>+		dbprintf(_("bad sb version # %#x in ag %u\n"),
> >>+			sb->sb_versionnum, agno);
> >>+	}
> >>+
> >>+	if (agno == 0 && sb->sb_inprogress != 0) {
> >>+		dbprintf(_("mkfs not completed successfully\n"));
> >>+	}
> >>+
> >>+	push_cur();     /* 2 pushed */
> >>+        set_cur(&typtab[TYP_AGF],
> >>+                XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
> >>+                XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
> >>+
> >>+	if ((agf = iocur_top->data) == NULL) {
> >>+                dbprintf(_("can't read agf block for ag %u\n"), agno);
> >>+                goto pop2_out;
> >>+        }
> >>+        if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
> >>+		dbprintf(_("bad agf magic # %#x in ag %u\n"),
> >>+		be32_to_cpu(agf->agf_magicnum), agno);
> >>+        }
> >>+        if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
> >>+		dbprintf(_("bad agf version # %#x in ag %u\n"),
> >>+			be32_to_cpu(agf->agf_versionnum), agno);
> >>+        }
> >>+
> >>+	dbprintf(_("\nAGF %d\n\n"), agno);
> >>+	argv = breakline("print", &c);
> >>+	command(c, argv);
> >>+
> >>+	for (i = 0; i < TYP_MAP_SIZE; i++) {
> >>+		btype = btype_map[i];
> >>+		if (btype & TYP_MASK) {
> >>+			btree = type_to_btree_table[btype].btnum;
> >>+			scan_sbtree(agf, be32_to_cpu(agf->agf_roots[btree]),
> >>+					be32_to_cpu(agf->agf_levels[btree]),
> >>+					1, scanfunc_btree, btype);
> >>+		}
> >>+	}
> >>+
> >>+pop2_out:
> >>+	pop_cur();
> >>+pop1_out:
> >>+	pop_cur();
> >>+}
> >>+
> >>+
> >>+static void
> >>+scan_sbtree(
> >>+	xfs_agf_t	*agf,
> >>+	xfs_agblock_t	root,
> >>+	int		nlevels,
> >>+	int		isroot,
> >>+	scan_sbtree_f_t	func,
> >>+	typnm_t		btype)
> >>+{
> >>+	xfs_agnumber_t	seqno = be32_to_cpu(agf->agf_seqno);
> >>+	push_cur();
> >>+
> >>+	set_cur(&typtab[btype],
> >>+		XFS_AGB_TO_DADDR(mp, seqno, root), blkbb, DB_RING_IGN, NULL);
> >>+	if (iocur_top->data == NULL) {
> >>+		dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
> >>+		pop_cur();
> >>+		return;
> >>+	}
> >>+	(*func)(iocur_top->data, nlevels - 1, agf, root, isroot, btype);
> >>+	pop_cur();
> >>+}
> >>+
> >>+static int check_magic(
> >>+	struct xfs_btree_block	*block,
> >>+	xfs_agnumber_t		seqno,
> >>+	xfs_agblock_t		bno,
> >>+	typnm_t			btype)
> >>+{
> >>+	uint32_t magic = type_to_btree_table[btype].magic;
> >>+	uint32_t crc_magic = type_to_btree_table[btype].crc_magic;
> >>+	char *btree_name = typtab[btype].name;
> >>+	
> >>+	if (be32_to_cpu(block->bb_magic) != magic &&
> >>+		be32_to_cpu(block->bb_magic) != crc_magic) {
> >>+		dbprintf(_("bad magic # %#x in %s block %u/%u\n"),
> >>+			be32_to_cpu(block->bb_magic), btree_name, seqno, bno);
> >>+		return -1;
> >>+	}
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+static void
> >>+scanfunc_btree(
> >>+	struct xfs_btree_block	*block,
> >>+	int			level,
> >>+	xfs_agf_t		*agf,
> >>+	xfs_agblock_t		bno,
> >>+	int			isroot,
> >>+	typnm_t			btype)
> >>+{
> >>+	int			i, c;
> >>+	char			**argv;
> >>+	xfs_alloc_ptr_t         *pp;
> >>+	xfs_agnumber_t		seqno = be32_to_cpu(agf->agf_seqno);
> >>+
> >>+	if (check_magic(block, seqno, bno, btype))
> >>+		return;
> >>+
> >>+	dbprintf(_("\ncurrent blkno %d\n\n"), bno);
> >>+	if (level == 0) {
> >>+		if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
> >>+		    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
> >>+			dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
> >>+				 "btbno block %u/%u\n"),
> >>+				be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
> >>+				mp->m_alloc_mxr[0], seqno, bno);
> >>+			return;
> >>+		}
> >>+
> >>+		argv = breakline("print", &c);
> >>+		command(c, argv);
> >>+
> >>+		return;
> >>+	} else {
> >>+		argv = breakline("print", &c);
> >>+		command(c, argv);
> >>+	}
> >>+
> >>+	if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
> >>+	    (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
> >>+		dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
> >>+			 "%u/%u\n"),
> >>+			be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
> >>+			mp->m_alloc_mxr[1], seqno, bno);
> >>+		return;
> >>+	}
> >>+
> >>+	pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
> >>+	for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
> >>+		scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_btree, btype);
> >>+}
> >>diff --git a/db/treedump.h b/db/treedump.h
> >>new file mode 100644
> >>index 0000000..c9dbffc
> >>--- /dev/null
> >>+++ b/db/treedump.h
> >>@@ -0,0 +1,20 @@
> >>+/*
> >>+ * Copyright (c) 2017 Oracle.  All Rights Reserved.
> >>+ *
> >>+ * Author: Shan Hai <shan.hai@oracle.com>
> >>+ *
> >>+ * This program is free software; you can redistribute it and/or
> >>+ * modify it under the terms of the GNU General Public License as
> >>+ * published by the Free Software Foundation.
> >>+ *
> >>+ * This program is distributed in the hope that it would be useful,
> >>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>+ * GNU General Public License for more details.
> >>+ *
> >>+ * You should have received a copy of the GNU General Public License
> >>+ * along with this program; if not, write the Free Software Foundation,
> >>+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> >>+ */
> >>+
> >>+extern void	treedump_init(void);
> >>-- 
> >>2.7.4
> >>
> >>--
> >>To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> >>the body of a message to majordomo@vger.kernel.org
> >>More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >--
> >To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> >the body of a message to majordomo@vger.kernel.org
> >More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-04-06 23:57 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-06  1:44 [RFC PATCH 0/1] xfsprogs/db: add a command for dumping the btree blocks Shan Hai
2017-04-06  1:44 ` [RFC PATCH 1/1] " Shan Hai
2017-04-06  6:59   ` Darrick J. Wong
2017-04-06  7:54     ` Shan Hai
2017-04-06  9:02     ` Shan Hai
2017-04-06 23:57       ` Darrick J. Wong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.