Debugea - Experimental Lua based debugger

Hi everyone, I’ve created an experimental debugger that hooks into the draw loop, allowing you to see a full stack trace of errors as a watch expression, including the local variables at the time of the error. It also comes with an interactive console that you can bring up and also comes up when you have an error. This is just an experiment as we try to come up with better ways of debugging code natively in the app.

function __traceback()
    local result = ""
    local level = 3
    local indent = ""
    while true do
        local info = debug.getinfo(level, "nSl")
        if not info then break end
        if info.what == "C" then   -- is a C function?
            result = string.format("%s\
%d C function", result, level-2)
        else   -- a Lua function
            result = string.format("%s\
%d %s() : <line:%d>",result, level-2, info.name or "???", info.currentline)
        end
        level = level + 1
        indent = indent .. "  "
    end
    return result
end

function __errorHandler(e)    
    __db = e .. "\
" .. __traceback() .. "\
\
" .. "local variables:\
"
   
    local a = 1
    while true do
        local name, value = debug.getlocal(2, a)
        if not name then break end
        if name ~= "(*temporary)" then         
            __db = string.format("%s\
%s = %s", __db, name, value)
        end
        a = a + 1
    end 
end

function __keyboard(key)
    if true then
        if key == "\
" then
            pcall(loadstring(buffer))
            buffer = ""
        elseif key == BACKSPACE then
            if string.len(buffer) > 0 then
                buffer = string.sub(buffer, 1, string.len(buffer)-1)    
            end    
        else
            buffer = buffer .. key
        end
    end
end

function res()
    __paused = false  
    __db = ""  
end

function debugea()
    iparameter("showConsole",0,1)
    __draw = draw
    __db = ""
    watch("__db")
    __paused = false
    __sc = false
    buffer = ""
    draw = function()
        if __paused == false then
            local status, err = xpcall(__draw, __errorHandler)
            if status == false then
                __paused = true             
                showConsole = 1
            end
        else
            background(0, 0, 0, 255)
        end
        if showConsole == 1 and __sc == false then
            __sc = true
            __oldKB = keyboard
            keyboard = __keyboard
            showKeyboard()
        elseif showConsole == 0 and __sc == true then
            __sc = false
            hideKeyboard()
            keyboard = __oldKB
        end
        if __sc then
            --background(0, 0, 0, 255)
            font("Inconsolata")
            fontSize(22)
            textAlign(LEFT)
            textMode(CORNER)
            --textWrapWidth(WIDTH)
            fill(255, 255, 255, 255)
            text(":> " .. buffer, 5, HEIGHT-30)
        end
    end
end

Heres the Main.lua, which contains an example that lets you throw an error on purpose to test the debugging.

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    iparameter("testError", 1,10)
    debugea()
end

function test()
    local blah = "what?"
    local x = 10
    bog = fog[5]
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)
    
    fill(255, 0, 0, 255)
    stroke(255, 255, 255, 255)
    ellipse(WIDTH/2 + math.sin(ElapsedTime) * 100, HEIGHT/2, 50)
    
    if testError == 10 then
        test()
    end

    -- Do your drawing here
    
end

One of the other things we are thinking of is break points and stepping, if we can get them to work. Let me know what you think.

Also, you can type res() in the interactive console to attempt to resume the draw loop when you encounter an error.

Nice! Thanks a lot!

Thanks @John for this.

I’d love to see break points and stepping… I have some terribly gnarly code (a graphic implementation of BF Joust) which I wrote a few months back that contains a bug which I’ve all but given up finding – I feel the ability to add a break point and be able to step through the code would help me find said miscreant and squash it. :slight_smile: :slight_smile:

@John, what happened to the development of Debugea?

As a long time Excel VBA developer, I found stepping and breakpoints utterly invaluable

I guess most of this stuff is built in now, but I don’t see a need for stepping and break points. I’d suggest just using print statements.

@Zoyt, print is essentially how i debug currently, but stepping and breakpoints would make life easier

@JakAttak - Well, even though I won’t use it, I’m writing a really cool library for breakpoints and stepping. How would you like the stepping API?
Here’s what I have so far in the library: You can set breakpoints at certain lines and when certain functions are called. It’s as simple as saying Debugger:addBreakoint(line) or Debugger:addBreakoint(function). Also, the addBreakpoint function return and ID so you can also say Debugger:removeBreakoint(id).
Hope that’s good so far.

@Zoyt that sounds great

@JakAttak - How would you like the stepping? Would you like it something like pause every so many lines? Or is what I have good?
Thanks!

@Zoyt Sounds really interesting. I miss stepping and breakpoints from VS.

@Zoyt, just being able to go line by line would be sufficient as far as stepping goes

@Zoyt, normal stepping functionality that I have seen, allows you to stop on particular lines, step through a line at a time, and step over functions.

The last one means that if you have a line like
a=B(c)
stepping would normally take you into the first line of function B, but the step over option allows you to execute B in full (eg if you know that function works perfectly) and go to the next line in the current function.

I’m not asking for this, because I’m sure it’s hard, but just saying…

Basic stepping would be really good

@Ignatz - I don’t know about stepping over functions, but stepping and breaking is as easy as pie. There’s a wonderful function called debug.sethook in Lua.

@Zoyt did you find a way to actually make that work in codea? In standard lua you would call the debugger from the hook, but that does not work for codea because of its event driven nature. An alternative approach is to run the debugged code in a coroutine, and yield from the hook, that would work with codeas event loop, but alas, you cannot reliably yield from hooks in lua 5.1, that feature is only available from lua 5.2 onwards. So I don’t see how hooks can be useful for this in codea… my current approach, which I fiddle with on and off is to actually parse the code I want to debug and add code to facilitate stepping and stuff… not pretty but AFAICS the only way to make this work, more or less. Still, I wrestle with metamethods and pcall. I have written a wrapper for the latter, bit without rewriting parts of the lua interpreter in lua, metamethods remain a problem… an alternative approach, which would handle everything would be to have the debugger frontend run on a separate computer, which in the absence of sockets would mean using http requests for this, and having the debugger act as a http server onnthe remote side… weird, but might work. What is your approach?

I’m using hooks to call a function for every even, which then deals with the event from there. Sorry I dropped the project. School’s starting tomorrow, and I’m trying to get a much done with my own app before then.

@zoyt I’m working on a project that checks any questionable code from an untrusted source. I have it working however i would like to add in the ability to aprove or deny a function in real time (currently done after the fact).

How were you using debug.sethook()?