'debug' serialization

This commit is contained in:
Jean-Marie Mineau 2025-01-13 17:09:17 +01:00
parent bc3392d946
commit f3fcb5b086
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2

View file

@ -57,173 +57,6 @@ impl DebugInfoItem {
pub fn parameters_size_field(&self) -> Uleb128 { pub fn parameters_size_field(&self) -> Uleb128 {
Uleb128(self.parameter_names.len() as u32) Uleb128(self.parameter_names.len() as u32)
} }
pub fn from_debug_infos(
line_start: Uleb128,
parameter_names: Vec<Uleb128p1>,
debug_infos: Vec<DebugInfo>,
) -> Self {
let mut bytecode = vec![];
let mut address = 0;
let mut line = line_start.0;
let mut register_states = vec![];
for dbg_info in debug_infos {
match dbg_info {
DebugInfo::DefLocal { addr, reg, val } => {
while register_states.len() < (reg + 1) as usize {
register_states.push(DebugRegState {
name_idx: None,
type_idx: None,
sig_idx: None,
in_scope: false,
});
}
if addr != address {
let addr_diff = addr - address;
bytecode.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
address += addr_diff;
}
let mut old_val = register_states[reg as usize];
let old_val_in_scope = old_val.in_scope;
old_val.in_scope = true;
if old_val_in_scope && old_val == val {
register_states[reg as usize].in_scope = true;
bytecode.push(DbgBytecode::RestartLocal {
register_num: Uleb128(reg),
});
} else {
register_states[reg as usize] = val;
if val.sig_idx.is_some() {
bytecode.push(DbgBytecode::StartLocalExtended {
register_num: Uleb128(reg),
name_idx: if let Some(name_idx) = val.name_idx {
Uleb128p1(name_idx)
} else {
NO_INDEX
},
type_idx: if let Some(type_idx) = val.type_idx {
Uleb128p1(type_idx)
} else {
NO_INDEX
},
sig_idx: if let Some(sig_idx) = val.sig_idx {
Uleb128p1(sig_idx)
} else {
NO_INDEX
},
})
} else {
bytecode.push(DbgBytecode::StartLocal {
register_num: Uleb128(reg),
name_idx: if let Some(name_idx) = val.name_idx {
Uleb128p1(name_idx)
} else {
NO_INDEX
},
type_idx: if let Some(type_idx) = val.type_idx {
Uleb128p1(type_idx)
} else {
NO_INDEX
},
})
}
}
}
DebugInfo::EndLocal { addr, reg } => {
while register_states.len() < (reg + 1) as usize {
register_states.push(DebugRegState {
name_idx: None,
type_idx: None,
sig_idx: None,
in_scope: false,
});
}
if addr != address {
let addr_diff = addr - address;
bytecode.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
address += addr_diff;
}
bytecode.push(DbgBytecode::EndLocal {
register_num: Uleb128(reg),
});
}
DebugInfo::PrologueEnd { addr } => {
if addr != address {
let addr_diff = addr - address;
bytecode.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
address += addr_diff;
}
bytecode.push(DbgBytecode::SetPrologueEnd);
}
DebugInfo::EpilogueBegin { addr } => {
if addr != address {
let addr_diff = addr - address;
bytecode.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
address += addr_diff;
}
bytecode.push(DbgBytecode::SetEpilogueBegin);
}
DebugInfo::SetSourceFile {
addr,
source_file_idx,
} => {
if addr != address {
let addr_diff = addr - address;
bytecode.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
address += addr_diff;
}
bytecode.push(DbgBytecode::SetFile {
name_idx: if let Some(source_file_idx) = source_file_idx {
Uleb128p1(source_file_idx)
} else {
NO_INDEX
},
});
}
DebugInfo::SetLineNumber { addr, line_num } => {
let mut line_diff = line_num as i32 - line as i32;
let mut addr_diff = addr - address;
if addr_diff > (0xff - 0x0a) / 15 {
bytecode.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
address = addr;
addr_diff = 0;
}
if !(-4..15 - 4).contains(&line_diff) {
bytecode.push(DbgBytecode::AdvanceLine {
line_diff: Sleb128(line_diff),
});
line = line_num;
line_diff = 0;
}
let op = 0x0a + addr_diff as u8 * 15 + (line_diff + 4) as u8;
bytecode.push(DbgBytecode::SpecialOpcode(op));
}
DebugInfo::EndOfData => {
bytecode.push(DbgBytecode::EndSequence);
break;
}
}
}
if bytecode.is_empty() || bytecode[bytecode.len() - 1] != DbgBytecode::EndSequence {
bytecode.push(DbgBytecode::EndSequence);
}
Self {
line_start,
parameter_names,
bytecode,
}
}
} }
impl Serializable for DebugInfoItem { impl Serializable for DebugInfoItem {
@ -316,7 +149,7 @@ impl DebugInfo {
/// A state machine that interpret a [`DebugInfoItem`]. /// A state machine that interpret a [`DebugInfoItem`].
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct DebugStateMachine<'a> { pub struct DebugInfoReader<'a> {
debug_info: &'a DebugInfoItem, debug_info: &'a DebugInfoItem,
pub pc: usize, pub pc: usize,
pub address: u32, pub address: u32,
@ -328,7 +161,7 @@ pub struct DebugStateMachine<'a> {
pub register_states: Vec<DebugRegState>, pub register_states: Vec<DebugRegState>,
} }
impl<'a> DebugStateMachine<'a> { impl<'a> DebugInfoReader<'a> {
pub fn new( pub fn new(
debug_info: &'a DebugInfoItem, debug_info: &'a DebugInfoItem,
//source_file_idx: Option<u32>, //source_file_idx: Option<u32>,
@ -537,6 +370,271 @@ impl<'a> DebugStateMachine<'a> {
} }
} }
/// A state machine that generate a [`DebugInfoItem`].
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DebugInfoBuilder {
debug_infos: Vec<DbgBytecode>,
line_start: u32,
parameter_names: Vec<Uleb128p1>,
//pub pc: usize,
address: u32,
line: u32,
// Those are registers described in the doc but not necessary in the end
//pub source_file_idx: Option<u32>,
//pub prologue_end: bool,
//pub epilogue_begin: bool,
register_states: Vec<DebugRegState>,
finished: bool,
}
impl DebugInfoBuilder {
pub fn new(parameter_names: Vec<Uleb128p1>) -> Self {
Self {
debug_infos: vec![],
line_start: 0,
parameter_names,
//pc: 0,
address: 0,
line: 0,
//source_file_idx,
//prologue_end: false,
//epilogue_begin: false,
//register_states: vec![
// DebugRegState {
// name_idx: None,
// type_idx: None,
// sig_idx: None,
// in_scope: false,
// };
// nb_reg
//],
register_states: vec![], // In the end, it's easier to grow this on the fly
finished: false,
}
}
pub fn add_info(&mut self, info: &DebugInfo) -> Result<()> {
if self.finished {
return Err(Error::SerializationError(
"Cannot add more information: EndSequence has already been send".into(),
));
}
match info {
DebugInfo::DefLocal { addr, reg, val } => {
if *addr < self.address {
return Err(Error::SerializationError(format!(
"The address register can only increase, \
found 0x{addr:02x} while register is already \
0x{:02x}",
self.address
)));
}
while self.register_states.len() < (reg + 1) as usize {
self.register_states.push(DebugRegState {
name_idx: None,
type_idx: None,
sig_idx: None,
in_scope: false,
});
}
if *addr != self.address {
let addr_diff = *addr - self.address;
self.debug_infos.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
self.address += addr_diff;
}
let mut old_val = self.register_states[*reg as usize];
let old_val_in_scope = old_val.in_scope;
old_val.in_scope = true;
if old_val_in_scope && old_val == *val {
self.register_states[*reg as usize].in_scope = true;
self.debug_infos.push(DbgBytecode::RestartLocal {
register_num: Uleb128(*reg),
});
} else {
self.register_states[*reg as usize] = *val;
if val.sig_idx.is_some() {
self.debug_infos.push(DbgBytecode::StartLocalExtended {
register_num: Uleb128(*reg),
name_idx: if let Some(name_idx) = val.name_idx {
Uleb128p1(name_idx)
} else {
NO_INDEX
},
type_idx: if let Some(type_idx) = val.type_idx {
Uleb128p1(type_idx)
} else {
NO_INDEX
},
sig_idx: if let Some(sig_idx) = val.sig_idx {
Uleb128p1(sig_idx)
} else {
NO_INDEX
},
})
} else {
self.debug_infos.push(DbgBytecode::StartLocal {
register_num: Uleb128(*reg),
name_idx: if let Some(name_idx) = val.name_idx {
Uleb128p1(name_idx)
} else {
NO_INDEX
},
type_idx: if let Some(type_idx) = val.type_idx {
Uleb128p1(type_idx)
} else {
NO_INDEX
},
})
}
}
Ok(())
}
DebugInfo::EndLocal { addr, reg } => {
if *addr < self.address {
return Err(Error::SerializationError(format!(
"The address register can only increase, \
found 0x{addr:02x} while register is already \
0x{:02x}",
self.address
)));
}
while self.register_states.len() < (reg + 1) as usize {
self.register_states.push(DebugRegState {
name_idx: None,
type_idx: None,
sig_idx: None,
in_scope: false,
});
}
if *addr != self.address {
let addr_diff = *addr - self.address;
self.debug_infos.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
self.address += addr_diff;
}
self.debug_infos.push(DbgBytecode::EndLocal {
register_num: Uleb128(*reg),
});
Ok(())
}
DebugInfo::PrologueEnd { addr } => {
if *addr < self.address {
return Err(Error::SerializationError(format!(
"The address register can only increase, \
found 0x{addr:02x} while register is already \
0x{:02x}",
self.address
)));
}
if *addr != self.address {
let addr_diff = *addr - self.address;
self.debug_infos.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
self.address += addr_diff;
}
self.debug_infos.push(DbgBytecode::SetPrologueEnd);
Ok(())
}
DebugInfo::EpilogueBegin { addr } => {
if *addr < self.address {
return Err(Error::SerializationError(format!(
"The address register can only increase, \
found 0x{addr:02x} while register is already \
0x{:02x}",
self.address
)));
}
if *addr != self.address {
let addr_diff = *addr - self.address;
self.debug_infos.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
self.address += addr_diff;
}
self.debug_infos.push(DbgBytecode::SetEpilogueBegin);
Ok(())
}
DebugInfo::SetLineNumber { addr, line_num } => {
if *addr < self.address {
return Err(Error::SerializationError(format!(
"The address register can only increase, \
found 0x{addr:02x} while register is already \
0x{:02x}",
self.address
)));
}
if self.line_start == 0 {
self.line_start = *line_num;
self.line = *line_num;
}
let mut line_diff = *line_num as i32 - self.line as i32;
let mut addr_diff = addr - self.address;
if addr_diff > (0xff - 0x0a) / 15 {
self.debug_infos.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
self.address = *addr;
addr_diff = 0;
}
if !(-4..15 - 4).contains(&line_diff) {
self.debug_infos.push(DbgBytecode::AdvanceLine {
line_diff: Sleb128(line_diff),
});
self.line = *line_num;
line_diff = 0;
}
let op = 0x0a + addr_diff as u8 * 15 + (line_diff + 4) as u8;
self.debug_infos.push(DbgBytecode::SpecialOpcode(op));
self.address += addr_diff;
self.line = (self.line as i32 + line_diff) as u32;
Ok(())
}
DebugInfo::SetSourceFile {
addr,
source_file_idx,
} => {
if *addr != self.address {
let addr_diff = *addr - self.address;
self.debug_infos.push(DbgBytecode::AdvancePC {
addr_diff: Uleb128(addr_diff),
});
self.address += addr_diff;
}
self.debug_infos.push(DbgBytecode::SetFile {
name_idx: if let Some(source_file_idx) = source_file_idx {
Uleb128p1(*source_file_idx)
} else {
NO_INDEX
},
});
Ok(())
}
DebugInfo::EndOfData => {
self.finished = true;
Ok(())
}
}
}
/// If they are no debug information, return None, else compute and return the [`DebugInfoItem`].
pub fn build(self) -> Option<DebugInfoItem> {
if self.debug_infos.is_empty() && self.parameter_names.iter().all(|&idx| idx == NO_INDEX) {
None
} else {
Some(DebugInfoItem {
line_start: Uleb128(self.line_start),
parameter_names: self.parameter_names,
bytecode: self.debug_infos,
})
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::DbgBytecode::*; use super::DbgBytecode::*;
@ -574,4 +672,27 @@ mod test {
DbgBytecode::deserialize_from_slice(&advance_line.serialize_to_vec().unwrap()).unwrap() DbgBytecode::deserialize_from_slice(&advance_line.serialize_to_vec().unwrap()).unwrap()
); );
} }
#[test]
fn test_get_expl_debug() {
const RAW_DEBUG: [u8; 10] = [23, 0, 14, 135, 3, 0, 16, 2, 150, 0];
let debug = DebugInfoItem::deserialize_from_slice(&RAW_DEBUG).unwrap();
let mut reader = DebugInfoReader::new(&debug);
let mut list_info = vec![];
loop {
list_info.push(reader.next_info());
if list_info.last() == Some(&DebugInfo::EndOfData) {
break;
}
}
let mut builder = DebugInfoBuilder::new(debug.parameter_names.clone());
for info in list_info {
builder.add_info(&info).unwrap();
}
let debug_computed = builder.build().unwrap();
assert_eq!(
&RAW_DEBUG,
&(debug_computed.serialize_to_vec().unwrap()).as_slice()
);
}
} }