debugger part 2
This commit is contained in:
@@ -9,7 +9,7 @@ local dbg = { }
|
|||||||
local function hookBreakpoint(info)
|
local function hookBreakpoint(info)
|
||||||
if dbg.breakpoints then
|
if dbg.breakpoints then
|
||||||
for _,v in pairs(dbg.breakpoints) do
|
for _,v in pairs(dbg.breakpoints) do
|
||||||
if v.line == info.currentline and v.file == info.short_src then
|
if v.line == info.currentline and v.file == info.short_src and not v.disabled then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -24,8 +24,8 @@ end
|
|||||||
|
|
||||||
local function hookStep()
|
local function hookStep()
|
||||||
local co = coroutine.running()
|
local co = coroutine.running()
|
||||||
return function()
|
return function(info)
|
||||||
return co == coroutine.running()
|
return co == coroutine.running() or hookBreakpoint(info)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -39,12 +39,13 @@ local function hookStepStacksize(n)
|
|||||||
end
|
end
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
return function()
|
return function(info)
|
||||||
if co == coroutine.running() then
|
if co == coroutine.running() then
|
||||||
if not debug.getinfo(i - n) then
|
if not debug.getinfo(i - n) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return hookBreakpoint(info)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -63,13 +64,13 @@ local hookEval = function() end
|
|||||||
local function local_bindings(offset, stack_inspect_offset)
|
local function local_bindings(offset, stack_inspect_offset)
|
||||||
offset = offset + 1 + stack_inspect_offset -- add this function to the offset
|
offset = offset + 1 + stack_inspect_offset -- add this function to the offset
|
||||||
local func = debug.getinfo(offset).func
|
local func = debug.getinfo(offset).func
|
||||||
local bindings = {}
|
local bindings = { }
|
||||||
|
|
||||||
-- Retrieve the upvalues
|
-- Retrieve the upvalues
|
||||||
do local i = 1; while true do
|
do local i = 1; while true do
|
||||||
local name, value = debug.getupvalue(func, i)
|
local name, value = debug.getupvalue(func, i)
|
||||||
if not name then break end
|
if not name then break end
|
||||||
bindings[name] = value
|
bindings[name] = { type = 'U', raw = value }
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end end
|
end end
|
||||||
|
|
||||||
@@ -77,21 +78,32 @@ local function local_bindings(offset, stack_inspect_offset)
|
|||||||
do local i = 1; while true do
|
do local i = 1; while true do
|
||||||
local name, value = debug.getlocal(offset, i)
|
local name, value = debug.getlocal(offset, i)
|
||||||
if not name then break end
|
if not name then break end
|
||||||
bindings[name] = value
|
bindings[name] = { type = 'L', raw = value }
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end end
|
end end
|
||||||
|
|
||||||
-- Retrieve the varargs (works in Lua 5.2 and LuaJIT)
|
-- Retrieve the varargs (works in Lua 5.2 and LuaJIT)
|
||||||
local varargs = {}
|
local varargs = { }
|
||||||
do local i = 1; while true do
|
do local i = 1; while true do
|
||||||
local name, value = debug.getlocal(offset, -i)
|
local name, value = debug.getlocal(offset, -i)
|
||||||
if not name then break end
|
if not name then break end
|
||||||
varargs[i] = value
|
varargs[i] = value
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end end
|
end end
|
||||||
if #varargs > 0 then bindings["..."] = varargs end
|
if #varargs > 0 then
|
||||||
|
bindings["..."] = { type = 'V', value = varargs }
|
||||||
|
end
|
||||||
|
|
||||||
return bindings
|
local t = { }
|
||||||
|
for k,v in pairs(bindings) do
|
||||||
|
if v.raw ~= nil then
|
||||||
|
v.name = k
|
||||||
|
v.value = tostring(v.raw)
|
||||||
|
table.insert(t, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_trace(offset, stack_inspect_offset)
|
local function get_trace(offset, stack_inspect_offset)
|
||||||
@@ -172,6 +184,17 @@ local function hook()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local cocreate = coroutine.create
|
||||||
|
_ENV.coroutine = { }
|
||||||
|
for k,v in pairs(_G.coroutine) do
|
||||||
|
_ENV.coroutine[k] = v
|
||||||
|
end
|
||||||
|
_ENV.coroutine.create = function(f, ...)
|
||||||
|
local co = cocreate(f, ...)
|
||||||
|
debug.sethook(co, dbg.hook, "l")
|
||||||
|
return co
|
||||||
|
end
|
||||||
|
|
||||||
debug.sethook(hook, 'l')
|
debug.sethook(hook, 'l')
|
||||||
|
|
||||||
-- Expose the debugger's functions
|
-- Expose the debugger's functions
|
||||||
|
|||||||
4
debugger/autorun/startup.lua
Normal file
4
debugger/autorun/startup.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
local completion = require('cc.shell.completion')
|
||||||
|
|
||||||
|
_ENV.shell.setCompletionFunction("packages/debugger/debug.lua",
|
||||||
|
completion.build(completion.program))
|
||||||
@@ -16,12 +16,14 @@ if not filename then
|
|||||||
error('file not found')
|
error('file not found')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
UI:disableEffects()
|
||||||
|
|
||||||
local config = Config.load('debugger')
|
local config = Config.load('debugger')
|
||||||
if not config.filename then
|
if not config[filename] then
|
||||||
config.filename = { }
|
config[filename] = { }
|
||||||
end
|
end
|
||||||
|
|
||||||
local breakpoints = config.filename
|
local breakpoints = config[filename]
|
||||||
local currentFile
|
local currentFile
|
||||||
local debugFile, debugLine
|
local debugFile, debugLine
|
||||||
|
|
||||||
@@ -37,14 +39,10 @@ local function startClient()
|
|||||||
args = args,
|
args = args,
|
||||||
fn = function(...)
|
fn = function(...)
|
||||||
local dbg = require('debugger')
|
local dbg = require('debugger')
|
||||||
local fn = loadfile(filename, env)
|
local fn, msg = loadfile(filename, env)
|
||||||
|
|
||||||
local cocreate = coroutine.create
|
if not fn then
|
||||||
env.coroutine = require('opus.util').shallowCopy(coroutine)
|
error(msg, -1)
|
||||||
env.coroutine.create = function(f, ...)
|
|
||||||
local co = cocreate(f, ...)
|
|
||||||
debug.sethook(co, dbg.hook, "l")
|
|
||||||
return co
|
|
||||||
end
|
end
|
||||||
|
|
||||||
dbg.debugger = debugger
|
dbg.debugger = debugger
|
||||||
@@ -56,10 +54,32 @@ local function startClient()
|
|||||||
client = kernel.find(clientId)
|
client = kernel.find(clientId)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local romFiles = {
|
||||||
|
load = function(self)
|
||||||
|
local function recurse(dir)
|
||||||
|
local files = fs.list(dir)
|
||||||
|
for _,f in ipairs(files) do
|
||||||
|
local fullName = fs.combine(dir, f)
|
||||||
|
if fs.isDir(fullName) then
|
||||||
|
recurse(fullName)
|
||||||
|
else
|
||||||
|
self.files[f] = fullName
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
recurse('rom/apis')
|
||||||
|
end,
|
||||||
|
get = function(self, file)
|
||||||
|
return self.files[file]
|
||||||
|
end,
|
||||||
|
files = { },
|
||||||
|
}
|
||||||
|
romFiles:load()
|
||||||
|
|
||||||
local function loadSource(file)
|
local function loadSource(file)
|
||||||
currentFile = file:match('@?(.*)')
|
currentFile = romFiles:get(file) or file:match('@?(.*)')
|
||||||
local src = { }
|
local src = { }
|
||||||
local lines = Util.readLines(file:match('@?(.*)'))
|
local lines = Util.readLines(currentFile)
|
||||||
|
|
||||||
if lines then
|
if lines then
|
||||||
for i = 1, #lines do
|
for i = 1, #lines do
|
||||||
@@ -77,11 +97,11 @@ end
|
|||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = 'Continue', event = 'cmd', cmd = 'c' },
|
{ text = 'Continue', event = 'cmd', cmd = 'c' },
|
||||||
{ text = 'Step', event = 'cmd', cmd = 's' },
|
{ text = 'Step', event = 'cmd', cmd = 's' },
|
||||||
{ text = 'Step Over', event = 'cmd', cmd = 'n' },
|
{ text = 'Over', event = 'cmd', cmd = 'n' },
|
||||||
{ text = 'Step Out', event = 'cmd', cmd = 'f' },
|
{ text = 'Out', event = 'cmd', cmd = 'f' },
|
||||||
{ text = 'Restart', event = 'restart', width = 9, ex = -1 },
|
{ text = 'Restart', event = 'restart', width = 9, ex = -1 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -94,11 +114,15 @@ local page = UI.Page {
|
|||||||
{ heading = 'Key', key = 'name' },
|
{ heading = 'Key', key = 'name' },
|
||||||
{ heading = 'Value', key = 'value', textColor = 'yellow' },
|
{ heading = 'Value', key = 'value', textColor = 'yellow' },
|
||||||
},
|
},
|
||||||
--sortColumn = 'name',
|
|
||||||
autospace = true,
|
autospace = true,
|
||||||
accelerators = {
|
accelerators = {
|
||||||
grid_select = 'show_variable',
|
grid_select = 'show_variable',
|
||||||
},
|
},
|
||||||
|
getRowTextColor = function(self, row, selected)
|
||||||
|
return row.type == 'U' and 'cyan'
|
||||||
|
or row.type == 'V' and 'lime'
|
||||||
|
or UI.Grid.getRowTextColor(self, row, selected)
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
ex = -7, y = -1,
|
ex = -7, y = -1,
|
||||||
@@ -128,7 +152,7 @@ local page = UI.Page {
|
|||||||
for _,v in pairs(breakpoints) do
|
for _,v in pairs(breakpoints) do
|
||||||
if v.file == currentFile and v.line == row.line then
|
if v.file == currentFile and v.line == row.line then
|
||||||
return {
|
return {
|
||||||
marker = '!',
|
marker = v.disabled and 'x' or '!',
|
||||||
line = row.line,
|
line = row.line,
|
||||||
source = row.source,
|
source = row.source,
|
||||||
}
|
}
|
||||||
@@ -136,6 +160,9 @@ local page = UI.Page {
|
|||||||
end
|
end
|
||||||
return row
|
return row
|
||||||
end,
|
end,
|
||||||
|
accelerators = {
|
||||||
|
t = 'toggle_enabled'
|
||||||
|
},
|
||||||
getRowTextColor = function(self, row, selected)
|
getRowTextColor = function(self, row, selected)
|
||||||
return row.line == debugLine and currentFile == debugFile and 'yellow'
|
return row.line == debugLine and currentFile == debugFile and 'yellow'
|
||||||
or UI.Grid.getRowTextColor(self, row, selected)
|
or UI.Grid.getRowTextColor(self, row, selected)
|
||||||
@@ -147,6 +174,17 @@ local page = UI.Page {
|
|||||||
file = currentFile,
|
file = currentFile,
|
||||||
line = event.selected.line,
|
line = event.selected.line,
|
||||||
})
|
})
|
||||||
|
elseif event.type == 'toggle_enabled' then
|
||||||
|
local line = self:getSelected() and self:getSelected().line
|
||||||
|
if line then
|
||||||
|
for _,v in pairs(breakpoints) do
|
||||||
|
if v.file == currentFile and v.line == line then
|
||||||
|
v.disabled = not v.disabled
|
||||||
|
self:emit({ type = 'update_breakpoints' })
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return UI.Grid.eventHandler(self, event)
|
return UI.Grid.eventHandler(self, event)
|
||||||
end,
|
end,
|
||||||
@@ -157,6 +195,7 @@ local page = UI.Page {
|
|||||||
title = 'Stack',
|
title = 'Stack',
|
||||||
index = 3,
|
index = 3,
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
|
disableHeader = true,
|
||||||
columns = {
|
columns = {
|
||||||
{ key = 'index', width = 2 },
|
{ key = 'index', width = 2 },
|
||||||
{ heading = 'heading', key = 'desc' },
|
{ heading = 'heading', key = 'desc' },
|
||||||
@@ -166,14 +205,14 @@ local page = UI.Page {
|
|||||||
or UI.Grid.getRowTextColor(self, row, selected)
|
or UI.Grid.getRowTextColor(self, row, selected)
|
||||||
end,
|
end,
|
||||||
sortColumn = 'index',
|
sortColumn = 'index',
|
||||||
|
eventHandler = function(self, event)
|
||||||
|
if event.type == 'grid_select' then
|
||||||
|
message('i', event.selected.index)
|
||||||
|
else
|
||||||
|
return UI.Grid.eventHandler(self, event)
|
||||||
|
end
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
eventHandler = function(self, event)
|
|
||||||
if event.type == 'grid_select' then
|
|
||||||
message('r', event.selected.index)
|
|
||||||
else
|
|
||||||
return UI.Grid.eventHandler(self, event)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
env = UI.Tab {
|
env = UI.Tab {
|
||||||
@@ -371,10 +410,10 @@ Event.on('debugger', function(_, cmd, data)
|
|||||||
kernel.raise(debugger.uid)
|
kernel.raise(debugger.uid)
|
||||||
|
|
||||||
-- local tab
|
-- local tab
|
||||||
local t = { }
|
local t = data.locals
|
||||||
for k,v in pairs(data.locals or { }) do
|
-- for k,v in pairs(data.locals or { }) do
|
||||||
table.insert(t, { name = k, value = tostring(v), raw = v })
|
-- table.insert(t, { name = k, value = tostring(v), raw = v })
|
||||||
end
|
-- end
|
||||||
table.sort(t, function(a, b) return a.name < b.name end)
|
table.sort(t, function(a, b) return a.name < b.name end)
|
||||||
page.container.locals:setValues(t)
|
page.container.locals:setValues(t)
|
||||||
page.container.locals.orig = Util.shallowCopy(t)
|
page.container.locals.orig = Util.shallowCopy(t)
|
||||||
@@ -404,4 +443,6 @@ end)
|
|||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
UI:start()
|
UI:start()
|
||||||
|
|
||||||
message('d')
|
if kernel.find(client.uid) then
|
||||||
|
client:resume('terminate')
|
||||||
|
end
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ local function method(times)
|
|||||||
end
|
end
|
||||||
|
|
||||||
print('before')
|
print('before')
|
||||||
-- breakpoint
|
term.current().clear()
|
||||||
--dbg()
|
|
||||||
print('after')
|
print('after')
|
||||||
|
|
||||||
local i = 2
|
local i = 2
|
||||||
|
|||||||
6
debugger/help/debug.txt
Normal file
6
debugger/help/debug.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
An interactive debugger for lua
|
||||||
|
|
||||||
|
debug must be enabled!
|
||||||
|
|
||||||
|
Run from a shell prompt
|
||||||
|
> debug FILE [ARGS]
|
||||||
Reference in New Issue
Block a user