Skip to main content

DebugController

Struct DebugController 

Source
pub struct DebugController { /* private fields */ }
Expand description

Controls a DebugSession from a frontend (MCP server, DAP adapter, Web console, …).

Thread-safe: Clone + Send + Sync.

Implementations§

Source§

impl DebugController

Source

pub fn state(&self) -> SessionState

Current session state (non-blocking, coarse-grained).

Returns whether the VM is Idle, Running, Paused, or Terminated. PauseReason is not preserved in the atomic representation — use wait_event to obtain the precise reason from DebugEvent::Paused.

Source

pub fn wait_event(&self) -> Result<DebugEvent, DebugError>

Block until the next event arrives from the engine.

This is the authoritative source for session events. In particular, DebugEvent::Paused carries the precise PauseReason that state() does not preserve.

Examples found in repository?
examples/basic_debug.rs (line 40)
10fn main() -> Result<(), Box<dyn std::error::Error>> {
11    let lua = Lua::new();
12
13    // Create debug session + controller.
14    let (session, controller) = DebugSession::new();
15    session.attach(&lua)?;
16
17    // Set a breakpoint on line 4.
18    let bp_id = controller.set_breakpoint("@example.lua", 4, None)?;
19    println!("Breakpoint {bp_id} set at @example.lua:4");
20
21    // Run Lua code on a separate thread.
22    // mlua 0.11's LuaError is not Send, so convert to a string error
23    // for cross-thread transport.
24    let handle = std::thread::spawn(move || -> Result<i64, String> {
25        lua.load(
26            r#"
27local a = 10
28local b = 20
29local c = 30
30local result = a + b + c
31return result
32"#,
33        )
34        .set_name("@example.lua")
35        .eval::<i64>()
36        .map_err(|e| e.to_string())
37    });
38
39    // Wait for the breakpoint hit.
40    match controller.wait_event()? {
41        DebugEvent::Paused { reason, stack } => {
42            println!("VM paused: {reason:?}");
43            for frame in &stack {
44                let line_str = frame
45                    .line
46                    .map_or_else(|| "?".to_string(), |l| l.to_string());
47                println!(
48                    "  frame {}: {} ({}:{})",
49                    frame.id, frame.name, frame.source, line_str
50                );
51            }
52
53            // Inspect locals.
54            match controller.get_locals(0) {
55                Ok(locals) => {
56                    println!("Locals:");
57                    for var in &locals {
58                        println!("  {} = {} ({})", var.name, var.value, var.type_name);
59                    }
60                }
61                Err(e) => println!("Failed to get locals: {e}"),
62            }
63
64            // Evaluate an expression.
65            match controller.evaluate("a + b", None) {
66                Ok(result) => println!("Eval 'a + b' = {result}"),
67                Err(e) => println!("Eval failed: {e}"),
68            }
69        }
70        other => println!("Unexpected event: {other:?}"),
71    }
72
73    // Resume.
74    controller.continue_execution()?;
75
76    // Wait for completion.
77    let result = handle.join().unwrap()?;
78    println!("Lua returned: {result}");
79
80    Ok(())
81}
Source

pub fn try_event(&self) -> Result<Option<DebugEvent>, DebugError>

Try to receive an event without blocking.

Source

pub fn wait_event_timeout( &self, timeout: Duration, ) -> Result<Option<DebugEvent>, DebugError>

Block until the next event arrives, with a timeout.

Returns Ok(None) if the timeout elapses without an event.

Source

pub fn set_breakpoint( &self, source: &str, line: usize, condition: Option<&str>, ) -> Result<BreakpointId, DebugError>

Set a breakpoint. Returns the assigned ID.

Works in any session state (Idle, Running, Paused, Terminated) — the breakpoint registry is shared via Arc<Mutex<…>> and accessed directly, not through the command channel.

