Skip to content

Commit

Permalink
feature: add canonic operator
Browse files Browse the repository at this point in the history
  • Loading branch information
godzie44 committed May 16, 2024
1 parent 90d9dcd commit 4c1933e
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ All notable changes to this project will be documented in this file.
objects for advanced searching
- console: improve index operation, now index accepts literal objects
- debugger: added address operator in data query expressions
- debugger: added watchpoints over hardware breakpoints
- debugger: added canonic operator

### Changed

Expand Down
25 changes: 25 additions & 0 deletions src/debugger/variable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,31 @@ impl VariableIR {
}
}

/// If a variable has a specialized ir (vectors, strings, etc.) then return
/// an underlying structure
fn canonic(self) -> Self {
let VariableIR::Specialized(spec) = self else {
return self;
};

match spec {
SpecializedVariableIR::Vector { original, .. }
| SpecializedVariableIR::VecDeque { original, .. }
| SpecializedVariableIR::HashMap { original, .. }
| SpecializedVariableIR::HashSet { original, .. }
| SpecializedVariableIR::BTreeMap { original, .. }
| SpecializedVariableIR::BTreeSet { original, .. }
| SpecializedVariableIR::String { original, .. }
| SpecializedVariableIR::Str { original, .. }
| SpecializedVariableIR::Tls { original, .. }
| SpecializedVariableIR::Cell { original, .. }
| SpecializedVariableIR::RefCell { original, .. }
| SpecializedVariableIR::Rc { original, .. }
| SpecializedVariableIR::Arc { original, .. }
| SpecializedVariableIR::Uuid { original, .. } => VariableIR::Struct(original),
}
}

/// Try to dereference variable and returns underline variable IR.
/// Return `None` if dereference not allowed.
fn deref(self, eval_ctx: &EvaluationContext, variable_parser: &VariableParser) -> Option<Self> {
Expand Down
11 changes: 9 additions & 2 deletions src/debugger/variable/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ pub enum DQE {
Slice(Box<DQE>, Option<usize>, Option<usize>),
Deref(Box<DQE>),
Address(Box<DQE>),
Canonic(Box<DQE>),
}

impl DQE {
Expand Down Expand Up @@ -313,7 +314,8 @@ impl<'a> SelectExpressionEvaluator<'a> {
| DQE::Index(expr, _)
| DQE::Slice(expr, _, _)
| DQE::Deref(expr)
| DQE::Address(expr) => self.evaluate_inner(expr),
| DQE::Address(expr)
| DQE::Canonic(expr) => self.evaluate_inner(expr),
}
}

Expand Down Expand Up @@ -421,7 +423,8 @@ impl<'a> SelectExpressionEvaluator<'a> {
| DQE::Index(expr, _)
| DQE::Slice(expr, _, _)
| DQE::Deref(expr)
| DQE::Address(expr) => self.evaluate_on_arguments_inner(expr),
| DQE::Address(expr)
| DQE::Canonic(expr) => self.evaluate_on_arguments_inner(expr),
}
}

