245 lines
8.1 KiB
Lua
Executable File
245 lines
8.1 KiB
Lua
Executable File
-- GDB-style interface to clidebug
|
|
scite_require 'gdb.lua'
|
|
local find = string.find
|
|
local match = string.match
|
|
local push = table.insert
|
|
local pop = table.remove
|
|
local esc = string.char(26)
|
|
local gprefix = esc..esc
|
|
--local ferr = io.stderr
|
|
|
|
local GTK = scite_GetProp('PLAT_GTK')
|
|
|
|
-- the convention with cli debug is that all Windows filenames are lowercase!
|
|
local function canonical(file)
|
|
if not GTK then file = file:lower() end
|
|
return file
|
|
end
|
|
|
|
local function fpath(file)
|
|
return canonical(fullpath(file))
|
|
end
|
|
|
|
function slashify(s)
|
|
return s:gsub('\\','\\\\')
|
|
end
|
|
|
|
local function file_is_lua (file)
|
|
return extension_of(file) == 'lua'
|
|
end
|
|
|
|
local info_line_success = '^Line %d+ of'
|
|
local info_line_error = '^No line number information available'
|
|
|
|
function info_line_handler(line,dbg)
|
|
local success = line:find(info_line_success)
|
|
if success then -- we can set a break command
|
|
--dbg:queue_command('tbreak *'..dbg.addr)
|
|
spawner_command('tbreak *'..dbg.addr)
|
|
dbg.addresses[dbg.addr] = true
|
|
end
|
|
-- either way, we want to get out of GDB mode at the earliest opportunity!
|
|
--dbg:queue_command 'continue'
|
|
spawner_command('continue')
|
|
end
|
|
|
|
LGdb = class(Gdb)
|
|
|
|
function LGdb.discriminator(target)
|
|
local res = find(target,'^:gdb') == 1
|
|
return res
|
|
end
|
|
|
|
function LGdb:init (root)
|
|
Gdb.init(self,root)
|
|
self.root = root
|
|
self.target_dir = canonical(props['FileDir'])
|
|
self.no_target_ext = false
|
|
-- this is added to the package.path of the Lua program
|
|
print(extman_Path())
|
|
self.clidebug_path = scite_GetProp('clidebug.path',join(extman_Path(),'lua_clidebugger'))
|
|
self.clidebug_debugger = canonical(join(self.clidebug_path,'debugger.lua'))
|
|
|
|
-- self.no_quit_confirm = true
|
|
self.skip_system_extension = ".lua"
|
|
self.deferred_stack = {}
|
|
|
|
self.postprocess_command['info line'] = {pattern=info_line_success,
|
|
action=info_line_handler, alt_pat=info_line_error}
|
|
|
|
self.addresses = {}
|
|
self.mode = 'gdb'
|
|
|
|
-- GDB likes forward slashes, on both platforms...
|
|
local dbgl_file = join(self.clidebug_path,"dbgl.c"):gsub('\\','/')
|
|
|
|
-- this is a persistent event handler which monitors every program break,
|
|
-- and keeps track of whether we are in GDB or clidebug. Will raise
|
|
-- the events 'gdb' and 'lua' accordingly.
|
|
self:set_event('break',function(file,line)
|
|
local new_mode
|
|
local lf = file_is_lua(file)
|
|
-- don't respond to any breaks in dbgl.c
|
|
if not lf and file == dbgl_file then
|
|
return true,true
|
|
end
|
|
if self.mode ~= 'lua' and lf then
|
|
new_mode = 'lua'
|
|
end
|
|
if self.mode == 'lua' and not lf then
|
|
new_mode = 'gdb'
|
|
end
|
|
if new_mode then
|
|
self.mode = new_mode
|
|
self:raise_event(new_mode)
|
|
end
|
|
return true
|
|
end)
|
|
|
|
end
|
|
|
|
function LGdb:check_breakpoint (b)
|
|
return not file_is_lua(b.file)
|
|
end
|
|
|
|
function LGdb:parameter_string ()
|
|
local parms = Gdb.parameter_string(self)
|
|
-- we have to modify the package path and cpath for this process so that
|
|
-- the clidebug and dbgl packages are visible.
|
|
local so = choose(GTK,'?.so;','?.dll;')
|
|
local ppath = "'"..slashify(join(self.clidebug_path,'?.lua;')).."'"
|
|
local cpath = "'"..slashify(join(self.clidebug_path,so)).."'"
|
|
local p = 'package'
|
|
local cmdline
|
|
-- if the target isn't Lua, then we assume it's a program that hosts Lua and that
|
|
-- there's an explicit clidebug initialization somewhere in a user Lua script.
|
|
if self.not_lua then
|
|
cmdline = ''
|
|
else
|
|
cmdline = ('-e "%s.path=%s..%s.path; %s.cpath=%s..%s.cpath; GDB=true; WIN=%s" -lclidebug %s'):format(
|
|
p,ppath,p,p,cpath,p,choose(GTK,'false','true'),self.lua_target)
|
|
end
|
|
print('*',cmdline)
|
|
return cmdline..' '..parms
|
|
end
|
|
|
|
function LGdb:command_line(target)
|
|
local gtarget,ltarget = target:match('^:gdb;([^;]+);(.*)')
|
|
self.lua_target = ltarget
|
|
print('+',gtarget,ltarget)
|
|
local idx = gtarget:find('%[h%]$')
|
|
if idx then
|
|
gtarget = gtarget:sub(1,idx-1)
|
|
self.not_lua = true
|
|
end
|
|
--- we are going to embed a clidebug session inside a GDB session, so it's
|
|
--- necessary to explicitly create the clidebug.cmd file. This folows the
|
|
--- sequence in create_existing_breakpoints() in debugger.lua
|
|
local lua_cmd = join(self.root,'clidebug.cmd')
|
|
local out = io.open(lua_cmd,'w')
|
|
for b in Breakpoints() do
|
|
if file_is_lua(b.file) then
|
|
out:write('break '..canonical(b.file)..':'..b.line..'\n')
|
|
end
|
|
end
|
|
out:write('rootpath '..self.target_dir..'\n')
|
|
if not self.not_lua then
|
|
out:write('run\n')
|
|
end
|
|
out:close()
|
|
return Gdb.command_line(self,gtarget)
|
|
end
|
|
|
|
-- need to put at least one breakpoint into the system, so that we can drop into
|
|
-- gdb mode when necessary. Under Windows, you definitely do not want a separate
|
|
-- console window, since we want to capture the result of running clidebug inside GDB.
|
|
function LGdb:special_debugger_setup(out)
|
|
Gdb.special_debugger_setup(self,out)
|
|
if not GTK then
|
|
out:write('set new-console off\n')
|
|
end
|
|
-- a useful command when in C Lua code.
|
|
out:write[[
|
|
define lstack
|
|
p debug_lua_stack($arg0)
|
|
end
|
|
]]
|
|
out:write('directory ',self.clidebug_path,'\n')
|
|
-- clidebug will use this break to get us into gdb
|
|
out:write('break dbgl.c:9\n')
|
|
end
|
|
|
|
function LGdb:set_breakpoint(file,lno)
|
|
local lf = file_is_lua(file)
|
|
-- clidebug works best with absolute paths
|
|
if lf then file = fullpath(file) end
|
|
-- if we are in the wrong mode, then the actual setting of a breakpoint
|
|
-- needs to happen when we next switch to the correct mode.
|
|
if (self.mode == 'lua') ~= lf then
|
|
local function set_break ()
|
|
Gdb.set_breakpoint(self,file,lno)
|
|
end
|
|
if self.mode == 'lua' then
|
|
spawner_command('debugbreak')
|
|
self:set_event('break',set_break)
|
|
self:queue_command 'continue'
|
|
else
|
|
self:set_event('lua',set_break)
|
|
end
|
|
else
|
|
Gdb.set_breakpoint(self,file,lno)
|
|
end
|
|
end
|
|
|
|
function LGdb:goto_file_line(file,line)
|
|
ProcessOutput(gprefix..self.target_dir..'/'..file..":"..line..'\n')
|
|
end
|
|
|
|
-- there is some clidebugger hackery going on here. It will put us into its own version of debug.stacktrace,
|
|
-- and we need to put the program into frame #3, which is where the wobby originally happened. The usual Lua
|
|
-- error message is put out by the 'Message: ' line, which we use to capture the file:line needed to jump to.
|
|
-- The jumping is achieved by pushing the correct break pattern back into the input above (there must be
|
|
-- a more elegant way of doing this!)
|
|
local fmsg,lmsg
|
|
|
|
function LGdb:find_execution_break(line)
|
|
local _,_,file,lineno = find(line,self.break_line)
|
|
if _ then
|
|
-- has our program thrown a wobbly in Lua?
|
|
if file == self.clidebug_debugger and fmsg then
|
|
self:frame(3)
|
|
return fmsg,lmsg,true
|
|
else
|
|
return file,lineno
|
|
end
|
|
else
|
|
fmsg,lmsg = match(line,'Message: (%S+):(%d+)')
|
|
if fmsg then return end
|
|
-- clidebug emits this pattern when Lua is entering a C function
|
|
-- we have to check whether this function has any debug symbols
|
|
-- before trying to step into it.
|
|
local addr = match(line,'//@//%s(.+)')
|
|
if addr then
|
|
self.addr = strip_eol(addr)
|
|
-- at this point, we have entered GDB at debug_break (forced by clidebug)
|
|
local cached = self.addresses[self.addr]
|
|
if cached == nil then
|
|
-- haven't met this function before; check for line info.
|
|
self:set_event('break',function()
|
|
dbg_command('info line','*'..self.addr)
|
|
-- info_line_handler() above will process the output...
|
|
end)
|
|
elseif cached == true then
|
|
--we know this function has line numbers defined
|
|
self:set_event('break',function()
|
|
self:queue_command('tbreak *'..self.addr)
|
|
self:queue_command('continue')
|
|
end)
|
|
end
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
register_debugger('luagdb','lua',LGdb)
|