Skip to main content

DebugSession

Struct DebugSession 

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

A debug session attached to a single Lua instance.

Create with DebugSession::new, which also returns a DebugController for the frontend.

§Lifecycle

new() → Idle → attach() → Running ⇄ Paused → Terminated

attach is a one-shot operation (per DAP semantics). Calling it on a non-Idle session returns an error. To re-attach, create a new DebugSession.

Implementations§

Source§

impl DebugSession

Source

pub fn new() -> (Self, DebugController)

Create a new debug session and its associated controller.

The session is in Idle state until attach is called.

Examples found in repository?
examples/basic_debug.rs (line 14)
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 attach(&self, lua: &Lua) -> LuaResult<()>

Install the debug hook on the given Lua instance.

After this call, breakpoints and stepping become active.

§Errors

Returns an error if the session is not in SessionState::Idle (e.g. already attached or terminated). Per DAP semantics, attach/launch is a one-shot operation per session.

Examples found in repository?
examples/basic_debug.rs (line 15)
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 detach(&self, lua: &Lua)

Detach from the Lua instance and terminate the session.

If the VM is paused (blocked in the command loop), this sends a disconnect command to unblock it. A DebugEvent::Terminated event is emitted to notify the frontend.

After detach, this session instance should be dropped. To debug again, create a new DebugSession.

Source

pub fn register_source( &self, name: &str, content: &str, ) -> Result<(), DebugError>

Register source code so the engine can validate breakpoints and display source context.

§Errors

Returns DebugError::Internal if the source lock is poisoned (another thread panicked while holding it).

Source

pub fn set_stop_on_entry(&self, stop: bool)

Set whether to pause on the first executable line.

Must be called before attach to guarantee the setting takes effect before the first hook fires.

Source

pub fn completion_notifier(&self) -> CompletionNotifier

Create a CompletionNotifier for signaling execution completion.

Move the returned handle into the thread that runs Lua code. Call CompletionNotifier::notify when execution finishes.

§Example
let notifier = session.completion_notifier();
std::thread::spawn(move || {
    let result = lua.load("...").exec();
    notifier.notify(result.err().map(|e| e.to_string()));
});

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> 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, 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,