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