miden_debug_engine/debug/
breakpoint.rs1use 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(miden_core::operations::Operation),
77 AsmOpcode(&'static str),
79 Called(Pattern),
81 Trace(TraceEvent),
83}
84impl BreakpointType {
85 pub fn should_break_for(&self, current_op: &miden_core::operations::Operation) -> bool {
87 match self {
88 Self::Opcode(op) => current_op == op,
89 _ => false,
90 }
91 }
92
93 pub fn should_break_in(&self, procedure: &str) -> bool {
95 match self {
96 Self::Called(pattern) => pattern.matches(procedure),
97 _ => false,
98 }
99 }
100
101 pub fn should_break_at(&self, loc: &ResolvedLocation) -> bool {
103 match self {
104 Self::File(pattern) => {
105 pattern.matches_path(Path::new(loc.source_file.deref().content().uri().as_str()))
106 }
107 Self::Line { pattern, line } if line == &loc.line => {
108 pattern.matches_path(Path::new(loc.source_file.deref().content().uri().as_str()))
109 }
110 _ => false,
111 }
112 }
113
114 pub fn is_internal(&self) -> bool {
116 matches!(
117 self,
118 BreakpointType::Next
119 | BreakpointType::NextLine
120 | BreakpointType::Step
121 | BreakpointType::Finish
122 )
123 }
124
125 pub fn is_one_shot(&self) -> bool {
127 matches!(
128 self,
129 BreakpointType::Next
130 | BreakpointType::NextLine
131 | BreakpointType::Finish
132 | BreakpointType::Step
133 | BreakpointType::StepN(_)
134 | BreakpointType::StepTo(_)
135 )
136 }
137}
138
139impl FromStr for BreakpointType {
140 type Err = String;
141
142 fn from_str(s: &str) -> Result<Self, Self::Err> {
143 let s = s.trim();
144
145 if s == "next" {
153 return Ok(BreakpointType::Next);
154 }
155 if s == "finish" {
156 return Ok(BreakpointType::Finish);
157 }
158 if let Some(n) = s.strip_prefix("after ") {
159 let n = n.trim().parse::<usize>().map_err(|err| {
160 format!("invalid breakpoint expression: could not parse cycle count: {err}")
161 })?;
162 return Ok(BreakpointType::StepN(n));
163 }
164 if let Some(_opcode) = s.strip_prefix("for ") {
165 todo!()
166 }
167 if let Some(cycle) = s.strip_prefix("at ") {
168 let cycle = cycle.trim().parse::<usize>().map_err(|err| {
169 format!("invalid breakpoint expression: could not parse cycle value: {err}")
170 })?;
171 return Ok(BreakpointType::StepTo(cycle));
172 }
173 if let Some(procedure) = s.strip_prefix("in ") {
174 let pattern = Pattern::new(procedure.trim())
175 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
176 return Ok(BreakpointType::Called(pattern));
177 }
178 match s.split_once(':') {
179 Some((file, line)) => {
180 let pattern = Pattern::new(file.trim())
181 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
182 let line = line.trim().parse::<u32>().map_err(|err| {
183 format!("invalid breakpoint expression: could not parse line: {err}")
184 })?;
185 Ok(BreakpointType::Line { pattern, line })
186 }
187 None => {
188 let pattern = Pattern::new(s.trim())
189 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
190 Ok(BreakpointType::File(pattern))
191 }
192 }
193 }
194}