Skip to content

Commit d8e2cdc

Browse files
committed
add "list_bkeys" command; also handle members of named embedded structs
previously only anonymous embedded structs worked right also various refactorings Signed-off-by: Thomas Bertschinger <[email protected]>
1 parent d77e946 commit d8e2cdc

File tree

6 files changed

+107
-79
lines changed

6 files changed

+107
-79
lines changed

c_src/bcachefs.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ void bcachefs_usage(void)
8686
"\n"
8787
"Debug:\n"
8888
"These commands work on offline, unmounted filesystems\n"
89+
" debug Operate directly on the underlying btrees of a filesystem\n"
8990
" dump Dump filesystem metadata to a qcow2 image\n"
9091
" list List filesystem metadata in textual form\n"
9192
" list_journal List contents of journal\n"
@@ -94,7 +95,8 @@ void bcachefs_usage(void)
9495
" fusemount Mount a filesystem via FUSE\n"
9596
"\n"
9697
"Miscellaneous:\n"
97-
" completions Generate shell completions\n"
98+
" list_bkeys List all bkey types known to the current bcachefs version\n"
99+
" completions Generate shell completions\n"
98100
" version Display the version of the invoked bcachefs tool\n");
99101
}
100102

src/bcachefs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,12 @@ fn main() {
103103
};
104104

105105
let ret = match cmd {
106+
"debug" => commands::debug(args[1..].to_vec()),
106107
"completions" => commands::completions(args[1..].to_vec()),
107108
"list" => commands::list(args[1..].to_vec()),
109+
"list_bkeys" => commands::list_bkeys(),
108110
"mount" => commands::mount(args, symlink_cmd),
109111
"subvolume" => commands::subvolume(args[1..].to_vec()),
110-
"debug" => commands::debug(args[1..].to_vec()),
111112
_ => handle_c_command(args, symlink_cmd),
112113
};
113114

src/commands/debug/bkey_types.rs

Lines changed: 88 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
//!
33
//! This is adapted from `gimli/crates/examples/src/bin/simple.rs`.
44
5-
use gimli::Reader as _;
65
use object::{Object, ObjectSection};
76
use std::collections::HashSet;
87
use std::{borrow, error, fs};
@@ -29,6 +28,22 @@ impl BkeyTypes {
2928
}
3029
}
3130

31+
impl std::fmt::Display for BkeyTypes {
32+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33+
for bkey in self.0.iter() {
34+
for memb in bkey.members.iter() {
35+
writeln!(
36+
f,
37+
"{} {} {} {}",
38+
bkey.name, memb.name, memb.size, memb.offset
39+
)?;
40+
}
41+
writeln!(f)?;
42+
}
43+
Ok(())
44+
}
45+
}
46+
3247
#[derive(Debug)]
3348
pub struct BchStruct {
3449
name: String,
@@ -53,33 +68,13 @@ pub struct BchMember {
5368
offset: u64,
5469
}
5570

56-
// This is a simple wrapper around `object::read::RelocationMap` that implements
57-
// `gimli::read::Relocate` for use with `gimli::RelocateReader`.
58-
// You only need this if you are parsing relocatable object files.
59-
#[derive(Debug, Default)]
60-
struct RelocationMap(object::read::RelocationMap);
61-
62-
impl<'a> gimli::read::Relocate for &'a RelocationMap {
63-
fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result<u64> {
64-
Ok(self.0.relocate(offset as u64, value))
65-
}
66-
67-
fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result<usize> {
68-
<usize as gimli::ReaderOffset>::from_u64(self.0.relocate(offset as u64, value as u64))
69-
}
70-
}
71-
7271
// The section data that will be stored in `DwarfSections` and `DwarfPackageSections`.
7372
#[derive(Default)]
7473
struct Section<'data> {
7574
data: borrow::Cow<'data, [u8]>,
76-
relocations: RelocationMap,
7775
}
7876

79-
// The reader type that will be stored in `Dwarf` and `DwarfPackage`.
80-
// If you don't need relocations, you can use `gimli::EndianSlice` directly.
81-
type Reader<'data> =
82-
gimli::RelocateReader<gimli::EndianSlice<'data, gimli::RunTimeEndian>, &'data RelocationMap>;
77+
type Reader<'data> = gimli::EndianSlice<'data, gimli::RunTimeEndian>;
8378

8479
fn process_file(
8580
object: &object::File,
@@ -99,27 +94,17 @@ fn process_file(
9994
Ok(match object.section_by_name(name) {
10095
Some(section) => Section {
10196
data: section.uncompressed_data()?,
102-
relocations: section.relocation_map().map(RelocationMap)?,
10397
},
10498
None => Default::default(),
10599
})
106100
}
107101

108-
// Borrow a `Section` to create a `Reader`.
109-
fn borrow_section<'data>(
110-
section: &'data Section<'data>,
111-
endian: gimli::RunTimeEndian,
112-
) -> Reader<'data> {
113-
let slice = gimli::EndianSlice::new(borrow::Cow::as_ref(&section.data), endian);
114-
gimli::RelocateReader::new(slice, &section.relocations)
115-
}
116-
117-
// Load all of the sections.
118102
let dwarf_sections = gimli::DwarfSections::load(|id| load_section(object, id.name()))?;
119103