Examples found in repository?
examples/basic_debug.rs (line 18)
10fn main() -> Result<(), Box<dyn std::error::Error>> {
11    let lua = Lua::new();
12
13    // Create debug session + controller.
14    let (session, controller) = DebugSession::new();
15    session.attach(&lua)?;
16
17    // Set a breakpoint on line 4.
18    let bp_id = controller.set_breakpoint("@example.lua", 4, None)?;
19    println!("Breakpoint {bp_id} set at @example.lua:4");
20
21    // Run Lua code on a separate thread.
22    // mlua 0.11's LuaError is not Send, so convert to a string error
23    // for cross-thread transport.
24    let handle = std::thread::spawn(move || -> Result<i64, String> {
25        lua.load(
26            r#"
27local a = 10
28local b = 20
29local c = 30
30local result = a + b + c
31return result
32"#,
33        )
34        .set_name("@example.lua")
35        .eval::<i64>()
36        .map_err(|e| e.to_string())
37    });
38
39    // Wait for the breakpoint hit.
40    match controller.wait_event()? {
41        DebugEvent::Paused { reason, stack } => {
42            println!("VM paused: {reason:?}");
43            for frame in &stack {
44                let line_str = frame
45                    .line
46                    .map_or_else(|| "?".to_string(), |l| l.to_string());
47                println!(
48                    "  frame {}: {} ({}:{})",
49                    frame.id, frame.name, frame.source, line_str
50                );
51            }
52
53            // Inspect locals.
54            match controller.get_locals(0) {
55                Ok(locals) => {
56                    println!("Locals:");
57                    for var in &locals {
58                        println!("  {} = {} ({})", var.name, var.value, var.type_name);
59                    }
60                }
61                Err(e) => println!("Failed to get locals: {e}"),
62            }
63
64            // Evaluate an expression.
65            match controller.evaluate("a + b", None) {
66                Ok(result) => println!("Eval 'a + b' = {result}"),
67                Err(e) => println!("Eval failed: {e}"),
68            }
69        }
70        other => println!("Unexpected event: {other:?}"),
71    }
72
73    // Resume.
74    controller.continue_execution()?;
75
76    // Wait for completion.
77    let result = handle.join().unwrap()?;
78    println!("Lua returned: {result}");
79
80    Ok(())
81}
Source

pub fn remove_breakpoint(&self, id: BreakpointId) -> Result<bool, DebugError>

Remove a breakpoint by ID.

Source

pub fn list_breakpoints(&self) -> Result<Vec<Breakpoint>, DebugError>

List all breakpoints.

Source

pub fn continue_execution(&self) -> Result<(), DebugError>

Resume execution.

Examples found in repository?
examples/basic_debug.rs (line 74)
10fn main() -> Result<(), Box<dyn std::error::Error>> {
11    let lua = Lua::new();
12
13    // Create debug session + controller.
14    let (session, controller) = DebugSession::new();
15    session.attach(&lua)?;
16
17    // Set a breakpoint on line 4.
18    let bp_id = controller.set_breakpoint("@example.lua", 4, None)?;
19    println!("Breakpoint {bp_id} set at @example.lua:4");
20
21    // Run Lua code on a separate thread.
22    // mlua 0.11's LuaError is not Send, so convert to a string error
23    // for cross-thread transport.
24    let handle = std::thread::spawn(move || -> Result<i64, String> {
25        lua.load(
26            r#"
27local a = 10
28local b = 20
29local c = 30
30local result = a + b + c
31return result
32"#,
33        )
34        .set_name("@example.lua")
35        .eval::<i64>()
36        .map_err(|e| e.to_string())
37    });
38
39    // Wait for the breakpoint hit.
40    match controller.wait_event()? {
41        DebugEvent::Paused { reason, stack } => {
42            println!("VM paused: {reason:?}");
43            for frame in &stack {
44                let line_str = frame
45                    .line
46                    .map_or_else(|| "?".to_string(), |l| l.to_string());
47                println!(
48                    "  frame {}: {} ({}:{})",
49                    frame.id, frame.name, frame.source, line_str
50                );
51            }
52
53            // Inspect locals.
54            match controller.get_locals(0) {
55                Ok(locals) => {
56                    println!("Locals:");
57                    for var in &locals {
58                        println!("  {} = {} ({})", var.name, var.value, var.type_name);
59                    }
60                }
61                Err(e) => println!("Failed to get locals: {e}"),
62            }
63
64            // Evaluate an expression.
65            match controller.evaluate("a + b", None) {
66                Ok(result) => println!("Eval 'a + b' = {result}"),
67                Err(e) => println!("Eval failed: {e}"),
68            }
69        }
70        other => println!("Unexpected event: {other:?}"),
71    }
72
73    // Resume.
74    controller.continue_execution()?;
75
76    // Wait for completion.
77    let result = handle.join().unwrap()?;
78    println!("Lua returned: {result}");
79
80    Ok(())
81}
Source

pub fn step_into(&self) -> Result<(), DebugError>

Step into the next line (descend into calls).

Source

pub fn step_over(&self) -> Result<(), DebugError>

Step to the next line at the same or shallower call depth.

Source

pub fn step_out(&self) -> Result<(), DebugError>

Step out of the current function.

Source

pub fn pause(&self) -> Result<(), DebugError>

Request the VM to pause at the next opportunity.

Sets an atomic flag that is checked on every Lua line event. Effective in any state — if the VM is Running, it will pause at the next executed line.

Source

pub fn get_stack_trace(&self) -> Result<Vec<StackFrame>, DebugError>

Get the current call stack.

Source

pub fn get_locals(&self, frame_id: usize) -> Result<Vec<Variable>, DebugError>

Get local variables at a stack frame.

