/example.lua Secret
Created
September 29, 2022 22:46
debugger for OpenComputer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local libdbg = require("libdbg") | |
local dbgInstance = libdbg.Debugger.new(nil,nil,true) --default to localhost:4545 | |
while true do | |
dbgInstance.breakpoint() --will break | |
os.sleep() | |
end | |
-- On the machine, open a listening socket (nc -k -N -l 4545) | |
-- available cmd are : break,continue,getenv,getproc,getall |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local component = require("component") | |
local event = require("event") | |
local os = require("os") | |
local s = require("serialization").serialize | |
local internet = require("internet") | |
local process = require("process") | |
assert(component.isAvailable("internet"), "No internet component") | |
assert(component.internet.isTcpEnabled(), "Cannot open TPC sockets. See mod config") | |
local libdbgu = {} | |
libdbgu.LOG_LEVEL = {} | |
libdbgu.LOG_LEVEL.DEBUG = 1 | |
libdbgu.LOG_LEVEL.INFO = libdbgu.LOG_LEVEL.DEBUG + 1 | |
libdbgu.LOG_LEVEL.WARNING = libdbgu.LOG_LEVEL.INFO + 1 | |
libdbgu.LOG_LEVEL.ERROR = libdbgu.LOG_LEVEL.WARNING + 1 | |
libdbgu.LOG_LEVEL[libdbgu.LOG_LEVEL.DEBUG] = "DEBUG" | |
libdbgu.LOG_LEVEL[libdbgu.LOG_LEVEL.INFO] = "INFO" | |
libdbgu.LOG_LEVEL[libdbgu.LOG_LEVEL.WARNING] = "WARNING" | |
libdbgu.LOG_LEVEL[libdbgu.LOG_LEVEL.ERROR] = "ERROR" | |
libdbgu.Debugger = {} | |
function libdbgu.Debugger.new(addr, port, waitForDebugger, env) | |
local self = {type = "Debugger"} | |
addr = addr or "localhost" | |
port = port or 4545 | |
local doBreak = false | |
local socket, rawSocket, timer | |
env = env or _ENV | |
local function dumpAll() | |
if (not rawSocket.finishConnect()) then return end | |
socket:setvbuf("full") | |
socket:write("_ENV====================================\n") | |
for k, val in pairs(_ENV) do | |
local v = "yolo" | |
socket:write(string.format("%s\t%q\n", k, type(val))) | |
end | |
socket:flush() | |
socket:write("_G======================================\n") | |
for k, val in pairs(_G) do | |
local v = "yole" | |
socket:write(string.format("%s\t%q\n", k, type(val))) | |
end | |
socket:flush() | |
socket:write("debug.getinfo()=========================\n") | |
local l = 0 | |
while (debug.getinfo(l, "nSlufL")) do | |
local info = debug.getinfo(l, "nSlufL") | |
socket:write(string.format("%d :\n", l)) | |
for key, val in pairs(info) do | |
socket:write(string.format("\t%s\t%q\n", key, tostring(val))) | |
end | |
socket:write("\n\tdebug.getlocal()\n") | |
local i = 1 | |
while (debug.getlocal(l, i)) do | |
socket:write(string.format("\t%d\t%q\n", i, s(table.pack(debug.getlocal(l, i))))) | |
i = i + 1 | |
end | |
l = l + 1 | |
socket:flush() | |
end | |
socket:flush() | |
local info = debug.getinfo(dumpAll) | |
info.func = dumpAll | |
if (info.func and info.nups) then | |
for i = 0, info.nups do | |
socket:write(string.format("%d\t%q\n", i, s(table.pack(debug.getupvalue(info.func, i))))) | |
end | |
end | |
socket:flush() | |
socket:setvbuf("no") | |
end | |
local function dumpEnv() | |
for k, v in pairs(env) do | |
socket:write(string.format("[ENV] %s\t%s\t%q\n", k, type(v), tostring(v))) | |
end | |
end | |
local function doEval(data) | |
local f, err = load(data, "t", _ENV) | |
if (f) then | |
socket:write(string.format("[EVAL]\t%s\n", s(table.pack(pcall(f))))) | |
else | |
socket:write(string.format("[EVAL]\t%s\n", err)) | |
end | |
end | |
local function dumpProc() | |
for k, v in pairs(process.findProcess()) do | |
socket:write(string.format("[PROC] %q\t%s\t%q\n", k, type(v), tostring(v))) | |
end | |
for k, v in pairs(process.findProcess().env) do | |
socket:write(string.format("[PROC][ENV] %q\t%s\t%q\n", k, type(v), tostring(v))) | |
end | |
for k, v in pairs(process.findProcess().data) do | |
socket:write(string.format("[PROC][DATA] %q\t%s\t%q\n", k, type(v), tostring(v))) | |
end | |
for k, v in pairs(process.findProcess().instances) do | |
socket:write(string.format("[PROC][INSTANCES] %q\t%s\t%q\n", k, type(v), tostring(v))) | |
end | |
end | |
function self.tick() | |
local data, status | |
repeat | |
status, data = pcall(socket.read, socket) --hide buffer timeouts | |
if (status and data) then | |
if (data:sub(1, 1) == "=") then | |
doEval(data:sub(2)) | |
end | |
if (data == "break" or data == "b") then | |
doBreak = true | |
self.breakpoint() | |
elseif (data == "continue" or data == "c") then | |
doBreak = false | |
elseif (data == "getenv") then | |
dumpEnv() | |
elseif (data == "getproc") then | |
dumpProc() | |
elseif (data == "getall") then | |
dumpAll() | |
end | |
end | |
until (not data or not status) | |
end | |
function self.breakpoint() | |
self.log("Breaking", libdbgu.LOG_LEVEL.DEBUG) | |
dumpAll() | |
--[[ for key, val in pairs(debug.getinfo(2)) do | |
socket:write(string.format("[DEBUG_GETINFO] %s\t%q\n", key, tostring(val))) | |
end | |
local i = 1 | |
while (debug.getlocal(2, i)) do | |
socket:write(string.format("[DEBUG_GETLOCAL] %d\t%q\n", i, s(table.pack(debug.getlocal(2, i))))) | |
i = i + 1 | |
end ]] | |
while doBreak do | |
---@diagnostic disable-next-line: undefined-field | |
os.sleep() | |
end | |
end | |
local varCache = {} --key = tostring(var). only for function and tables | |
function self.dumpVar(value, varName) | |
--get caller info | |
--cache the var if table or function | |
local valueType = type(value) | |
if (valueType == "table" or valueType == "function") then | |
varCache[tostring(value)] = {valueType, value, varName or "unknown"} | |
end | |
--TODO : handle table | |
socket:write(string.format("[VAR] %q,%q,%q\n", varName or "unknonw", valueType, tostring(value))) | |
end | |
function self.close() | |
self.log("Closing debugger session") | |
if (timer) then event.cancel(timer) end | |
socket:close() | |
end | |
function self.log(msg, level) | |
checkArg(1, msg, "string") | |
checkArg(2, level, "number", "nil") | |
level = level or libdbgu.LOG_LEVEL.INFO | |
socket:write(string.format("[%s] %s\n", libdbgu.LOG_LEVEL[level], msg)) | |
end | |
socket = internet.open(addr, port) | |
rawSocket = socket.stream.socket | |
socket:setTimeout(0) | |
socket:setvbuf("no") | |
while waitForDebugger do | |
waitForDebugger = not rawSocket.finishConnect() | |
event.pull(0, "dummy") | |
end | |
timer = event.timer(0.5, self.tick, math.huge) | |
self.log("Debugger attached") | |
return self | |
end | |
return libdbgu |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment