1use anyhow::Result;
7use chrono::{DateTime, Utc};
8use indexmap::IndexMap;
9use parking_lot::{Mutex, RwLock};
10use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, VecDeque};
12use std::sync::Arc;
13use uuid::Uuid;
14
15use crate::DebugConfig;
16
17#[derive(Debug)]
19pub struct InteractiveDebugger {
20 #[allow(dead_code)]
21 config: DebugConfig,
22 state: Arc<RwLock<DebuggerState>>,
23 breakpoints: Arc<RwLock<HashMap<String, Breakpoint>>>,
24 execution_history: Arc<Mutex<VecDeque<ExecutionSnapshot>>>,
25 current_step: Arc<Mutex<usize>>,
26 max_history_size: usize,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct DebuggerState {
32 pub is_running: bool,
33 pub is_paused: bool,
34 pub current_location: Option<DebugLocation>,
35 pub call_stack: Vec<StackFrame>,
36 pub variables: IndexMap<String, VariableValue>,
37 pub step_mode: StepMode,
38 pub session_start: DateTime<Utc>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct DebugLocation {
44 pub module: String,
45 pub function: String,
46 pub line: Option<u32>,
47 pub instruction: Option<String>,
48 pub context: Option<String>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct StackFrame {
54 pub id: Uuid,
55 pub location: DebugLocation,
56 pub locals: IndexMap<String, VariableValue>,
57 pub timestamp: DateTime<Utc>,
58 pub depth: usize,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct VariableValue {
64 pub name: String,
65 pub value: String,
66 pub type_name: String,
67 pub size_bytes: Option<usize>,
68 pub shape: Option<Vec<usize>>,
69 pub is_tensor: bool,
70 pub metadata: HashMap<String, String>,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
75pub enum StepMode {
76 StepInto,
78 StepOver,
80 StepOut,
82 Continue,
84 SingleStep,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct Breakpoint {
91 pub id: Uuid,
92 pub location: DebugLocation,
93 pub condition: Option<String>,
94 pub hit_count: usize,
95 pub enabled: bool,
96 pub temporary: bool,
97 pub log_message: Option<String>,
98 pub created_at: DateTime<Utc>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ExecutionSnapshot {
104 pub id: Uuid,
105 pub timestamp: DateTime<Utc>,
106 pub step_number: usize,
107 pub location: DebugLocation,
108 pub call_stack: Vec<StackFrame>,
109 pub variables: IndexMap<String, VariableValue>,
110 pub memory_usage: Option<usize>,
111 pub performance_metrics: HashMap<String, f64>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub enum DebuggerCommand {
117 Start,
118 Pause,
119 Resume,
120 Step(StepMode),
121 SetBreakpoint(DebugLocation, Option<String>),
122 RemoveBreakpoint(Uuid),
123 InspectVariable(String),
124 EvaluateExpression(String),
125 ShowCallStack,
126 ShowHistory,
127 JumpToStep(usize),
128 Reset,
129 Exit,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub enum DebuggerResponse {
135 Started,
136 Paused(DebugLocation),
137 Resumed,
138 Stepped(ExecutionSnapshot),
139 BreakpointHit(Breakpoint, ExecutionSnapshot),
140 VariableInspected(VariableValue),
141 ExpressionEvaluated(String),
142 CallStackShown(Vec<StackFrame>),
143 HistoryShown(Vec<ExecutionSnapshot>),
144 JumpedToStep(ExecutionSnapshot),
145 Reset,
146 Error(String),
147}
148
149impl InteractiveDebugger {
150 pub fn new(config: &DebugConfig) -> Self {
152 Self {
153 config: config.clone(),
154 state: Arc::new(RwLock::new(DebuggerState {
155 is_running: false,
156 is_paused: false,
157 current_location: None,
158 call_stack: Vec::new(),
159 variables: IndexMap::new(),
160 step_mode: StepMode::Continue,
161 session_start: Utc::now(),
162 })),
163 breakpoints: Arc::new(RwLock::new(HashMap::new())),
164 execution_history: Arc::new(Mutex::new(VecDeque::new())),
165 current_step: Arc::new(Mutex::new(0)),
166 max_history_size: config.max_gradient_history, }
168 }
169
170 pub async fn start(&mut self) -> Result<()> {
172 let mut state = self.state.write();
173 state.is_running = true;
174 state.session_start = Utc::now();
175 tracing::info!("Interactive debugger started");
176 Ok(())
177 }
178
179 pub async fn process_command(&self, command: DebuggerCommand) -> Result<DebuggerResponse> {
181 match command {
182 DebuggerCommand::Start => {
183 let mut state = self.state.write();
184 state.is_running = true;
185 Ok(DebuggerResponse::Started)
186 },
187
188 DebuggerCommand::Pause => {
189 let mut state = self.state.write();
190 state.is_paused = true;
191 if let Some(location) = &state.current_location {
192 Ok(DebuggerResponse::Paused(location.clone()))
193 } else {
194 Ok(DebuggerResponse::Paused(DebugLocation {
195 module: "unknown".to_string(),
196 function: "unknown".to_string(),
197 line: None,
198 instruction: None,
199 context: None,
200 }))
201 }
202 },
203
204 DebuggerCommand::Resume => {
205 let mut state = self.state.write();
206 state.is_paused = false;
207 state.step_mode = StepMode::Continue;
208 Ok(DebuggerResponse::Resumed)
209 },
210
211 DebuggerCommand::Step(mode) => self.execute_step(mode).await,
212
213 DebuggerCommand::SetBreakpoint(location, condition) => {
214 self.set_breakpoint(location, condition).await
215 },
216
217 DebuggerCommand::RemoveBreakpoint(id) => self.remove_breakpoint(id).await,
218
219 DebuggerCommand::InspectVariable(name) => self.inspect_variable(&name).await,
220
221 DebuggerCommand::EvaluateExpression(expr) => self.evaluate_expression(&expr).await,
222
223 DebuggerCommand::ShowCallStack => {
224 let state = self.state.read();
225 Ok(DebuggerResponse::CallStackShown(state.call_stack.clone()))
226 },
227
228 DebuggerCommand::ShowHistory => {
229 let history = self.execution_history.lock();
230 Ok(DebuggerResponse::HistoryShown(
231 history.iter().cloned().collect(),
232 ))
233 },
234
235 DebuggerCommand::JumpToStep(step_num) => self.jump_to_step(step_num).await,
236
237 DebuggerCommand::Reset => self.reset().await,
238
239 DebuggerCommand::Exit => {
240 let mut state = self.state.write();
241 state.is_running = false;
242 Ok(DebuggerResponse::Reset)
243 },
244 }
245 }
246
247 async fn execute_step(&self, mode: StepMode) -> Result<DebuggerResponse> {
249 let mut state = self.state.write();
250 state.step_mode = mode;
251 state.is_paused = true;
252
253 let snapshot = ExecutionSnapshot {
255 id: Uuid::new_v4(),
256 timestamp: Utc::now(),
257 step_number: {
258 let mut step = self.current_step.lock();
259 *step += 1;
260 *step
261 },
262 location: state.current_location.clone().unwrap_or_else(|| DebugLocation {
263 module: "runtime".to_string(),
264 function: "step".to_string(),
265 line: None,
266 instruction: Some(format!("Step {:?}", mode)),
267 context: None,
268 }),
269 call_stack: state.call_stack.clone(),
270 variables: state.variables.clone(),
271 memory_usage: None,
272 performance_metrics: HashMap::new(),
273 };
274
275 {
277 let mut history = self.execution_history.lock();
278 history.push_back(snapshot.clone());
279 if history.len() > self.max_history_size {
280 history.pop_front();
281 }
282 }
283
284 Ok(DebuggerResponse::Stepped(snapshot))
285 }
286
287 async fn set_breakpoint(
289 &self,
290 location: DebugLocation,
291 condition: Option<String>,
292 ) -> Result<DebuggerResponse> {
293 let breakpoint = Breakpoint {
294 id: Uuid::new_v4(),
295 location,
296 condition,
297 hit_count: 0,
298 enabled: true,
299 temporary: false,
300 log_message: None,
301 created_at: Utc::now(),
302 };
303
304 self.breakpoints.write().insert(breakpoint.id.to_string(), breakpoint.clone());
305
306 tracing::info!(
307 "Breakpoint set at {}::{}",
308 breakpoint.location.module,
309 breakpoint.location.function
310 );
311 Ok(DebuggerResponse::Started) }
313
314 async fn remove_breakpoint(&self, id: Uuid) -> Result<DebuggerResponse> {
316 if self.breakpoints.write().remove(&id.to_string()).is_some() {
317 tracing::info!("Breakpoint {} removed", id);
318 }
319 Ok(DebuggerResponse::Started)
320 }
321
322 async fn inspect_variable(&self, name: &str) -> Result<DebuggerResponse> {
324 let state = self.state.read();
325 if let Some(var) = state.variables.get(name) {
326 Ok(DebuggerResponse::VariableInspected(var.clone()))
327 } else {
328 Ok(DebuggerResponse::Error(format!(
329 "Variable '{}' not found",
330 name
331 )))
332 }
333 }
334
335 async fn evaluate_expression(&self, _expr: &str) -> Result<DebuggerResponse> {
337 Ok(DebuggerResponse::ExpressionEvaluated(
340 "Expression evaluation not implemented".to_string(),
341 ))
342 }
343
344 async fn jump_to_step(&self, step_num: usize) -> Result<DebuggerResponse> {
346 let history = self.execution_history.lock();
347 if let Some(snapshot) = history.iter().find(|s| s.step_number == step_num) {
348 let snapshot = snapshot.clone();
349 drop(history);
350
351 {
353 let mut state = self.state.write();
354 state.current_location = Some(snapshot.location.clone());
355 state.call_stack = snapshot.call_stack.clone();
356 state.variables = snapshot.variables.clone();
357 state.is_paused = true;
358 }
359
360 *self.current_step.lock() = step_num;
361 Ok(DebuggerResponse::JumpedToStep(snapshot))
362 } else {
363 Ok(DebuggerResponse::Error(format!(
364 "Step {} not found in history",
365 step_num
366 )))
367 }
368 }
369
370 pub async fn reset(&self) -> Result<DebuggerResponse> {
372 {
373 let mut state = self.state.write();
374 *state = DebuggerState {
375 is_running: false,
376 is_paused: false,
377 current_location: None,
378 call_stack: Vec::new(),
379 variables: IndexMap::new(),
380 step_mode: StepMode::Continue,
381 session_start: Utc::now(),
382 };
383 }
384
385 self.breakpoints.write().clear();
386 self.execution_history.lock().clear();
387 *self.current_step.lock() = 0;
388
389 Ok(DebuggerResponse::Reset)
390 }
391
392 pub async fn add_variable(&self, name: String, value: String, type_name: String) -> Result<()> {
394 let var = VariableValue {
395 name: name.clone(),
396 value,
397 type_name,
398 size_bytes: None,
399 shape: None,
400 is_tensor: false,
401 metadata: HashMap::new(),
402 };
403
404 self.state.write().variables.insert(name, var);
405 Ok(())
406 }
407
408 pub async fn update_location(&self, location: DebugLocation) -> Result<()> {
410 let mut state = self.state.write();
411 state.current_location = Some(location.clone());
412
413 let breakpoints = self.breakpoints.read();
415 for (_, breakpoint) in breakpoints.iter() {
416 if breakpoint.enabled
417 && breakpoint.location.module == location.module
418 && breakpoint.location.function == location.function
419 {
420 state.is_paused = true;
421 tracing::info!(
422 "Breakpoint hit at {}::{}",
423 location.module,
424 location.function
425 );
426 break;
427 }
428 }
429
430 Ok(())
431 }
432
433 pub async fn push_frame(&self, location: DebugLocation) -> Result<()> {
435 let frame = StackFrame {
436 id: Uuid::new_v4(),
437 location,
438 locals: IndexMap::new(),
439 timestamp: Utc::now(),
440 depth: self.state.read().call_stack.len(),
441 };
442
443 self.state.write().call_stack.push(frame);
444 Ok(())
445 }
446
447 pub async fn pop_frame(&self) -> Result<Option<StackFrame>> {
449 Ok(self.state.write().call_stack.pop())
450 }
451
452 pub async fn generate_report(&self) -> Result<InteractiveDebuggerReport> {
454 let state = self.state.read();
455 let breakpoints = self.breakpoints.read();
456 let history = self.execution_history.lock();
457
458 Ok(InteractiveDebuggerReport {
459 session_duration: Utc::now() - state.session_start,
460 total_steps: *self.current_step.lock(),
461 total_breakpoints: breakpoints.len(),
462 breakpoint_hits: breakpoints.values().map(|b| b.hit_count).sum(),
463 max_call_stack_depth: state.call_stack.len(),
464 variables_tracked: state.variables.len(),
465 history_entries: history.len(),
466 current_state: state.clone(),
467 })
468 }
469
470 pub fn is_paused(&self) -> bool {
472 self.state.read().is_paused
473 }
474
475 pub fn current_step(&self) -> usize {
477 *self.current_step.lock()
478 }
479
480 pub fn get_breakpoints(&self) -> Vec<Breakpoint> {
482 self.breakpoints.read().values().cloned().collect()
483 }
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize)]
488pub struct InteractiveDebuggerReport {
489 pub session_duration: chrono::Duration,
490 pub total_steps: usize,
491 pub total_breakpoints: usize,
492 pub breakpoint_hits: usize,
493 pub max_call_stack_depth: usize,
494 pub variables_tracked: usize,
495 pub history_entries: usize,
496 pub current_state: DebuggerState,
497}
498
499impl Default for DebuggerState {
500 fn default() -> Self {
501 Self {
502 is_running: false,
503 is_paused: false,
504 current_location: None,
505 call_stack: Vec::new(),
506 variables: IndexMap::new(),
507 step_mode: StepMode::Continue,
508 session_start: Utc::now(),
509 }
510 }
511}