Implement and archive Phase 5 debugger
This commit is contained in:
@@ -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()?;
|
||||
|
||||
Reference in New Issue
Block a user