Skip to content

Commit

Permalink
add "list_bkeys" command; also handle members of named embedded structs
Browse files Browse the repository at this point in the history
previously only anonymous embedded structs worked right

also various refactorings

Signed-off-by: Thomas Bertschinger <[email protected]>
  • Loading branch information
bertschingert committed May 2, 2024
1 parent d77e946 commit deaf108
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 40 deletions.
4 changes: 3 additions & 1 deletion c_src/bcachefs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -94,7 +95,8 @@ void bcachefs_usage(void)
" fusemount Mount a filesystem via FUSE\n"
"\n"
"Miscellaneous:\n"
" completions Generate shell completions\n"
" list_bkeys List all bkey types known to the current bcachefs version\n"
" completions Generate shell completions\n"
" version Display the version of the invoked bcachefs tool\n");
}

Expand Down
3 changes: 2 additions & 1 deletion src/bcachefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,12 @@ fn main() {
};

let ret = match cmd {
"debug" => commands::debug(args[1..].to_vec()),
"completions" => commands::completions(args[1..].to_vec()),
"list" => commands::list(args[1..].to_vec()),
"list_bkeys" => commands::list_bkeys(),
"mount" => commands::mount(args, symlink_cmd),
"subvolume" => commands::subvolume(args[1..].to_vec()),
"debug" => commands::debug(args[1..].to_vec()),
_ => handle_c_command(args, symlink_cmd),
};

Expand Down
123 changes: 85 additions & 38 deletions src/commands/debug/bkey_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ impl BkeyTypes {
}
}

impl std::fmt::Display for BkeyTypes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for bkey in self.0.iter() {
for memb in bkey.members.iter() {
writeln!(
f,
"{} {} {} {}",
bkey.name, memb.name, memb.size, memb.offset
)?;
}
writeln!(f)?;
}
Ok(())
}
}

#[derive(Debug)]
pub struct BchStruct {
name: String,
Expand Down Expand Up @@ -167,6 +183,31 @@ enum CompType {
Struct,
}

/// Used to keep track of info needed for structs that contain
/// other compound types.
struct ParentInfo<'a> {
ty: CompType,
starting_offset: u64,
member_prefix: &'a str,
}

fn entry_name(
dwarf: &gimli::Dwarf<Reader>,
unit: &gimli::Unit<Reader>,
entry: &gimli::DebuggingInformationEntry<Reader>,
) -> Option<String> {
entry.attr(gimli::DW_AT_name).ok()?.and_then(|name| {
Some(
dwarf
.attr_string(unit, name.value())
.ok()?
.to_string_lossy()
.ok()?
.into_owned(),
)
})
}

