use crate::VARS_APP;
use crate::common::TestHooks;
use crate::common::{TestInfo, rust_version};
use crate::{assert_no_proc, prepare_debugee_process};
use bugstalker::debugger::DebuggerBuilder;
use bugstalker::debugger::call::fmt::call_debug_fmt;
use bugstalker::debugger::variable::dqe::{Dqe, Literal, LiteralOrWildcard, PointerCast, Selector};
use bugstalker::debugger::variable::render::RenderValue;
use bugstalker::debugger::variable::value::{Member, SpecializedValue, SupportedScalar, Value};
use bugstalker::version::Version;
use bugstalker::version_switch;
use serial_test::serial;
use std::collections::HashMap;
pub fn assert_scalar(value: &Value, exp_type: &str, exp_val: Option<SupportedScalar>) {
let Value::Scalar(scalar) = value else {
panic!("not a scalar");
};
assert_eq!(value.r#type().name_fmt(), exp_type);
assert_eq!(scalar.value, exp_val);
}
fn assert_struct(val: &Value, exp_type: &str, for_each_member: impl Fn(usize, &Member)) {
let Value::Struct(structure) = val else {
panic!("not a struct");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
for (i, member) in structure.members.iter().enumerate() {
for_each_member(i, member)
}
}
fn assert_member(member: &Member, expected_field_name: &str, with_value: impl Fn(&Value)) {
assert_eq!(member.field_name.as_deref(), Some(expected_field_name));
with_value(&member.value);
}
fn assert_array(val: &Value, exp_type: &str, for_each_item: impl Fn(usize, &Value)) {
let Value::Array(array) = val else {
panic!("not an array");
};
assert_eq!(array.type_ident.name_fmt(), exp_type);
for (i, item) in array.items.as_ref().unwrap_or(&vec![]).iter().enumerate() {
for_each_item(i, &item.value)
}
}
fn assert_c_enum(val: &Value, exp_type: &str, exp_value: Option<String>) {
let Value::CEnum(c_enum) = val else {
panic!("not a c_enum");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
assert_eq!(c_enum.value, exp_value);
}
fn assert_rust_enum(val: &Value, exp_type: &str, with_value: impl FnOnce(&Value)) {
let Value::RustEnum(rust_enum) = val else {
panic!("not a c_enum");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
with_value(&rust_enum.value.as_ref().unwrap().value);
}
fn assert_pointer(val: &Value, exp_type: &str) {
let Value::Pointer(ptr) = val else {
panic!("not a pointer");
};
assert_eq!(ptr.type_ident.name_fmt(), exp_type);
}
fn assert_vec(val: &Value, exp_type: &str, exp_cap: usize, with_buf: impl FnOnce(&Value)) {
let Value::Specialized {
value: Some(SpecializedValue::Vector(vector)),
..
} = val
else {
panic!("not a vector");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
let Value::Scalar(capacity) = &vector.structure.members[1].value else {
panic!("no capacity");
};
assert_eq!(capacity.value, Some(SupportedScalar::Usize(exp_cap)));
with_buf(&vector.structure.members[0].value);
}
fn assert_string(val: &Value, exp_value: &str) {
let Value::Specialized {
value: Some(SpecializedValue::String(string)),
..
} = val
else {
panic!("not a string");
};
assert_eq!(string.value, exp_value);
}
fn assert_str(val: &Value, exp_value: &str) {
let Value::Specialized {
value: Some(SpecializedValue::Str(str)),
..
} = val
else {
panic!("not a &str");
};
assert_eq!(str.value, exp_value);
}
fn assert_init_tls(val: &Value, exp_type: &str, with_inner: impl FnOnce(&Value)) {
let Value::Specialized {
value: Some(SpecializedValue::Tls(tls)),
..
} = val
else {
panic!("not a tls");
};
assert_eq!(tls.inner_type.name_fmt(), exp_type);
with_inner(tls.inner_value.as_ref().unwrap());
}
fn assert_uninit_tls(val: &Value, exp_type: &str) {
let Value::Specialized {
value: Some(SpecializedValue::Tls(tls)),
..
} = val
else {
panic!("not a tls");
};
assert_eq!(tls.inner_type.name_fmt(), exp_type);
assert!(tls.inner_value.is_none());
}
fn assert_hashmap(val: &Value, exp_type: &str, with_kv_items: impl FnOnce(&Vec<(Value, Value)>)) {
let Value::Specialized {
value: Some(SpecializedValue::HashMap(map)),
..
} = val
else {
panic!("not a hashmap");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
let mut items = map.kv_items.clone();
items.sort_by(|v1, v2| {
let k1_render = format!("{:?}", v1.0.value_layout());
let k2_render = format!("{:?}", v2.0.value_layout());
k1_render.cmp(&k2_render)
});
with_kv_items(&items);
}
fn assert_hashset(val: &Value, exp_type: &str, with_items: impl FnOnce(&Vec<Value>)) {
let Value::Specialized {
value: Some(SpecializedValue::HashSet(set)),
..
} = val
else {
panic!("not a hashset");
};
assert_eq!(set.type_ident.name_fmt(), exp_type);
let mut items = set.items.clone();
items.sort_by(|v1, v2| {
let k1_render = format!("{:?}", v1.value_layout());
let k2_render = format!("{:?}", v2.value_layout());
k1_render.cmp(&k2_render)
});
with_items(&items);
}
fn assert_btree_map(val: &Value, exp_type: &str, with_kv_items: impl FnOnce(&Vec<(Value, Value)>)) {
let Value::Specialized {
value: Some(SpecializedValue::BTreeMap(map)),
..
} = val
else {
panic!("not a BTreeMap");
};
assert_eq!(map.type_ident.name_fmt(), exp_type);
with_kv_items(&map.kv_items);
}
fn assert_btree_set(val: &Value, exp_type: &str, with_items: impl FnOnce(&Vec<Value>)) {
let Value::Specialized {
value: Some(SpecializedValue::BTreeSet(set)),
..
} = val
else {
panic!("not a BTreeSet");
};
assert_eq!(set.type_ident.name_fmt(), exp_type);
with_items(&set.items);
}
fn assert_vec_deque(val: &Value, exp_type: &str, exp_cap: usize, with_buf: impl FnOnce(&Value)) {
let Value::Specialized {
value: Some(SpecializedValue::VecDeque(vector)),
..
} = val
else {
panic!("not a VecDeque");
};
assert_eq!(vector.structure.type_ident.name_fmt(), exp_type);
let Value::Scalar(capacity) = &vector.structure.members[1].value else {
panic!("no capacity");
};
assert_eq!(capacity.value, Some(SupportedScalar::Usize(exp_cap)));
with_buf(&vector.structure.members[0].value);
}
fn assert_cell(val: &Value, exp_type: &str, with_value: impl FnOnce(&Value)) {
let Value::Specialized {
value: Some(SpecializedValue::Cell(value)),
..
} = val
else {
panic!("not a Cell");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
with_value(value.as_ref());
}
fn assert_refcell(val: &Value, exp_type: &str, exp_borrow: isize, with_inner: impl FnOnce(&Value)) {
let Value::Specialized {
value: Some(SpecializedValue::RefCell(value)),
..
} = val
else {
panic!("not a Cell");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
let Value::Struct(as_struct) = value.as_ref() else {
panic!("not a struct")
};
let Value::Scalar(borrow) = &as_struct.members[0].value else {
panic!("no borrow flag");
};
assert_eq!(
borrow.value.as_ref().unwrap(),
&SupportedScalar::Isize(exp_borrow)
);
with_inner(&as_struct.members[1].value);
}
fn assert_rc(val: &Value, exp_type: &str) {
let Value::Specialized {
value: Some(SpecializedValue::Rc(_)),
..
} = val
else {
panic!("not an rc");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
}
fn assert_arc(val: &Value, exp_type: &str) {
let Value::Specialized {
value: Some(SpecializedValue::Arc(_)),
..
} = val
else {
panic!("not an arc");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
}
fn assert_uuid(val: &Value, exp_type: &str) {
let Value::Specialized {
value: Some(SpecializedValue::Uuid(_)),
..
} = val
else {
panic!("not an uuid");
};
assert_eq!(val.r#type().name_fmt(), exp_type);
}
fn assert_system_time(val: &Value, exp_value: (i64, u32)) {
let Value::Specialized {
value: Some(SpecializedValue::SystemTime(value)),
..
} = val
else {
panic!("not a SystemTime");
};
assert_eq!(*value, exp_value);
}
fn assert_instant(val: &Value) {
let Value::Specialized {
value: Some(SpecializedValue::Instant(_)),
..
} = val
else {
panic!("not an Instant");
};
}
macro_rules! read_locals {
($debugger: expr => $($var: ident),*) => {
let vars = $debugger.read_local_variables().unwrap();
let &[$($var),*] = &vars.as_slice() else {
panic!("Invalid variables count")
};
};
}
macro_rules! read_var_dqe {
($debugger: expr, $dqe: expr => $($var: ident),*) => {
let vars = $debugger.read_variable($dqe).unwrap();
let &[$($var),*] = &vars.as_slice() else {
panic!("Invalid variables count")
};
};
}
macro_rules! read_arg_dqe {
($debugger: expr, $dqe: expr => $($var: ident),*) => {
let args = $debugger.read_argument($dqe).unwrap();
let &[$($var),*] = &args.as_slice() else {
panic!("Invalid variables count")
};
};
}
macro_rules! read_var_dqe_type_order {
($debugger: expr, $dqe: expr => $($var: ident),*) => {
let mut vars = $debugger.read_variable($dqe).unwrap();
vars.sort_by(|v1, v2| v1.value().r#type().cmp(v2.value().r#type()));
let &[$($var),*] = &vars.as_slice() else {
panic!("Invalid variables count")
};
};
}
macro_rules! assert_idents {
($($var: ident => $name: literal),*) => {
$(
assert_eq!($var.identity().to_string(), $name);
)*
};
}
#[test]
#[serial]
fn test_read_scalar_variables() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 30).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(30));
read_locals!(debugger => int8, int16, int32, int64, int128, isize, uint8, uint16, uint32, uint64, uint128, usize, f32, f64, b_true, b_false, ch_ascii, c_n_ascii);
assert_idents!(
int8 => "int8", int16 => "int16", int32 => "int32", int64 => "int64", int128 => "int128",
isize => "isize", uint8 => "uint8", uint16 => "uint16", uint32 => "uint32", uint64 => "uint64",
uint128 => "uint128", usize => "usize", f32 => "f32", f64 => "f64", b_true => "boolean_true",
b_false => "boolean_false", ch_ascii => "char_ascii", c_n_ascii => "char_non_ascii"
);
assert_scalar(int8.value(), "i8", Some(SupportedScalar::I8(1)));
assert_scalar(int16.value(), "i16", Some(SupportedScalar::I16(-1)));
assert_scalar(int32.value(), "i32", Some(SupportedScalar::I32(2)));
assert_scalar(int64.value(), "i64", Some(SupportedScalar::I64(-2)));
assert_scalar(int128.value(), "i128", Some(SupportedScalar::I128(3)));
assert_scalar(isize.value(), "isize", Some(SupportedScalar::Isize(-3)));
assert_scalar(uint8.value(), "u8", Some(SupportedScalar::U8(1)));
assert_scalar(uint16.value(), "u16", Some(SupportedScalar::U16(2)));
assert_scalar(uint32.value(), "u32", Some(SupportedScalar::U32(3)));
assert_scalar(uint64.value(), "u64", Some(SupportedScalar::U64(4)));
assert_scalar(uint128.value(), "u128", Some(SupportedScalar::U128(5)));
assert_scalar(usize.value(), "usize", Some(SupportedScalar::Usize(6)));
assert_scalar(f32.value(), "f32", Some(SupportedScalar::F32(1.1)));
assert_scalar(f64.value(), "f64", Some(SupportedScalar::F64(1.2)));
assert_scalar(b_true.value(), "bool", Some(SupportedScalar::Bool(true)));
assert_scalar(b_false.value(), "bool", Some(SupportedScalar::Bool(false)));
assert_scalar(ch_ascii.value(), "char", Some(SupportedScalar::Char('a')));
assert_scalar(c_n_ascii.value(), "char", Some(SupportedScalar::Char('😊')));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_scalar_variables_at_place() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 11).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(11));
let vars = debugger.read_local_variables().unwrap();
assert_eq!(vars.len(), 5);
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_struct() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 53).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(53));
read_locals!(debugger => tuple_0, tuple_1, tuple_2, foo, foo2);
assert_idents!(tuple_0 => "tuple_0", tuple_1 => "tuple_1", tuple_2 => "tuple_2", foo => "foo", foo2 => "foo2");
assert_scalar(tuple_0.value(), "()", Some(SupportedScalar::Empty()));
assert_struct(tuple_1.value(), "(f64, f64)", |i, member| match i {
0 => assert_member(member, "__0", |val| {
assert_scalar(val, "f64", Some(SupportedScalar::F64(0f64)))
}),
1 => assert_member(member, "__1", |val| {
assert_scalar(val, "f64", Some(SupportedScalar::F64(1.1f64)))
}),
_ => panic!("2 members expected"),
});
assert_struct(
tuple_2.value(),
"(u64, i64, char, bool)",
|i, member| match i {
0 => assert_member(member, "__0", |val| {
assert_scalar(val, "u64", Some(SupportedScalar::U64(1)))
}),
1 => assert_member(member, "__1", |val| {
assert_scalar(val, "i64", Some(SupportedScalar::I64(-1)))
}),
2 => assert_member(member, "__2", |val| {
assert_scalar(val, "char", Some(SupportedScalar::Char('a')))
}),
3 => assert_member(member, "__3", |val| {
assert_scalar(val, "bool", Some(SupportedScalar::Bool(false)))
}),
_ => panic!("4 members expected"),
},
);
assert_struct(foo.value(), "Foo", |i, member| match i {
0 => assert_member(member, "bar", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(100)))
}),
1 => assert_member(member, "baz", |val| {
assert_scalar(val, "char", Some(SupportedScalar::Char('9')))
}),
_ => panic!("2 members expected"),
});
assert_struct(foo2.value(), "Foo2", |i, member| match i {
0 => assert_member(member, "foo", |val| {
assert_struct(val, "Foo", |i, member| match i {
0 => assert_member(member, "bar", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(100)))
}),
1 => assert_member(member, "baz", |val| {
assert_scalar(val, "char", Some(SupportedScalar::Char('9')))
}),
_ => panic!("2 members expected"),
})
}),
1 => assert_member(member, "additional", |val| {
assert_scalar(val, "bool", Some(SupportedScalar::Bool(true)))
}),
_ => panic!("2 members expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_array() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 61).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(61));
read_locals!(debugger => arr_1, arr_2);
assert_idents!(arr_1 => "arr_1", arr_2 => "arr_2");
assert_array(arr_1.value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-1))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
3 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-2))),
4 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("5 items expected"),
});
assert_array(arr_2.value(), "[[i32]]", |i, item| match i {
0 => assert_array(item, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-1))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
3 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-2))),
4 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("5 items expected"),
}),
1 => assert_array(item, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(0))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
3 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
4 => assert_scalar(item, "i32", Some(SupportedScalar::I32(4))),
_ => panic!("5 items expected"),
}),
2 => assert_array(item, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(0))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-1))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-2))),
3 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-3))),
4 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-4))),
_ => panic!("5 items expected"),
}),
_ => panic!("3 items expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_enum() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 93).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(93));
read_locals!(debugger => enum_1, enum_2, enum_3, enum_4, enum_5, enum_6, enum_7);
assert_idents!(
enum_1 => "enum_1", enum_2 => "enum_2", enum_3 => "enum_3", enum_4 => "enum_4",
enum_5 => "enum_5", enum_6 => "enum_6", enum_7 => "enum_7"
);
assert_c_enum(enum_1.value(), "EnumA", Some("B".to_string()));
assert_rust_enum(enum_2.value(), "EnumC", |enum_val| {
assert_struct(enum_val, "C", |_, member| {
assert_member(member, "__0", |val| {
assert_scalar(val, "char", Some(SupportedScalar::Char('b')))
})
});
});
assert_rust_enum(enum_3.value(), "EnumC", |enum_val| {
assert_struct(enum_val, "D", |i, member| {
match i {
0 => assert_member(member, "__0", |val| {
assert_scalar(val, "f64", Some(SupportedScalar::F64(1.1)))
}),
1 => assert_member(member, "__1", |val| {
assert_scalar(val, "f32", Some(SupportedScalar::F32(1.2)))
}),
_ => panic!("2 members expected"),
};
});
});
assert_rust_enum(enum_4.value(), "EnumC", |enum_val| {
assert_struct(enum_val, "E", |_, _| {
panic!("expected empty struct");
});
});
assert_rust_enum(enum_5.value(), "EnumF", |enum_val| {
assert_struct(enum_val, "F", |i, member| {
match i {
0 => assert_member(member, "__0", |val| {
assert_rust_enum(val, "EnumC", |enum_val| {
assert_struct(enum_val, "C", |_, member| {
assert_member(member, "__0", |val| {
assert_scalar(val, "char", Some(SupportedScalar::Char('f')))
})
});
})
}),
_ => panic!("1 members expected"),
};
});
});
assert_rust_enum(enum_6.value(), "EnumF", |enum_val| {
assert_struct(enum_val, "G", |i, member| {
match i {
0 => assert_member(member, "__0", |val| {
assert_struct(val, "Foo", |i, member| match i {
0 => assert_member(member, "a", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
1 => assert_member(member, "b", |val| {
assert_scalar(val, "char", Some(SupportedScalar::Char('1')))
}),
_ => panic!("2 members expected"),
})
}),
_ => panic!("1 members expected"),
};
});
});
assert_rust_enum(enum_7.value(), "EnumF", |enum_val| {
assert_struct(enum_val, "J", |i, member| {
match i {
0 => assert_member(member, "__0", |val| {
assert_c_enum(val, "EnumA", Some("A".to_string()))
}),
_ => panic!("1 members expected"),
};
});
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_pointers() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 119).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(119));
read_locals!(debugger => a, ref_a, ptr_a, ptr_ptr_a, b, mut_ref_b, c, mut_ptr_c, box_d, f, ref_f);
assert_idents!(
a => "a", ref_a => "ref_a", ptr_a => "ptr_a", ptr_ptr_a => "ptr_ptr_a", b => "b",
mut_ref_b => "mut_ref_b",c => "c", mut_ptr_c => "mut_ptr_c", box_d => "box_d", f => "f", ref_f => "ref_f"
);
assert_scalar(a.value(), "i32", Some(SupportedScalar::I32(2)));
assert_pointer(ref_a.value(), "&i32");
let deref = ref_a.clone().modify_value(|pcx, val| val.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
assert_pointer(ptr_a.value(), "*const i32");
let deref = ptr_a.clone().modify_value(|pcx, val| val.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
assert_pointer(ptr_ptr_a.value(), "*const *const i32");
let deref = ptr_ptr_a.clone().modify_value(|pcx, val| val.deref(pcx));
assert_pointer(deref.unwrap().value(), "*const i32");
let deref = ptr_ptr_a
.clone()
.modify_value(|pcx, v| v.deref(pcx).and_then(|v| v.deref(pcx)));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
assert_scalar(b.value(), "i32", Some(SupportedScalar::I32(2)));
assert_pointer(mut_ref_b.value(), "&mut i32");
let deref = mut_ref_b.clone().modify_value(|pcx, v| v.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
assert_scalar(c.value(), "i32", Some(SupportedScalar::I32(2)));
assert_pointer(mut_ptr_c.value(), "*mut i32");
let deref = mut_ptr_c.clone().modify_value(|pcx, v| v.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
assert_pointer(
box_d.value(),
"alloc::boxed::Box<i32, alloc::alloc::Global>",
);
let deref = box_d.clone().modify_value(|pcx, v| v.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
assert_struct(f.value(), "Foo", |i, member| match i {
0 => assert_member(member, "bar", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
1 => assert_member(member, "baz", |val| {
assert_array(val, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
_ => panic!("2 items expected"),
})
}),
2 => {
assert_member(member, "foo", |val| assert_pointer(val, "&i32"));
let foo_val = member.value.clone();
let deref = f.clone().modify_value(|pcx, _| foo_val.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
}
_ => panic!("3 members expected"),
});
assert_pointer(ref_f.value(), "&vars::references::Foo");
let deref = ref_f.clone().modify_value(|pcx, v| v.deref(pcx));
assert_struct(deref.unwrap().value(), "Foo", |i, member| match i {
0 => assert_member(member, "bar", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
1 => assert_member(member, "baz", |val| {
assert_array(val, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
_ => panic!("2 items expected"),
})
}),
2 => {
assert_member(member, "foo", |val| assert_pointer(val, "&i32"));
let foo_val = member.value.clone();
let deref = ref_f.clone().modify_value(|pcx, _| foo_val.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
}
_ => panic!("3 members expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_type_alias() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 126).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(126));
read_locals!(debugger => a_alias);
assert_idents!(a_alias => "a_alias");
assert_scalar(a_alias.value(), "i32", Some(SupportedScalar::I32(1)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_type_parameters() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 135).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(135));
read_locals!(debugger => a);
assert_idents!(a => "a");
assert_struct(a.value(), "Foo<i32>", |i, member| match i {
0 => assert_member(member, "bar", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
_ => panic!("1 members expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_vec_and_slice() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 151).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(151));
read_locals!(debugger => vec1, vec2, vec3, slice1, slice2);
assert_idents!(vec1 => "vec1", vec2 => "vec2", vec3 => "vec3", slice1 => "slice1", slice2 => "slice2");
assert_vec(vec1.value(), "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
})
});
assert_vec(
vec2.value(),
"Vec<vars::vec_and_slice_types::Foo, alloc::alloc::Global>",
2,
|buf| {
assert_array(buf, "[Foo]", |i, item| match i {
0 => assert_struct(item, "Foo", |i, member| match i {
0 => assert_member(member, "foo", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
_ => panic!("1 members expected"),
}),
1 => assert_struct(item, "Foo", |i, member| match i {
0 => assert_member(member, "foo", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(2)))
}),
_ => panic!("1 members expected"),
}),
_ => panic!("2 items expected"),
})
},
);
assert_vec(
vec3.value(),
"Vec<alloc::vec::Vec<i32, alloc::alloc::Global>, alloc::alloc::Global>",
2,
|buf| {
assert_array(buf, "[Vec<i32, alloc::alloc::Global>]", |i, item| match i {
0 => assert_vec(item, "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
})
}),
1 => assert_vec(item, "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
})
}),
_ => panic!("2 items expected"),
})
},
);
assert_pointer(slice1.value(), "&[i32; 3]");
let deref = slice1.clone().modify_value(|pcx, val| val.deref(pcx));
assert_array(deref.unwrap().value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
});
assert_pointer(slice2.value(), "&[&[i32; 3]; 2]");
let deref = slice2.clone().modify_value(|pcx, val| val.deref(pcx));
assert_array(deref.unwrap().value(), "[&[i32; 3]]", |i, item| match i {
0 => {
assert_pointer(item, "&[i32; 3]");
let item_val = item.clone();
let deref = slice2.clone().modify_value(|pcx, _| item_val.deref(pcx));
assert_array(deref.unwrap().value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
});
}
1 => {
assert_pointer(item, "&[i32; 3]");
let item_val = item.clone();
let deref = slice2.clone().modify_value(|pcx, _| item_val.deref(pcx));
assert_array(deref.unwrap().value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
});
}
_ => panic!("2 items expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_strings() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 159).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(159));
read_locals!(debugger => s1, s2, s3);
assert_idents!(s1 => "s1", s2 => "s2", s3 => "s3");
assert_string(s1.value(), "hello world");
assert_str(s2.value(), "hello world");
assert_str(s3.value(), "hello world");
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_static_variables() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 168).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(168));
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name("GLOB_1", false)) => glob_1);
assert_idents!(glob_1 => "vars::GLOB_1");
assert_str(glob_1.value(), "glob_1");
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name("GLOB_2", false)) => glob_2);
assert_idents!(glob_2 => "vars::GLOB_2");
assert_scalar(glob_2.value(), "i32", Some(SupportedScalar::I32(2)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_only_local_variables() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 168).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(168));
let vars = debugger
.read_variable(Dqe::Variable(Selector::by_name("GLOB_1", true)))
.unwrap();
assert_eq!(vars.len(), 0);
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_static_variables_different_modules() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 179).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(179));
read_var_dqe_type_order!(debugger, Dqe::Variable(Selector::by_name("GLOB_3", false)) => glob_3_1, glob_3_2);
assert_idents!(glob_3_1 => "vars::ns_1::GLOB_3");
assert_str(glob_3_1.value(), "glob_3");
assert_idents!(glob_3_2 => "vars::GLOB_3");
assert_scalar(glob_3_2.value(), "i32", Some(SupportedScalar::I32(3)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_tls_variables() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
let rust_version = rust_version(VARS_APP).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 194).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(194));
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name(
"THREAD_LOCAL_VAR_1",
false,
)) => tls_var_1);
version_switch!(
rust_version,
.. (1 . 80) => {
assert_idents!(tls_var_1 => "vars::THREAD_LOCAL_VAR_1::__getit::__KEY");
},
(1 . 80) .. (1 . 92) => {
assert_idents!(tls_var_1 => "vars::THREAD_LOCAL_VAR_1::{constant#0}::{closure#1}::VAL");
},
(1 . 92) .. => {
assert_idents!(tls_var_1 => "vars::THREAD_LOCAL_VAR_1::{constant#0}::{closure#1}::__RUST_STD_INTERNAL_VAL");
}
);
assert_init_tls(tls_var_1.value(), "Cell<i32>", |inner| {
assert_cell(inner, "Cell<i32>", |value| {
assert_scalar(value, "i32", Some(SupportedScalar::I32(2)))
})
});
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name(
"THREAD_LOCAL_VAR_2",
false,
)) => tls_var_2);
version_switch!(
rust_version,
.. (1 . 80) => {
assert_idents!(tls_var_2 => "vars::THREAD_LOCAL_VAR_2::__getit::__KEY");
},
(1 . 80) .. (1 . 92) => {
assert_idents!(tls_var_2 => "vars::THREAD_LOCAL_VAR_2::{constant#0}::{closure#1}::VAL");
},
(1 . 92) .. => {
assert_idents!(tls_var_2 => "vars::THREAD_LOCAL_VAR_2::{constant#0}::{closure#1}::__RUST_STD_INTERNAL_VAL");
}
);
assert_init_tls(tls_var_2.value(), "Cell<&str>", |inner| {
assert_cell(inner, "Cell<&str>", |value| assert_str(value, "2"))
});
debugger.set_breakpoint_at_line("vars.rs", 199).unwrap();
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(199));
version_switch!(
rust_version,
.. (1 . 80) => {
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name(
"THREAD_LOCAL_VAR_1",
false,
)) => tls_var_1);
assert_idents!(tls_var_1 => "vars::THREAD_LOCAL_VAR_1::__getit::__KEY");
assert_uninit_tls(tls_var_1.value(), "Cell<i32>");
},
(1 . 80) .. => {
let vars = debugger.read_variable(Dqe::Variable(Selector::by_name(
"THREAD_LOCAL_VAR_1",
false,
))).unwrap();
assert!(vars.is_empty());
},
);
debugger.set_breakpoint_at_line("vars.rs", 203).unwrap();
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(203));
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name(
"THREAD_LOCAL_VAR_1",
false,
)) => tls_var_1);
assert_init_tls(tls_var_1.value(), "Cell<i32>", |inner| {
assert_cell(inner, "Cell<i32>", |value| {
assert_scalar(value, "i32", Some(SupportedScalar::I32(1)))
})
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_tls_const_variables() {
let rust_version = rust_version(VARS_APP).unwrap();
if rust_version < Version((1, 79, 0)) {
return;
}
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 538).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(538));
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name(
"CONSTANT_THREAD_LOCAL",
false,
)) => const_tls);
version_switch!(
rust_version,
.. (1 . 80) => {
assert_idents!(const_tls => "vars::thread_local_const_init::CONSTANT_THREAD_LOCAL::__getit::VAL");
},
(1 . 80) .. (1 . 92) => {
assert_idents!(const_tls => "vars::thread_local_const_init::CONSTANT_THREAD_LOCAL::{constant#0}::{closure#0}::VAL");
},
(1 . 92) .. => {
assert_idents!(const_tls => "vars::thread_local_const_init::CONSTANT_THREAD_LOCAL::{constant#0}::{closure#0}::__RUST_STD_INTERNAL_VAL");
}
);
assert_init_tls(const_tls.value(), "i32", |value| {
assert_scalar(value, "i32", Some(SupportedScalar::I32(1337)))
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_closures() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 223).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(223));
read_locals!(debugger => inc, inc_mut, _outer, closure, _a, _b, _c, trait_once, trait_mut, trait_fn, fn_ptr);
assert_idents!(
inc => "inc", inc_mut => "inc_mut", closure => "closure", trait_once => "trait_once",
trait_mut => "trait_mut", trait_fn => "trait_fn", fn_ptr => "fn_ptr"
);
assert_struct(inc.value(), "{closure_env#0}", |_, _| {
panic!("no members expected")
});
assert_struct(inc_mut.value(), "{closure_env#1}", |_, _| {
panic!("no members expected")
});
assert_struct(closure.value(), "{closure_env#2}", |_, member| {
assert_member(member, "outer", |val| assert_string(val, "outer val"))
});
let rust_version = rust_version(VARS_APP).unwrap();
assert_struct(
trait_once.value(),
"alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>",
|i, member| match i {
0 => {
assert_member(member, "pointer", |val| {
assert_pointer(val, "*dyn core::ops::function::FnOnce<(), Output=()>")
});
let member_val = member.value.clone();
let deref = trait_once
.clone()
.modify_value(|pcx, _| member_val.deref(pcx));
assert_struct(
deref.unwrap().value(),
"dyn core::ops::function::FnOnce<(), Output=()>",
|_, _| {},
);
}
1 => {
let exp_type = if rust_version >= Version((1, 80, 0)) {
"&[usize; 4]"
} else {
"&[usize; 3]"
};
assert_member(member, "vtable", |val| assert_pointer(val, exp_type));
let member_val = member.value.clone();
let deref = trait_once
.clone()
.modify_value(|pcx, _| member_val.deref(pcx));
assert_array(deref.unwrap().value(), "[usize]", |_, _| {});
}
_ => panic!("2 members expected"),
},
);
assert_struct(
trait_mut.value(),
"alloc::boxed::Box<dyn core::ops::function::FnMut<(), Output=()>, alloc::alloc::Global>",
|i, member| match i {
0 => {
assert_member(member, "pointer", |val| {
assert_pointer(val, "*dyn core::ops::function::FnMut<(), Output=()>")
});
let member_val = member.value.clone();
let deref = trait_mut
.clone()
.modify_value(|pcx, _| member_val.deref(pcx));
assert_struct(
deref.unwrap().value(),
"dyn core::ops::function::FnMut<(), Output=()>",
|_, _| {},
);
}
1 => {
let exp_type = if rust_version >= Version((1, 80, 0)) {
"&[usize; 5]"
} else {
"&[usize; 3]"
};
assert_member(member, "vtable", |val| assert_pointer(val, exp_type));
let member_val = member.value.clone();
let deref = trait_mut
.clone()
.modify_value(|pcx, _| member_val.deref(pcx));
assert_array(deref.unwrap().value(), "[usize]", |_, _| {});
}
_ => panic!("2 members expected"),
},
);
assert_struct(
trait_fn.value(),
"alloc::boxed::Box<dyn core::ops::function::Fn<(), Output=()>, alloc::alloc::Global>",
|i, member| match i {
0 => {
assert_member(member, "pointer", |val| {
assert_pointer(val, "*dyn core::ops::function::Fn<(), Output=()>")
});
let member_val = member.value.clone();
let deref = trait_fn
.clone()
.modify_value(|pcx, _| member_val.deref(pcx));
assert_struct(
deref.unwrap().value(),
"dyn core::ops::function::Fn<(), Output=()>",
|_, _| {},
);
}
1 => {
let exp_type = if rust_version >= Version((1, 80, 0)) {
"&[usize; 6]"
} else {
"&[usize; 3]"
};
assert_member(member, "vtable", |val| assert_pointer(val, exp_type));
let member_val = member.value.clone();
let deref = trait_fn
.clone()
.modify_value(|pcx, _| member_val.deref(pcx));
assert_array(deref.unwrap().value(), "[usize]", |_, _| {});
}
_ => panic!("2 members expected"),
},
);
assert_pointer(fn_ptr.value(), "fn() -> u8");
let deref_fn_ptr = fn_ptr.clone().modify_value(|ctx, v| v.deref(ctx));
assert!(deref_fn_ptr.is_none());
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_arguments() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 232).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(232));
read_arg_dqe!(debugger, Dqe::Variable(Selector::Any) => by_val, by_ref, vec, box_arr);
assert_idents!(by_val => "by_val", by_ref => "by_ref", vec => "vec", box_arr => "box_arr");
assert_scalar(by_val.value(), "i32", Some(SupportedScalar::I32(1)));
assert_pointer(by_ref.value(), "&i32");
let deref = by_ref.clone().modify_value(|pcx, value| value.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
assert_vec(vec.value(), "Vec<u8, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[u8]", |i, item| match i {
0 => assert_scalar(item, "u8", Some(SupportedScalar::U8(3))),
1 => assert_scalar(item, "u8", Some(SupportedScalar::U8(4))),
2 => assert_scalar(item, "u8", Some(SupportedScalar::U8(5))),
_ => panic!("3 items expected"),
})
});
assert_struct(
box_arr.value(),
"alloc::boxed::Box<[u8], alloc::alloc::Global>",
|i, member| match i {
0 => {
assert_member(member, "data_ptr", |val| assert_pointer(val, "*u8"));
let data_ptr_val = member.value.clone();
let deref = box_arr
.clone()
.modify_value(|pcx, _| data_ptr_val.deref(pcx));
assert_scalar(deref.unwrap().value(), "u8", Some(SupportedScalar::U8(6)));
}
1 => assert_member(member, "length", |val| {
assert_scalar(val, "usize", Some(SupportedScalar::Usize(3)))
}),
_ => panic!("2 members expected"),
},
);
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_union() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 244).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(244));
read_locals!(debugger => union);
assert_idents!(union => "union");
assert_struct(union.value(), "Union1", |i, member| match i {
0 => assert_member(member, "f1", |val| {
assert_scalar(val, "f32", Some(SupportedScalar::F32(1.1)))
}),
1 => {}
2 => {}
_ => panic!("3 members expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_hashmap() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 290).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(290));
let rust_version = rust_version(VARS_APP).unwrap();
let hash_map_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<bool, i64, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<bool, i64, std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<bool, i64, std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
read_locals!(debugger => hm1, hm2, hm3, hm4, _a, b, _hm5, _hm6);
assert_idents!(hm1 => "hm1", hm2 => "hm2", hm3 => "hm3", hm4 => "hm4");
assert_hashmap(hm1.value(), hash_map_type, |items| {
assert_eq!(items.len(), 2);
assert_scalar(&items[0].0, "bool", Some(SupportedScalar::Bool(false)));
assert_scalar(&items[0].1, "i64", Some(SupportedScalar::I64(5)));
assert_scalar(&items[1].0, "bool", Some(SupportedScalar::Bool(true)));
assert_scalar(&items[1].1, "i64", Some(SupportedScalar::I64(3)));
});
let hash_map_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<&str, alloc::vec::Vec<i32, alloc::alloc::Global>, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<&str, alloc::vec::Vec<i32, alloc::alloc::Global>, std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<&str, alloc::vec::Vec<i32, alloc::alloc::Global>, std::hash::random::RandomState, alloc::alloc::Global>",
).unwrap();
assert_hashmap(hm2.value(), hash_map_type, |items| {
assert_eq!(items.len(), 2);
assert_str(&items[0].0, "abc");
assert_vec(&items[0].1, "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
})
});
assert_str(&items[1].0, "efg");
assert_vec(&items[1].1, "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(11))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(12))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(13))),
_ => panic!("3 items expected"),
})
});
});
let hash_map_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<i32, i32, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<i32, i32, std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<i32, i32, std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
assert_hashmap(hm3.value(), hash_map_type, |items| {
assert_eq!(items.len(), 100);
let mut exp_items = (0..100).collect::<Vec<_>>();
exp_items.sort_by_key(|i1| i1.to_string());
for i in 0..100 {
assert_scalar(&items[i].0, "i32", Some(SupportedScalar::I32(exp_items[i])));
}
for i in 0..100 {
assert_scalar(&items[i].1, "i32", Some(SupportedScalar::I32(exp_items[i])));
}
});
let hash_map_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<alloc::string::String, std::collections::hash::map::HashMap<i32, i32, std::collections::hash::map::RandomState>, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<alloc::string::String, std::collections::hash::map::HashMap<i32, i32, std::hash::random::RandomState>, std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<alloc::string::String, std::collections::hash::map::HashMap<i32, i32, std::hash::random::RandomState, alloc::alloc::Global>, std::hash::random::RandomState, alloc::alloc::Global>",
).unwrap();
let inner_hash_map_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<i32, i32, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<i32, i32, std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<i32, i32, std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
assert_hashmap(hm4.value(), hash_map_type, |items| {
assert_eq!(items.len(), 2);
assert_string(&items[0].0, "1");
assert_hashmap(&items[0].1, inner_hash_map_type, |items| {
assert_eq!(items.len(), 2);
assert_scalar(&items[0].0, "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[0].1, "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[1].0, "i32", Some(SupportedScalar::I32(2)));
assert_scalar(&items[1].1, "i32", Some(SupportedScalar::I32(2)));
});
assert_string(&items[1].0, "3");
assert_hashmap(&items[1].1, inner_hash_map_type, |items| {
assert_eq!(items.len(), 2);
assert_scalar(&items[0].0, "i32", Some(SupportedScalar::I32(3)));
assert_scalar(&items[0].1, "i32", Some(SupportedScalar::I32(3)));
assert_scalar(&items[1].0, "i32", Some(SupportedScalar::I32(4)));
assert_scalar(&items[1].1, "i32", Some(SupportedScalar::I32(4)));
});
});
let make_idx_dqe = |var: &str, literal| {
Dqe::Index(Dqe::Variable(Selector::by_name(var, true)).boxed(), literal)
};
let dqe = make_idx_dqe("hm1", Literal::Bool(true));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "i64", Some(SupportedScalar::I64(3)));
let dqe = make_idx_dqe("hm2", Literal::String("efg".to_string()));
read_var_dqe!(debugger, dqe => val);
assert_vec(val.value(), "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(11))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(12))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(13))),
_ => panic!("3 items expected"),
})
});
let dqe = make_idx_dqe("hm3", Literal::Int(99));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "i32", Some(SupportedScalar::I32(99)));
let Value::Pointer(ptr) = &b.value() else {
panic!("not a pointer")
};
let ptr_val = ptr.value.unwrap() as usize;
let dqe = make_idx_dqe("hm5", Literal::Address(ptr_val));
read_var_dqe!(debugger, dqe => val);
assert_str(val.value(), "b");
let dqe = make_idx_dqe(
"hm6",
Literal::AssocArray(HashMap::from([
(
"field_1".to_string(),
LiteralOrWildcard::Literal(Literal::Int(1)),
),
(
"field_2".to_string(),
LiteralOrWildcard::Literal(Literal::Array(Box::new([
LiteralOrWildcard::Literal(Literal::String("a".to_string())),
LiteralOrWildcard::Wildcard,
]))),
),
(
"field_3".to_string(),
LiteralOrWildcard::Literal(Literal::EnumVariant(
"Some".to_string(),
Some(Box::new(Literal::Array(Box::new([
LiteralOrWildcard::Literal(Literal::Bool(true)),
])))),
)),
),
])),
);
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "i32", Some(SupportedScalar::I32(1)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_hashset() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 307).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(307));
let rust_version = rust_version(VARS_APP).unwrap();
let hashset_type = version_switch!(
rust_version,
.. (1 . 76) => "HashSet<i32, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashSet<i32, std::hash::random::RandomState>",
(1 . 94) .. => "HashSet<i32, std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
read_locals!(debugger => hs1, hs2, hs3, _a, b, _hs4);
assert_idents!(hs1 => "hs1", hs2 => "hs2", hs3 => "hs3");
assert_hashset(hs1.value(), hashset_type, |items| {
assert_eq!(items.len(), 4);
assert_scalar(&items[0], "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[1], "i32", Some(SupportedScalar::I32(2)));
assert_scalar(&items[2], "i32", Some(SupportedScalar::I32(3)));
assert_scalar(&items[3], "i32", Some(SupportedScalar::I32(4)));
});
assert_hashset(hs2.value(), hashset_type, |items| {
assert_eq!(items.len(), 100);
let mut exp_items = (0..100).collect::<Vec<_>>();
exp_items.sort_by_key(|i1| i1.to_string());
for i in 0..100 {
assert_scalar(&items[i], "i32", Some(SupportedScalar::I32(exp_items[i])));
}
});
let hashset_type = version_switch!(
rust_version,
.. (1 . 76) => "HashSet<alloc::vec::Vec<i32, alloc::alloc::Global>, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashSet<alloc::vec::Vec<i32, alloc::alloc::Global>, std::hash::random::RandomState>",
(1 . 94) .. => "HashSet<alloc::vec::Vec<i32, alloc::alloc::Global>, std::hash::random::RandomState, alloc::alloc::Global>",
).unwrap();
assert_hashset(hs3.value(), hashset_type, |items| {
assert_eq!(items.len(), 1);
assert_vec(&items[0], "Vec<i32, alloc::alloc::Global>", 2, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
_ => panic!("2 items expected"),
})
});
});
let make_idx_dqe = |var: &str, literal| {
Dqe::Index(Dqe::Variable(Selector::by_name(var, true)).boxed(), literal)
};
let dqe = make_idx_dqe("hs1", Literal::Int(2));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(true)));
let dqe = make_idx_dqe("hs1", Literal::Int(5));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(false)));
let Value::Pointer(ptr) = &b.value() else {
panic!("not a pointer")
};
let ptr_val = ptr.value.unwrap() as usize;
let dqe = make_idx_dqe("hs4", Literal::Address(ptr_val));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(true)));
let dqe = make_idx_dqe("hs4", Literal::Address(0));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(false)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_circular_ref_types() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 334).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(334));
read_locals!(debugger => a_circ, b_circ);
assert_idents!(a_circ => "a_circ", b_circ => "b_circ");
assert_rc(
a_circ.value(),
"Rc<vars::circular::List, alloc::alloc::Global>",
);
assert_rc(
b_circ.value(),
"Rc<vars::circular::List, alloc::alloc::Global>",
);
let deref = a_circ.clone().modify_value(|pcx, v| v.deref(pcx));
let rust_version = rust_version(VARS_APP).unwrap();
let deref_type = version_switch!(
rust_version,
.. (1 . 84) => "RcBox<vars::circular::List>",
(1 . 84) .. => "RcInner<vars::circular::List>",
)
.unwrap();
assert_struct(deref.unwrap().value(), deref_type, |i, member| match i {
0 => assert_member(member, "strong", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(2)))
})
}),
1 => assert_member(member, "weak", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(1)))
})
}),
2 => {
assert_member(member, "value", |val| {
assert_rust_enum(val, "List", |enum_member| {
assert_struct(enum_member, "Cons", |i, cons_member| match i {
0 => assert_member(cons_member, "__0", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(5)))
}),
1 => assert_member(cons_member, "__1", |val| {
assert_refcell(
val,
"RefCell<alloc::rc::Rc<vars::circular::List, alloc::alloc::Global>>",
0,
|inner| {
assert_rc(
inner,
"Rc<vars::circular::List, alloc::alloc::Global>",
)
},
)
}),
_ => panic!("2 members expected"),
});
})
});
}
_ => panic!("3 members expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_lexical_blocks() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 340).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(340));
read_locals!(debugger => alpha, _beta);
assert_idents!(alpha => "alpha");
debugger.set_breakpoint_at_line("vars.rs", 342).unwrap();
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(342));
read_locals!(debugger => alpha, beta);
assert_idents!(alpha => "alpha", beta => "beta");
debugger.set_breakpoint_at_line("vars.rs", 343).unwrap();
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(343));
read_locals!(debugger => alpha, beta, gama);
assert_idents!(alpha => "alpha", beta => "beta", gama => "gama");
debugger.set_breakpoint_at_line("vars.rs", 349).unwrap();
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(349));
read_locals!(debugger => alpha, delta);
assert_idents!(alpha => "alpha", delta => "delta");
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_btree_map() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 396).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(396));
read_locals!(debugger => hm1, hm2, hm3, hm4, _a, b, _hm5, _hm6);
assert_idents!(hm1 => "hm1", hm2 => "hm2", hm3 => "hm3", hm4 => "hm4");
assert_btree_map(
hm1.value(),
"BTreeMap<bool, i64, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 2);
assert_scalar(&items[0].0, "bool", Some(SupportedScalar::Bool(false)));
assert_scalar(&items[0].1, "i64", Some(SupportedScalar::I64(5)));
assert_scalar(&items[1].0, "bool", Some(SupportedScalar::Bool(true)));
assert_scalar(&items[1].1, "i64", Some(SupportedScalar::I64(3)));
},
);
assert_btree_map(
hm2.value(),
"BTreeMap<&str, alloc::vec::Vec<i32, alloc::alloc::Global>, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 2);
assert_str(&items[0].0, "abc");
assert_vec(&items[0].1, "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
})
});
assert_str(&items[1].0, "efg");
assert_vec(&items[1].1, "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(11))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(12))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(13))),
_ => panic!("3 items expected"),
})
});
},
);
assert_btree_map(
hm3.value(),
"BTreeMap<i32, i32, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 100);
let exp_items = (0..100).collect::<Vec<_>>();
for i in 0..100 {
assert_scalar(&items[i].0, "i32", Some(SupportedScalar::I32(exp_items[i])));
}
for i in 0..100 {
assert_scalar(&items[i].1, "i32", Some(SupportedScalar::I32(exp_items[i])));
}
},
);
assert_btree_map(
hm4.value(),
"BTreeMap<alloc::string::String, alloc::collections::btree::map::BTreeMap<i32, i32, alloc::alloc::Global>, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 2);
assert_string(&items[0].0, "1");
assert_btree_map(
&items[0].1,
"BTreeMap<i32, i32, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 2);
assert_scalar(&items[0].0, "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[0].1, "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[1].0, "i32", Some(SupportedScalar::I32(2)));
assert_scalar(&items[1].1, "i32", Some(SupportedScalar::I32(2)));
},
);
assert_string(&items[1].0, "3");
assert_btree_map(
&items[1].1,
"BTreeMap<i32, i32, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 2);
assert_scalar(&items[0].0, "i32", Some(SupportedScalar::I32(3)));
assert_scalar(&items[0].1, "i32", Some(SupportedScalar::I32(3)));
assert_scalar(&items[1].0, "i32", Some(SupportedScalar::I32(4)));
assert_scalar(&items[1].1, "i32", Some(SupportedScalar::I32(4)));
},
);
},
);
let make_idx_dqe = |var: &str, literal| {
Dqe::Index(Dqe::Variable(Selector::by_name(var, true)).boxed(), literal)
};
let dqe = make_idx_dqe("hm1", Literal::Bool(true));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "i64", Some(SupportedScalar::I64(3)));
let dqe = make_idx_dqe("hm2", Literal::String("efg".to_string()));
read_var_dqe!(debugger, dqe => val);
assert_vec(val.value(), "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(11))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(12))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(13))),
_ => panic!("3 items expected"),
})
});
let dqe = make_idx_dqe("hm3", Literal::Int(99));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "i32", Some(SupportedScalar::I32(99)));
let Value::Pointer(ptr) = b.value() else {
panic!("not a pointer")
};
let ptr_val = ptr.value.unwrap() as usize;
let dqe = make_idx_dqe("hm5", Literal::Address(ptr_val));
read_var_dqe!(debugger, dqe => val);
assert_str(val.value(), "b");
let dqe = make_idx_dqe(
"hm6",
Literal::AssocArray(HashMap::from([
("field_1".to_string(), LiteralOrWildcard::Wildcard),
(
"field_2".to_string(),
LiteralOrWildcard::Literal(Literal::Array(Box::new([
LiteralOrWildcard::Literal(Literal::String("c".to_string())),
LiteralOrWildcard::Wildcard,
LiteralOrWildcard::Wildcard,
]))),
),
(
"field_3".to_string(),
LiteralOrWildcard::Literal(Literal::EnumVariant("None".to_string(), None)),
),
])),
);
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "i32", Some(SupportedScalar::I32(2)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_btree_set() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 413).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(413));
read_locals!(debugger => hs1, hs2, hs3, _a, b, _hs4);
assert_idents!(hs1 => "hs1", hs2 => "hs2", hs3 => "hs3");
assert_btree_set(
hs1.value(),
"BTreeSet<i32, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 4);
assert_scalar(&items[0], "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[1], "i32", Some(SupportedScalar::I32(2)));
assert_scalar(&items[2], "i32", Some(SupportedScalar::I32(3)));
assert_scalar(&items[3], "i32", Some(SupportedScalar::I32(4)));
},
);
assert_btree_set(
hs2.value(),
"BTreeSet<i32, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 100);
let exp_items = (0..100).collect::<Vec<_>>();
for i in 0..100 {
assert_scalar(&items[i], "i32", Some(SupportedScalar::I32(exp_items[i])));
}
},
);
assert_btree_set(
hs3.value(),
"BTreeSet<alloc::vec::Vec<i32, alloc::alloc::Global>, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 1);
assert_vec(&items[0], "Vec<i32, alloc::alloc::Global>", 2, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
_ => panic!("2 items expected"),
})
});
},
);
let make_idx_dqe = |var: &str, literal| {
Dqe::Index(Dqe::Variable(Selector::by_name(var, true)).boxed(), literal)
};
let dqe = make_idx_dqe("hs1", Literal::Int(2));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(true)));
let dqe = make_idx_dqe("hs1", Literal::Int(5));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(false)));
let Value::Pointer(ptr) = b.value() else {
panic!("not a pointer")
};
let ptr_val = ptr.value.unwrap() as usize;
let dqe = make_idx_dqe("hs4", Literal::Address(ptr_val));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(true)));
let dqe = make_idx_dqe("hs4", Literal::Address(0));
read_var_dqe!(debugger, dqe => val);
assert_scalar(val.value(), "bool", Some(SupportedScalar::Bool(false)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_vec_deque() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 431).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(431));
read_locals!(debugger => vd1, vd2);
assert_idents!(vd1 => "vd1", vd2 => "vd2");
assert_vec_deque(
vd1.value(),
"VecDeque<i32, alloc::alloc::Global>",
8,
|buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(9))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(10))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(0))),
3 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
4 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
_ => panic!("5 items expected"),
})
},
);
assert_vec_deque(
vd2.value(),
"VecDeque<alloc::collections::vec_deque::VecDeque<i32, alloc::alloc::Global>, alloc::alloc::Global>",
4,
|buf| {
assert_array(
buf,
"[VecDeque<i32, alloc::alloc::Global>]",
|i, item| match i {
0 => assert_vec_deque(item, "VecDeque<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-2))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-1))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(0))),
_ => panic!("3 items expected"),
})
}),
1 => assert_vec_deque(item, "VecDeque<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
})
}),
2 => assert_vec_deque(item, "VecDeque<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(4))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(5))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(6))),
_ => panic!("3 items expected"),
})
}),
_ => panic!("3 items expected"),
},
)
},
);
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_atomic() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 441).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(441));
read_locals!(debugger => int32_atomic, _int32, int32_atomic_ptr);
assert_idents!(int32_atomic => "int32_atomic", int32_atomic_ptr => "int32_atomic_ptr");
assert_struct(int32_atomic.value(), "AtomicI32", |i, member| match i {
0 => assert_member(member, "v", |val| {
assert_struct(val, "UnsafeCell<i32>", |i, member| match i {
0 => assert_member(member, "value", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
_ => panic!("1 members expected"),
})
}),
_ => panic!("1 members expected"),
});
assert_struct(
int32_atomic_ptr.value(),
"AtomicPtr<i32>",
|i, member| match i {
0 => assert_member(member, "p", |val| {
assert_struct(val, "UnsafeCell<*mut i32>", |i, member| match i {
0 => assert_member(member, "value", |val| assert_pointer(val, "*mut i32")),
_ => panic!("1 members expected"),
})
}),
_ => panic!("1 members expected"),
},
);
let deref = int32_atomic_ptr
.clone()
.modify_value(|pcx, v| v.field("p").unwrap().field("value").unwrap().deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_cell() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 453).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(453));
read_locals!(debugger => a_cell, b_refcell, _b_refcell_borrow_1, _b_refcell_borrow_2);
assert_idents!(a_cell => "a_cell", b_refcell => "b_refcell");
assert_cell(a_cell.value(), "Cell<i32>", |value| {
assert_scalar(value, "i32", Some(SupportedScalar::I32(1)))
});
assert_refcell(
b_refcell.value(),
"RefCell<alloc::vec::Vec<i32, alloc::alloc::Global>>",
2,
|value| {
assert_vec(value, "Vec<i32, alloc::alloc::Global>", 3, |buf| {
assert_array(buf, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("3 items expected"),
})
})
},
);
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_shared_ptr() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
let rust_version = rust_version(VARS_APP).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 475).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(475));
read_locals!(debugger => rc0, rc1, weak_rc2, arc0, arc1, weak_arc2);
assert_idents!(
rc0 => "rc0", rc1 => "rc1", weak_rc2 => "weak_rc2", arc0 => "arc0", arc1 => "arc1", weak_arc2 => "weak_arc2"
);
assert_rc(rc0.value(), "Rc<i32, alloc::alloc::Global>");
let deref = rc0.clone().modify_value(|pcx, v| v.deref(pcx));
let deref_type = version_switch!(
rust_version,
.. (1 . 84) => "RcBox<i32>",
(1 . 84) .. => "RcInner<i32>",
)
.unwrap();
assert_struct(deref.unwrap().value(), deref_type, |i, member| match i {
0 => assert_member(member, "strong", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(2)))
})
}),
1 => assert_member(member, "weak", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(2)))
})
}),
2 => assert_member(member, "value", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
_ => panic!("3 members expected"),
});
assert_rc(rc1.value(), "Rc<i32, alloc::alloc::Global>");
let deref = rc1.clone().modify_value(|pcx, v| v.deref(pcx));
assert_struct(deref.unwrap().value(), deref_type, |i, member| match i {
0 => assert_member(member, "strong", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(2)))
})
}),
1 => assert_member(member, "weak", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(2)))
})
}),
2 => assert_member(member, "value", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
_ => panic!("3 members expected"),
});
assert_rc(weak_rc2.value(), "Weak<i32, alloc::alloc::Global>");
let deref = weak_rc2.clone().modify_value(|pcx, v| v.deref(pcx));
assert_struct(deref.unwrap().value(), deref_type, |i, member| match i {
0 => assert_member(member, "strong", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(2)))
})
}),
1 => assert_member(member, "weak", |val| {
assert_cell(val, "Cell<usize>", |inner| {
assert_scalar(inner, "usize", Some(SupportedScalar::Usize(2)))
})
}),
2 => assert_member(member, "value", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
_ => panic!("3 members expected"),
});
assert_arc(arc0.value(), "Arc<i32, alloc::alloc::Global>");
let deref = arc0.clone().modify_value(|pcx, v| v.deref(pcx));
assert_struct(
deref.unwrap().value(),
"ArcInner<i32>",
|i, member| match i {
0 => assert_member(member, "strong", |val| {
assert_struct(val, "AtomicUsize", |i, member| match i {
0 => assert_member(member, "v", |val| {
assert_struct(val, "UnsafeCell<usize>", |_, member| {
assert_member(member, "value", |val| {
assert_scalar(val, "usize", Some(SupportedScalar::Usize(2)))
})
})
}),
_ => panic!("1 member expected"),
})
}),
1 => assert_member(member, "weak", |val| {
assert_struct(val, "AtomicUsize", |i, member| match i {
0 => assert_member(member, "v", |val| {
assert_struct(val, "UnsafeCell<usize>", |_, member| {
assert_member(member, "value", |val| {
assert_scalar(val, "usize", Some(SupportedScalar::Usize(2)))
})
})
}),
_ => panic!("1 member expected"),
})
}),
2 => assert_member(member, "data", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(2)))
}),
_ => panic!("3 members expected"),
},
);
assert_arc(arc1.value(), "Arc<i32, alloc::alloc::Global>");
let deref = arc1.clone().modify_value(|pcx, v| v.deref(pcx));
assert_struct(
deref.unwrap().value(),
"ArcInner<i32>",
|i, member| match i {
0 => assert_member(member, "strong", |val| {
assert_struct(val, "AtomicUsize", |i, member| match i {
0 => assert_member(member, "v", |val| {
assert_struct(val, "UnsafeCell<usize>", |_, member| {
assert_member(member, "value", |val| {
assert_scalar(val, "usize", Some(SupportedScalar::Usize(2)))
})
})
}),
_ => panic!("1 member expected"),
})
}),
1 => assert_member(member, "weak", |val| {
assert_struct(val, "AtomicUsize", |i, member| match i {
0 => assert_member(member, "v", |val| {
assert_struct(val, "UnsafeCell<usize>", |_, member| {
assert_member(member, "value", |val| {
assert_scalar(val, "usize", Some(SupportedScalar::Usize(2)))
})
})
}),
_ => panic!("1 member expected"),
})
}),
2 => assert_member(member, "data", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(2)))
}),
_ => panic!("3 members expected"),
},
);
assert_arc(weak_arc2.value(), "Weak<i32, alloc::alloc::Global>");
let deref = weak_arc2
.clone()
.modify_value(|pcx, v| v.deref(pcx))
.unwrap();
assert_struct(deref.value(), "ArcInner<i32>", |i, member| match i {
0 => assert_member(member, "strong", |val| {
assert_struct(val, "AtomicUsize", |i, member| match i {
0 => assert_member(member, "v", |val| {
assert_struct(val, "UnsafeCell<usize>", |_, member| {
assert_member(member, "value", |val| {
assert_scalar(val, "usize", Some(SupportedScalar::Usize(2)))
})
})
}),
_ => panic!("1 member expected"),
})
}),
1 => assert_member(member, "weak", |val| {
assert_struct(val, "AtomicUsize", |i, member| match i {
0 => assert_member(member, "v", |val| {
assert_struct(val, "UnsafeCell<usize>", |_, member| {
assert_member(member, "value", |val| {
assert_scalar(val, "usize", Some(SupportedScalar::Usize(2)))
})
})
}),
_ => panic!("1 member expected"),
})
}),
2 => assert_member(member, "data", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(2)))
}),
_ => panic!("3 members expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_zst_types() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 496).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(496));
read_locals!(
debugger => ptr_zst, array_zst, vec_zst, slice_zst, struct_zst, enum_zst, vecdeque_zst,
hash_map_zst_key, hash_map_zst_val, hash_map_zst, hash_set_zst, btree_map_zst_key,
btree_map_zst_val, btree_map_zst, btree_set_zst
);
assert_idents!(
ptr_zst => "ptr_zst", array_zst => "array_zst", vec_zst => "vec_zst",
slice_zst => "slice_zst", struct_zst => "struct_zst", enum_zst => "enum_zst",
vecdeque_zst => "vecdeque_zst", hash_map_zst_key => "hash_map_zst_key",
hash_map_zst_val => "hash_map_zst_val", hash_map_zst => "hash_map_zst",
hash_set_zst => "hash_set_zst", btree_map_zst_key => "btree_map_zst_key",
btree_map_zst_val => "btree_map_zst_val", btree_map_zst => "btree_map_zst",
btree_set_zst => "btree_set_zst"
);
assert_pointer(ptr_zst.value(), "&()");
let deref = ptr_zst.clone().modify_value(|pcx, v| v.deref(pcx));
assert_scalar(deref.unwrap().value(), "()", Some(SupportedScalar::Empty()));
assert_array(array_zst.value(), "[()]", |i, item| match i {
0 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
1 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
_ => panic!("2 members expected"),
});
assert_vec(vec_zst.value(), "Vec<(), alloc::alloc::Global>", 0, |buf| {
assert_array(buf, "[()]", |i, item| match i {
0 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
1 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
2 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
_ => panic!("3 members expected"),
})
});
assert_pointer(slice_zst.value(), "&[(); 4]");
let deref = slice_zst.clone().modify_value(|pcx, v| v.deref(pcx));
assert_array(deref.unwrap().value(), "[()]", |i, item| match i {
0 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
1 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
2 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
3 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
_ => panic!("4 members expected"),
});
assert_struct(struct_zst.value(), "StructZst", |i, member| match i {
0 => assert_member(member, "__0", |val| {
assert_scalar(val, "()", Some(SupportedScalar::Empty()))
}),
_ => panic!("1 member expected"),
});
assert_rust_enum(enum_zst.value(), "Option<()>", |member| {
assert_struct(member, "Some", |i, member| match i {
0 => assert_member(member, "__0", |val| {
assert_scalar(val, "()", Some(SupportedScalar::Empty()))
}),
_ => panic!("1 member expected"),
})
});
assert_vec_deque(
vecdeque_zst.value(),
"VecDeque<(), alloc::alloc::Global>",
0,
|buf| {
assert_array(buf, "[()]", |i, item| match i {
0 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
1 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
2 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
3 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
4 => assert_scalar(item, "()", Some(SupportedScalar::Empty())),
_ => panic!("5 members expected"),
})
},
);
let rust_version = rust_version(VARS_APP).unwrap();
let hashmap_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<(), i32, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<(), i32, std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<(), i32, std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
assert_hashmap(hash_map_zst_key.value(), hashmap_type, |items| {
assert_eq!(items.len(), 1);
assert_scalar(&items[0].0, "()", Some(SupportedScalar::Empty()));
assert_scalar(&items[0].1, "i32", Some(SupportedScalar::I32(1)));
});
let hashmap_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<i32, (), std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<i32, (), std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<i32, (), std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
assert_hashmap(hash_map_zst_val.value(), hashmap_type, |items| {
assert_eq!(items.len(), 1);
assert_scalar(&items[0].0, "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[0].1, "()", Some(SupportedScalar::Empty()));
});
let hashmap_type = version_switch!(
rust_version,
.. (1 . 76) => "HashMap<(), (), std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashMap<(), (), std::hash::random::RandomState>",
(1 . 94) .. => "HashMap<(), (), std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
assert_hashmap(hash_map_zst.value(), hashmap_type, |items| {
assert_eq!(items.len(), 1);
assert_scalar(&items[0].0, "()", Some(SupportedScalar::Empty()));
assert_scalar(&items[0].1, "()", Some(SupportedScalar::Empty()));
});
let hashset_type = version_switch!(
rust_version,
.. (1 . 76) => "HashSet<(), std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "HashSet<(), std::hash::random::RandomState>",
(1 . 94) .. => "HashSet<(), std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
assert_hashset(hash_set_zst.value(), hashset_type, |items| {
assert_eq!(items.len(), 1);
assert_scalar(&items[0], "()", Some(SupportedScalar::Empty()));
});
assert_btree_map(
btree_map_zst_key.value(),
"BTreeMap<(), i32, alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 1);
assert_scalar(&items[0].0, "()", Some(SupportedScalar::Empty()));
assert_scalar(&items[0].1, "i32", Some(SupportedScalar::I32(1)));
},
);
assert_btree_map(
btree_map_zst_val.value(),
"BTreeMap<i32, (), alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 2);
assert_scalar(&items[0].0, "i32", Some(SupportedScalar::I32(1)));
assert_scalar(&items[0].1, "()", Some(SupportedScalar::Empty()));
assert_scalar(&items[1].0, "i32", Some(SupportedScalar::I32(2)));
assert_scalar(&items[1].1, "()", Some(SupportedScalar::Empty()));
},
);
assert_btree_map(
btree_map_zst.value(),
"BTreeMap<(), (), alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 1);
assert_scalar(&items[0].0, "()", Some(SupportedScalar::Empty()));
assert_scalar(&items[0].1, "()", Some(SupportedScalar::Empty()));
},
);
assert_btree_set(
btree_set_zst.value(),
"BTreeSet<(), alloc::alloc::Global>",
|items| {
assert_eq!(items.len(), 1);
assert_scalar(&items[0], "()", Some(SupportedScalar::Empty()));
},
);
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_static_in_fn_variable() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 504).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 678).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(504));
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name("INNER_STATIC", false)) => inner_static);
assert_idents!(inner_static => "vars::inner_static::INNER_STATIC");
assert_scalar(inner_static.value(), "u32", Some(SupportedScalar::U32(1)));
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(678));
read_var_dqe!(debugger, Dqe::Variable(Selector::by_name("INNER_STATIC", false)) => inner_static);
assert_idents!(inner_static => "vars::inner_static::INNER_STATIC");
assert_scalar(inner_static.value(), "u32", Some(SupportedScalar::U32(1)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_slice_operator() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 61).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(61));
read_var_dqe!(debugger, Dqe::Slice(
Dqe::Variable(Selector::by_name("arr_1", true)).boxed(),
None,
None,
) => arr_1);
assert_idents!(arr_1 => "arr_1");
assert_array(arr_1.value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-1))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
3 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-2))),
4 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("5 items expected"),
});
read_var_dqe!(debugger, Dqe::Slice(
Dqe::Variable(Selector::by_name("arr_1", true)).boxed(),
Some(3),
None,
) => arr_1);
assert_idents!(arr_1 => "arr_1");
assert_array(arr_1.value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-2))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(3))),
_ => panic!("2 items expected"),
});
read_var_dqe!(debugger, Dqe::Slice(
Dqe::Variable(Selector::by_name("arr_1", true)).boxed(),
None,
Some(2),
) => arr_1);
assert_idents!(arr_1 => "arr_1");
assert_array(arr_1.value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-1))),
_ => panic!("2 items expected"),
});
read_var_dqe!(debugger, Dqe::Slice(
Dqe::Variable(Selector::by_name("arr_1", true)).boxed(),
Some(1),
Some(4),
) => arr_1);
assert_idents!(arr_1 => "arr_1");
assert_array(arr_1.value(), "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
2 => assert_scalar(item, "i32", Some(SupportedScalar::I32(-2))),
_ => panic!("3 items expected"),
});
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_cast_pointers() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 119).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(119));
read_locals!(debugger => a, ref_a, _ptr_a, _ptr_ptr_a, _b, _mut_ref_b, _c, _mut_ptr_c, _box_d, _f, _ref_f);
assert_scalar(a.value(), "i32", Some(SupportedScalar::I32(2)));
let Value::Pointer(pointer) = ref_a.value() else {
panic!("expect a pointer");
};
let raw_ptr = pointer.value.unwrap();
read_var_dqe!(debugger, Dqe::Deref(
Dqe::PtrCast(PointerCast::new(raw_ptr as usize, "*const i32")).boxed(),
) => val);
assert_scalar(val.value(), "i32", Some(SupportedScalar::I32(2)));
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_uuid() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 519).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(519));
read_locals!(debugger => uuid_v4, uuid_v7);
assert_idents!(uuid_v4 => "uuid_v4", uuid_v7 => "uuid_v7");
assert_uuid(uuid_v4.value(), "Uuid");
assert_uuid(uuid_v7.value(), "Uuid");
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_address_operator() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 119).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(119));
fn addr_of(name: &str, loc: bool) -> Dqe {
Dqe::Address(Dqe::Variable(Selector::by_name(name, loc)).boxed())
}
fn addr_of_index(name: &str, index: i32) -> Dqe {
Dqe::Address(
Dqe::Index(
Dqe::Variable(Selector::by_name(name, true)).boxed(),
Literal::Int(index as i64),
)
.boxed(),
)
}
fn addr_of_field(name: &str, field: &str) -> Dqe {
Dqe::Address(
Dqe::Field(
Dqe::Variable(Selector::by_name(name, true)).boxed(),
field.to_string(),
)
.boxed(),
)
}
let addr_a_dqe = addr_of("a", true);
read_var_dqe!(debugger, addr_a_dqe.clone() => a);
assert_pointer(a.value(), "&i32");
read_var_dqe!(debugger, Dqe::Deref(addr_a_dqe.boxed()) => a);
assert_scalar(a.value(), "i32", Some(SupportedScalar::I32(2)));
read_var_dqe!(debugger, addr_of("ref_a", true) => addr_ptr_a);
assert_pointer(addr_ptr_a.value(), "&&i32");
read_var_dqe!(debugger, Dqe::Deref(
Dqe::Deref(addr_of("ref_a", true).boxed()).boxed(),
) => a);
assert_scalar(a.value(), "i32", Some(SupportedScalar::I32(2)));
read_var_dqe!(debugger, addr_of("f", true) => addr_f);
assert_pointer(addr_f.value(), "&Foo");
read_var_dqe!(debugger, Dqe::Deref(addr_of("f", true).boxed()) => f);
assert_struct(f.value(), "Foo", |i, member| match i {
0 => assert_member(member, "bar", |val| {
assert_scalar(val, "i32", Some(SupportedScalar::I32(1)))
}),
1 => assert_member(member, "baz", |val| {
assert_array(val, "[i32]", |i, item| match i {
0 => assert_scalar(item, "i32", Some(SupportedScalar::I32(1))),
1 => assert_scalar(item, "i32", Some(SupportedScalar::I32(2))),
_ => panic!("2 items expected"),
})
}),
2 => {
assert_member(member, "foo", |val| assert_pointer(val, "&i32"));
let member_val = member.value.clone();
let deref = f.clone().modify_value(|pcx, _| member_val.deref(pcx));
assert_scalar(deref.unwrap().value(), "i32", Some(SupportedScalar::I32(2)));
}
_ => panic!("3 members expected"),
});
read_var_dqe!(debugger, addr_of_field("f", "bar") => addr_f_bar);
assert_pointer(addr_f_bar.value(), "&i32");
read_var_dqe!(debugger, Dqe::Deref(addr_of_field("f", "bar").boxed()) => f_bar);
assert_scalar(f_bar.value(), "i32", Some(SupportedScalar::I32(1)));
debugger.set_breakpoint_at_line("vars.rs", 151).unwrap();
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(151));
read_var_dqe!(debugger, addr_of("vec1", true) => addr_vec1);
assert_pointer(addr_vec1.value(), "&Vec<i32, alloc::alloc::Global>");
read_var_dqe!(debugger, addr_of_index("vec1", 1) => addr_el_1);
assert_pointer(addr_el_1.value(), "&i32");
read_var_dqe!(debugger, Dqe::Deref(addr_of_index("vec1", 1).boxed()) => el_1);
assert_scalar(el_1.value(), "i32", Some(SupportedScalar::I32(2)));
debugger.set_breakpoint_at_line("vars.rs", 290).unwrap();
debugger.continue_debugee().unwrap();
assert_eq!(info.line.take(), Some(290));
read_var_dqe!(debugger, addr_of("hm3", true) => addr_hm3);
let inner_hash_map_type = version_switch!(
rust_version(VARS_APP).unwrap(),
.. (1 . 76) => "&HashMap<i32, i32, std::collections::hash::map::RandomState>",
(1 . 76) .. (1 . 94) => "&HashMap<i32, i32, std::hash::random::RandomState>",
(1 . 94) .. => "&HashMap<i32, i32, std::hash::random::RandomState, alloc::alloc::Global>",
)
.unwrap();
assert_pointer(addr_hm3.value(), inner_hash_map_type);
read_var_dqe!(debugger, addr_of_index("hm3", 11) => addr_el_11);
assert_pointer(addr_el_11.value(), "&i32");
read_var_dqe!(debugger, Dqe::Deref(addr_of_index("hm3", 11).boxed()) => el_11);
assert_scalar(el_11.value(), "i32", Some(SupportedScalar::I32(11)));
read_var_dqe!(debugger, addr_of("GLOB_1", false) => addr_glob_1);
assert_pointer(addr_glob_1.value(), "&&str");
read_var_dqe!(debugger, Dqe::Deref(addr_of("GLOB_1", false).boxed()) => glob_1);
assert_str(glob_1.value(), "glob_1");
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_read_time() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 529).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(529));
read_locals!(debugger => system_time, instant);
assert_idents!(system_time => "system_time", instant => "instant");
assert_system_time(system_time.value(), (0, 0));
assert_instant(instant.value());
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_debug_trait_repr_vars() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_line("vars.rs", 641).unwrap();
debugger.start_debugee().unwrap();
assert_eq!(info.line.take(), Some(641));
read_locals!(debugger => v1, v2, v3, s0, s1, s2, s3, s4, str_array, c_enum, r_enum1, r_enum2, opt, my_str, my_string);
let fmt_string = call_debug_fmt(&debugger, &v1).unwrap();
assert_eq!(fmt_string, "[]");
let fmt_string = call_debug_fmt(&debugger, &v2).unwrap();
assert_eq!(fmt_string, "[]");
let fmt_string = call_debug_fmt(&debugger, &v3).unwrap();
assert_eq!(fmt_string, "[1, 23, 3]");
let fmt_string = call_debug_fmt(&debugger, &s0).unwrap();
assert_eq!(fmt_string, "Struct0 { a: 1 }");
let fmt_string = call_debug_fmt(&debugger, &s1).unwrap();
assert_eq!(fmt_string, "Struct1 { field1: 1, field2: 3 }");
let fmt_string = call_debug_fmt(&debugger, &s2).unwrap();
assert_eq!(fmt_string, "Struct1 { field1: 1, field2: \"44\" }");
let fmt_string = call_debug_fmt(&debugger, &s3).unwrap();
assert_eq!(fmt_string, "Struct2 { field1: \"66\", field2: 55 }");
let fmt_string = call_debug_fmt(&debugger, &s4).unwrap();
assert_eq!(fmt_string, "Struct3 { field1: 11, field2: 12 }");
let fmt_string = call_debug_fmt(&debugger, &str_array).unwrap();
assert_eq!(fmt_string, "[\"abc\", \"ef\", \"g\"]");
let fmt_string = call_debug_fmt(&debugger, &c_enum).unwrap();
assert_eq!(fmt_string, "A");
let fmt_string = call_debug_fmt(&debugger, &r_enum1).unwrap();
assert_eq!(fmt_string, "S1(Struct1 { field1: 100, field2: \"100\" })");
let fmt_string = call_debug_fmt(&debugger, &r_enum2).unwrap();
assert_eq!(fmt_string, "S2(Struct2 { field1: 1, field2: 2 })");
let fmt_string = call_debug_fmt(&debugger, &opt).unwrap();
assert_eq!(fmt_string, "Some(1)");
let fmt_string = call_debug_fmt(&debugger, &my_str).unwrap();
assert_eq!(fmt_string, "\"some str\"");
let fmt_string = call_debug_fmt(&debugger, &my_string).unwrap();
assert_eq!(fmt_string, "\"some string\"");
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}
#[test]
#[serial]
fn test_debug_trait_repr_args() {
let process = prepare_debugee_process(VARS_APP, &[]);
let debugee_pid = process.pid();
let info = TestInfo::default();
let builder = DebuggerBuilder::new().with_hooks(TestHooks::new(info.clone()));
let mut debugger = builder.build(process).unwrap();
debugger.set_breakpoint_at_fn("debug_fmt_args").unwrap();
debugger.start_debugee().unwrap();
read_arg_dqe!(debugger, Dqe::Variable(Selector::Any) => arg1, arg2);
let fmt_string = call_debug_fmt(&debugger, &arg1).unwrap();
assert_eq!(fmt_string, "\"one\"");
let fmt_string = call_debug_fmt(&debugger, &arg2).unwrap();
assert_eq!(fmt_string, "[\"two\", \"three\"]");
debugger.continue_debugee().unwrap();
assert_no_proc!(debugee_pid);
}