From 4697efb7a01affe650dbc9a4e81a8203d0316d96 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 12 Feb 2023 21:44:33 -0500 Subject: [PATCH] cmd_list_journal: Add filter options Instead of having to use grep, this adds the ability to print out only transactions that update a particular key, or to filter out entirely keys except those updating certain btrees. Signed-off-by: Kent Overstreet --- cmd_list_journal.c | 243 +++++++++++++++++++++++++++++++++------------ tools-util.c | 44 ++++++-- tools-util.h | 3 + 3 files changed, 216 insertions(+), 74 deletions(-) diff --git a/cmd_list_journal.c b/cmd_list_journal.c index e89f7de9..ee467d43 100644 --- a/cmd_list_journal.c +++ b/cmd_list_journal.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -21,10 +22,12 @@ static void list_journal_usage(void) "Usage: bcachefs list_journal [OPTION]... \n" "\n" "Options:\n" - " -a Read entire journal, not just dirty entries\n" - " -n Number of journal entries to print, starting from the most recent\n" - " -v Verbose mode\n" - " -h Display this help and exit\n" + " -a Read entire journal, not just dirty entries\n" + " -n, --nr-entries=nr Number of journal entries to print, starting from the most recent\n" + " -t, --transaction-filter=bbpos Filter transactions not updating \n" + " -k, --key-filter=btree Filter keys not updating btree\n" + " -v, --verbose Verbose mode\n" + " -h, --help Display this help and exit\n" "Report bugs to "); } @@ -39,10 +42,172 @@ static void star_start_of_lines(char *buf) p[1] = '*'; } +static inline bool entry_is_transaction_start(struct jset_entry *entry) +{ + return entry->type == BCH_JSET_ENTRY_log && !entry->level; +} + +typedef DARRAY(struct bbpos) d_bbpos; +typedef DARRAY(enum btree_id) d_btree_id; + +static bool bkey_matches_filter(d_bbpos filter, struct jset_entry *entry, struct bkey_i *k) +{ + struct bbpos *i; + + darray_for_each(filter, i) { + if (i->btree != entry->btree_id) + continue; + + if (!btree_node_type_is_extents(i->btree)) { + if (bkey_eq(i->pos, k->k.p)) + return true; + } else { + if (bkey_ge(i->pos, bkey_start_pos(&k->k)) && + bkey_lt(i->pos, k->k.p)) + return true; + } + } + return false; +} + +static bool should_print_transaction(struct jset_entry *entry, struct jset_entry *end, + d_bbpos filter) +{ + if (!filter.nr) + return true; + + for (entry = vstruct_next(entry); + entry != end && !entry_is_transaction_start(entry); + entry = vstruct_next(entry)) { + if (entry->type == BCH_JSET_ENTRY_btree_root || + entry->type == BCH_JSET_ENTRY_btree_keys) { + struct bkey_i *k; + + vstruct_for_each(entry, k) + if (bkey_matches_filter(filter, entry, k)) + return true; + } + } + + return false; +} + +static bool should_print_entry(struct jset_entry *entry, d_btree_id filter) +{ + struct bkey_i *k; + enum btree_id *id; + + if (!filter.nr) + return true; + + if (entry->type != BCH_JSET_ENTRY_btree_root && + entry->type != BCH_JSET_ENTRY_btree_keys && + entry->type != BCH_JSET_ENTRY_overwrite) + return true; + + vstruct_for_each(entry, k) + darray_for_each(filter, id) + if (entry->btree_id == *id) + return true; + + return false; +} + +static void journal_entries_print(struct bch_fs *c, unsigned nr_entries, + d_bbpos transaction_filter, + d_btree_id key_filter) +{ + struct journal_replay *p, **_p; + struct genradix_iter iter; + struct printbuf buf = PRINTBUF; + + genradix_for_each(&c->journal_entries, iter, _p) { + p = *_p; + if (!p) + continue; + + if (le64_to_cpu(p->j.seq) + nr_entries < atomic64_read(&c->journal.seq)) + continue; + + bool blacklisted = + bch2_journal_seq_is_blacklisted(c, + le64_to_cpu(p->j.seq), false); + + if (!transaction_filter.nr) { + if (blacklisted) + printf("blacklisted "); + + printf("journal entry %llu\n", le64_to_cpu(p->j.seq)); + + printbuf_reset(&buf); + + prt_printf(&buf, + " version %u\n" + " last seq %llu\n" + " flush %u\n" + " written at ", + le32_to_cpu(p->j.version), + le64_to_cpu(p->j.last_seq), + !JSET_NO_FLUSH(&p->j)); + bch2_journal_ptrs_to_text(&buf, c, p); + + if (blacklisted) + star_start_of_lines(buf.buf); + printf("%s\n", buf.buf); + printbuf_reset(&buf); + } + + struct jset_entry *entry = p->j.start; + struct jset_entry *end = vstruct_last(&p->j); + while (entry != end) { + + /* + * log entries denote the start of a new transaction + * commit: + */ + if (entry_is_transaction_start(entry)) { + if (!should_print_transaction(entry, end, transaction_filter)) { + do { + entry = vstruct_next(entry); + } while (entry != end && !entry_is_transaction_start(entry)); + + continue; + } + + prt_newline(&buf); + } + + if (should_print_entry(entry, key_filter)) { + printbuf_indent_add(&buf, 4); + bch2_journal_entry_to_text(&buf, c, entry); + + if (blacklisted) + star_start_of_lines(buf.buf); + printf("%s\n", buf.buf); + printbuf_reset(&buf); + } + + entry = vstruct_next(entry); + } + } + + printbuf_exit(&buf); +} + int cmd_list_journal(int argc, char *argv[]) { + static const struct option longopts[] = { + { "nr-entries", required_argument, NULL, 'n' }, + { "transaction-filter", required_argument, NULL, 't' }, + { "key-filter", required_argument, NULL, 'k' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { NULL } + }; struct bch_opts opts = bch2_opts_empty(); u32 nr_entries = U32_MAX; + d_bbpos transaction_filter = { 0 }; + d_btree_id key_filter = { 0 }; int opt; opt_set(opts, nochanges, true); @@ -53,15 +218,23 @@ int cmd_list_journal(int argc, char *argv[]) opt_set(opts, keep_journal, true); opt_set(opts, read_journal_only,true); - while ((opt = getopt(argc, argv, "an:vh")) != -1) + while ((opt = getopt_long(argc, argv, "an:t:k:vh", + longopts, NULL)) != -1) switch (opt) { case 'a': opt_set(opts, read_entire_journal, true); break; case 'n': - nr_entries = kstrtouint(optarg, 10, &nr_entries); + if (kstrtouint(optarg, 10, &nr_entries)) + die("error parsing nr_entries"); opt_set(opts, read_entire_journal, true); break; + case 't': + darray_push(&transaction_filter, bbpos_parse(optarg)); + break; + case 'k': + darray_push(&key_filter, read_string_list_or_die(optarg, bch2_btree_ids, "btree id")); + break; case 'v': opt_set(opts, verbose, true); break; @@ -78,63 +251,7 @@ int cmd_list_journal(int argc, char *argv[]) if (IS_ERR(c)) die("error opening %s: %s", argv[0], bch2_err_str(PTR_ERR(c))); - struct journal_replay *p, **_p; - struct genradix_iter iter; - struct jset_entry *entry; - struct printbuf buf = PRINTBUF; - - genradix_for_each(&c->journal_entries, iter, _p) { - p = *_p; - if (!p) - continue; - - if (le64_to_cpu(p->j.seq) + nr_entries < atomic64_read(&c->journal.seq)) - continue; - - bool blacklisted = - bch2_journal_seq_is_blacklisted(c, - le64_to_cpu(p->j.seq), false); - - if (blacklisted) - printf("blacklisted "); - - printf("journal entry %llu\n", le64_to_cpu(p->j.seq)); - - printbuf_reset(&buf); - - prt_printf(&buf, - " version %u\n" - " last seq %llu\n" - " flush %u\n" - " written at ", - le32_to_cpu(p->j.version), - le64_to_cpu(p->j.last_seq), - !JSET_NO_FLUSH(&p->j)); - bch2_journal_ptrs_to_text(&buf, c, p); - - if (blacklisted) - star_start_of_lines(buf.buf); - printf("%s\n", buf.buf); - - vstruct_for_each(&p->j, entry) { - printbuf_reset(&buf); - - /* - * log entries denote the start of a new transaction - * commit: - */ - if (entry->type == BCH_JSET_ENTRY_log && !entry->level) - prt_newline(&buf); - printbuf_indent_add(&buf, 4); - bch2_journal_entry_to_text(&buf, c, entry); - - if (blacklisted) - star_start_of_lines(buf.buf); - printf("%s\n", buf.buf); - } - } - - printbuf_exit(&buf); + journal_entries_print(c, nr_entries, transaction_filter, key_filter); bch2_fs_stop(c); return 0; } diff --git a/tools-util.c b/tools-util.c index f29d2026..65835edf 100644 --- a/tools-util.c +++ b/tools-util.c @@ -607,19 +607,41 @@ int dev_mounted(char *dev) struct bpos bpos_parse(char *buf) { - char *s = buf, *field; + char *orig = strdup(buf); + char *s = buf; + + char *inode_s = strsep(&s, ":"); + char *offset_s = strsep(&s, ":"); + char *snapshot_s = strsep(&s, ":"); + + if (!inode_s || !offset_s || s) + die("invalid bpos %s", orig); + free(orig); + u64 inode_v = 0, offset_v = 0; + u32 snapshot_v = 0; + if (kstrtoull(inode_s, 10, &inode_v)) + die("invalid bpos.inode %s", inode_s); - if (!(field = strsep(&s, ":")) || - kstrtoull(field, 10, &inode_v)) - die("invalid bpos %s", buf); + if (kstrtoull(offset_s, 10, &offset_v)) + die("invalid bpos.offset %s", offset_s); - if ((field = strsep(&s, ":")) && - kstrtoull(field, 10, &offset_v)) - die("invalid bpos %s", buf); + if (snapshot_s && + kstrtouint(snapshot_s, 10, &snapshot_v)) + die("invalid bpos.snapshot %s", snapshot_s); - if (s) - die("invalid bpos %s", buf); - - return (struct bpos) { .inode = inode_v, .offset = offset_v }; + return (struct bpos) { .inode = inode_v, .offset = offset_v, .snapshot = snapshot_v }; +} + +struct bbpos bbpos_parse(char *buf) +{ + char *s = buf, *field; + struct bbpos ret; + + if (!(field = strsep(&s, ":"))) + die("invalid bbpos %s", buf); + + ret.btree = read_string_list_or_die(field, bch2_btree_ids, "btree id"); + ret.pos = bpos_parse(s); + return ret; } diff --git a/tools-util.h b/tools-util.h index d1122f5d..aa7c0270 100644 --- a/tools-util.h +++ b/tools-util.h @@ -18,6 +18,8 @@ #include #include #include +#include "libbcachefs/bcachefs.h" +#include "libbcachefs/bbpos.h" #include "libbcachefs/darray.h" #define noreturn __attribute__((noreturn)) @@ -159,5 +161,6 @@ do { \ }) struct bpos bpos_parse(char *); +struct bbpos bbpos_parse(char *); #endif /* _TOOLS_UTIL_H */