Skip to content

Latest commit

 

History

History
40 lines (29 loc) · 8.45 KB

debug.md

File metadata and controls

40 lines (29 loc) · 8.45 KB

Debugging

Reference index · Official Lua 5.2 debug library · C Lua 5.2.4 source · Implementation

Install with state.OpenDebugLibrary(). Thread-aware functions accept an optional first thread; otherwise they inspect the current state. Metadata describes Lua bytecode and managed call frames, not a native Lua C stack.

API / official reference Inputs and result Lua-CSharp implementation notes and differences
debug.debug() Officially enters an interactive debug command loop. Unavailable: not registered. Interactive console/debugger integration must be provided by the host.
debug.gethook([thread]) Returns the installed hook, mask, and count. After removing the hook: nil, empty mask, and zero count. A host-installed hook is returned as its managed LuaFunction; C Lua reports a hook installed through its C API as the string "external hook". The host hook's execution policy can differ from a Lua-installed hook; see below.
debug.getinfo([thread,] f [, what]) Returns function/frame metadata selected by n, S, l, u, t, f, and L, or nil for a missing stack level. Numeric-string levels are accepted and fractions truncate toward zero, as in Lua 5.2. C# functions report what="C#" and source="=[C#]" instead of C-function labels. u exposes managed closure capture counts. For a Lua caller discarded by return hostFunction(), the recorded call site and prototype provide n, S, l, u, t, and L. The func field is nil because the caller's closure was released.
debug.getlocal([thread,] f, index) Inspects a frame local/temporary/vararg, or a Lua function's parameter name; returns nil when no such local exists. Managed frames expose their Lua-facing argument/temporary slots, not arbitrary C# local variables. The implementation operates on Lua-CSharp stack metadata. The caller discarded by a native tail call reports no locals (nil): its registers were reused, even though its level still exists for getinfo.
debug.getmetatable(value) Returns the actual metatable, bypassing __metatable protection. May return a host-frozen generated metatable. Reading the actual table does not make its contents writable.
debug.getregistry() Returns the state's registry table. Registry contents are managed runtime state; C API integer-slot conventions and native pointers are not a portable contract for Lua-CSharp.
debug.getupvalue(f, index) Returns an upvalue name/value, or no values for an invalid slot. Supports Lua closures and CSharpClosure.UpValues. Managed capture names and stripped Lua upvalue names are empty strings, as with C-function captures and stripped Lua names in C Lua 5.2.4.
debug.getuservalue(u) Reads a userdata's first user-value slot, or nil when absent. Uses ILuaUserData.UserValues. Host userdata can expose no slots; values are LuaValue entries rather than Lua 5.2's table/nil-only uservalue model.
debug.sethook([thread,] hook, mask [, count]) Installs/removes a hook; mask letters are c, r, and l, with an optional instruction count. Lua-installed hooks cannot Lua-yield. They can call C# code that awaits. Counting is based on VM instructions, not time spent inside a C# function. See hook details.
debug.setlocal([thread,] level, index, value) Writes a frame local and returns its name, or nil when unavailable. Writes Lua-facing managed stack slots; it cannot set arbitrary CLR locals.
debug.setmetatable(value, mt) Replaces/removes the actual metatable, bypassing __metatable protection; returns the value. Host-frozen tables still reject changing their metatable because LuaTable.Freeze() protects managed storage. Light userdata, including debug.upvalueid tokens and plain host-object values, share one type metatable within a global state and its coroutines, as in Lua 5.2; independent states have separate metatables.
debug.setupvalue(f, index, value) Writes an upvalue and returns its name, or no values for an invalid slot. Can update managed closure capture slots; their names are empty. Stripped Lua names are also empty, consistently with getupvalue and C Lua 5.2.4.
debug.setuservalue(u, value) Writes the first user-value slot and returns the userdata. Difference/limitation: any Lua value is accepted, rather than Lua 5.2's table/nil restriction. Userdata exposing no UserValues storage returns nil. This reflects the host-defined Span<LuaValue> storage API.
debug.traceback([thread,] [message [, level]]) Builds a traceback; non-string/non-number messages pass through unchanged without inspecting the level. Formats managed/C# frames and Lua source metadata rather than reproducing C Lua's exact trace text. See traceback details.
debug.upvalueid(f, index) Returns an identity token for an upvalue slot; joined Lua slots share an identity. Uses weakly associated managed identity objects instead of a native address. Retaining the token does not retain the captured payload; this fits managed GC without exposing movable CLR storage addresses.
debug.upvaluejoin(f1, n1, f2, n2) Makes one Lua closure's slot share another Lua closure's upvalue. Restricted to Lua closures, as in Lua 5.2. Managed capture arrays can be inspected/updated but are not joined by this API.

Hook details

Hook callbacks receive an event string and a line number or nil. Events include call, return, tail call, line, and count. Count hooks measure executed Lua instructions; they do not tick while execution is waiting inside a C# function. Nested Lua execution from C# can execute instructions and trigger hooks.

Hooks installed from C# through LuaState.SetHook retain the host hook policy, including supported async/yield behavior; they are distinguished internally from debug.sethook callbacks. This lets host instrumentation integrate with async execution while keeping Lua-installed callbacks compatible with non-yieldable Lua 5.2 hooks. See the C# debug-hook guide.

Stripped bytecode has no line/local names to recover. Source/line fields and local lookup reflect the metadata actually present; dumping and reloading a stripped chunk cannot reconstruct it. As in C Lua 5.2, getinfo reports 0 for an active stripped Lua frame, and -1 for a function-only query or a C# frame.

Traceback details

The default level is 1 for the current thread, including when that thread is supplied explicitly, and 0 for a different thread. Explicit levels skip that many frames. Negative levels and levels beyond the stack return only the message and traceback heading, as in C Lua 5.2.

Deep Lua-facing tracebacks use Lua 5.2's head/tail abbreviation and insert ... without formatting the omitted frames. The marker follows Lua 5.2's absolute stack-level rule; an explicit starting level past that marker can produce a longer trace. C# Traceback APIs retain complete traces.

Numeric messages follow NumberFormat. Other non-string/non-number messages are returned unchanged; for example, debug.traceback({}, 'bad') returns the table without validating the level. A thread in the first argument position selects the target thread; to pass a thread as the message, supply the target thread first.