mirror of
https://github.com/versity/scoutfs.git
synced 2025-12-23 05:25:18 +00:00
Add finer grained options to scoutfs print
The default output from scoutfs print can be very large, even when using the -S option. Add three new command line options to allow more targeted selection of btrees and their items. --allocs prints the metadata and data allocators --roots allows the selection of btree roots to walk (logs, srch, fs) --items allows the selection of items to print from the selected btrees Signed-off-by: Chris Kirby <ckirby@versity.com>
This commit is contained in:
@@ -402,25 +402,39 @@ before destroying an old empty data device.
|
|||||||
.PD
|
.PD
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "print {-S|--skip-likely-huge} META-DEVICE"
|
.BI "print {-a|--allocs} {-i|--items ITEMS} {-r|--roots ROOTS} {-S|--skip-likely-huge} META-DEVICE"
|
||||||
.sp
|
.sp
|
||||||
Prints out all of the metadata in the file system. This makes no effort
|
Prints out some or all of the metadata in the file system. This makes no effort
|
||||||
to ensure that the structures are consistent as they're traversed and
|
to ensure that the structures are consistent as they're traversed and
|
||||||
can present structures that seem corrupt as they change as they're
|
can present structures that seem corrupt as they change as they're
|
||||||
output.
|
output.
|
||||||
|
.sp
|
||||||
|
Structures that are related to the number of mounts and are maintained at a
|
||||||
|
relatively reasonable size are always printed. These include per-mount log
|
||||||
|
trees, srch files, allocators, and the metadata allocators used by server
|
||||||
|
commits. Other btrees and their items can be selected as desired.
|
||||||
.RS 1.0i
|
.RS 1.0i
|
||||||
.PD 0
|
.PD 0
|
||||||
.TP
|
|
||||||
.sp
|
.sp
|
||||||
|
.TP
|
||||||
|
.B "-a, --allocs"
|
||||||
|
Print the metadata and data allocators. Enabled by default.
|
||||||
|
.TP
|
||||||
|
.B "-r, --roots ROOTS"
|
||||||
|
This option can be used to select which btrees are traversed. It is a comma-separated list containing one or more of the following btree roots: logs, srch, fs. Default is all roots.
|
||||||
|
.TP
|
||||||
|
.B "-i, --items ITEMS"
|
||||||
|
This option can be used to choose which btree items are printed from the
|
||||||
|
selected btree roots. It is a comma-separated list containing one or
|
||||||
|
more of the following items: inode, xattr, dirent, symlink, backref, extent.
|
||||||
|
Default is all items.
|
||||||
|
.TP
|
||||||
.B "-S, --skip-likely-huge"
|
.B "-S, --skip-likely-huge"
|
||||||
Skip printing structures that are likely to be very large. The
|
Skip printing structures that are likely to be very large. The
|
||||||
structures that are skipped tend to be global and whose size tends to be
|
structures that are skipped tend to be global and whose size tends to be
|
||||||
related to the size of the volume. Examples of skipped structures include
|
related to the size of the volume. Examples of skipped structures include
|
||||||
the global fs items, srch files, and metadata and data
|
the global fs items, srch files, and metadata and data
|
||||||
allocators. Similar structures that are not skipped are related to the
|
allocators.
|
||||||
number of mounts and are maintained at a relatively reasonable size.
|
|
||||||
These include per-mount log trees, srch files, allocators, and the
|
|
||||||
metadata allocators used by server commits.
|
|
||||||
.sp
|
.sp
|
||||||
Skipping the larger structures limits the print output to a relatively
|
Skipping the larger structures limits the print output to a relatively
|
||||||
constant size rather than being a large multiple of the used metadata
|
constant size rather than being a large multiple of the used metadata
|
||||||
|
|||||||
@@ -29,6 +29,42 @@
|
|||||||
#include "leaf_item_hash.h"
|
#include "leaf_item_hash.h"
|
||||||
#include "dev.h"
|
#include "dev.h"
|
||||||
|
|
||||||
|
struct print_args {
|
||||||
|
char *meta_device;
|
||||||
|
bool skip_likely_huge;
|
||||||
|
bool roots_requested;
|
||||||
|
bool items_requested;
|
||||||
|
bool allocs_requested;
|
||||||
|
bool walk_allocs;
|
||||||
|
bool walk_logs_root;
|
||||||
|
bool walk_fs_root;
|
||||||
|
bool walk_srch_root;
|
||||||
|
bool print_inodes;
|
||||||
|
bool print_xattrs;
|
||||||
|
bool print_dirents;
|
||||||
|
bool print_symlinks;
|
||||||
|
bool print_backrefs;
|
||||||
|
bool print_extents;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct print_args print_args = {
|
||||||
|
.meta_device = NULL,
|
||||||
|
.skip_likely_huge = false,
|
||||||
|
.roots_requested = false,
|
||||||
|
.items_requested = false,
|
||||||
|
.allocs_requested = false,
|
||||||
|
.walk_allocs = true,
|
||||||
|
.walk_logs_root = true,
|
||||||
|
.walk_fs_root = true,
|
||||||
|
.walk_srch_root = true,
|
||||||
|
.print_inodes = true,
|
||||||
|
.print_xattrs = true,
|
||||||
|
.print_dirents = true,
|
||||||
|
.print_symlinks = true,
|
||||||
|
.print_backrefs = true,
|
||||||
|
.print_extents = true
|
||||||
|
};
|
||||||
|
|
||||||
static void print_block_header(struct scoutfs_block_header *hdr, int size)
|
static void print_block_header(struct scoutfs_block_header *hdr, int size)
|
||||||
{
|
{
|
||||||
u32 crc = crc_block(hdr, size);
|
u32 crc = crc_block(hdr, size);
|
||||||
@@ -195,7 +231,7 @@ static void print_inode_index(struct scoutfs_key *key, void *val, int val_len)
|
|||||||
|
|
||||||
typedef void (*print_func_t)(struct scoutfs_key *key, void *val, int val_len);
|
typedef void (*print_func_t)(struct scoutfs_key *key, void *val, int val_len);
|
||||||
|
|
||||||
static print_func_t find_printer(u8 zone, u8 type)
|
static print_func_t find_printer(u8 zone, u8 type, bool *suppress)
|
||||||
{
|
{
|
||||||
if (zone == SCOUTFS_INODE_INDEX_ZONE &&
|
if (zone == SCOUTFS_INODE_INDEX_ZONE &&
|
||||||
type >= SCOUTFS_INODE_INDEX_META_SEQ_TYPE &&
|
type >= SCOUTFS_INODE_INDEX_META_SEQ_TYPE &&
|
||||||
@@ -218,13 +254,34 @@ static print_func_t find_printer(u8 zone, u8 type)
|
|||||||
|
|
||||||
if (zone == SCOUTFS_FS_ZONE) {
|
if (zone == SCOUTFS_FS_ZONE) {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case SCOUTFS_INODE_TYPE: return print_inode;
|
case SCOUTFS_INODE_TYPE:
|
||||||
case SCOUTFS_XATTR_TYPE: return print_xattr;
|
if (!print_args.print_inodes)
|
||||||
case SCOUTFS_DIRENT_TYPE: return print_dirent;
|
*suppress = true;
|
||||||
case SCOUTFS_READDIR_TYPE: return print_dirent;
|
return print_inode;
|
||||||
case SCOUTFS_SYMLINK_TYPE: return print_symlink;
|
case SCOUTFS_XATTR_TYPE:
|
||||||
case SCOUTFS_LINK_BACKREF_TYPE: return print_dirent;
|
if (!print_args.print_xattrs)
|
||||||
case SCOUTFS_DATA_EXTENT_TYPE: return print_data_extent;
|
*suppress = true;
|
||||||
|
return print_xattr;
|
||||||
|
case SCOUTFS_DIRENT_TYPE:
|
||||||
|
if (!print_args.print_dirents)
|
||||||
|
*suppress = true;
|
||||||
|
return print_dirent;
|
||||||
|
case SCOUTFS_READDIR_TYPE:
|
||||||
|
if (!print_args.print_dirents)
|
||||||
|
*suppress = true;
|
||||||
|
return print_dirent;
|
||||||
|
case SCOUTFS_SYMLINK_TYPE:
|
||||||
|
if (!print_args.print_symlinks)
|
||||||
|
*suppress = true;
|
||||||
|
return print_symlink;
|
||||||
|
case SCOUTFS_LINK_BACKREF_TYPE:
|
||||||
|
if (!print_args.print_backrefs)
|
||||||
|
*suppress = true;
|
||||||
|
return print_dirent;
|
||||||
|
case SCOUTFS_DATA_EXTENT_TYPE:
|
||||||
|
if (!print_args.print_extents)
|
||||||
|
*suppress = true;
|
||||||
|
return print_data_extent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,12 +301,16 @@ static int print_fs_item(struct scoutfs_key *key, u64 seq, u8 flags, void *val,
|
|||||||
|
|
||||||
/* only items in leaf blocks have values */
|
/* only items in leaf blocks have values */
|
||||||
if (val != NULL && !(flags & SCOUTFS_ITEM_FLAG_DELETION)) {
|
if (val != NULL && !(flags & SCOUTFS_ITEM_FLAG_DELETION)) {
|
||||||
printer = find_printer(key->sk_zone, key->sk_type);
|
bool suppress = false;
|
||||||
if (printer)
|
|
||||||
printer(key, val, val_len);
|
printer = find_printer(key->sk_zone, key->sk_type, &suppress);
|
||||||
else
|
if (printer) {
|
||||||
|
if (!suppress)
|
||||||
|
printer(key, val, val_len);
|
||||||
|
} else {
|
||||||
printf(" (unknown zone %u type %u)\n",
|
printf(" (unknown zone %u type %u)\n",
|
||||||
key->sk_zone, key->sk_type);
|
key->sk_zone, key->sk_type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1037,12 +1098,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct print_args {
|
static int print_volume(int fd)
|
||||||
char *meta_device;
|
|
||||||
bool skip_likely_huge;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int print_volume(int fd, struct print_args *args)
|
|
||||||
{
|
{
|
||||||
struct scoutfs_super_block *super = NULL;
|
struct scoutfs_super_block *super = NULL;
|
||||||
struct print_recursion_args pa;
|
struct print_recursion_args pa;
|
||||||
@@ -1092,7 +1148,7 @@ static int print_volume(int fd, struct print_args *args)
|
|||||||
ret = err;
|
ret = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args->skip_likely_huge) {
|
if (print_args.walk_allocs) {
|
||||||
for (i = 0; i < array_size(super->meta_alloc); i++) {
|
for (i = 0; i < array_size(super->meta_alloc); i++) {
|
||||||
snprintf(str, sizeof(str), "meta_alloc[%u]", i);
|
snprintf(str, sizeof(str), "meta_alloc[%u]", i);
|
||||||
err = print_btree(fd, super, str, &super->meta_alloc[i].root,
|
err = print_btree(fd, super, str, &super->meta_alloc[i].root,
|
||||||
@@ -1119,18 +1175,21 @@ static int print_volume(int fd, struct print_args *args)
|
|||||||
|
|
||||||
pa.super = super;
|
pa.super = super;
|
||||||
pa.fd = fd;
|
pa.fd = fd;
|
||||||
if (!args->skip_likely_huge) {
|
if (print_args.walk_srch_root) {
|
||||||
err = print_btree_leaf_items(fd, super, &super->srch_root.ref,
|
err = print_btree_leaf_items(fd, super, &super->srch_root.ref,
|
||||||
print_srch_root_files, &pa);
|
print_srch_root_files, &pa);
|
||||||
if (err && !ret)
|
if (err && !ret)
|
||||||
ret = err;
|
ret = err;
|
||||||
}
|
}
|
||||||
err = print_btree_leaf_items(fd, super, &super->logs_root.ref,
|
|
||||||
print_log_trees_roots, &pa);
|
|
||||||
if (err && !ret)
|
|
||||||
ret = err;
|
|
||||||
|
|
||||||
if (!args->skip_likely_huge) {
|
if (print_args.walk_logs_root) {
|
||||||
|
err = print_btree_leaf_items(fd, super, &super->logs_root.ref,
|
||||||
|
print_log_trees_roots, &pa);
|
||||||
|
if (err && !ret)
|
||||||
|
ret = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (print_args.walk_fs_root) {
|
||||||
err = print_btree(fd, super, "fs_root", &super->fs_root,
|
err = print_btree(fd, super, "fs_root", &super->fs_root,
|
||||||
print_fs_item, NULL);
|
print_fs_item, NULL);
|
||||||
if (err && !ret)
|
if (err && !ret)
|
||||||
@@ -1143,16 +1202,16 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_print(struct print_args *args)
|
static int do_print(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = open(args->meta_device, O_RDONLY);
|
fd = open(print_args.meta_device, O_RDONLY);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
||||||
args->meta_device, strerror(errno), errno);
|
print_args.meta_device, strerror(errno), errno);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1160,30 +1219,169 @@ static int do_print(struct print_args *args)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = print_volume(fd, args);
|
ret = print_volume(fd);
|
||||||
out:
|
out:
|
||||||
close(fd);
|
close(fd);
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LOGS_OPT = 0,
|
||||||
|
FS_OPT,
|
||||||
|
SRCH_OPT
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *const root_tokens[] = {
|
||||||
|
[LOGS_OPT] = "logs",
|
||||||
|
[FS_OPT] = "fs",
|
||||||
|
[SRCH_OPT] = "srch",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
INODE_OPT = 0,
|
||||||
|
XATTR_OPT,
|
||||||
|
DIRENT_OPT,
|
||||||
|
SYMLINK_OPT,
|
||||||
|
BACKREF_OPT,
|
||||||
|
EXTENT_OPT
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *const item_tokens[] = {
|
||||||
|
[INODE_OPT] = "inode",
|
||||||
|
[XATTR_OPT] = "xattr",
|
||||||
|
[DIRENT_OPT] = "dirent",
|
||||||
|
[SYMLINK_OPT] = "symlink",
|
||||||
|
[BACKREF_OPT] = "backref",
|
||||||
|
[EXTENT_OPT] = "extent",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void clear_items(void)
|
||||||
|
{
|
||||||
|
print_args.print_inodes = false;
|
||||||
|
print_args.print_xattrs = false;
|
||||||
|
print_args.print_dirents = false;
|
||||||
|
print_args.print_symlinks = false;
|
||||||
|
print_args.print_backrefs = false;
|
||||||
|
print_args.print_extents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_roots(void)
|
||||||
|
{
|
||||||
|
print_args.walk_logs_root = false;
|
||||||
|
print_args.walk_fs_root = false;
|
||||||
|
print_args.walk_srch_root = false;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_opt(int key, char *arg, struct argp_state *state)
|
static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||||
{
|
{
|
||||||
struct print_args *args = state->input;
|
struct print_args *args = state->input;
|
||||||
|
char *subopts;
|
||||||
|
char *value;
|
||||||
|
bool parse_err = false;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'S':
|
case 'S':
|
||||||
args->skip_likely_huge = true;
|
args->skip_likely_huge = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
args->allocs_requested = true;
|
||||||
|
args->walk_allocs = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
/* Specific items being requested- clear them all to start */
|
||||||
|
if (!args->items_requested) {
|
||||||
|
clear_items();
|
||||||
|
if (!args->allocs_requested)
|
||||||
|
args->walk_allocs = false;
|
||||||
|
args->items_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
subopts = arg;
|
||||||
|
while (*subopts != '\0' && !parse_err) {
|
||||||
|
switch (getsubopt(&subopts, item_tokens, &value)) {
|
||||||
|
case INODE_OPT:
|
||||||
|
args->print_inodes = true;
|
||||||
|
break;
|
||||||
|
case XATTR_OPT:
|
||||||
|
args->print_xattrs = true;
|
||||||
|
break;
|
||||||
|
case DIRENT_OPT:
|
||||||
|
args->print_dirents = true;
|
||||||
|
break;
|
||||||
|
case SYMLINK_OPT:
|
||||||
|
args->print_symlinks = true;
|
||||||
|
break;
|
||||||
|
case BACKREF_OPT:
|
||||||
|
args->print_backrefs = true;
|
||||||
|
break;
|
||||||
|
case EXTENT_OPT:
|
||||||
|
args->print_extents = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
argp_usage(state);
|
||||||
|
parse_err = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
/* Specific roots being requested- clear them all to start */
|
||||||
|
if (!args->roots_requested) {
|
||||||
|
clear_roots();
|
||||||
|
if (!args->allocs_requested)
|
||||||
|
args->walk_allocs = false;
|
||||||
|
args->roots_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
subopts = arg;
|
||||||
|
while (*subopts != '\0' && !parse_err) {
|
||||||
|
switch (getsubopt(&subopts, root_tokens, &value)) {
|
||||||
|
case LOGS_OPT:
|
||||||
|
args->walk_logs_root = true;
|
||||||
|
break;
|
||||||
|
case FS_OPT:
|
||||||
|
args->walk_fs_root = true;
|
||||||
|
break;
|
||||||
|
case SRCH_OPT:
|
||||||
|
args->walk_srch_root = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
argp_usage(state);
|
||||||
|
parse_err = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ARGP_KEY_ARG:
|
case ARGP_KEY_ARG:
|
||||||
if (!args->meta_device)
|
if (!args->meta_device)
|
||||||
args->meta_device = strdup_or_error(state, arg);
|
args->meta_device = strdup_or_error(state, arg);
|
||||||
else
|
else
|
||||||
argp_error(state, "more than one argument given");
|
argp_error(state, "more than one argument given");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARGP_KEY_FINI:
|
case ARGP_KEY_FINI:
|
||||||
if (!args->meta_device)
|
if (!args->meta_device)
|
||||||
argp_error(state, "no metadata device argument given");
|
argp_error(state, "no metadata device argument given");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For backwards compatibility, translate -S. Should we warn if
|
||||||
|
* this conflicts with other explicit options?
|
||||||
|
*/
|
||||||
|
if (args->skip_likely_huge) {
|
||||||
|
if (!args->allocs_requested)
|
||||||
|
args->walk_allocs = false;
|
||||||
|
args->walk_fs_root = false;
|
||||||
|
args->walk_srch_root = false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1192,7 +1390,10 @@ static int parse_opt(int key, char *arg, struct argp_state *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct argp_option options[] = {
|
static struct argp_option options[] = {
|
||||||
{ "skip-likely-huge", 'S', NULL, 0, "Skip large structures to minimize output size"},
|
{ "allocs", 'a', NULL, 0, "Print metadata and data alloc lists" },
|
||||||
|
{ "items", 'i', "ITEMS", 0, "Item(s) to print (inode, xattr, dirent, symlink, backref, extent)" },
|
||||||
|
{ "roots", 'r', "ROOTS", 0, "Tree root(s) to walk (logs, srch, fs)" },
|
||||||
|
{ "skip-likely-huge", 'S', NULL, 0, "Skip allocs, srch root and fs root to minimize output size" },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1205,17 +1406,15 @@ static struct argp argp = {
|
|||||||
|
|
||||||
static int print_cmd(int argc, char **argv)
|
static int print_cmd(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct print_args print_args = {NULL};
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = argp_parse(&argp, argc, argv, 0, NULL, &print_args);
|
ret = argp_parse(&argp, argc, argv, 0, NULL, &print_args);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return do_print(&print_args);
|
return do_print();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void __attribute__((constructor)) print_ctor(void)
|
static void __attribute__((constructor)) print_ctor(void)
|
||||||
{
|
{
|
||||||
cmd_register_argp("print", &argp, GROUP_DEBUG, print_cmd);
|
cmd_register_argp("print", &argp, GROUP_DEBUG, print_cmd);
|
||||||
|
|||||||
Reference in New Issue
Block a user