Expand Down Expand Up @@ -484,6 +487,10 @@ impl<'a> SelectExpressionEvaluator<'a> {
let var = self.evaluate_single_variable(expr, variable_die, r#type)?;
var.address(evaluation_context, &parser)
}
DQE::Canonic(expr) => {
let var = self.evaluate_single_variable(expr, variable_die, r#type)?;
Some(var.canonic())
}
}
}
}
23 changes: 22 additions & 1 deletion src/ui/command/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ pub fn parser<'a>() -> impl Parser<'a, &'a str, DQE, Err<'a>> {
op('*')
.to(DQE::Deref as fn(_) -> _)
.or(op('&').to(DQE::Address as fn(_) -> _))
.or(op('~').to(DQE::Canonic as fn(_) -> _))
.repeated()
.foldr(expr, |op, rhs| op(Box::new(rhs)))
});
Expand Down Expand Up @@ -400,6 +401,13 @@ mod test {
only_local: false,
}))),
},
TestCase {
string: "~var1",
expr: DQE::Canonic(Box::new(DQE::Variable(VariableSelector::Name {
var_name: "var1".to_string(),
only_local: false,
}))),
},
TestCase {
string: "**var1",
expr: DQE::Deref(Box::new(DQE::Deref(Box::new(DQE::Variable(
Expand All @@ -409,6 +417,19 @@ mod test {
},
))))),
},
TestCase {
string: "~*var1",
expr: DQE::Canonic(
DQE::Deref(
DQE::Variable(VariableSelector::Name {
var_name: "var1".to_string(),
only_local: false,
})
.boxed(),
)
.boxed(),
),
},
TestCase {
string: "**var1.field1.field2",
expr: DQE::Deref(Box::new(DQE::Deref(Box::new(DQE::Field(
Expand Down Expand Up @@ -720,7 +741,7 @@ mod test {
},
TestCase {
string: "*",
err_text: "found end of input expected '*', '&', ':', or '('",
err_text: "found end of input expected '*', '&', '~', ':', or '('",
},
];

Expand Down
2 changes: 2 additions & 0 deletions src/ui/console/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ query expressions as such a tool.
Available operators:
`*` - dereference, available for references, pointers and smart pointers (Rc and Arc)
`&` - address operator, available for any variable, structure field or index value
`~` - canonic representation, types like vectors or hashmaps are prettyfied by default, use canonic operator to see a raw structure as is
`[{left}..{right}]` - slice operator, available for pointers
`.` - get field, available for structs, enums and hashmaps (with string keys)
`(` and `)` - parentheses to prioritize operations
Expand All @@ -50,6 +51,7 @@ Examples:
`**var1` - print the value pointed to by the pointer `*var1`
`**var1.field1` - print the value pointed to by the pointer `*var1.field1`
`&var1.field1` - print address of field `field1`
`(~vec1).len` - print lenght field of vector header structure
`&vec1[1]` - print address of second element in vector `vec1`
`(**var1).field1` - print field `field1` in struct pointed to by the pointer `*var1`
`*(*const i32)0x1234AA332` - cast memory address to `*const i32` pointer, then dereference it
Expand Down
40 changes: 39 additions & 1 deletion tests/integration/test_watchpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,42 @@ def test_watchpoint_at_addr_rw(self):
self.debugger.expect_exact('Hit watchpoint 1 (rw)')
self.debugger.expect_exact('value: data = u64(6)')

self.debugger.sendline('q')
def test_watchpoint_at_complex_data_types(self):
"""Add watchpoints for vector attribute"""
self.debugger.sendline('break calculations.rs:91')
self.debugger.expect_exact('New breakpoint')
self.debugger.sendline('run')
self.debugger.expect_exact('Hit breakpoint 1')

self.debugger.sendline('watch (~vector2).len')
self.debugger.expect_exact('New watchpoint')

self.debugger.sendline('c')
self.debugger.expect_exact('Hit watchpoint 1 (w)')
self.debugger.expect_exact('old value: (~vector2).len = usize(2)')
self.debugger.expect_exact('new value: (~vector2).len = usize(3)')
self.debugger.sendline('c')
self.debugger.expect_exact('Hit watchpoint 1 (w)')
self.debugger.expect_exact('old value: (~vector2).len = usize(3)')
self.debugger.expect_exact('new value: (~vector2).len = usize(4)')
self.debugger.sendline('c')
self.debugger.expect_exact('Watchpoint 1 end of scope')
self.debugger.expect_exact('old value: (~vector2).len = usize(4)')

def test_watchpoint_at_complex_data_types2(self):
"""Add watchpoints for vector attribute"""
self.debugger.sendline('break calculations.rs:95')
self.debugger.expect_exact('New breakpoint')
self.debugger.sendline('run')
self.debugger.expect_exact('Hit breakpoint 1')

self.debugger.sendline('watch (~(~string).vec).len')
self.debugger.expect_exact('New watchpoint')

self.debugger.sendline('c')
self.debugger.expect_exact('Hit watchpoint 1 (w)')
self.debugger.expect_exact('old value: (~(~string).vec).len = usize(3)')
self.debugger.expect_exact('new value: (~(~string).vec).len = usize(7)')
self.debugger.sendline('c')
self.debugger.expect_exact('Watchpoint 1 end of scope')
self.debugger.expect_exact('old value: (~(~string).vec).len = usize(7)')

0 comments on commit 4c1933e

Please sign in to comment.