From 30573478af9a00bea0805c6de04fb163e2f47bc4 Mon Sep 17 00:00:00 2001 From: Serena Duncan <57880238+smd21@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:59:32 -0500 Subject: [PATCH] Cider Dap - Variables displaying (#2357) bad branch name (oops) This branch adds the following functionality to the VSCode Cider debugger: 1. Displaying accurate port values 2. Continue requests now behave according to the spec by informing VSCode why the debugger was stopped 3. Cells (scopes) displayed now only shows the cells for the current component, rather then every cell in the environment 4. Small json tweaks (name, publisher, etc) --------- Co-authored-by: Serena --- Cargo.lock | 5 +- cider-dap/Cargo.toml | 3 +- cider-dap/calyxDebug/package.json | 4 +- cider-dap/src/adapter.rs | 172 ++++++++++++------ cider-dap/src/main.rs | 30 +-- interp/src/debugger/debugger_core.rs | 76 ++++++-- interp/src/debugger/mod.rs | 1 + .../src/flatten/structures/environment/env.rs | 87 +++++---- 8 files changed, 246 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5db4631e3..f6c0ca8599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -527,6 +527,7 @@ name = "cider-dap" version = "0.1.0" dependencies = [ "argh", + "baa", "dap", "interp", "owo-colors", @@ -787,8 +788,8 @@ dependencies = [ [[package]] name = "dap" -version = "0.3.1-alpha1" -source = "git+https://github.com/sztomi/dap-rs?tag=v0.3.1-alpha1#0a989dcd8bdd45233dca4af728120fa19566c416" +version = "0.4.1-alpha1" +source = "git+https://github.com/sztomi/dap-rs?tag=v0.4.1-alpha1#44c0aea6151f37b5411ab71d7d355fa3fc9b1ece" dependencies = [ "serde", "serde_json", diff --git a/cider-dap/Cargo.toml b/cider-dap/Cargo.toml index 2a16b698f4..6275d84bfc 100644 --- a/cider-dap/Cargo.toml +++ b/cider-dap/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dap = { git = "https://github.com/sztomi/dap-rs", tag = "v0.3.1-alpha1" } +dap = { git = "https://github.com/sztomi/dap-rs", tag = "v0.4.1-alpha1" } thiserror = "1.*" serde_json = "1.0" serde.workspace= true @@ -15,6 +15,7 @@ argh = "0.1" slog = "2.7.0" slog-term = "2.8.0" slog-async = "2.7.0" +baa = "0.14" interp = { path = "../interp" } diff --git a/cider-dap/calyxDebug/package.json b/cider-dap/calyxDebug/package.json index 1937774c7f..b29cfae2f0 100644 --- a/cider-dap/calyxDebug/package.json +++ b/cider-dap/calyxDebug/package.json @@ -1,8 +1,8 @@ { "name": "cider-dap", - "displayName": "Cider", + "displayName": "Cider DAP", "version": "0.0.1", - "publisher": "Cider", + "publisher": "Capra @ Cornell", "description": "A debug adapter for Calyx files", "author": { "name": "...", diff --git a/cider-dap/src/adapter.rs b/cider-dap/src/adapter.rs index 469e8449fb..87ef4f21ae 100644 --- a/cider-dap/src/adapter.rs +++ b/cider-dap/src/adapter.rs @@ -1,25 +1,30 @@ use crate::error::AdapterResult; +use baa::BitVecOps; +use dap::events::{Event, OutputEventBody, StoppedEventBody}; use dap::types::{ - Breakpoint, Scope, Source, SourceBreakpoint, StackFrame, Thread, Variable, + self, Breakpoint, Scope, Source, SourceBreakpoint, StackFrame, Thread, + Variable, }; use interp::debugger::commands::ParsedGroupName; use interp::debugger::source::structures::NewSourceMap; -use interp::debugger::OwnedDebugger; +use interp::debugger::{OwnedDebugger, StoppedReason}; +use interp::flatten::flat_ir::base::{GlobalCellIdx, PortValue}; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; pub struct MyAdapter { #[allow(dead_code)] debugger: OwnedDebugger, - break_count: Counter, + _break_count: Counter, thread_count: Counter, stack_count: Counter, breakpoints: HashSet, - stack_frames: Vec, // This field is a placeholder - threads: Vec, // This field is a placeholder - object_references: HashMap>, + stack_frames: Vec, + threads: Vec, // This field is a placeholder + object_references: HashMap>, source: String, ids: NewSourceMap, + frames_to_cmpts: HashMap, //stores mapping from frame ids to component idx } impl MyAdapter { @@ -28,7 +33,7 @@ impl MyAdapter { OwnedDebugger::from_file(&PathBuf::from(path), &std_path).unwrap(); Ok(MyAdapter { debugger, - break_count: Counter::new(), + _break_count: Counter::new(), thread_count: Counter::new(), stack_count: Counter::new(), breakpoints: HashSet::new(), @@ -37,6 +42,7 @@ impl MyAdapter { object_references: HashMap::new(), source: path.to_string(), ids: metadata, + frames_to_cmpts: HashMap::new(), }) } /// function to deal with setting breakpoints and updating debugger accordingly @@ -76,7 +82,7 @@ impl MyAdapter { let name = self.ids.lookup_line(source_point.line as u64); let breakpoint = make_breakpoint( - self.break_count.increment().into(), + Some(source_point.line), name.is_some(), Some(path.clone()), Some(source_point.line), @@ -115,7 +121,7 @@ impl MyAdapter { self.debugger.delete_breakpoints(to_debugger); } - ///Creates a thread using the parameter name. + /// Creates a thread using the parameter name. pub fn create_thread(&mut self, name: String) -> Thread { //how do we attach the thread to the program let thread = Thread { @@ -131,38 +137,46 @@ impl MyAdapter { self.threads.clone() } - //Returns a dummy stack frame, set to change. - pub fn create_stack(&mut self) -> Vec { - let frame = StackFrame { - id: self.stack_count.increment(), - // Maybe automate the name in the future? - name: String::from("Frame"), - source: Some(Source { - name: None, - path: Some(self.source.clone()), - source_reference: None, - presentation_hint: None, - origin: None, - sources: None, - adapter_data: None, - checksums: None, - }), - line: 1, - column: 0, - end_line: None, - end_column: None, - can_restart: None, - instruction_pointer_reference: None, - module_id: None, - presentation_hint: None, - }; - self.stack_frames.push(frame); - // Return all stack frames + /// returns all frames (components) in program + pub fn get_stack(&mut self) -> Vec { + if self.stack_frames.is_empty() { + self.create_stack(); + } self.stack_frames.clone() } - pub fn clone_stack(&self) -> Vec { - self.stack_frames.clone() + /// creates call stack where each frame is a component. Adds frames to current + /// call stack + fn create_stack(&mut self) { + let components = self.debugger.get_components(); + //turn the names into stack frames, ignore lines for right now + for (idx, comp) in components { + let frame = StackFrame { + id: self.stack_count.increment(), + // Maybe automate the name in the future? + name: String::from(comp), + source: Some(Source { + name: None, + path: Some(self.source.clone()), + source_reference: None, + presentation_hint: None, + origin: None, + sources: None, + adapter_data: None, + checksums: None, + }), + line: 1, // need to get this to be line component starts on + column: 0, + end_line: None, + end_column: None, + can_restart: None, + instruction_pointer_reference: None, + module_id: None, + presentation_hint: None, + }; + self.frames_to_cmpts.insert(frame.id, idx); + self.stack_frames.push(frame); + } } pub fn next_line(&mut self, _thread: i64) -> bool { @@ -199,26 +213,34 @@ impl MyAdapter { Some(p) => { let out: Vec = p .iter() - .map(|x| Variable { - name: String::from(x), - value: String::from("1"), - type_field: None, - presentation_hint: None, - evaluate_name: None, - variables_reference: 0, - named_variables: None, - indexed_variables: None, - memory_reference: None, + .map(|(nam, val)| { + let valu = val + .as_option() + .map(|x| x.val().to_u64().unwrap()) + .unwrap_or_default(); + Variable { + name: String::from(nam), + value: valu.to_string(), + type_field: None, + presentation_hint: None, + evaluate_name: None, + variables_reference: 0, + named_variables: None, + indexed_variables: None, + memory_reference: None, + } }) .collect(); out } } } - // return cells in calyx context (later should hopefully just return ones w current component) - pub fn get_scopes(&mut self, _frame: i64) -> Vec { + // return cells in calyx context + // todo: return only cells in current stack frame (component) + pub fn get_scopes(&mut self, frame: i64) -> Vec { let mut out_vec = vec![]; - let cell_names = self.debugger.get_cells(); + let component = self.frames_to_cmpts[&frame]; + let cell_names = self.debugger.get_comp_cells(component); let mut var_ref_count = 1; for (name, ports) in cell_names { self.object_references.insert(var_ref_count, ports); @@ -244,8 +266,56 @@ impl MyAdapter { } pub fn on_pause(&mut self) { + //self.debugger.pause(); self.object_references.clear(); } + + pub fn on_continue(&mut self, thread_id: i64) -> Event { + dbg!("continue - adapter"); + let result = self.debugger.cont(); + match result { + // honestly not sure if this is right behavior, still unsure what an output event IS lol. + Err(e) => Event::Output(OutputEventBody { + category: Some(types::OutputEventCategory::Stderr), + output: e.to_string(), + group: Some(types::OutputEventGroup::Start), + variables_reference: None, + source: None, + line: None, + column: None, + data: None, + }), + Ok(reason) => match reason { + StoppedReason::Done => Event::Terminated(None), + StoppedReason::Breakpoint(names) => { + let bp_lines: Vec = names + .into_iter() + .map(|x| self.ids.lookup(&x).unwrap().start_line as i64) + .collect(); + dbg!(&bp_lines); + //in map add adjusting stack frame lines + Event::Stopped(StoppedEventBody { + reason: types::StoppedEventReason::Breakpoint, + description: Some(String::from("hit breakpoint")), + thread_id: Some(thread_id), + preserve_focus_hint: None, + all_threads_stopped: Some(true), + text: None, + hit_breakpoint_ids: Some(bp_lines), + }) + } + StoppedReason::PauseReq => Event::Stopped(StoppedEventBody { + reason: types::StoppedEventReason::Pause, + description: Some(String::from("Paused")), + thread_id: Some(thread_id), + preserve_focus_hint: None, + all_threads_stopped: Some(true), + text: None, + hit_breakpoint_ids: None, + }), + }, + } + } } /// Simple struct used to keep an index of the breakpoints used. diff --git a/cider-dap/src/main.rs b/cider-dap/src/main.rs index 1f8131a987..1aca426dc5 100644 --- a/cider-dap/src/main.rs +++ b/cider-dap/src/main.rs @@ -108,7 +108,6 @@ where // Not sure if we need it // Make VSCode send disassemble request supports_stepping_granularity: Some(true), - supports_single_thread_execution_requests: Some(true), ..Default::default() })); server.respond(rsp)?; @@ -149,10 +148,8 @@ where // Construct the adapter let mut adapter = MyAdapter::new(program_path, std_path)?; - // Currently, we need two threads to run the debugger and step through, - // not sure why but would be good to look into for the future. + // one thread idk why but it works let thread = &adapter.create_thread(String::from("Main")); //does not seem as though this does anything - let thread2 = &adapter.create_thread(String::from("Thread 1")); // Notify server of first thread server.send_event(Event::Thread(ThreadEventBody { @@ -160,12 +157,6 @@ where thread_id: thread.id, }))?; - //Notify server of second thread - server.send_event(Event::Thread(ThreadEventBody { - reason: types::ThreadEventReason::Started, - thread_id: thread2.id, - }))?; - // Return the adapter instead of running the server Ok(adapter) } @@ -241,12 +232,7 @@ fn run_server( } // Send StackTrace, may be useful to make it more robust in the future Command::StackTrace(_args) => { - // Create new frame if empty, SUBJECT TO CHANGE - let frames = if adapter.clone_stack().is_empty() { - adapter.create_stack() - } else { - adapter.clone_stack() - }; + let frames = adapter.get_stack(); let rsp = req.success(ResponseBody::StackTrace(StackTraceResponse { stack_frames: frames, @@ -255,14 +241,16 @@ fn run_server( server.respond(rsp)?; } // Continue the debugger - Command::Continue(_args) => { + Command::Continue(args) => { // need to run debugger, ngl not really sure how to implement this functionality // run debugger until breakpoint or paused -> maybe have a process to deal w running debugger? + let stopped = adapter.on_continue(args.thread_id); let rsp = req.success(ResponseBody::Continue(ContinueResponse { - all_threads_continued: None, + all_threads_continued: Some(true), })); server.respond(rsp)?; + server.send_event(stopped)?; } // Send a Stopped event with reason Pause Command::Pause(args) => { @@ -347,19 +335,13 @@ fn run_server( server.send_event(stopped)?; } Command::Scopes(args) => { - //variables go in here most likely - //just get stuff displaying then figure out how to pretty it up - let frame_id = args.frame_id; let rsp = req.success(ResponseBody::Scopes(ScopesResponse { scopes: adapter.get_scopes(frame_id), })); - info!(logger, "responded with {rsp:?}"); server.respond(rsp)?; } Command::Variables(args) => { - info!(logger, "variables req"); - // never happening idk why let var_ref = args.variables_reference; let rsp = req.success(ResponseBody::Variables(VariablesResponse { diff --git a/interp/src/debugger/debugger_core.rs b/interp/src/debugger/debugger_core.rs index e9fc878c27..c89c2058d0 100644 --- a/interp/src/debugger/debugger_core.rs +++ b/interp/src/debugger/debugger_core.rs @@ -9,9 +9,12 @@ use crate::{ debugger::{ commands::PrintCommand, source::SourceMap, unwrap_error_message, }, - errors::{CiderError, CiderResult}, + errors::{BoxedCiderError, CiderError, CiderResult}, flatten::{ - flat_ir::prelude::GroupIdx, + flat_ir::{ + base::{GlobalCellIdx, PortValue}, + prelude::GroupIdx, + }, setup_simulation_with_metadata, structures::{ context::Context, @@ -68,6 +71,12 @@ pub enum DebuggerReturnStatus { Exit, } +pub enum StoppedReason { + Done, + Breakpoint(Vec<(String, String)>), //adapter then looks up line + PauseReq, +} + /// The interactive Calyx debugger. The debugger itself is run with the /// [Debugger::main_loop] function while this struct holds auxiliary /// information used to coordinate the debugging process. @@ -132,30 +141,44 @@ impl + Clone> Debugger { status: self .interpreter .get_currently_running_groups() - .map(|x| { - let group_name = - self.program_context.as_ref().lookup_name(x).clone(); - let parent_comp = self - .program_context - .as_ref() - .get_component_from_group(x); - let parent_name = self - .program_context - .as_ref() - .lookup_name(parent_comp) - .clone(); - (parent_name, group_name) - }) + .map(|x| self.grp_idx_to_name(x)) .collect(), done: self.interpreter.is_done(), } } - pub fn get_cells( + fn grp_idx_to_name(&self, x: GroupIdx) -> (String, String) { + let group_name = self.program_context.as_ref().lookup_name(x).clone(); + let parent_comp = + self.program_context.as_ref().get_component_from_group(x); + let parent_name = self + .program_context + .as_ref() + .lookup_name(parent_comp) + .clone(); + (parent_name, group_name) + } + + pub fn get_all_cells( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { self.interpreter.env().iter_cells() } + /// Get cell names and port values for the component specified by cmp_idx + pub fn get_comp_cells( + &self, + cmp_idx: GlobalCellIdx, + ) -> impl Iterator)> + '_ { + // component idx -> global cell idx + self.interpreter.env().iter_cmpt_cells(cmp_idx) + } + /// Get all components in the environment + pub fn get_components( + &self, + ) -> impl Iterator + '_ { + //this gets the names AND idx, now how to get the lines T.T + self.interpreter.env().iter_compts() + } // Go to next step pub fn step(&mut self, n: u32) -> CiderResult { @@ -175,6 +198,23 @@ impl + Clone> Debugger { .collect_vec(); self.manipulate_breakpoint(Command::Delete(parsed_bp_ids)); } + + pub fn cont(&mut self) -> Result { + self.do_continue()?; //need to error handle + let bps = self + .debugging_context + .hit_breakpoints() + .map(|x| self.grp_idx_to_name(x)) + .collect_vec(); + if self.interpreter.is_done() { + Ok(StoppedReason::Done) + } else if !bps.is_empty() { + Ok(StoppedReason::Breakpoint(bps)) + } else { + unreachable!() + } + } + #[inline] fn do_step(&mut self, n: u32) -> CiderResult<()> { for _ in 0..n { diff --git a/interp/src/debugger/mod.rs b/interp/src/debugger/mod.rs index cf36eecfa2..eb32fb7a8e 100644 --- a/interp/src/debugger/mod.rs +++ b/interp/src/debugger/mod.rs @@ -7,6 +7,7 @@ pub mod source; pub use debugger_core::{ Debugger, DebuggerInfo, DebuggerReturnStatus, OwnedDebugger, ProgramStatus, + StoppedReason, }; pub(crate) use macros::unwrap_error_message; diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index 2eb4d2ec4a..d08ca037f0 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -373,13 +373,45 @@ impl + Clone> Environment { self.pc.iter().map(|(_, x)| x) } + /// Method that returns an iterator over all component instances in the debugger + /// Used for Cider-DAP extension + pub fn iter_compts( + &self, + ) -> impl Iterator + '_ { + self.cells.iter().filter_map(|(idx, ledge)| match ledge { + CellLedger::Primitive { .. } => None, + CellLedger::Component(component_ledger) => { + Some((idx, self.ctx().lookup_name(component_ledger.comp_id))) + } + CellLedger::RaceDetectionPrimitive { .. } => None, //what this + }) + } + /// Method that returns an iterator over all cells in component cpt + /// Used for Cider-DAP extension + pub fn iter_cmpt_cells( + &self, + cpt: GlobalCellIdx, + ) -> impl Iterator)> + '_ { + // take globalcellid, look up in env to get compt ledger and get base indices + // w cmpt id, go to context look at ctx.secondary[cmptidx] to get aux info, want cell offset map just keys + // add local and globel offset, lookup full name and port info + let ledger = self.cells[cpt].as_comp().unwrap(); + let cells_keys = self.ctx().secondary.comp_aux_info[ledger.comp_id] + .cell_offset_map + .keys(); + cells_keys.map(|x| { + let idx = &ledger.index_bases + x; + (idx.get_full_name(self), self.ports_helper(idx)) + }) + } + /// Returns the full name and port list of each cell in the context pub fn iter_cells( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { let env = self; let cell_names = self.cells.iter().map(|(idx, _ledger)| { - (idx.get_full_name(env), self.get_ports_for_cell(idx)) + (idx.get_full_name(env), self.ports_helper(idx)) }); cell_names @@ -388,45 +420,32 @@ impl + Clone> Environment { } //not sure if beneficial to change this to be impl iterator as well - fn get_ports_for_cell(&self, cell: GlobalCellIdx) -> Vec { + fn ports_helper(&self, cell: GlobalCellIdx) -> Vec<(String, PortValue)> { let parent = self.get_parent_cell_from_cell(cell); match parent { None => { - let comp_ledger = self.cells[cell].as_comp().unwrap(); - let comp_info = - self.ctx().secondary.comp_aux_info.get(comp_ledger.comp_id); - let port_ids = comp_info.signature().into_iter().map(|x| { - &self.ctx().secondary.local_port_defs - [comp_info.port_offset_map[x]] - .name - }); - let port_names = port_ids - .map(|x| String::from(x.lookup_name(self.ctx()))) + let ports = self.get_ports_from_cell(cell); + let info = ports + .map(|(name, id)| { + ( + (name.lookup_name(self.ctx())).clone(), + self.ports[id].clone(), + ) + }) .collect_vec(); - port_names + info } Some(parent_cell) => { - let parent_comp_ledger = self.cells[parent_cell].unwrap_comp(); - let comp_info = self - .ctx() - .secondary - .comp_aux_info - .get(parent_comp_ledger.comp_id); - let local_offset = cell - &parent_comp_ledger.index_bases; - - let port_ids = self.ctx().secondary.local_cell_defs - [comp_info.cell_offset_map[local_offset]] - .ports - .into_iter() - .map(|x| { - &self.ctx().secondary.local_port_defs - [comp_info.port_offset_map[x]] - .name - }); - let names = port_ids - .map(|x| String::from(x.lookup_name(self.ctx()))) + let ports = self.get_ports_from_cell(parent_cell); + let info = ports + .map(|(name, id)| { + ( + (name.lookup_name(self.ctx())).clone(), + self.ports[id].clone(), + ) + }) .collect_vec(); - names + info } } }