Examples found in repository?
examples/basic_debug.rs (line 54)
10fn main() -> Result<(), Box<dyn std::error::Error>> {
11    let lua = Lua::new();
12
13    // Create debug session + controller.
14    let (session, controller) = DebugSession::new();
15    session.attach(&lua)?;
16
17    // Set a breakpoint on line 4.
18    let bp_id = controller.set_breakpoint("@example.lua", 4, None)?;
19    println!("Breakpoint {bp_id} set at @example.lua:4");
20
21    // Run Lua code on a separate thread.
22    // mlua 0.11's LuaError is not Send, so convert to a string error
23    // for cross-thread transport.
24    let handle = std::thread::spawn(move || -> Result<i64, String> {
25        lua.load(
26            r#"
27local a = 10
28local b = 20
29local c = 30
30local result = a + b + c
31return result
32"#,
33        )
34        .set_name("@example.lua")
35        .eval::<i64>()
36        .map_err(|e| e.to_string())
37    });
38
39    // Wait for the breakpoint hit.
40    match controller.wait_event()? {
41        DebugEvent::Paused { reason, stack } => {
42            println!("VM paused: {reason:?}");
43            for frame in &stack {
44                let line_str = frame
45                    .line
46                    .map_or_else(|| "?".to_string(), |l| l.to_string());
47                println!(
48                    "  frame {}: {} ({}:{})",
49                    frame.id, frame.name, frame.source, line_str
50                );
51            }
52
53            // Inspect locals.
54            match controller.get_locals(0) {
55                Ok(locals) => {
56                    println!("Locals:");
57                    for var in &locals {
58                        println!("  {} = {} ({})", var.name, var.value, var.type_name);
59                    }
60                }
61                Err(e) => println!("Failed to get locals: {e}"),
62            }
63
64            // Evaluate an expression.
65            match controller.evaluate("a + b", None) {
66                Ok(result) => println!("Eval 'a + b' = {result}"),
67                Err(e) => println!("Eval failed: {e}"),
68            }
69        }
70        other => println!("Unexpected event: {other:?}"),
71    }
72
73    // Resume.
74    controller.continue_execution()?;
75
76    // Wait for completion.
77    let result = handle.join().unwrap()?;
78    println!("Lua returned: {result}");
79
80    Ok(())
81}
Source

pub fn get_upvalues(&self, frame_id: usize) -> Result<Vec<Variable>, DebugError>

Get upvalues at a stack frame.

Source

pub fn evaluate( &self, expression: &str, frame_id: Option<usize>, ) -> Result<String, DebugError>

Evaluate a Lua expression while paused.

When frame_id is Some, the expression is evaluated in the scope of that stack frame — locals, upvalues, and globals are all accessible. When None, evaluation runs in the global scope only.

Examples found in repository?
examples/basic_debug.rs (line 65)
10fn main() -> Result<(), Box<dyn std::error::Error>> {
11    let lua = Lua::new();
12
13    // Create debug session + controller.
14    let (session, controller) = DebugSession::new();
15    session.attach(&lua)?;
16
17    // Set a breakpoint on line 4.
18    let bp_id = controller.set_breakpoint("@example.lua", 4, None)?;
19    println!("Breakpoint {bp_id} set at @example.lua:4");
20
21    // Run Lua code on a separate thread.
22    // mlua 0.11's LuaError is not Send, so convert to a string error
23    // for cross-thread transport.
24    let handle = std::thread::spawn(move || -> Result<i64, String> {
25        lua.load(
26            r#"
27local a = 10
28local b = 20
29local c = 30
30local result = a + b + c
31return result
32"#,
33        )
34        .set_name("@example.lua")
35        .eval::<i64>()
36        .map_err(|e| e.to_string())
37    });
38
39    // Wait for the breakpoint hit.
40    match controller.wait_event()? {
41        DebugEvent::Paused { reason, stack } => {
42            println!("VM paused: {reason:?}");
43            for frame in &stack {
44                let line_str = frame
45                    .line
46                    .map_or_else(|| "?".to_string(), |l| l.to_string());
47                println!(
48                    "  frame {}: {} ({}:{})",
49                    frame.id, frame.name, frame.source, line_str
50                );
51            }
52
53            // Inspect locals.
54            match controller.get_locals(0) {
55                Ok(locals) => {
56                    println!("Locals:");
57                    for var in &locals {
58                        println!("  {} = {} ({})", var.name, var.value, var.type_name);
59                    }
60                }
61                Err(e) => println!("Failed to get locals: {e}"),
62            }
63
64            // Evaluate an expression.
65            match controller.evaluate("a + b", None) {
66                Ok(result) => println!("Eval 'a + b' = {result}"),
67                Err(e) => println!("Eval failed: {e}"),
68            }
69        }
70        other => println!("Unexpected event: {other:?}"),
71    }
72
73    // Resume.
74    controller.continue_execution()?;
75
76    // Wait for completion.
77    let result = handle.join().unwrap()?;
78    println!("Lua returned: {result}");
79
80    Ok(())
81}
Source

pub fn disconnect(&self) -> Result<(), DebugError>

Disconnect from the session.

Trait Implementations§

Source§

impl Clone for DebugController

Source§

fn clone(&self) -> DebugController

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> MaybeSend for T
where T: Send,