1use std::{ops::Deref, path::Path, str::FromStr};
2
3use glob::Pattern;
4
5use super::ResolvedLocation;
6use crate::TraceEvent;
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 new(ty: BreakpointType) -> Self {
28 Self {
29 ty,
30 ..Default::default()
31 }
32 }
33
34 pub fn cycles_to_skip(&self, current_cycle: usize) -> Option<usize> {
38 let cycles_passed = current_cycle - self.creation_cycle;
39 match &self.ty {
40 BreakpointType::Step => Some(1),
41 BreakpointType::StepN(n) => Some(n.saturating_sub(cycles_passed)),
42 BreakpointType::StepTo(to) if to >= ¤t_cycle => Some(to.abs_diff(current_cycle)),
43 _ => None,
44 }
45 }
46}
47impl Deref for Breakpoint {
48 type Target = BreakpointType;
49
50 #[inline]
51 fn deref(&self) -> &Self::Target {
52 &self.ty
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum BreakpointType {
58 Step,
60 StepN(usize),
62 StepTo(usize),
64 Next,
66 NextLine,
68 Finish,
70 File(Pattern),
72 Line { pattern: Pattern, line: u32 },
75 Opcode(OperationMatcher),
77 Called(Pattern),
79 Trace(TraceEvent),
81}
82impl BreakpointType {
83 pub fn should_break_for(&self, current_op: &miden_core::operations::Operation) -> bool {
85 match self {
86 Self::Opcode(matcher) => matcher.should_break_for(current_op),
87 _ => false,
88 }
89 }
90
91 pub fn should_break_in(&self, procedure: &str) -> bool {
93 match self {
94 Self::Called(pattern) => pattern.matches(procedure),
95 _ => false,
96 }
97 }
98
99 pub fn should_break_at(&self, loc: &ResolvedLocation) -> bool {
101 match self {
102 Self::File(pattern) => {
103 pattern.matches_path(Path::new(loc.source_file.deref().content().uri().as_str()))
104 }
105 Self::Line { pattern, line } if line == &loc.line => {
106 pattern.matches_path(Path::new(loc.source_file.deref().content().uri().as_str()))
107 }
108 _ => false,
109 }
110 }
111
112 pub fn is_internal(&self) -> bool {
114 matches!(
115 self,
116 BreakpointType::Next
117 | BreakpointType::NextLine
118 | BreakpointType::Step
119 | BreakpointType::Finish
120 )
121 }
122
123 pub fn is_one_shot(&self) -> bool {
125 matches!(
126 self,
127 BreakpointType::Next
128 | BreakpointType::NextLine
129 | BreakpointType::Finish
130 | BreakpointType::Step
131 | BreakpointType::StepN(_)
132 | BreakpointType::StepTo(_)
133 )
134 }
135}
136
137impl FromStr for BreakpointType {
138 type Err = String;
139
140 fn from_str(s: &str) -> Result<Self, Self::Err> {
141 let s = s.trim();
142
143 if s == "next" {
151 return Ok(BreakpointType::Next);
152 }
153 if s == "finish" {
154 return Ok(BreakpointType::Finish);
155 }
156 if let Some(n) = s.strip_prefix("after ") {
157 let n = n.trim().parse::<usize>().map_err(|err| {
158 format!("invalid breakpoint expression: could not parse cycle count: {err}")
159 })?;
160 return Ok(BreakpointType::StepN(n));
161 }
162 if let Some(opcode) = s.strip_prefix("for ") {
163 return Ok(BreakpointType::Opcode(opcode.parse::<OperationMatcher>()?));
164 }
165 if let Some(cycle) = s.strip_prefix("at ") {
166 let cycle = cycle.trim().parse::<usize>().map_err(|err| {
167 format!("invalid breakpoint expression: could not parse cycle value: {err}")
168 })?;
169 return Ok(BreakpointType::StepTo(cycle));
170 }
171 if let Some(procedure) = s.strip_prefix("in ") {
172 let pattern = Pattern::new(procedure.trim())
173 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
174 return Ok(BreakpointType::Called(pattern));
175 }
176 match s.split_once(':') {
177 Some((file, line)) => {
178 let pattern = Pattern::new(file.trim())
179 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
180 let line = line.trim().parse::<u32>().map_err(|err| {
181 format!("invalid breakpoint expression: could not parse line: {err}")
182 })?;
183 Ok(BreakpointType::Line { pattern, line })
184 }
185 None => {
186 let pattern = Pattern::new(s.trim())
187 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
188 Ok(BreakpointType::File(pattern))
189 }
190 }
191 }
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
195#[non_exhaustive]
196pub enum OperationMatcher {
197 Asm(String),
198 Exact(miden_core::operations::Operation),
199 Assert,
200 Push,
201 Dup,
202 SwapW,
203 Movup,
204 Movdn,
205}
206
207impl OperationMatcher {
208 pub fn should_break_for(&self, op: &miden_core::operations::Operation) -> bool {
209 use miden_core::operations::Operation;
210 match self {
211 Self::Asm(_) => false,
212 Self::Exact(expected) => op == expected,
213 Self::Assert => matches!(op, Operation::Assert(_)),
214 Self::Push => matches!(op, Operation::Push(_)),
215 Self::Dup => matches!(
216 op,
217 Operation::Dup0
218 | Operation::Dup1
219 | Operation::Dup2
220 | Operation::Dup3
221 | Operation::Dup4
222 | Operation::Dup5
223 | Operation::Dup6
224 | Operation::Dup7
225 | Operation::Dup9
226 | Operation::Dup11
227 | Operation::Dup13
228 | Operation::Dup15
229 ),
230 Self::SwapW => matches!(op, Operation::SwapW | Operation::SwapW2 | Operation::SwapW3),
231 Self::Movup => matches!(
232 op,
233 Operation::MovUp2
234 | Operation::MovUp3
235 | Operation::MovUp4
236 | Operation::MovUp5
237 | Operation::MovUp6
238 | Operation::MovUp7
239 | Operation::MovUp8
240 ),
241 Self::Movdn => matches!(
242 op,
243 Operation::MovDn2
244 | Operation::MovDn3
245 | Operation::MovDn4
246 | Operation::MovDn5
247 | Operation::MovDn6
248 | Operation::MovDn7
249 | Operation::MovDn8
250 ),
251 }
252 }
253}
254
255impl core::fmt::Display for OperationMatcher {
256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 match self {
258 Self::Asm(op) => f.write_str(op),
259 Self::Exact(op) => core::fmt::Display::fmt(op, f),
260 Self::Assert => f.write_str("assert.*"),
261 Self::Push => f.write_str("push.*"),
262 Self::Dup => f.write_str("dup"),
263 Self::SwapW => f.write_str("swapw"),
264 Self::Movup => f.write_str("movup"),
265 Self::Movdn => f.write_str("movdn"),
266 }
267 }
268}
269
270impl FromStr for OperationMatcher {
271 type Err = String;
272
273 fn from_str(name: &str) -> Result<Self, Self::Err> {
274 use miden_core::operations::Operation::*;
275 let opcode_parts = name
276 .split_once('.')
277 .map(|(name, rest)| (name, Some(rest)))
278 .unwrap_or((name, None));
279 let opcode = match opcode_parts {
280 ("nop" | "noop", _) => Noop,
281 ("assert", Some("*")) => return Ok(OperationMatcher::Assert),
282 ("assert", Some(code)) => Assert(
283 code.parse::<u32>()
284 .map(miden_core::Felt::from_u32)
285 .map_err(|err| err.to_string())?,
286 ),
287 ("assert", None) => Assert(miden_core::Felt::from_u32(0)),
288 ("sdepth", None) => SDepth,
289 ("caller", None) => Caller,
290 ("clk", None) => Clk,
291 ("emit", None) => Emit,
292 ("add", None) => Add,
293 ("neg", None) => Neg,
294 ("mul", None) => Mul,
295 ("inv", None) => Inv,
296 ("incr", None) => Incr,
297 ("and", None) => And,
298 ("or", None) => Or,
299 ("not", None) => Not,
300 ("eq", None) => Eq,
301 ("eqz", None) => Eqz,
302 ("expacc", None) => Expacc,
303 ("ext2mul", None) => Ext2Mul,
304 ("u32split", None) => U32split,
305 ("u32add", None) => U32add,
306 ("u32assert2", Some(code)) => U32assert2(
307 code.parse::<u32>()
308 .map(miden_core::Felt::from_u32)
309 .map_err(|err| err.to_string())?,
310 ),
311 ("u32assert2", None) => U32assert2(miden_core::Felt::from_u32(0)),
312 ("u32add3", None) => U32add3,
313 ("u32sub", None) => U32sub,
314 ("u32mul", None) => U32mul,
315 ("u32madd", None) => U32madd,
316 ("u32div", None) => U32div,
317 ("u32and", None) => U32and,
318 ("u32xor", None) => U32xor,
319 ("pad", None) => Pad,
320 ("drop", None) => Drop,
321 ("dup", _) => return Ok(OperationMatcher::Dup),
322 ("swap", _) => Swap,
323 ("swapw", _) => return Ok(OperationMatcher::SwapW),
324 ("swapdw", _) => SwapDW,
325 ("movup", _) => return Ok(OperationMatcher::Movup),
326 ("movdn", _) => return Ok(OperationMatcher::Movdn),
327 ("cswap", _) => CSwap,
328 ("cswapw", _) => CSwapW,
329 ("push", _) => return Ok(OperationMatcher::Push),
330 ("advpop", _) => AdvPop,
331 ("advpopw", _) => AdvPopW,
332 ("mloadw", _) => MLoadW,
333 ("mstorew", _) => MStoreW,
334 ("mload", _) => MLoad,
335 ("mstore", _) => MStore,
336 ("mstream", _) => MStream,
337 ("pipe", _) => Pipe,
338 ("crypto_stream", _) => CryptoStream,
339 ("hperm", _) => HPerm,
340 ("mpverify", Some(code)) => MpVerify(
341 code.parse::<u32>()
342 .map(miden_core::Felt::from_u32)
343 .map_err(|err| err.to_string())?,
344 ),
345 ("mpverify", None) => MpVerify(miden_core::Felt::from_u32(0)),
346 ("mrupdate", None) => MrUpdate,
347 ("frie2f4", None) => FriE2F4,
348 ("horner_base", None) => HornerBase,
349 ("horner_ext", None) => HornerExt,
350 ("eval_circuit", None) => EvalCircuit,
351 ("log_precompile", None) => LogPrecompile,
352 _ => return Ok(OperationMatcher::Asm(name.to_string())),
353 };
354
355 Ok(OperationMatcher::Exact(opcode))
356 }
357}