Skip to main content

torsh_jit/debugger/
breakpoints.rs

1//! Breakpoint management for JIT debugging
2//!
3//! This module provides comprehensive breakpoint management capabilities including
4//! setting, removing, and checking breakpoints at various execution locations.
5
6use super::core::{Breakpoint, BreakpointId, BreakpointLocation, ExecutionLocation};
7use crate::{JitError, JitResult};
8use std::collections::HashMap;
9
10/// Breakpoint manager
11pub struct BreakpointManager {
12    breakpoints: HashMap<BreakpointId, Breakpoint>,
13    next_id: BreakpointId,
14}
15
16impl BreakpointManager {
17    /// Create a new breakpoint manager
18    pub fn new() -> Self {
19        Self {
20            breakpoints: HashMap::new(),
21            next_id: BreakpointId(0),
22        }
23    }
24
25    /// Set a breakpoint at the specified location
26    ///
27    /// # Arguments
28    /// * `location` - The location where the breakpoint should be set
29    ///
30    /// # Returns
31    /// The ID of the newly created breakpoint
32    ///
33    /// # Examples
34    /// ```rust
35    /// use torsh_jit::debugger::{BreakpointManager, BreakpointLocation};
36    /// use torsh_jit::NodeId;
37    ///
38    /// let mut manager = BreakpointManager::new();
39    /// let location = BreakpointLocation::GraphNode(NodeId::new(0));
40    /// let id = manager.set_breakpoint(location).unwrap();
41    /// ```
42    pub fn set_breakpoint(&mut self, location: BreakpointLocation) -> JitResult<BreakpointId> {
43        let id = self.next_id;
44        self.next_id = BreakpointId(self.next_id.0 + 1);
45
46        let breakpoint = Breakpoint {
47            id,
48            location,
49            condition: None,
50            enabled: true,
51            hit_count: 0,
52        };
53
54        self.breakpoints.insert(id, breakpoint);
55        Ok(id)
56    }
57
58    /// Set a conditional breakpoint at the specified location
59    ///
60    /// # Arguments
61    /// * `location` - The location where the breakpoint should be set
62    /// * `condition` - The condition that must be true for the breakpoint to trigger
63    ///
64    /// # Returns
65    /// The ID of the newly created breakpoint
66    pub fn set_conditional_breakpoint(
67        &mut self,
68        location: BreakpointLocation,
69        condition: String,
70    ) -> JitResult<BreakpointId> {
71        let id = self.next_id;
72        self.next_id = BreakpointId(self.next_id.0 + 1);
73
74        let breakpoint = Breakpoint {
75            id,
76            location,
77            condition: Some(condition),
78            enabled: true,
79            hit_count: 0,
80        };
81
82        self.breakpoints.insert(id, breakpoint);
83        Ok(id)
84    }
85
86    /// Remove a breakpoint by ID
87    ///
88    /// # Arguments
89    /// * `id` - The ID of the breakpoint to remove
90    ///
91    /// # Returns
92    /// `Ok(())` if the breakpoint was removed, error if not found
93    pub fn remove_breakpoint(&mut self, id: BreakpointId) -> JitResult<()> {
94        if self.breakpoints.remove(&id).is_some() {
95            Ok(())
96        } else {
97            Err(JitError::RuntimeError(format!(
98                "Breakpoint {} not found",
99                id.0
100            )))
101        }
102    }
103
104    /// Enable a breakpoint
105    ///
106    /// # Arguments
107    /// * `id` - The ID of the breakpoint to enable
108    pub fn enable_breakpoint(&mut self, id: BreakpointId) -> JitResult<()> {
109        if let Some(breakpoint) = self.breakpoints.get_mut(&id) {
110            breakpoint.enabled = true;
111            Ok(())
112        } else {
113            Err(JitError::RuntimeError(format!(
114                "Breakpoint {} not found",
115                id.0
116            )))
117        }
118    }
119
120    /// Disable a breakpoint
121    ///
122    /// # Arguments
123    /// * `id` - The ID of the breakpoint to disable
124    pub fn disable_breakpoint(&mut self, id: BreakpointId) -> JitResult<()> {
125        if let Some(breakpoint) = self.breakpoints.get_mut(&id) {
126            breakpoint.enabled = false;
127            Ok(())
128        } else {
129            Err(JitError::RuntimeError(format!(
130                "Breakpoint {} not found",
131                id.0
132            )))
133        }
134    }
135
136    /// Get a list of all breakpoints
137    ///
138    /// # Returns
139    /// A vector of references to all breakpoints
140    pub fn list_breakpoints(&self) -> Vec<&Breakpoint> {
141        self.breakpoints.values().collect()
142    }
143
144    /// Get a specific breakpoint by ID
145    ///
146    /// # Arguments
147    /// * `id` - The ID of the breakpoint to retrieve
148    ///
149    /// # Returns
150    /// An optional reference to the breakpoint
151    pub fn get_breakpoint(&self, id: BreakpointId) -> Option<&Breakpoint> {
152        self.breakpoints.get(&id)
153    }
154
155    /// Check if there is a breakpoint at the specified location
156    ///
157    /// # Arguments
158    /// * `location` - The execution location to check
159    ///
160    /// # Returns
161    /// `true` if there is an enabled breakpoint at the location, `false` otherwise
162    pub fn is_breakpoint_at(&self, location: &ExecutionLocation) -> bool {
163        self.breakpoints.values().any(|bp| {
164            bp.enabled
165                && match (&bp.location, location) {
166                    (
167                        BreakpointLocation::GraphNode(bp_node),
168                        ExecutionLocation::GraphNode(loc_node),
169                    ) => bp_node == loc_node,
170                    (
171                        BreakpointLocation::Instruction {
172                            function: bp_func,
173                            instruction: bp_inst,
174                        },
175                        ExecutionLocation::Instruction {
176                            function: loc_func,
177                            instruction_index: loc_inst,
178                        },
179                    ) => bp_func == loc_func && *bp_inst == *loc_inst,
180                    _ => false,
181                }
182        })
183    }
184
185    /// Increment hit count for breakpoints at the specified location
186    ///
187    /// # Arguments
188    /// * `location` - The execution location where a hit occurred
189    ///
190    /// # Returns
191    /// The number of breakpoints hit at this location
192    pub fn hit_breakpoints_at(&mut self, location: &ExecutionLocation) -> usize {
193        let mut hit_count = 0;
194        for breakpoint in self.breakpoints.values_mut() {
195            if breakpoint.enabled
196                && match (&breakpoint.location, location) {
197                    (
198                        BreakpointLocation::GraphNode(bp_node),
199                        ExecutionLocation::GraphNode(loc_node),
200                    ) => bp_node == loc_node,
201                    (
202                        BreakpointLocation::Instruction {
203                            function: bp_func,
204                            instruction: bp_inst,
205                        },
206                        ExecutionLocation::Instruction {
207                            function: loc_func,
208                            instruction_index: loc_inst,
209                        },
210                    ) => bp_func == loc_func && *bp_inst == *loc_inst,
211                    _ => false,
212                }
213            {
214                breakpoint.hit_count += 1;
215                hit_count += 1;
216            }
217        }
218        hit_count
219    }
220
221    /// Clear all breakpoints
222    pub fn clear_all_breakpoints(&mut self) {
223        self.breakpoints.clear();
224    }
225
226    /// Get the number of breakpoints
227    pub fn count(&self) -> usize {
228        self.breakpoints.len()
229    }
230
231    /// Get the number of enabled breakpoints
232    pub fn enabled_count(&self) -> usize {
233        self.breakpoints.values().filter(|bp| bp.enabled).count()
234    }
235
236    /// Get all breakpoints at a specific location
237    ///
238    /// # Arguments
239    /// * `location` - The location to search for breakpoints
240    ///
241    /// # Returns
242    /// A vector of references to breakpoints at the specified location
243    pub fn get_breakpoints_at(&self, location: &BreakpointLocation) -> Vec<&Breakpoint> {
244        self.breakpoints
245            .values()
246            .filter(|bp| match (&bp.location, location) {
247                (
248                    BreakpointLocation::GraphNode(bp_node),
249                    BreakpointLocation::GraphNode(loc_node),
250                ) => bp_node == loc_node,
251                (
252                    BreakpointLocation::Instruction {
253                        function: bp_func,
254                        instruction: bp_inst,
255                    },
256                    BreakpointLocation::Instruction {
257                        function: loc_func,
258                        instruction: loc_inst,
259                    },
260                ) => bp_func == loc_func && bp_inst == loc_inst,
261                _ => false,
262            })
263            .collect()
264    }
265}
266
267impl Default for BreakpointManager {
268    fn default() -> Self {
269        Self::new()
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276    use crate::NodeId;
277
278    #[test]
279    fn test_breakpoint_manager_creation() {
280        let manager = BreakpointManager::new();
281        assert_eq!(manager.count(), 0);
282        assert_eq!(manager.enabled_count(), 0);
283    }
284
285    #[test]
286    fn test_set_and_remove_breakpoint() {
287        let mut manager = BreakpointManager::new();
288
289        let location = BreakpointLocation::GraphNode(NodeId::new(0));
290        let id = manager.set_breakpoint(location).unwrap();
291
292        assert_eq!(manager.count(), 1);
293        assert_eq!(manager.enabled_count(), 1);
294        assert!(manager.remove_breakpoint(id).is_ok());
295        assert_eq!(manager.count(), 0);
296    }
297
298    #[test]
299    fn test_conditional_breakpoint() {
300        let mut manager = BreakpointManager::new();
301
302        let location = BreakpointLocation::GraphNode(NodeId::new(0));
303        let condition = "x > 10".to_string();
304        let id = manager
305            .set_conditional_breakpoint(location, condition.clone())
306            .unwrap();
307
308        let breakpoint = manager.get_breakpoint(id).unwrap();
309        assert_eq!(breakpoint.condition, Some(condition));
310    }
311
312    #[test]
313    fn test_enable_disable_breakpoint() {
314        let mut manager = BreakpointManager::new();
315
316        let location = BreakpointLocation::GraphNode(NodeId::new(0));
317        let id = manager.set_breakpoint(location).unwrap();
318
319        assert_eq!(manager.enabled_count(), 1);
320
321        manager.disable_breakpoint(id).unwrap();
322        assert_eq!(manager.enabled_count(), 0);
323
324        manager.enable_breakpoint(id).unwrap();
325        assert_eq!(manager.enabled_count(), 1);
326    }
327
328    #[test]
329    fn test_is_breakpoint_at() {
330        let mut manager = BreakpointManager::new();
331
332        let node_id = NodeId::new(0);
333        let location = BreakpointLocation::GraphNode(node_id);
334        manager.set_breakpoint(location).unwrap();
335
336        let exec_location = ExecutionLocation::GraphNode(node_id);
337        assert!(manager.is_breakpoint_at(&exec_location));
338
339        let other_exec_location = ExecutionLocation::GraphNode(NodeId::new(1));
340        assert!(!manager.is_breakpoint_at(&other_exec_location));
341    }
342
343    #[test]
344    fn test_hit_breakpoints() {
345        let mut manager = BreakpointManager::new();
346
347        let node_id = NodeId::new(0);
348        let location = BreakpointLocation::GraphNode(node_id);
349        let id = manager.set_breakpoint(location).unwrap();
350
351        let exec_location = ExecutionLocation::GraphNode(node_id);
352        let hit_count = manager.hit_breakpoints_at(&exec_location);
353        assert_eq!(hit_count, 1);
354
355        let breakpoint = manager.get_breakpoint(id).unwrap();
356        assert_eq!(breakpoint.hit_count, 1);
357    }
358
359    #[test]
360    fn test_clear_all_breakpoints() {
361        let mut manager = BreakpointManager::new();
362
363        manager
364            .set_breakpoint(BreakpointLocation::GraphNode(NodeId::new(0)))
365            .unwrap();
366        manager
367            .set_breakpoint(BreakpointLocation::GraphNode(NodeId::new(1)))
368            .unwrap();
369
370        assert_eq!(manager.count(), 2);
371        manager.clear_all_breakpoints();
372        assert_eq!(manager.count(), 0);
373    }
374}