120104
// Create `Reader`s for all of the sections and do preliminary parsing.
121105
// Alternatively, we could have used `Dwarf::load` with an owned type such as `EndianRcSlice`.
122-
let dwarf = dwarf_sections.borrow(|section| borrow_section(section, endian));
106+
let dwarf = dwarf_sections
107+
.borrow(|section| gimli::EndianSlice::new(borrow::Cow::as_ref(&section.data), endian));
123108

124109
let mut bkey_types = HashSet::new();
125110
load_bkey_types(&mut bkey_types);
@@ -167,6 +152,30 @@ enum CompType {
167152
Struct,
168153
}
169154

155+
/// Used to keep track of info needed for structs that contain
156+
/// other compound types.
157+
struct ParentInfo<'a> {
158+
ty: CompType,
159+
starting_offset: u64,
160+
member_prefix: &'a str,
161+
}
162+
163+
fn entry_name(
164+
dwarf: &gimli::Dwarf<Reader>,
165+
unit: &gimli::Unit<Reader>,
166+
entry: &gimli::DebuggingInformationEntry<Reader>,
167+
) -> Option<String> {
168+
entry.attr(gimli::DW_AT_name).ok()?.and_then(|name| {
169+
Some(
170+
dwarf
171+
.attr_string(unit, name.value())
172+
.ok()?
173+
.to_string_lossy()
174+
.into_owned(),
175+
)
176+
})
177+
}
178+
170179
fn process_tree(
171180
dwarf: &gimli::Dwarf<Reader>,
172181
unit: &gimli::Unit<Reader>,
@@ -176,15 +185,18 @@ fn process_tree(
176185
) -> gimli::Result<()> {
177186
let entry = node.entry();
178187
if entry.tag() == gimli::DW_TAG_structure_type {
179-
if let Some(name) = entry.attr(gimli::DW_AT_name)? {
180-
if let Ok(name) = dwarf.attr_string(unit, name.value()) {
181-
let name = name.to_string_lossy()?.into_owned();
182-
if bkey_types.remove(&name.clone()) {
183-
let mut members: Vec<BchMember> = Vec::new();
184-
process_compound_type(dwarf, unit, node, &mut members, 0, CompType::Struct)?;
185-
struct_list.0.push(BchStruct { name, members });
186-
}
187-
}
188+
let name = entry_name(dwarf, unit, entry);
189+
let Some(name) = name else { return Ok(()); };
190+
191+
if bkey_types.remove(&name) {
192+
let mut members: Vec<BchMember> = Vec::new();
193+
let parent_info = ParentInfo {
194+
ty: CompType::Struct,
195+
starting_offset: 0,
196+
member_prefix: "",
197+
};
198+
process_compound_type(dwarf, unit, node, &mut members, &parent_info)?;
199+
struct_list.0.push(BchStruct { name, members });
188200
}
189201
} else {
190202
let mut children = node.children();
@@ -200,12 +212,11 @@ fn process_compound_type(
200212
unit: &gimli::Unit<Reader>,
201213
node: gimli::EntriesTreeNode<Reader>,
202214
members: &mut Vec<BchMember>,
203-
starting_offset: u64,
204-
comp: CompType,
215+
parent: &ParentInfo,
205216
) -> gimli::Result<()> {
206217
let mut children = node.children();
207218
while let Some(child) = children.next()? {
208-
process_comp_member(dwarf, unit, child, members, starting_offset, comp)?;
219+
process_comp_member(dwarf, unit, child, members, parent)?;
209220
}
210221

211222
Ok(())
@@ -239,38 +250,51 @@ fn process_comp_member(
239250
unit: &gimli::Unit<Reader>,
240251
node: gimli::EntriesTreeNode<Reader>,
241252
members: &mut Vec<BchMember>,
242-
starting_offset: u64,
243-
comp: CompType,
253+
parent: &ParentInfo,
244254
) -> gimli::Result<()> {
245255
let entry = node.entry().clone();
246256

247-
let offset = match comp {
257+
let Some(offset) = (match parent.ty {
248258
CompType::Union => Some(0),
249259
CompType::Struct => entry
250260
.attr(gimli::DW_AT_data_member_location)?
251261
.and_then(|offset| offset.value().udata_value()),
252-
};
253-
let Some(offset) = offset else {
262+
}) else {
254263
return Ok(());
255264
};
256265

266+
let name = entry_name(dwarf, unit, &entry);
267+
257268
if let Some((ref_type, comp)) = get_comp_ref(unit, &entry) {
269+
let prefix = if let Some(ref name) = name {
270+
let mut prefix = name.clone();
271+
prefix.push('.');
272+
prefix
273+
} else {
274+
String::from("")
275+
};
276+
let parent = ParentInfo {
277+
ty: comp,
278+
starting_offset: offset,
279+
member_prefix: &prefix,
280+
};
258281
let mut tree = unit.entries_tree(Some(ref_type))?;
259-
process_compound_type(dwarf, unit, tree.root()?, members, offset, comp)?;
282+
process_compound_type(dwarf, unit, tree.root()?, members, &parent)?;
283+
284+
return Ok(());
260285
};
261286

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

266-
let name = entry.attr(gimli::DW_AT_name)?;
267291
let Some(name) = name else { return Ok(()) };
268-
let name = dwarf.attr_string(unit, name.value())?;
269-
let name = name.to_string_lossy()?.into_owned();
292+
let mut name_with_prefix = String::from(parent.member_prefix);
293+
name_with_prefix.push_str(&name);
270294

271295
members.push(BchMember {
272-
name,
273-
offset: offset + starting_offset,
296+
name: name_with_prefix,
297+
offset: offset + parent.starting_offset,
274298
size,
275299
});
276300

@@ -285,13 +309,12 @@ fn get_size(
285309
return size.udata_value();
286310
}
287311

288-
if let Some(ref_type) = entry.attr(gimli::DW_AT_type).ok()? {
289-
if let gimli::AttributeValue::UnitRef(offset) = ref_type.value() {
290-
let mut type_entry = unit.entries_at_offset(offset).ok()?;
291-
type_entry.next_entry().ok()?;
292-
if let Some(t) = type_entry.current() {
293-
return get_size(unit, t);
294-
}
312+
let ref_type = entry.attr(gimli::DW_AT_type).ok()??;
313+
if let gimli::AttributeValue::UnitRef(offset) = ref_type.value() {
314+
let mut type_entry = unit.entries_at_offset(offset).ok()?;
315+
type_entry.next_entry().ok()?;
316+
if let Some(t) = type_entry.current() {
317+
return get_size(unit, t);
295318
}
296319
}
297320

@@ -301,21 +324,12 @@ fn get_size(
301324
/// Return a list of the known bkey types.
302325
pub fn get_bkey_type_info() -> BkeyTypes {
303326
let path = fs::read_link("/proc/self/exe").unwrap();
304-
305327
let file = fs::File::open(path).unwrap();
306328
let mmap = unsafe { memmap2::Mmap::map(&file).unwrap() };
307329
let object = object::File::parse(&*mmap).unwrap();
330+
308331
let mut struct_list = BkeyTypes::new();
309332
process_file(&object, &mut struct_list).unwrap();
310333

311-
/*
312-
for s in struct_list.0.iter() {
313-
for m in s.members.iter() {
314-
println!("{} {} {} {}", s.name, m.name, m.offset, m.size);
315-
}
316-
println!("");
317-
}
318-
*/
319-
320334
struct_list
321335
}

src/commands/debug/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,9 @@ pub fn debug(argv: Vec<String>) -> i32 {
148148

149149
0
150150
}
151+
152+
pub fn list_bkeys() -> i32 {
153+
print!("{}", bkey_types::get_bkey_type_info());
154+
155+
0
156+
}

src/commands/debug/parser.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,24 @@ fn parse_dump_cmd(input: &str) -> IResult<&str, DebugCommand> {
4141
))
4242
}
4343

44-
fn symbol_name(input: &str) -> IResult<&str, &str> {
44+
fn bkey_name(input: &str) -> IResult<&str, &str> {
4545
take_while(|c: char| c.is_alphabetic() || c == '_')(input)
4646
}
4747

48+
fn field_name(input: &str) -> IResult<&str, &str> {
49+
take_while(|c: char| c.is_alphabetic() || c == '_' || c == '.')(input)
50+
}
51+
4852
fn parse_update_cmd(input: &str) -> IResult<&str, DebugCommand> {
4953
let (input, (_, btree, _, bpos, _, bkey, _, field, _, value)) = all_consuming(tuple((
5054
space1,
5155
alpha1,
5256
space1,
5357
parse_bpos,
5458
space1,
55-
symbol_name,
59+
bkey_name,
5660
char('.'),
57-
symbol_name,
61+
field_name,
5862
char('='),
5963
u64,
6064
)))(input)?;

src/commands/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub use list::list;
1212
pub use completions::completions;
1313
pub use subvolume::subvolume;
1414
pub use debug::debug;
15+
pub use debug::list_bkeys;
1516

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

0 commit comments

Comments
 (0)