midenc_debug/debug/
breakpoint.rs1use std::{ops::Deref, str::FromStr};
2
3use glob::Pattern;
4use miden_processor::VmState;
5
6use super::ResolvedLocation;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Breakpoint {
10 pub id: u8,
11 pub creation_cycle: usize,
12 pub ty: BreakpointType,
13}
14
15impl Default for Breakpoint {
16 fn default() -> Self {
17 Self {
18 id: 0,
19 creation_cycle: 0,
20 ty: BreakpointType::Step,
21 }
22 }
23}
24
25impl Breakpoint {
26 pub fn cycles_to_skip(&self, current_cycle: usize) -> Option<usize> {
30 let cycles_passed = current_cycle - self.creation_cycle;
31 match &self.ty {
32 BreakpointType::Step => Some(1),
33 BreakpointType::StepN(n) => Some(n.saturating_sub(cycles_passed)),
34 BreakpointType::StepTo(to) if to >= ¤t_cycle => Some(to.abs_diff(current_cycle)),
35 _ => None,
36 }
37 }
38}
39impl Deref for Breakpoint {
40 type Target = BreakpointType;
41
42 #[inline]
43 fn deref(&self) -> &Self::Target {
44 &self.ty
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum BreakpointType {
50 Step,
52 StepN(usize),
54 StepTo(usize),
56 Next,
58 Finish,
60 File(Pattern),
62 Line { pattern: Pattern, line: u32 },
65 Opcode(miden_core::Operation),
67 Called(Pattern),
69}
70impl BreakpointType {
71 pub fn should_break_for(&self, current_op: &miden_core::Operation) -> bool {
73 match self {
74 Self::Opcode(op) => current_op == op,
75 _ => false,
76 }
77 }
78
79 pub fn should_break_in(&self, procedure: &str) -> bool {
81 match self {
82 Self::Called(pattern) => pattern.matches(procedure),
83 _ => false,
84 }
85 }
86
87 pub fn should_break_at(&self, loc: &ResolvedLocation) -> bool {
89 match self {
90 Self::File(pattern) => pattern.matches_path(loc.source_file.path()),
91 Self::Line { pattern, line } if line == &loc.line => {
92 pattern.matches_path(loc.source_file.path())
93 }
94 _ => false,
95 }
96 }
97
98 pub fn is_internal(&self) -> bool {
100 matches!(self, BreakpointType::Next | BreakpointType::Step | BreakpointType::Finish)
101 }
102
103 pub fn is_one_shot(&self) -> bool {
105 matches!(
106 self,
107 BreakpointType::Next
108 | BreakpointType::Finish
109 | BreakpointType::Step
110 | BreakpointType::StepN(_)
111 | BreakpointType::StepTo(_)
112 )
113 }
114}
115
116impl FromStr for BreakpointType {
117 type Err = String;
118
119 fn from_str(s: &str) -> Result<Self, Self::Err> {
120 let s = s.trim();
121
122 if s == "next" {
130 return Ok(BreakpointType::Next);
131 }
132 if s == "finish" {
133 return Ok(BreakpointType::Finish);
134 }
135 if let Some(n) = s.strip_prefix("after ") {
136 let n = n.trim().parse::<usize>().map_err(|err| {
137 format!("invalid breakpoint expression: could not parse cycle count: {err}")
138 })?;
139 return Ok(BreakpointType::StepN(n));
140 }
141 if let Some(_opcode) = s.strip_prefix("for ") {
142 todo!()
143 }
144 if let Some(cycle) = s.strip_prefix("at ") {
145 let cycle = cycle.trim().parse::<usize>().map_err(|err| {
146 format!("invalid breakpoint expression: could not parse cycle value: {err}")
147 })?;
148 return Ok(BreakpointType::StepTo(cycle));
149 }
150 if let Some(procedure) = s.strip_prefix("in ") {
151 let pattern = Pattern::new(procedure.trim())
152 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
153 return Ok(BreakpointType::Called(pattern));
154 }
155 match s.split_once(':') {
156 Some((file, line)) => {
157 let pattern = Pattern::new(file.trim())
158 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
159 let line = line.trim().parse::<u32>().map_err(|err| {
160 format!("invalid breakpoint expression: could not parse line: {err}")
161 })?;
162 Ok(BreakpointType::Line { pattern, line })
163 }
164 None => {
165 let pattern = Pattern::new(s.trim())
166 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
167 Ok(BreakpointType::File(pattern))
168 }
169 }
170 }
171}