fn process_tree(
dwarf: &gimli::Dwarf<Reader>,
unit: &gimli::Unit<Reader>,
Expand All @@ -176,15 +217,18 @@ fn process_tree(
) -> gimli::Result<()> {
let entry = node.entry();
if entry.tag() == gimli::DW_TAG_structure_type {
if let Some(name) = entry.attr(gimli::DW_AT_name)? {
if let Ok(name) = dwarf.attr_string(unit, name.value()) {
let name = name.to_string_lossy()?.into_owned();
if bkey_types.remove(&name.clone()) {
let mut members: Vec<BchMember> = Vec::new();
process_compound_type(dwarf, unit, node, &mut members, 0, CompType::Struct)?;
struct_list.0.push(BchStruct { name, members });
}
}
let name = entry_name(dwarf, unit, entry);
let Some(name) = name else { return Ok(()); };

if bkey_types.remove(&name) {
let mut members: Vec<BchMember> = Vec::new();
let parent_info = ParentInfo {
ty: CompType::Struct,
starting_offset: 0,
member_prefix: "",
};
process_compound_type(dwarf, unit, node, &mut members, &parent_info)?;
struct_list.0.push(BchStruct { name, members });
}
} else {
let mut children = node.children();
Expand All @@ -200,12 +244,11 @@ fn process_compound_type(
unit: &gimli::Unit<Reader>,
node: gimli::EntriesTreeNode<Reader>,
members: &mut Vec<BchMember>,
starting_offset: u64,
comp: CompType,
parent: &ParentInfo,
) -> gimli::Result<()> {
let mut children = node.children();
while let Some(child) = children.next()? {
process_comp_member(dwarf, unit, child, members, starting_offset, comp)?;
process_comp_member(dwarf, unit, child, members, &parent)?;
}

Ok(())
Expand Down Expand Up @@ -239,12 +282,11 @@ fn process_comp_member(
unit: &gimli::Unit<Reader>,
node: gimli::EntriesTreeNode<Reader>,
members: &mut Vec<BchMember>,
starting_offset: u64,
comp: CompType,
parent: &ParentInfo,
) -> gimli::Result<()> {
let entry = node.entry().clone();

let offset = match comp {
let offset = match parent.ty {
CompType::Union => Some(0),
CompType::Struct => entry
.attr(gimli::DW_AT_data_member_location)?
Expand All @@ -254,23 +296,38 @@ fn process_comp_member(
return Ok(());
};

let name = entry_name(dwarf, unit, &entry);

if let Some((ref_type, comp)) = get_comp_ref(unit, &entry) {
let prefix = if let Some(ref name) = name {
let mut prefix = name.clone();
prefix.push('.');
prefix
} else {
String::from("")
};
let parent = ParentInfo {
ty: comp,
starting_offset: offset,
member_prefix: &prefix,
};
let mut tree = unit.entries_tree(Some(ref_type))?;
process_compound_type(dwarf, unit, tree.root()?, members, offset, comp)?;
process_compound_type(dwarf, unit, tree.root()?, members, &parent)?;

return Ok(());
};

let Some(size) = get_size(unit, &entry) else {
return Ok(());
};

let name = entry.attr(gimli::DW_AT_name)?;
let Some(name) = name else { return Ok(()) };
let name = dwarf.attr_string(unit, name.value())?;
let name = name.to_string_lossy()?.into_owned();
let mut name_with_prefix = String::from(parent.member_prefix);
name_with_prefix.push_str(&name);

members.push(BchMember {
name,
offset: offset + starting_offset,
name: name_with_prefix,
offset: offset + parent.starting_offset,
size,
});

Expand All @@ -285,13 +342,12 @@ fn get_size(
return size.udata_value();
}

if let Some(ref_type) = entry.attr(gimli::DW_AT_type).ok()? {
if let gimli::AttributeValue::UnitRef(offset) = ref_type.value() {
let mut type_entry = unit.entries_at_offset(offset).ok()?;
type_entry.next_entry().ok()?;
if let Some(t) = type_entry.current() {
return get_size(unit, t);
}
let ref_type = entry.attr(gimli::DW_AT_type).ok()??;
if let gimli::AttributeValue::UnitRef(offset) = ref_type.value() {
let mut type_entry = unit.entries_at_offset(offset).ok()?;
type_entry.next_entry().ok()?;
if let Some(t) = type_entry.current() {
return get_size(unit, t);
}
}

Expand All @@ -301,21 +357,12 @@ fn get_size(
/// Return a list of the known bkey types.
pub fn get_bkey_type_info() -> BkeyTypes {
let path = fs::read_link("/proc/self/exe").unwrap();

let file = fs::File::open(path).unwrap();
let mmap = unsafe { memmap2::Mmap::map(&file).unwrap() };
let object = object::File::parse(&*mmap).unwrap();

let mut struct_list = BkeyTypes::new();
process_file(&object, &mut struct_list).unwrap();

/*
for s in struct_list.0.iter() {
for m in s.members.iter() {
println!("{} {} {} {}", s.name, m.name, m.offset, m.size);
}
println!("");
}
*/

struct_list
}
6 changes: 6 additions & 0 deletions src/commands/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,9 @@ pub fn debug(argv: Vec<String>) -> i32 {

0
}

pub fn list_bkeys() -> i32 {
print!("{}", bkey_types::get_bkey_type_info());

0
}
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub use list::list;
pub use completions::completions;
pub use subvolume::subvolume;
pub use debug::debug;
pub use debug::list_bkeys;

#[derive(clap::Parser, Debug)]
#[command(name = "bcachefs")]
Expand Down

0 comments on commit deaf108

Please sign in to comment.