Implement and archive Phase 5 debugger

This commit is contained in:
2026-09-06 21:24:38 +02:00
parent ce97700a98
commit e236306657
25 changed files with 3102 additions and 52 deletions

View File

@@ -19,6 +19,10 @@ use tb_runtime::traps::Quelle;
use tb_runtime::value::{self, default_value, ArrayObj, RecordObj, TypeInit, Value, VarRef};
use tb_ui::forms::{FormEvent, FormsModel, PropertyValue, ShowResult};
#[path = "debugger.rs"]
mod debugger;
pub use debugger::*;
/// Warum die VM die Kontrolle abgibt.
#[derive(Debug, Clone, PartialEq)]
pub enum RunEvent {
@@ -69,6 +73,8 @@ enum Handler {
}
struct Frame {
id: u64,
debug_line: Option<(u32, u32, usize)>,
proc: usize,
pc: usize,
locals_base: usize,
@@ -115,6 +121,7 @@ enum FormEventReturn {
pub struct Vm {
module: CompiledModule,
pub debug: Debugger,
globals: Vec<Value>,
locals: Vec<Value>,
stack: Vec<Value>,
@@ -194,6 +201,7 @@ impl Vm {
forms.show(form, false).expect("validiertes Startformular");
}
let mut vm = Vm {
debug: Debugger::default(),
globals,
locals: Vec::new(),
stack: Vec::new(),
@@ -258,7 +266,10 @@ impl Vm {
self.locals[locals_base + i] = v;
}
let stack_base = self.stack.len();
self.debug.next_frame += 1;
self.frames.push(Frame {
id: self.debug.next_frame,
debug_line: None,
source: 0,
column: 0,
proc,
@@ -785,7 +796,10 @@ impl Vm {
fn trap_frame_aufsetzen(&mut self, q: Quelle, ziel: u32) {
let locals_base = self.frames[0].locals_base;
let stack_base = self.stack.len();
self.debug.next_frame += 1;
self.frames.push(Frame {
id: self.debug.next_frame,
debug_line: None,
source: 0,
column: 0,
proc: 0,
@@ -1114,6 +1128,9 @@ impl Vm {
continue;
}
}
if self.debug.enabled {
self.record_debug_execution();
}
let Some(frame) = self.frames.last_mut() else {
return PollResult::Event(RunEvent::Ended);
};
@@ -1121,7 +1138,19 @@ impl Vm {
let Some(instr) = self.module.procs[frame.proc].code.get(pc).cloned() else {
return PollResult::Event(RunEvent::Ended);
};
frame.pc = pc + 1;
if matches!(instr, Instr::RetProc | Instr::RetFn)
&& self
.debug
.immediate
.as_ref()
.is_some_and(|e| self.frames.len() == e.depth + 1)
{
self.finish_immediate();
return PollResult::Event(RunEvent::Stopped {
line: self.current_line(),
});
}
self.frames.last_mut().unwrap().pc = pc + 1;
match self.exec(instr, pc, host) {
Ok(Flow::Normal) => {}
Ok(Flow::Event(ev)) => return PollResult::Event(ev),
@@ -1132,6 +1161,13 @@ impl Vm {
f.sleep = None;
}
if let Some(ev) = self.handle_error(e.0, host) {
if matches!(ev, RunEvent::Error { .. }) && self.immediate_active() {
self.debug.immediate_error = Some(format!("{ev:?}"));
self.finish_immediate();
return PollResult::Event(RunEvent::Stopped {
line: self.current_line(),
});
}
return PollResult::Event(ev);
}
}
@@ -1151,6 +1187,7 @@ impl Vm {
/// `Some(event)` = unbehandelt (Programmabbruch).
fn handle_error(&mut self, code: u16, _host: &mut dyn Host) -> Option<RunEvent> {
let line = self.current_line();
let error_location = self.debug_location();
if self.in_handler {
// Fehler im Handler: fatal, keine Kaskade.
return Some(self.error_event(code, line));
@@ -1175,9 +1212,24 @@ impl Vm {
}
}
}
let Some((depth, handler)) = target else {
let Some((mut depth, handler)) = target else {
return Some(self.error_event(code, line));
};
if let Some(entry) = &mut self.debug.immediate {
if depth < entry.depth {
let handler_proc = self.frames[depth].proc;
let handler_base = self.frames[depth].locals_base;
let f = &mut self.frames[entry.depth];
if handler == Handler::ResumeNext {
depth = entry.depth;
} else {
entry.handler_return = Some((f.last_stmt_pc, f.pc));
f.proc = handler_proc;
f.locals_base = handler_base;
depth = entry.depth;
}
}
}
// Unwinding bis zum Handler-Frame.
while self.frames.len() > depth + 1 {
self.pop_frame();
@@ -1203,6 +1255,16 @@ impl Vm {
}
Handler::None => unreachable!(),
}
if self.debug.break_errors {
self.debug.error = Some(DebugError {
code,
origin: error_location,
handler: self.next_debug_location(),
resume_pc: self.resume_pc,
});
self.debug.cancel_motion();
self.debug.error_pending = true;
}
None
}
@@ -1435,21 +1497,32 @@ impl Vm {
}
let erster_eintritt =
std::mem::take(&mut self.frames.last_mut().unwrap().handler_start);
let caller = self.frames.len() - 1;
if !initializing
&& !erster_eintritt
&& !self.debug.error_pending
&& self.zustellen(host, Zustellpunkt::Anweisung)
{
if self.debug.enabled {
self.frames[caller].pc = pc;
}
return Ok(Flow::Normal);
}
if self.flags != 0 {
if self.flags & F_STEP != 0 {
return Ok(Flow::Event(RunEvent::Stepped { line }));
if self.debug.enabled {
if let Some(event) = self.debug_boundary(pc, initializing) {
return Ok(Flow::Event(event));
}
}
if self.flags != 0 {
if self.flags & F_BREAK != 0
&& self.breakpoints.contains(&(self.current_module(), line))
{
self.debug.cancel_motion();
return Ok(Flow::Event(RunEvent::Breakpoint { line }));
}
if self.flags & F_STEP != 0 && line > 0 {
return Ok(Flow::Event(RunEvent::Stepped { line }));
}
if self.flags & F_POLL != 0 && self.rt.abbruch {
return Ok(Flow::Event(RunEvent::Interrupted { line }));
}
@@ -2577,11 +2650,17 @@ impl Vm {
}
Ok(Flow::Normal)
}
I::Resume0 => self.do_resume(|vm| vm.resume_pc),
I::ResumeNext => self.do_resume(|vm| {
let f = vm.frames.last().unwrap();
vm.next_stmt_pc(f.proc, vm.resume_pc)
}),
I::Resume0 => {
self.restore_debug_handler();
self.do_resume(|vm| vm.resume_pc)
}
I::ResumeNext => {
self.restore_debug_handler();
self.do_resume(|vm| {
let f = vm.frames.last().unwrap();
vm.next_stmt_pc(f.proc, vm.resume_pc)
})
}
I::ResumeLabel(t) => self.do_resume(move |_| t as usize),
I::RaiseError => {
let code = self.pop_i16()?;