From: Thomas Bertschinger <tahbertschinger@gmail.com>
To: kent.overstreet@linux.dev, linux-bcachefs@vger.kernel.org,
bfoster@redhat.com
Cc: Thomas Bertschinger <tahbertschinger@gmail.com>
Subject: [PATCH TOOLS v2 4/5] introduce "debug" command and "dump" subcommand
Date: Sun, 14 Jul 2024 14:02:25 -0600 [thread overview]
Message-ID: <20240714200226.746471-5-tahbertschinger@gmail.com> (raw)
In-Reply-To: <20240714200226.746471-1-tahbertschinger@gmail.com>
This introduces a new command, "debug", that is used for directly
manipulating bkeys in the underlying btrees.
It has a subcommand, "dump", which takes a btree and bpos and
prints the data from that bkey.
For example:
$ bcachefs debug ~/test-img -c "dump inodes 0:4096:U32_MAX"
u64s 17 type inode_v3 0:4096:U32_MAX len 0 ver 0: mode=40755
flags= (16300000)
journal_seq=9
...
Signed-off-by: Thomas Bertschinger <tahbertschinger@gmail.com>
---
c_src/bcachefs.c | 4 +-
c_src/cmd_debug.c | 38 ++++++++++++
c_src/cmds.h | 2 +
src/bcachefs.rs | 1 +
src/commands/debug/mod.rs | 108 +++++++++++++++++++++++++++++++++++
src/commands/debug/parser.rs | 57 ++++++++++++++++++
src/commands/mod.rs | 1 +
7 files changed, 210 insertions(+), 1 deletion(-)
create mode 100644 c_src/cmd_debug.c
create mode 100644 src/commands/debug/parser.rs
diff --git a/c_src/bcachefs.c b/c_src/bcachefs.c
index c5b61097..311de278 100644
--- a/c_src/bcachefs.c
+++ b/c_src/bcachefs.c
@@ -86,6 +86,7 @@ void bcachefs_usage(void)
"\n"
"Debug:\n"
"These commands work on offline, unmounted filesystems\n"
+ " debug Operate directly on the underlying btrees of a filesystem\n"
" dump Dump filesystem metadata to a qcow2 image\n"
" list List filesystem metadata in textual form\n"
" list_journal List contents of journal\n"
@@ -94,7 +95,8 @@ void bcachefs_usage(void)
" fusemount Mount a filesystem via FUSE\n"
"\n"
"Miscellaneous:\n"
- " completions Generate shell completions\n"
+ " completions Generate shell completions\n"
+ " list_bkeys List all bkey types known to the current bcachefs version\n"
" version Display the version of the invoked bcachefs tool\n");
}
diff --git a/c_src/cmd_debug.c b/c_src/cmd_debug.c
new file mode 100644
index 00000000..73ba3995
--- /dev/null
+++ b/c_src/cmd_debug.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+
+#include "libbcachefs/bkey_types.h"
+#include "libbcachefs/btree_update.h"
+#include "libbcachefs/printbuf.h"
+
+#include "cmds.h"
+
+int cmd_dump_bkey(struct bch_fs *c, enum btree_id id, struct bpos pos)
+{
+ struct btree_trans *trans = bch2_trans_get(c);
+ struct btree_iter iter = { NULL };
+ struct printbuf buf = PRINTBUF;
+ int ret = 0;
+
+ bch2_trans_iter_init(trans, &iter, id, pos, BTREE_ITER_all_snapshots);
+
+ struct bkey_s_c k = bch2_btree_iter_peek(&iter);
+ if ((ret = bkey_err(k))) {
+ fprintf(stderr, "bch2_btree_iter_peek() failed: %s\n", bch2_err_str(ret));
+ goto out;
+ }
+ if (!k.k || !bpos_eq(pos, k.k->p)) {
+ bch2_bpos_to_text(&buf, pos);
+ printf("no key at pos %s\n", buf.buf);
+ ret = 1;
+ goto out;
+ }
+
+ bch2_bkey_val_to_text(&buf, c, k);
+ printf("%s\n", buf.buf);
+
+out:
+ bch2_trans_iter_exit(trans, &iter);
+ bch2_trans_put(trans);
+
+ return ret;
+}
diff --git a/c_src/cmds.h b/c_src/cmds.h
index 64267dc4..d55a1440 100644
--- a/c_src/cmds.h
+++ b/c_src/cmds.h
@@ -54,6 +54,8 @@ int cmd_subvolume_snapshot(int argc, char *argv[]);
int cmd_fusemount(int argc, char *argv[]);
+int cmd_dump_bkey(struct bch_fs *, enum btree_id, struct bpos);
+
void bcachefs_usage(void);
int device_cmds(int argc, char *argv[]);
int fs_cmds(int argc, char *argv[]);
diff --git a/src/bcachefs.rs b/src/bcachefs.rs
index 4b8cef49..0087334d 100644
--- a/src/bcachefs.rs
+++ b/src/bcachefs.rs
@@ -102,6 +102,7 @@ fn main() -> ExitCode {
};
match cmd {
+ "debug" => commands::debug(args[1..].to_vec()).report(),
"completions" => {
commands::completions(args[1..].to_vec());
ExitCode::SUCCESS
diff --git a/src/commands/debug/mod.rs b/src/commands/debug/mod.rs
index 30ffd16b..31b23c7e 100644
--- a/src/commands/debug/mod.rs
+++ b/src/commands/debug/mod.rs
@@ -1,7 +1,115 @@
+use clap::Parser;
+use std::io::{BufRead, Write};
+
+use bch_bindgen::bcachefs;
+use bch_bindgen::c;
+use bch_bindgen::fs::Fs;
+
mod bkey_types;
+mod parser;
+
+use bch_bindgen::c::bpos;
use anyhow::Result;
+/// Debug a bcachefs filesystem.
+#[derive(Parser, Debug)]
+pub struct Cli {
+ #[arg(required(true))]
+ devices: Vec<std::path::PathBuf>,
+
+ #[arg(short, long)]
+ command: Option<String>,
+}
+
+#[derive(Debug)]
+enum DebugCommand {
+ Dump(DumpCommand),
+}
+
+#[derive(Debug)]
+struct DumpCommand {
+ btree: String,
+ bpos: bpos,
+}
+
+fn dump(fs: &Fs, cmd: DumpCommand) {
+ let id: bch_bindgen::c::btree_id = match cmd.btree.parse() {
+ Ok(b) => b,
+ Err(_) => {
+ eprintln!("unknown btree '{}'", cmd.btree);
+ return;
+ }
+ };
+
+ unsafe {
+ c::cmd_dump_bkey(fs.raw, id, cmd.bpos);
+ }
+}
+
+fn usage() {
+ println!("Usage:");
+ println!(" dump <btree_type> <bpos>");
+}
+
+fn do_command(fs: &Fs, cmd: &str) -> i32 {
+ match parser::parse_command(cmd) {
+ Ok(cmd) => {
+ match cmd {
+ DebugCommand::Dump(cmd) => dump(fs, cmd),
+ };
+
+ 0
+ }
+ Err(e) => {
+ println!("{e}");
+ usage();
+
+ 1
+ }
+ }
+}
+
+pub fn debug(argv: Vec<String>) -> Result<()> {
+ fn prompt() {
+ print!("bcachefs> ");
+ std::io::stdout().flush().unwrap();
+ }
+
+ let opt = Cli::parse_from(argv);
+ let fs_opts: bcachefs::bch_opts = Default::default();
+
+ if let Some(cmd) = opt.command {
+ return match parser::parse_command(&cmd) {
+ Ok(cmd) => {
+ let fs = Fs::open(&opt.devices, fs_opts)?;
+ match cmd {
+ DebugCommand::Dump(cmd) => dump(&fs, cmd),
+ }
+
+ Ok(())
+ }
+ Err(e) => {
+ println!("{e}");
+ usage();
+
+ Ok(())
+ }
+ };
+ }
+
+ let fs = Fs::open(&opt.devices, fs_opts)?;
+
+ prompt();
+ let stdin = std::io::stdin();
+ for line in stdin.lock().lines() {
+ do_command(&fs, &line.unwrap());
+ prompt();
+ }
+
+ Ok(())
+}
+
pub fn list_bkeys() -> Result<()> {
print!("{}", bkey_types::get_bkey_type_info()?);
diff --git a/src/commands/debug/parser.rs b/src/commands/debug/parser.rs
new file mode 100644
index 00000000..fa036447
--- /dev/null
+++ b/src/commands/debug/parser.rs
@@ -0,0 +1,57 @@
+use nom::branch::alt;
+use nom::bytes::complete::tag;
+use nom::character::complete::{alpha1, char, space1, u32, u64};
+use nom::combinator::{all_consuming, value};
+use nom::sequence::tuple;
+use nom::IResult;
+
+use bch_bindgen::c::bpos;
+
+use crate::commands::debug::{DebugCommand, DumpCommand};
+
+fn parse_bpos(input: &str) -> IResult<&str, bpos> {
+ let (input, (inode, _, offset, _, snapshot)) = tuple((
+ u64,
+ char(':'),
+ u64,
+ char(':'),
+ alt((u32, value(u32::MAX, tag("U32_MAX")))),
+ ))(input)?;
+
+ Ok((
+ input,
+ bpos {
+ inode,
+ offset,
+ snapshot,
+ },
+ ))
+}
+
+fn parse_dump_cmd(input: &str) -> IResult<&str, DebugCommand> {
+ let (input, (_, btree, _, bpos)) =
+ all_consuming(tuple((space1, alpha1, space1, parse_bpos)))(input)?;
+
+ Ok((
+ input,
+ DebugCommand::Dump(DumpCommand {
+ btree: btree.to_string(),
+ bpos,
+ }),
+ ))
+}
+
+fn parse_command_inner(input: &str) -> IResult<&str, DebugCommand> {
+ let (input, _) = tag("dump")(input)?;
+
+ parse_dump_cmd(input)
+}
+
+/// Given an input string, tries to parse it into a valid
+/// command to the debug tool.
+pub fn parse_command(input: &str) -> anyhow::Result<DebugCommand> {
+ match parse_command_inner(input) {
+ Ok((_, c)) => Ok(c),
+ Err(e) => Err(anyhow::anyhow!("{e}")),
+ }
+}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 9365f981..425e0849 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -7,6 +7,7 @@ pub mod mount;
pub mod subvolume;
pub use completions::completions;
+pub use debug::debug;
pub use debug::list_bkeys;
pub use list::list;
pub use mount::mount;
--
2.45.2
next prev parent reply other threads:[~2024-07-14 20:03 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-14 20:02 [PATCH TOOLS v2 0/5] new debug command Thomas Bertschinger
2024-07-14 20:02 ` [PATCH TOOLS v2 1/5] include debuginfo in bcachefs binary by default Thomas Bertschinger
2024-07-14 20:20 ` Kent Overstreet
2024-07-14 20:02 ` [PATCH TOOLS v2 2/5] update minimum Rust version to 1.74.0 Thomas Bertschinger
2024-07-14 20:02 ` [PATCH TOOLS v2 3/5] introduce "list_bkeys" command Thomas Bertschinger
2024-07-14 20:02 ` Thomas Bertschinger [this message]
2024-07-14 20:19 ` [PATCH TOOLS v2 4/5] introduce "debug" command and "dump" subcommand Kent Overstreet
2024-07-14 20:02 ` [PATCH TOOLS v2 5/5] introduce new "debug update" command Thomas Bertschinger
2024-07-14 20:21 ` [PATCH TOOLS v2 0/5] new debug command Kent Overstreet
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240714200226.746471-5-tahbertschinger@gmail.com \
--to=tahbertschinger@gmail.com \
--cc=bfoster@redhat.com \
--cc=kent.overstreet@linux.dev \
--cc=linux-bcachefs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox