From a04b43c838353ecd5bfdbf90760360aacf94df35 Mon Sep 17 00:00:00 2001 From: Timur Safin <tsafin@tarantool.org> Date: Thu, 15 Sep 2022 19:19:43 +0300 Subject: [PATCH] refactor: format luadebug.lua Attempt to better format style of the imported luadebug.lua (formerly known as debugger.lua) NO_DOC=refactoring NO_CHANGELOG=rafactoring NO_TEST=refactoring --- third_party/lua/luadebug.lua | 956 +++++++++++++------------- third_party/lua/luadebug_tutorial.lua | 418 +++++------ 2 files changed, 705 insertions(+), 669 deletions(-) diff --git a/third_party/lua/luadebug.lua b/third_party/lua/luadebug.lua index 487b2c51ba..6069ae9692 100644 --- a/third_party/lua/luadebug.lua +++ b/third_party/lua/luadebug.lua @@ -35,35 +35,35 @@ local COLOR_RESET = "" local GREEN_CARET = " => " local function pretty(obj, max_depth) - if max_depth == nil then max_depth = dbg.pretty_depth end - - -- Returns true if a table has a __tostring metamethod. - local function coerceable(tbl) - local meta = getmetatable(tbl) - return (meta and meta.__tostring) - end - - local function recurse(obj, depth) - if type(obj) == "string" then - -- Dump the string so that escape sequences are printed. - return string.format("%q", obj) - elseif type(obj) == "table" and depth < max_depth and not coerceable(obj) then - local str = "{" - - for k, v in pairs(obj) do - local pair = pretty(k, 0).." = "..recurse(v, depth + 1) - str = str..(str == "{" and pair or ", "..pair) - end - - return str.."}" - else - -- tostring() can fail if there is an error in a __tostring metamethod. - local success, value = pcall(function() return tostring(obj) end) - return (success and value or "<!!error in __tostring metamethod!!>") - end - end - - return recurse(obj, 0) + if max_depth == nil then max_depth = dbg.pretty_depth end + + -- Returns true if a table has a __tostring metamethod. + local function coerceable(tbl) + local meta = getmetatable(tbl) + return (meta and meta.__tostring) + end + + local function recurse(obj, depth) + if type(obj) == "string" then + -- Dump the string so that escape sequences are printed. + return string.format("%q", obj) + elseif type(obj) == "table" and depth < max_depth and not coerceable(obj) then + local str = "{" + + for k, v in pairs(obj) do + local pair = pretty(k, 0) .. " = " .. recurse(v, depth + 1) + str = str .. (str == "{" and pair or ", " .. pair) + end + + return str .. "}" + else + -- tostring() can fail if there is an error in a __tostring metamethod. + local success, value = pcall(function() return tostring(obj) end) + return (success and value or "<!!error in __tostring metamethod!!>") + end + end + + return recurse(obj, 0) end -- The stack level that cmd_* functions use to access locals or info @@ -83,31 +83,34 @@ local LUA_JIT_SETLOCAL_WORKAROUND = 0 -- Default dbg.read function local function dbg_read(prompt) - dbg.write(prompt) - io.flush() - return io.read() + dbg.write(prompt) + io.flush() + return io.read() end -- Default dbg.write function local function dbg_write(str) - io.write(str) + io.write(str) end local function dbg_writeln(str, ...) - if select("#", ...) == 0 then - dbg.write((str or "<NULL>").."\n") - else - dbg.write(string.format(str.."\n", ...)) - end + if select("#", ...) == 0 then + dbg.write((str or "<NULL>") .. "\n") + else + dbg.write(string.format(str .. "\n", ...)) + end end -local function format_loc(file, line) return COLOR_BLUE..file..COLOR_RESET..":"..COLOR_YELLOW..line..COLOR_RESET end +local function format_loc(file, line) return COLOR_BLUE .. file .. COLOR_RESET .. ":" .. COLOR_YELLOW .. + line .. COLOR_RESET end + local function format_stack_frame_info(info) - local filename = info.source:match("@(.*)") - local source = filename and dbg.shorten_path(filename) or info.short_src - local namewhat = (info.namewhat == "" and "chunk at" or info.namewhat) - local name = (info.name and "'"..COLOR_BLUE..info.name..COLOR_RESET.."'" or format_loc(source, info.linedefined)) - return format_loc(source, info.currentline).." in "..namewhat.." "..name + local filename = info.source:match("@(.*)") + local source = filename and dbg.shorten_path(filename) or info.short_src + local namewhat = (info.namewhat == "" and "chunk at" or info.namewhat) + local name = (info.name and "'" .. COLOR_BLUE .. info.name .. COLOR_RESET .. "'" or + format_loc(source, info.linedefined)) + return format_loc(source, info.currentline) .. " in " .. namewhat .. " " .. name end local repl @@ -117,21 +120,21 @@ local repl local function frame_has_line(info) return info.currentline >= 0 end local function hook_factory(repl_threshold) - return function(offset, reason) - return function(event, _) - -- Skip events that don't have line information. - if not frame_has_line(debug.getinfo(2)) then return end - - -- Tail calls are specifically ignored since they also will have tail returns to balance out. - if event == "call" then - offset = offset + 1 - elseif event == "return" and offset > repl_threshold then - offset = offset - 1 - elseif event == "line" and offset <= repl_threshold then - repl(reason) - end - end - end + return function(offset, reason) + return function(event, _) + -- Skip events that don't have line information. + if not frame_has_line(debug.getinfo(2)) then return end + + -- Tail calls are specifically ignored since they also will have tail returns to balance out. + if event == "call" then + offset = offset + 1 + elseif event == "return" and offset > repl_threshold then + offset = offset - 1 + elseif event == "line" and offset <= repl_threshold then + repl(reason) + end + end + end end local hook_step = hook_factory(1) @@ -141,376 +144,407 @@ local hook_finish = hook_factory(-1) -- Create a table of all the locally accessible variables. -- Globals are not included when running the locals command, but are when running the print command. local function local_bindings(offset, include_globals) - local level = offset + stack_inspect_offset + CMD_STACK_LEVEL - local func = debug.getinfo(level).func - local bindings = {} - - -- Retrieve the upvalues - do local i = 1; while true do - local name, value = debug.getupvalue(func, i) - if not name then break end - bindings[name] = value - i = i + 1 - end end - - -- Retrieve the locals (overwriting any upvalues) - do local i = 1; while true do - local name, value = debug.getlocal(level, i) - if not name then break end - bindings[name] = value - i = i + 1 - end end - - -- Retrieve the varargs (works in Lua 5.2 and LuaJIT) - local varargs = {} - do local i = 1; while true do - local name, value = debug.getlocal(level, -i) - if not name then break end - varargs[i] = value - i = i + 1 - end end - if #varargs > 0 then bindings["..."] = varargs end - - if include_globals then - -- In Lua 5.2, you have to get the environment table from the function's locals. - local env = (_VERSION <= "Lua 5.1" and getfenv(func) or bindings._ENV) - return setmetatable(bindings, {__index = env or _G}) - else - return bindings - end + local level = offset + stack_inspect_offset + CMD_STACK_LEVEL + local func = debug.getinfo(level).func + local bindings = {} + + -- Retrieve the upvalues + do local i = 1; + while true do + local name, value = debug.getupvalue(func, i) + if not name then break end + bindings[name] = value + i = i + 1 + end + end + + -- Retrieve the locals (overwriting any upvalues) + do local i = 1; + while true do + local name, value = debug.getlocal(level, i) + if not name then break end + bindings[name] = value + i = i + 1 + end + end + + -- Retrieve the varargs (works in Lua 5.2 and LuaJIT) + local varargs = {} + do local i = 1; + while true do + local name, value = debug.getlocal(level, -i) + if not name then break end + varargs[i] = value + i = i + 1 + end + end + if #varargs > 0 then bindings["..."] = varargs end + + if include_globals then + -- In Lua 5.2, you have to get the environment table from the function's locals. + local env = (_VERSION <= "Lua 5.1" and getfenv(func) or bindings._ENV) + return setmetatable(bindings, { __index = env or _G }) + else + return bindings + end end -- Used as a __newindex metamethod to modify variables in cmd_eval(). local function mutate_bindings(_, name, value) - local FUNC_STACK_OFFSET = 3 -- Stack depth of this function. - local level = stack_inspect_offset + FUNC_STACK_OFFSET + CMD_STACK_LEVEL - - -- Set a local. - do local i = 1; repeat - local var = debug.getlocal(level, i) - if name == var then - dbg_writeln(COLOR_YELLOW.."luadebug.lua"..GREEN_CARET.."Set local variable "..COLOR_BLUE..name..COLOR_RESET) - return debug.setlocal(level + LUA_JIT_SETLOCAL_WORKAROUND, i, value) - end - i = i + 1 - until var == nil end - - -- Set an upvalue. - local func = debug.getinfo(level).func - do local i = 1; repeat - local var = debug.getupvalue(func, i) - if name == var then - dbg_writeln(COLOR_YELLOW.."luadebug.lua"..GREEN_CARET.."Set upvalue "..COLOR_BLUE..name..COLOR_RESET) - return debug.setupvalue(func, i, value) - end - i = i + 1 - until var == nil end - - -- Set a global. - dbg_writeln(COLOR_YELLOW.."luadebug.lua"..GREEN_CARET.."Set global variable "..COLOR_BLUE..name..COLOR_RESET) - _G[name] = value + local FUNC_STACK_OFFSET = 3 -- Stack depth of this function. + local level = stack_inspect_offset + FUNC_STACK_OFFSET + CMD_STACK_LEVEL + + -- Set a local. + do local i = 1; + repeat + local var = debug.getlocal(level, i) + if name == var then + dbg_writeln(COLOR_YELLOW .. + "luadebug.lua" .. GREEN_CARET .. "Set local variable " .. COLOR_BLUE .. name .. COLOR_RESET) + return debug.setlocal(level + LUA_JIT_SETLOCAL_WORKAROUND, i, value) + end + i = i + 1 + until var == nil + end + + -- Set an upvalue. + local func = debug.getinfo(level).func + do local i = 1; + repeat + local var = debug.getupvalue(func, i) + if name == var then + dbg_writeln(COLOR_YELLOW .. "luadebug.lua" .. GREEN_CARET .. + "Set upvalue " .. COLOR_BLUE .. name .. COLOR_RESET) + return debug.setupvalue(func, i, value) + end + i = i + 1 + until var == nil + end + + -- Set a global. + dbg_writeln(COLOR_YELLOW .. "luadebug.lua" .. GREEN_CARET .. "Set global variable " .. COLOR_BLUE .. + name .. COLOR_RESET) + _G[name] = value end -- Compile an expression with the given variable bindings. local function compile_chunk(block, env) - local source = "luadebug.lua REPL" - local chunk = nil - - if _VERSION <= "Lua 5.1" then - chunk = loadstring(block, source) - if chunk then setfenv(chunk, env) end - else - -- The Lua 5.2 way is a bit cleaner - chunk = load(block, source, "t", env) - end - - if not chunk then dbg_writeln(COLOR_RED.."Error: Could not compile block:\n"..COLOR_RESET..block) end - return chunk + local source = "luadebug.lua REPL" + local chunk = nil + + if _VERSION <= "Lua 5.1" then + chunk = loadstring(block, source) + if chunk then setfenv(chunk, env) end + else + -- The Lua 5.2 way is a bit cleaner + chunk = load(block, source, "t", env) + end + + if not chunk then dbg_writeln(COLOR_RED .. "Error: Could not compile block:\n" .. COLOR_RESET .. block) end + return chunk end local SOURCE_CACHE = {} local function where(info, context_lines) - local source = SOURCE_CACHE[info.source] - if not source then - source = {} - local filename = info.source:match("@(.*)") - if filename then - pcall(function() for line in io.lines(filename) do table.insert(source, line) end end) - elseif info.source then - for line in info.source:gmatch("(.-)\n") do table.insert(source, line) end - end - SOURCE_CACHE[info.source] = source - end - - if source and source[info.currentline] then - for i = info.currentline - context_lines, info.currentline + context_lines do - local tab_or_caret = (i == info.currentline and GREEN_CARET or " ") - local line = source[i] - if line then dbg_writeln(COLOR_GRAY.."% 4d"..tab_or_caret.."%s", i, line) end - end - else - dbg_writeln(COLOR_RED.."Error: Source not available for "..COLOR_BLUE..info.short_src); - end - - return false + local source = SOURCE_CACHE[info.source] + if not source then + source = {} + local filename = info.source:match("@(.*)") + if filename then + pcall(function() for line in io.lines(filename) do table.insert(source, line) end end) + elseif info.source then + for line in info.source:gmatch("(.-)\n") do table.insert(source, line) end + end + SOURCE_CACHE[info.source] = source + end + + if source and source[info.currentline] then + for i = info.currentline - context_lines, info.currentline + context_lines do + local tab_or_caret = (i == info.currentline and GREEN_CARET or " ") + local line = source[i] + if line then dbg_writeln(COLOR_GRAY .. "% 4d" .. tab_or_caret .. "%s", i, line) end + end + else + dbg_writeln(COLOR_RED .. "Error: Source not available for " .. COLOR_BLUE .. info.short_src); + end + + return false end -- Wee version differences local unpack = unpack or table.unpack -local pack = function(...) return {n = select("#", ...), ...} end +local pack = function(...) return { n = select("#", ...), ... } end local function cmd_step() - stack_inspect_offset = stack_top - return true, hook_step + stack_inspect_offset = stack_top + return true, hook_step end local function cmd_next() - stack_inspect_offset = stack_top - return true, hook_next + stack_inspect_offset = stack_top + return true, hook_next end local function cmd_finish() - local offset = stack_top - stack_inspect_offset - stack_inspect_offset = stack_top - return true, offset < 0 and hook_factory(offset - 1) or hook_finish + local offset = stack_top - stack_inspect_offset + stack_inspect_offset = stack_top + return true, offset < 0 and hook_factory(offset - 1) or hook_finish end local function cmd_print(expr) - local env = local_bindings(1, true) - local chunk = compile_chunk("return "..expr, env) - if chunk == nil then return false end - - -- Call the chunk and collect the results. - local results = pack(pcall(chunk, unpack(rawget(env, "...") or {}))) - - -- The first result is the pcall error. - if not results[1] then - dbg_writeln(COLOR_RED.."Error:"..COLOR_RESET.." "..results[2]) - else - local output = "" - for i = 2, results.n do - output = output..(i ~= 2 and ", " or "")..pretty(results[i]) - end - - if output == "" then output = "<no result>" end - dbg_writeln(COLOR_BLUE..expr.. GREEN_CARET..output) - end - - return false + local env = local_bindings(1, true) + local chunk = compile_chunk("return " .. expr, env) + if chunk == nil then return false end + + -- Call the chunk and collect the results. + local results = pack(pcall(chunk, unpack(rawget(env, "...") or {}))) + + -- The first result is the pcall error. + if not results[1] then + dbg_writeln(COLOR_RED .. "Error:" .. COLOR_RESET .. " " .. results[2]) + else + local output = "" + for i = 2, results.n do + output = output .. (i ~= 2 and ", " or "") .. pretty(results[i]) + end + + if output == "" then output = "<no result>" end + dbg_writeln(COLOR_BLUE .. expr .. GREEN_CARET .. output) + end + + return false end local function cmd_eval(code) - local env = local_bindings(1, true) - local mutable_env = setmetatable({}, { - __index = env, - __newindex = mutate_bindings, - }) - - local chunk = compile_chunk(code, mutable_env) - if chunk == nil then return false end - - -- Call the chunk and collect the results. - local success, err = pcall(chunk, unpack(rawget(env, "...") or {})) - if not success then - dbg_writeln(COLOR_RED.."Error:"..COLOR_RESET.." "..tostring(err)) - end - - return false + local env = local_bindings(1, true) + local mutable_env = setmetatable({}, { + __index = env, + __newindex = mutate_bindings, + }) + + local chunk = compile_chunk(code, mutable_env) + if chunk == nil then return false end + + -- Call the chunk and collect the results. + local success, err = pcall(chunk, unpack(rawget(env, "...") or {})) + if not success then + dbg_writeln(COLOR_RED .. "Error:" .. COLOR_RESET .. " " .. tostring(err)) + end + + return false end local function cmd_down() - local offset = stack_inspect_offset - local info - - repeat -- Find the next frame with a file. - offset = offset + 1 - info = debug.getinfo(offset + CMD_STACK_LEVEL) - until not info or frame_has_line(info) - - if info then - stack_inspect_offset = offset - dbg_writeln("Inspecting frame: "..format_stack_frame_info(info)) - if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end - else - info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) - dbg_writeln("Already at the bottom of the stack.") - end - - return false + local offset = stack_inspect_offset + local info + + repeat -- Find the next frame with a file. + offset = offset + 1 + info = debug.getinfo(offset + CMD_STACK_LEVEL) + until not info or frame_has_line(info) + + if info then + stack_inspect_offset = offset + dbg_writeln("Inspecting frame: " .. format_stack_frame_info(info)) + if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + else + info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) + dbg_writeln("Already at the bottom of the stack.") + end + + return false end local function cmd_up() - local offset = stack_inspect_offset - local info - - repeat -- Find the next frame with a file. - offset = offset - 1 - if offset < stack_top then info = nil; break end - info = debug.getinfo(offset + CMD_STACK_LEVEL) - until frame_has_line(info) - - if info then - stack_inspect_offset = offset - dbg_writeln("Inspecting frame: "..format_stack_frame_info(info)) - if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end - else - info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) - dbg_writeln("Already at the top of the stack.") - end - - return false + local offset = stack_inspect_offset + local info + + repeat -- Find the next frame with a file. + offset = offset - 1 + if offset < stack_top then info = nil; break end + info = debug.getinfo(offset + CMD_STACK_LEVEL) + until frame_has_line(info) + + if info then + stack_inspect_offset = offset + dbg_writeln("Inspecting frame: " .. format_stack_frame_info(info)) + if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + else + info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) + dbg_writeln("Already at the top of the stack.") + end + + return false end local function cmd_where(context_lines) - local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) - return (info and where(info, tonumber(context_lines) or 5)) + local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) + return (info and where(info, tonumber(context_lines) or 5)) end local function cmd_trace() - dbg_writeln("Inspecting frame %d", stack_inspect_offset - stack_top) - local i = 0; while true do - local info = debug.getinfo(stack_top + CMD_STACK_LEVEL + i) - if not info then break end - - local is_current_frame = (i + stack_top == stack_inspect_offset) - local tab_or_caret = (is_current_frame and GREEN_CARET or " ") - dbg_writeln(COLOR_GRAY.."% 4d"..COLOR_RESET..tab_or_caret.."%s", i, format_stack_frame_info(info)) - i = i + 1 - end - - return false + dbg_writeln("Inspecting frame %d", stack_inspect_offset - stack_top) + local i = 0; + while true do + local info = debug.getinfo(stack_top + CMD_STACK_LEVEL + i) + if not info then break end + + local is_current_frame = (i + stack_top == stack_inspect_offset) + local tab_or_caret = (is_current_frame and GREEN_CARET or " ") + dbg_writeln(COLOR_GRAY .. "% 4d" .. COLOR_RESET .. tab_or_caret .. "%s", i, format_stack_frame_info(info)) + i = i + 1 + end + + return false end local function cmd_locals() - local bindings = local_bindings(1, false) - - -- Get all the variable binding names and sort them - local keys = {} - for k, _ in pairs(bindings) do table.insert(keys, k) end - table.sort(keys) - - for _, k in ipairs(keys) do - local v = bindings[k] - - -- Skip the debugger object itself, "(*internal)" values, and Lua 5.2's _ENV object. - if not rawequal(v, dbg) and k ~= "_ENV" and not k:match("%(.*%)") then - dbg_writeln(" "..COLOR_BLUE..k.. GREEN_CARET..pretty(v)) - end - end - - return false + local bindings = local_bindings(1, false) + + -- Get all the variable binding names and sort them + local keys = {} + for k, _ in pairs(bindings) do table.insert(keys, k) end + table.sort(keys) + + for _, k in ipairs(keys) do + local v = bindings[k] + + -- Skip the debugger object itself, "(*internal)" values, and Lua 5.2's _ENV object. + if not rawequal(v, dbg) and k ~= "_ENV" and not k:match("%(.*%)") then + dbg_writeln(" " .. COLOR_BLUE .. k .. GREEN_CARET .. pretty(v)) + end + end + + return false end local function cmd_help() - dbg.write("" - .. COLOR_BLUE.." <return>"..GREEN_CARET.."re-run last command\n" - .. COLOR_BLUE.." c"..COLOR_YELLOW.."(ontinue)"..GREEN_CARET.."continue execution\n" - .. COLOR_BLUE.." s"..COLOR_YELLOW.."(tep)"..GREEN_CARET.."step forward by one line (into functions)\n" - .. COLOR_BLUE.." n"..COLOR_YELLOW.."(ext)"..GREEN_CARET.."step forward by one line (skipping over functions)\n" - .. COLOR_BLUE.." f"..COLOR_YELLOW.."(inish)"..GREEN_CARET.."step forward until exiting the current function\n" - .. COLOR_BLUE.." u"..COLOR_YELLOW.."(p)"..GREEN_CARET.."move up the stack by one frame\n" - .. COLOR_BLUE.." d"..COLOR_YELLOW.."(own)"..GREEN_CARET.."move down the stack by one frame\n" - .. COLOR_BLUE.." w"..COLOR_YELLOW.."(here) "..COLOR_BLUE.."[line count]"..GREEN_CARET.."print source code around the current line\n" - .. COLOR_BLUE.." e"..COLOR_YELLOW.."(val) "..COLOR_BLUE.."[statement]"..GREEN_CARET.."execute the statement\n" - .. COLOR_BLUE.." p"..COLOR_YELLOW.."(rint) "..COLOR_BLUE.."[expression]"..GREEN_CARET.."execute the expression and print the result\n" - .. COLOR_BLUE.." t"..COLOR_YELLOW.."(race)"..GREEN_CARET.."print the stack trace\n" - .. COLOR_BLUE.." l"..COLOR_YELLOW.."(ocals)"..GREEN_CARET.."print the function arguments, locals and upvalues.\n" - .. COLOR_BLUE.." h"..COLOR_YELLOW.."(elp)"..GREEN_CARET.."print this message\n" - .. COLOR_BLUE.." q"..COLOR_YELLOW.."(uit)"..GREEN_CARET.."halt execution\n" - ) - return false + dbg.write("" + .. COLOR_BLUE .. " <return>" .. GREEN_CARET .. "re-run last command\n" + .. COLOR_BLUE .. " c" .. COLOR_YELLOW .. "(ontinue)" .. GREEN_CARET .. "continue execution\n" + .. COLOR_BLUE .. " s" .. COLOR_YELLOW .. "(tep)" .. GREEN_CARET .. + "step forward by one line (into functions)\n" + .. + COLOR_BLUE .. " n" .. + COLOR_YELLOW .. "(ext)" .. GREEN_CARET .. "step forward by one line (skipping over functions)\n" + .. COLOR_BLUE .. + " f" .. COLOR_YELLOW .. "(inish)" .. GREEN_CARET .. "step forward until exiting the current function\n" + .. COLOR_BLUE .. " u" .. COLOR_YELLOW .. "(p)" .. GREEN_CARET .. "move up the stack by one frame\n" + .. COLOR_BLUE .. " d" .. COLOR_YELLOW .. "(own)" .. GREEN_CARET .. "move down the stack by one frame\n" + .. + COLOR_BLUE .. + " w" .. + COLOR_YELLOW .. + "(here) " .. COLOR_BLUE .. "[line count]" .. GREEN_CARET .. "print source code around the current line\n" + .. COLOR_BLUE .. + " e" .. COLOR_YELLOW .. "(val) " .. COLOR_BLUE .. "[statement]" .. GREEN_CARET .. "execute the statement\n" + .. + COLOR_BLUE .. + " p" .. + COLOR_YELLOW .. + "(rint) " .. COLOR_BLUE .. "[expression]" .. GREEN_CARET .. "execute the expression and print the result\n" + .. COLOR_BLUE .. " t" .. COLOR_YELLOW .. "(race)" .. GREEN_CARET .. "print the stack trace\n" + .. + COLOR_BLUE .. + " l" .. COLOR_YELLOW .. "(ocals)" .. GREEN_CARET .. "print the function arguments, locals and upvalues.\n" + .. COLOR_BLUE .. " h" .. COLOR_YELLOW .. "(elp)" .. GREEN_CARET .. "print this message\n" + .. COLOR_BLUE .. " q" .. COLOR_YELLOW .. "(uit)" .. GREEN_CARET .. "halt execution\n" + ) + return false end local last_cmd = false local commands = { - ["^c$"] = function() return true end, - ["^s$"] = cmd_step, - ["^n$"] = cmd_next, - ["^f$"] = cmd_finish, - ["^p%s+(.*)$"] = cmd_print, - ["^e%s+(.*)$"] = cmd_eval, - ["^u$"] = cmd_up, - ["^d$"] = cmd_down, - ["^w%s*(%d*)$"] = cmd_where, - ["^t$"] = cmd_trace, - ["^l$"] = cmd_locals, - ["^h$"] = cmd_help, - ["^q$"] = function() dbg.exit(0); return true end, + ["^c$"] = function() return true end, + ["^s$"] = cmd_step, + ["^n$"] = cmd_next, + ["^f$"] = cmd_finish, + ["^p%s+(.*)$"] = cmd_print, + ["^e%s+(.*)$"] = cmd_eval, + ["^u$"] = cmd_up, + ["^d$"] = cmd_down, + ["^w%s*(%d*)$"] = cmd_where, + ["^t$"] = cmd_trace, + ["^l$"] = cmd_locals, + ["^h$"] = cmd_help, + ["^q$"] = function() dbg.exit(0); return true end, } local function match_command(line) - for pat, func in pairs(commands) do - -- Return the matching command and capture argument. - if line:find(pat) then return func, line:match(pat) end - end + for pat, func in pairs(commands) do + -- Return the matching command and capture argument. + if line:find(pat) then return func, line:match(pat) end + end end -- Run a command line -- Returns true if the REPL should exit and the hook function factory local function run_command(line) - -- GDB/LLDB exit on ctrl-d - if line == nil then dbg.exit(1); return true end - - -- Re-execute the last command if you press return. - if line == "" then line = last_cmd or "h" end - - local command, command_arg = match_command(line) - if command then - last_cmd = line - -- unpack({...}) prevents tail call elimination so the stack frame indices are predictable. - return unpack({command(command_arg)}) - elseif dbg.auto_eval then - return unpack({cmd_eval(line)}) - else - dbg_writeln(COLOR_RED.."Error:"..COLOR_RESET.." command '%s' not recognized.\nType 'h' and press return for a command list.", line) - return false - end + -- GDB/LLDB exit on ctrl-d + if line == nil then dbg.exit(1); return true end + + -- Re-execute the last command if you press return. + if line == "" then line = last_cmd or "h" end + + local command, command_arg = match_command(line) + if command then + last_cmd = line + -- unpack({...}) prevents tail call elimination so the stack frame indices are predictable. + return unpack({ command(command_arg) }) + elseif dbg.auto_eval then + return unpack({ cmd_eval(line) }) + else + dbg_writeln(COLOR_RED .. + "Error:" .. COLOR_RESET .. " command '%s' not recognized.\nType 'h' and press return for a command list.", + line) + return false + end end repl = function(reason) - -- Skip frames without source info. - while not frame_has_line(debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3)) do - stack_inspect_offset = stack_inspect_offset + 1 - end - - local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3) - reason = reason and (COLOR_YELLOW.."break via "..COLOR_RED..reason..GREEN_CARET) or "" - dbg_writeln(reason..format_stack_frame_info(info)) - - if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end - - repeat - local success, done, hook = pcall(run_command, dbg.read(COLOR_RED.."luadebug.lua> "..COLOR_RESET)) - if success then - debug.sethook(hook and hook(0), "crl") - else - local message = COLOR_RED.."INTERNAL DEBUGGER.LUA ERROR. ABORTING\n:"..COLOR_RESET.." "..done - dbg_writeln(message) - error(message) - end - until done + -- Skip frames without source info. + while not frame_has_line(debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3)) do + stack_inspect_offset = stack_inspect_offset + 1 + end + + local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3) + reason = reason and (COLOR_YELLOW .. "break via " .. COLOR_RED .. reason .. GREEN_CARET) or "" + dbg_writeln(reason .. format_stack_frame_info(info)) + + if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + + repeat + local success, done, hook = pcall(run_command, dbg.read(COLOR_RED .. "luadebug.lua> " .. COLOR_RESET)) + if success then + debug.sethook(hook and hook(0), "crl") + else + local message = COLOR_RED .. "INTERNAL DEBUGGER.LUA ERROR. ABORTING\n:" .. COLOR_RESET .. " " .. done + dbg_writeln(message) + error(message) + end + until done end -- Make the debugger object callable like a function. dbg = setmetatable({}, { - __call = function(_, condition, top_offset, source) - if condition then return end - - top_offset = (top_offset or 0) - stack_inspect_offset = top_offset - stack_top = top_offset - - debug.sethook(hook_next(1, source or "dbg()"), "crl") - return - end, + __call = function(_, condition, top_offset, source) + if condition then return end + + top_offset = (top_offset or 0) + stack_inspect_offset = top_offset + stack_top = top_offset + + debug.sethook(hook_next(1, source or "dbg()"), "crl") + return + end, }) -- Expose the debugger's IO functions. dbg.read = dbg_read dbg.write = dbg_write -dbg.shorten_path = function (path) return path end +dbg.shorten_path = function(path) return path end dbg.exit = function(err) os.exit(err) end dbg.writeln = dbg_writeln @@ -526,43 +560,45 @@ local lua_error, lua_assert = error, assert -- Works like error(), but invokes the debugger. function dbg.error(err, level) - level = level or 1 - dbg_writeln(COLOR_RED.."ERROR: "..COLOR_RESET..pretty(err)) - dbg(false, level, "dbg.error()") - - lua_error(err, level) + level = level or 1 + dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. pretty(err)) + dbg(false, level, "dbg.error()") + + lua_error(err, level) end -- Works like assert(), but invokes the debugger on a failure. function dbg.assert(condition, message) - if not condition then - dbg_writeln(COLOR_RED.."ERROR:"..COLOR_RESET..message) - dbg(false, 1, "dbg.assert()") - end - - return lua_assert(condition, message) + if not condition then + dbg_writeln(COLOR_RED .. "ERROR:" .. COLOR_RESET .. message) + dbg(false, 1, "dbg.assert()") + end + + return lua_assert(condition, message) end -- Works like pcall(), but invokes the debugger on an error. function dbg.call(f, ...) - return xpcall(f, function(err) - dbg_writeln(COLOR_RED.."ERROR: "..COLOR_RESET..pretty(err)) - dbg(false, 1, "dbg.call()") - - return err - end, ...) + return xpcall(f, function(err) + dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. pretty(err)) + dbg(false, 1, "dbg.call()") + + return err + end, ...) end -- Error message handler that can be used with lua_pcall(). function dbg.msgh(...) - if debug.getinfo(2) then - dbg_writeln(COLOR_RED.."ERROR: "..COLOR_RESET..pretty(...)) - dbg(false, 1, "dbg.msgh()") - else - dbg_writeln(COLOR_RED.."luadebug.lua: "..COLOR_RESET.."Error did not occur in Lua code. Execution will continue after dbg_pcall().") - end - - return ... + if debug.getinfo(2) then + dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. pretty(...)) + dbg(false, 1, "dbg.msgh()") + else + dbg_writeln(COLOR_RED .. + "luadebug.lua: " .. + COLOR_RESET .. "Error did not occur in Lua code. Execution will continue after dbg_pcall().") + end + + return ... end -- Assume stdin/out are TTYs unless we can use LuaJIT's FFI to properly check them. @@ -572,7 +608,7 @@ local stdout_isatty = true -- Conditionally enable the LuaJIT FFI. local ffi = (jit and require("ffi")) if ffi then - ffi.cdef[[ + ffi.cdef [[ int isatty(int); // Unix int _isatty(int); // Windows void free(void *ptr); @@ -580,99 +616,99 @@ if ffi then char *readline(const char *); int add_history(const char *); ]] - - local function get_func_or_nil(sym) - local success, func = pcall(function() return ffi.C[sym] end) - return success and func or nil - end - - local isatty = get_func_or_nil("isatty") or get_func_or_nil("_isatty") - stdin_isatty = isatty(0) - stdout_isatty = isatty(1) + + local function get_func_or_nil(sym) + local success, func = pcall(function() return ffi.C[sym] end) + return success and func or nil + end + + local isatty = get_func_or_nil("isatty") or get_func_or_nil("_isatty") + stdin_isatty = isatty(0) + stdout_isatty = isatty(1) end -- Conditionally enable color support. local color_maybe_supported = (stdout_isatty and os.getenv("TERM") and os.getenv("TERM") ~= "dumb") if color_maybe_supported and not os.getenv("DBG_NOCOLOR") then - COLOR_GRAY = string.char(27) .. "[90m" - COLOR_RED = string.char(27) .. "[91m" - COLOR_BLUE = string.char(27) .. "[94m" - COLOR_YELLOW = string.char(27) .. "[33m" - COLOR_RESET = string.char(27) .. "[0m" - GREEN_CARET = string.char(27) .. "[92m => "..COLOR_RESET + COLOR_GRAY = string.char(27) .. "[90m" + COLOR_RED = string.char(27) .. "[91m" + COLOR_BLUE = string.char(27) .. "[94m" + COLOR_YELLOW = string.char(27) .. "[33m" + COLOR_RESET = string.char(27) .. "[0m" + GREEN_CARET = string.char(27) .. "[92m => " .. COLOR_RESET end if stdin_isatty and not os.getenv("DBG_NOREADLINE") then - pcall(function() - local linenoise = require 'linenoise' - - -- Load command history from ~/.lua_history - local hist_path = os.getenv('HOME') .. '/.lua_history' - linenoise.historyload(hist_path) - linenoise.historysetmaxlen(50) - - local function autocomplete(env, input, matches) - for name, _ in pairs(env) do - if name:match('^' .. input .. '.*') then - linenoise.addcompletion(matches, name) - end - end - end - - -- Auto-completion for locals and globals - linenoise.setcompletion(function(matches, input) - -- First, check the locals and upvalues. - local env = local_bindings(1, true) - autocomplete(env, input, matches) - - -- Then, check the implicit environment. - env = getmetatable(env).__index - autocomplete(env, input, matches) - end) - - dbg.read = function(prompt) - local str = linenoise.linenoise(prompt) - if str and not str:match "^%s*$" then - linenoise.historyadd(str) - linenoise.historysave(hist_path) - end - return str - end - dbg_writeln(COLOR_YELLOW.."luadebug.lua: "..COLOR_RESET.."Linenoise support enabled.") - end) - - -- Conditionally enable LuaJIT readline support. - pcall(function() - if dbg.read == nil and ffi then - local readline = ffi.load("readline") - dbg.read = function(prompt) - local cstr = readline.readline(prompt) - if cstr ~= nil then - local str = ffi.string(cstr) - if string.match(str, "[^%s]+") then - readline.add_history(cstr) - end - - ffi.C.free(cstr) - return str - else - return nil - end - end - dbg_writeln(COLOR_YELLOW.."luadebug.lua: "..COLOR_RESET.."Readline support enabled.") - end - end) + pcall(function() + local linenoise = require 'linenoise' + + -- Load command history from ~/.lua_history + local hist_path = os.getenv('HOME') .. '/.lua_history' + linenoise.historyload(hist_path) + linenoise.historysetmaxlen(50) + + local function autocomplete(env, input, matches) + for name, _ in pairs(env) do + if name:match('^' .. input .. '.*') then + linenoise.addcompletion(matches, name) + end + end + end + + -- Auto-completion for locals and globals + linenoise.setcompletion(function(matches, input) + -- First, check the locals and upvalues. + local env = local_bindings(1, true) + autocomplete(env, input, matches) + + -- Then, check the implicit environment. + env = getmetatable(env).__index + autocomplete(env, input, matches) + end) + + dbg.read = function(prompt) + local str = linenoise.linenoise(prompt) + if str and not str:match "^%s*$" then + linenoise.historyadd(str) + linenoise.historysave(hist_path) + end + return str + end + dbg_writeln(COLOR_YELLOW .. "luadebug.lua: " .. COLOR_RESET .. "Linenoise support enabled.") + end) + + -- Conditionally enable LuaJIT readline support. + pcall(function() + if dbg.read == nil and ffi then + local readline = ffi.load("readline") + dbg.read = function(prompt) + local cstr = readline.readline(prompt) + if cstr ~= nil then + local str = ffi.string(cstr) + if string.match(str, "[^%s]+") then + readline.add_history(cstr) + end + + ffi.C.free(cstr) + return str + else + return nil + end + end + dbg_writeln(COLOR_YELLOW .. "luadebug.lua: " .. COLOR_RESET .. "Readline support enabled.") + end + end) end -- Detect Lua version. if jit then -- LuaJIT - LUA_JIT_SETLOCAL_WORKAROUND = -1 - dbg_writeln(COLOR_YELLOW.."luadebug.lua: "..COLOR_RESET.."Loaded for "..jit.version) + LUA_JIT_SETLOCAL_WORKAROUND = -1 + dbg_writeln(COLOR_YELLOW .. "luadebug.lua: " .. COLOR_RESET .. "Loaded for " .. jit.version) elseif "Lua 5.1" <= _VERSION and _VERSION <= "Lua 5.4" then - dbg_writeln(COLOR_YELLOW.."luadebug.lua: "..COLOR_RESET.."Loaded for ".._VERSION) + dbg_writeln(COLOR_YELLOW .. "luadebug.lua: " .. COLOR_RESET .. "Loaded for " .. _VERSION) else - dbg_writeln(COLOR_YELLOW.."luadebug.lua: "..COLOR_RESET.."Not tested against ".._VERSION) - dbg_writeln("Please send me feedback!") + dbg_writeln(COLOR_YELLOW .. "luadebug.lua: " .. COLOR_RESET .. "Not tested against " .. _VERSION) + dbg_writeln("Please send me feedback!") end return dbg diff --git a/third_party/lua/luadebug_tutorial.lua b/third_party/lua/luadebug_tutorial.lua index fc97106286..ccf021307e 100644 --- a/third_party/lua/luadebug_tutorial.lua +++ b/third_party/lua/luadebug_tutorial.lua @@ -3,84 +3,84 @@ local dbg = require("luadebug") -print[[ - Welcome to the interactive luadebug.lua tutorial. - You'll want to open tutorial.lua in an editor to follow along. - - First of all, just drop luadebug.lua in your project. It's one file. - Load it the usual way using require. Ex: - local dbg = require("luadebug") - - luadebug.lua doesn't support traditional breakpoints. - So to get into the debugger, call it like a function. - Real breakpoints would be better, but this - keeps luadebug.lua simple and very fast. - At the end you'll find out how to open it automatically on a crash. - - Notice how luadebug.lua prints out your current file and line - as well as which function you are in. - Keep a close watch on this as you follow along. - It should be stopped a line after the dbg() call. - (Line 86 unless I forgot to double update it) - - Sometimes functions don't have global names. - It might print the name of a method, local variable - that held the function, or file:line where it starts. - - Type 'w' to show 5 lines of surrounding code directly in - the debugger. (w = Where) Type 'w 3' to show 3 lines, etc. - Alternatively, set dbg.auto_where to a number - to run it automatically every time the program advances. - - Once you've tried the where command, type 's' to step to - the next line. (s = Step to the next executable line) +print [[ + Welcome to the interactive luadebug.lua tutorial. + You'll want to open tutorial.lua in an editor to follow along. + + First of all, just drop luadebug.lua in your project. It's one file. + Load it the usual way using require. Ex: + local dbg = require("luadebug") + + luadebug.lua doesn't support traditional breakpoints. + So to get into the debugger, call it like a function. + Real breakpoints would be better, but this + keeps luadebug.lua simple and very fast. + At the end you'll find out how to open it automatically on a crash. + + Notice how luadebug.lua prints out your current file and line + as well as which function you are in. + Keep a close watch on this as you follow along. + It should be stopped a line after the dbg() call. + (Line 86 unless I forgot to double update it) + + Sometimes functions don't have global names. + It might print the name of a method, local variable + that held the function, or file:line where it starts. + + Type 'w' to show 5 lines of surrounding code directly in + the debugger. (w = Where) Type 'w 3' to show 3 lines, etc. + Alternatively, set dbg.auto_where to a number + to run it automatically every time the program advances. + + Once you've tried the where command, type 's' to step to + the next line. (s = Step to the next executable line) ]] -- Multi-line strings are executable statements apparently -- need to put this in an local to make the tutorial flow nicely. local str1 = [[ - The 's' command steps to the next executable line. - This may step you into a function call. - - In this case, then next line was a C function that printed this message. - You can't step into C functions, so it just steps over them. - - If you hit <return>, the debugger will rerun your last command. - Hit <return> 5 times to step into and through func1(). - Watch the line numbers. + The 's' command steps to the next executable line. + This may step you into a function call. + + In this case, then next line was a C function that printed this message. + You can't step into C functions, so it just steps over them. + + If you hit <return>, the debugger will rerun your last command. + Hit <return> 5 times to step into and through func1(). + Watch the line numbers. ]] local str2 = [[ - Stop! - You've now stepped through func1() - Notice how entering and exiting a function takes a step. - - Now try the 'n' command. - (n = step to the Next line in the source code) + Stop! + You've now stepped through func1() + Notice how entering and exiting a function takes a step. + + Now try the 'n' command. + (n = step to the Next line in the source code) ]] local function func1() - print(" Stepping through func1()...") - print(" Almost there...") + print(" Stepping through func1()...") + print(" Almost there...") end local function func2() - print(" You used the 'n' command.") - print(" So it's skipping over the lines in func2().") - - local function f() - print(" ... and anything it might call.") - end - - f() - - print() - print[[ - The 'n' command steps to the next line in the source file. - Unlike the 's' command, it steps over function calls, and not into them. - - Now try the 'c' command to continue on to the next breakpoint. - (c = Continue execution) + print(" You used the 'n' command.") + print(" So it's skipping over the lines in func2().") + + local function f() + print(" ... and anything it might call.") + end + + f() + + print() + print [[ + The 'n' command steps to the next line in the source file. + Unlike the 's' command, it steps over function calls, and not into them. + + Now try the 'c' command to continue on to the next breakpoint. + (c = Continue execution) ]] end @@ -93,28 +93,28 @@ print(str2) func2() local function func3() - print[[ - You are now sitting at a breakpoint inside of func3(). - Let's say you got here by stepping into the function. - After poking around for a bit, you just want to step until the - function returns, but don't want to - run the next command over and over. - - For this you would use the 'f' command. Try it now. - (f = Finish current function) + print [[ + You are now sitting at a breakpoint inside of func3(). + Let's say you got here by stepping into the function. + After poking around for a bit, you just want to step until the + function returns, but don't want to + run the next command over and over. + + For this you would use the 'f' command. Try it now. + (f = Finish current function) ]] - - dbg() - - print[[ - Now you are inside func4(), right after where it called func3(). - func4() has some arguments, local variables and upvalues. - Let's assume you want to see them. - - Try the 'l' command to list all the locally available variables. - (l = List local variables) - - Type 'c' to continue on to the next section. + + dbg() + + print [[ + Now you are inside func4(), right after where it called func3(). + func4() has some arguments, local variables and upvalues. + Let's assume you want to see them. + + Try the 'l' command to list all the locally available variables. + (l = List local variables) + + Type 'c' to continue on to the next section. ]] end @@ -123,157 +123,157 @@ local my_upvalue2 = "Awww, can't see this one" globalvar = "Weeee a global" function func4(a, b, ...) - local c = "sea" - local varargs_copy = {...} - - -- Functions only get upvalues if you reference them. - local d = my_upvalue1.." ... with stuff appended to it" - - func3() - - print[[ - Some things to notice about the local variables list. - '...' - This is the list of varargs passed to the function. - (This only works with Lua 5.2+ or LuaJIT 2.0+) - Note: varargs are not an array, but luadebug.lua stores them that way. - 'my_upvalue1' - This is a local variable defined outside of, but - referenced by the function. Upvalues show up - *only* when you reference them within your - function. 'my_upvalue2' isn't in the list - because func4() doesn't reference it. - - Listing the locals is nice, but sometimes there are too many to see at once. - Often times it's useful to print just a single variable, - evaluate an expression, or call a function to see what it returns. - For that you use the 'p' command. - (p = Print the result of an expression) - - Try these commands: - p my_upvalue1 - p 1 + 1 - p print("foo") - p math.cos(0) - - You can also interact with varargs. (Except on Lua 5.1) - For example: - p ... - p select(2, ...) - p {...} - - Type 'c' to continue to the next section. + local c = "sea" + local varargs_copy = { ... } + + -- Functions only get upvalues if you reference them. + local d = my_upvalue1 .. " ... with stuff appended to it" + + func3() + + print [[ + Some things to notice about the local variables list. + '...' + This is the list of varargs passed to the function. + (This only works with Lua 5.2+ or LuaJIT 2.0+) + Note: varargs are not an array, but luadebug.lua stores them that way. + 'my_upvalue1' + This is a local variable defined outside of, but + referenced by the function. Upvalues show up + *only* when you reference them within your + function. 'my_upvalue2' isn't in the list + because func4() doesn't reference it. + + Listing the locals is nice, but sometimes there are too many to see at once. + Often times it's useful to print just a single variable, + evaluate an expression, or call a function to see what it returns. + For that you use the 'p' command. + (p = Print the result of an expression) + + Try these commands: + p my_upvalue1 + p 1 + 1 + p print("foo") + p math.cos(0) + + You can also interact with varargs. (Except on Lua 5.1) + For example: + p ... + p select(2, ...) + p {...} + + Type 'c' to continue to the next section. ]] - dbg() - - print[[ - One thing you can't do with the print command is set variables. - Assignments are statements, and need to be run with the - 'e' command. (e = Evaluate a statement) - - Try these commands: - e my_upvalue1 = "foobar" - e print(my_upvalue1) - - Type 'c' to continue to the next section. + dbg() + + print [[ + One thing you can't do with the print command is set variables. + Assignments are statements, and need to be run with the + 'e' command. (e = Evaluate a statement) + + Try these commands: + e my_upvalue1 = "foobar" + e print(my_upvalue1) + + Type 'c' to continue to the next section. ]] - dbg() + dbg() end func4(1, "two", "vararg1", "vararg2", "vararg3") local function func5() - local my_var = "func5()" - print[[ - You are now in func5() which was called from func6(). - func6() was called from func7(). - - Try the 't' command to print out a backtrace and see for yourself. - (t = backTrace) - - Type 'c' to continue to the next section + local my_var = "func5()" + print [[ + You are now in func5() which was called from func6(). + func6() was called from func7(). + + Try the 't' command to print out a backtrace and see for yourself. + (t = backTrace) + + Type 'c' to continue to the next section ]] - dbg() - - print[[ - Notice that func5(), func6() and func7() all have a - 'my_var' local. You can print the func5()'s my_var easily enough. - What if you wanted to see what local variables were in func6() - or func7() to see how you got where you were? - - For that you use the 'u' and 'd' commands. - (u = Move up a stack frame) - (d = Move down a stack frame) - - Try the 'u' and 'd' commands a few times. - Print out the value of my_var using the 'p' command each time. - - Type 'c' to continue. + dbg() + + print [[ + Notice that func5(), func6() and func7() all have a + 'my_var' local. You can print the func5()'s my_var easily enough. + What if you wanted to see what local variables were in func6() + or func7() to see how you got where you were? + + For that you use the 'u' and 'd' commands. + (u = Move up a stack frame) + (d = Move down a stack frame) + + Try the 'u' and 'd' commands a few times. + Print out the value of my_var using the 'p' command each time. + + Type 'c' to continue. ]] - dbg() + dbg() end local function func6() - local my_var = "func6()" - func5() + local my_var = "func6()" + func5() end local function func7() - local my_var = "func7()" - func6() + local my_var = "func7()" + func6() end func7() -print[[ - That leaves only one more command. - Wouldn't it be nice if there was a way to remember - all these one letter debugger commands? - - Type 'h' to show the command list. - (h = Help) - - Type 'c' to continue. +print [[ + That leaves only one more command. + Wouldn't it be nice if there was a way to remember + all these one letter debugger commands? + + Type 'h' to show the command list. + (h = Help) + + Type 'c' to continue. ]] dbg() -print[[ - The following loop uses an assert-style breakpoint. - It will only engage when the conditional fails. (when i == 5) - - Type 'c' to continue. +print [[ + The following loop uses an assert-style breakpoint. + It will only engage when the conditional fails. (when i == 5) + + Type 'c' to continue. ]] -for i=0, 10 do - print("i = "..tostring(i)) - - dbg(i ~= 5) +for i = 0, 10 do + print("i = " .. tostring(i)) + + dbg(i ~= 5) end -print[[ - Last but not least, is the dbg.call() function. - It works sort of like Lua's xpcall() function, - but starts the debugger when an uncaught error occurs. - You can pretty much use it as a drop in replacement. - - For example: - dbg.call(function() - -- Potentially buggy code goes here. - end) - - Wrap it around your program's main loop or main entry point. - Then when your program crashes, you won't need to go back - and add breakpoints. - - That pretty much wraps ups the basics. - Hopefully you find luadebug.lua to be simple but useful. +print [[ + Last but not least, is the dbg.call() function. + It works sort of like Lua's xpcall() function, + but starts the debugger when an uncaught error occurs. + You can pretty much use it as a drop in replacement. + + For example: + dbg.call(function() + -- Potentially buggy code goes here. + end) + + Wrap it around your program's main loop or main entry point. + Then when your program crashes, you won't need to go back + and add breakpoints. + + That pretty much wraps ups the basics. + Hopefully you find luadebug.lua to be simple but useful. ]] dbg.call(function() - local foo = "foo" - - -- Try adding a string and integer - local bar = foo + 12 - - -- Program never makes it to here... + local foo = "foo" + + -- Try adding a string and integer + local bar = foo + 12 + + -- Program never makes it to here... end) -- GitLab