diff --git a/src/langserver/debugger/evaluate.rs b/src/langserver/debugger/evaluate.rs index 6011f65d..f57f352b 100644 --- a/src/langserver/debugger/evaluate.rs +++ b/src/langserver/debugger/evaluate.rs @@ -13,10 +13,15 @@ impl Debugger { let extools = self.extools.get()?; + guard!(let Some(frame_id) = params.frameId else { + return Err(Box::new(GenericError("Must select a stack frame to evaluate in"))); + }); + + let (thread, frame_no) = extools.get_thread_by_frame_id(frame_id)?; + if input.starts_with('#') { if input == "#dis" || input == "#disassemble" { - let thread = extools.get_default_thread()?; - guard!(let Some(frame) = thread.call_stack.get(params.frameId.unwrap_or(0) as usize) else { + guard!(let Some(frame) = thread.call_stack.get(frame_no) else { return Err(Box::new(GenericError("Stack frame out of range"))); }); diff --git a/src/langserver/debugger/extools.rs b/src/langserver/debugger/extools.rs index ee25b3d5..25bdaa8e 100644 --- a/src/langserver/debugger/extools.rs +++ b/src/langserver/debugger/extools.rs @@ -196,8 +196,8 @@ impl Extools { (extools, thread) } - pub fn get_default_thread(&self) -> Result> { - self.get_thread(0) + pub fn get_all_threads(&self) -> std::sync::MutexGuard> { + self.threads.lock().unwrap() } pub fn get_thread(&self, thread_id: i64) -> Result> { @@ -205,6 +205,16 @@ impl Extools { .ok_or_else(|| Box::new(super::GenericError("Getting call stack failed")) as Box) } + pub fn get_thread_by_frame_id(&self, frame_id: i64) -> Result<(ThreadInfo, usize), Box> { + let frame_id = frame_id as usize; + let threads = self.threads.lock().unwrap(); + let thread_id = (frame_id % threads.len()) as i64; + let frame_no = frame_id / threads.len(); + let thread = threads.get(&thread_id).cloned() + .ok_or_else(|| Box::new(super::GenericError("Getting call stack failed")) as Box)?; + Ok((thread, frame_no)) + } + pub fn bytecode(&mut self, proc_ref: &str, override_id: usize) -> &[DisassembledInstruction] { let Extools { bytecode, sender, seq: _seq, bytecode_rx, .. } = self; bytecode.entry((proc_ref.to_owned(), override_id)).or_insert_with(|| { @@ -402,6 +412,22 @@ impl ExtoolsThread { debug_output!(in self.seq, "[extools] Dropping {:?}", _e); } } + + fn stopped(&self, base: dap_types::StoppedEvent) { + for &k in self.threads.lock().unwrap().keys() { + if k != 0 { + self.seq.issue_event(dap_types::StoppedEvent { + reason: "sleep".to_owned(), + threadId: Some(k), + .. Default::default() + }); + } + } + self.seq.issue_event(dap_types::StoppedEvent { + threadId: Some(0), + .. base + }); + } } handle_extools! { @@ -420,25 +446,22 @@ handle_extools! { on BreakpointHit(&mut self, hit) { match hit.reason { BreakpointHitReason::Step => { - self.seq.issue_event(dap_types::StoppedEvent { + self.stopped(dap_types::StoppedEvent { reason: dap_types::StoppedEvent::REASON_STEP.to_owned(), - threadId: Some(0), .. Default::default() }); } BreakpointHitReason::Pause => { - self.seq.issue_event(dap_types::StoppedEvent { + self.stopped(dap_types::StoppedEvent { reason: dap_types::StoppedEvent::REASON_PAUSE.to_owned(), description: Some("Paused by request".to_owned()), - threadId: Some(0), .. Default::default() }) } _ => { debug_output!(in self.seq, "[extools] {}#{}@{} hit", hit.proc, hit.override_id, hit.offset); - self.seq.issue_event(dap_types::StoppedEvent { + self.stopped(dap_types::StoppedEvent { reason: dap_types::StoppedEvent::REASON_BREAKPOINT.to_owned(), - threadId: Some(0), .. Default::default() }); } @@ -447,10 +470,9 @@ handle_extools! { on Runtime(&mut self, runtime) { output!(in self.seq, "[extools] Runtime in {}: {}", runtime.proc, runtime.message); - self.seq.issue_event(dap_types::StoppedEvent { + self.stopped(dap_types::StoppedEvent { reason: dap_types::StoppedEvent::REASON_EXCEPTION.to_owned(), text: Some(runtime.message.clone()), - threadId: Some(0), .. Default::default() }); self.queue(&self.runtime_tx, runtime); diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index e74c9083..58623dfe 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -531,7 +531,7 @@ handle_request! { for (i, ex_frame) in thread.call_stack.into_iter().enumerate() { let mut dap_frame = StackFrame { name: ex_frame.proc.clone(), - id: i as i64, + id: (i * extools.get_all_threads().len()) as i64 + params.threadId, instructionPointerReference: Some(format!("{}@{}#{}", ex_frame.proc, ex_frame.override_id, ex_frame.offset)), .. Default::default() }; @@ -588,9 +588,14 @@ handle_request! { on Scopes(&mut self, ScopesArguments { frameId }) { let extools = self.extools.get()?; - let thread = extools.get_default_thread()?; - guard!(let Some(frame) = thread.call_stack.get(frameId as usize) else { - return Err(Box::new(GenericError("Stack frame out of range"))); + let frame_id = frameId as usize; + + let threads = extools.get_all_threads(); + let thread_id = (frame_id % threads.len()) as i64; + let frame_no = frame_id / threads.len(); + + guard!(let Some(frame) = threads[&thread_id].call_stack.get(frame_no) else { + return Err(Box::new(GenericError2(format!("Stack frame out of range: {} (thread {}, depth {})", frameId, thread_id, frame_no)))); }); ScopesResponse { @@ -674,12 +679,11 @@ handle_request! { } // Stack frame, arguments or locals - let frame_idx = (params.variablesReference - 1) / 2; + let frame_id = (params.variablesReference - 1) / 2; let mod2 = params.variablesReference % 2; - // TODO: variablesReference should be different based on thread ID - let thread = extools.get_default_thread()?; - guard!(let Some(frame) = thread.call_stack.get(frame_idx as usize) else { + let (thread, frame_no) = extools.get_thread_by_frame_id(frame_id)?; + guard!(let Some(frame) = thread.call_stack.get(frame_no) else { return Err(Box::new(GenericError("Stack frame out of range"))); }); @@ -944,6 +948,19 @@ impl std::fmt::Display for GenericError { } } +#[derive(Debug)] +pub struct GenericError2(String); + +impl Error for GenericError2 { + fn description(&self) -> &str { &self.0 } +} + +impl std::fmt::Display for GenericError2 { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str(&self.0) + } +} + // ---------------------------------------------------------------------------- // Implementation-specific DAP extensions.