Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some bugfixes and test refactoring #9

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions libraries/luadbgpclient/build.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
-- Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- Merges all debugger modules into a single file.
-------------------------------------------------------------------------------

local FILE_HEADER = [[
-- /!\ This file is auto-generated. Do not alter manually /!\

--------------------------------------------------------------------------------
-- Submodules body
--------------------------------------------------------------------------------

]]

local MODULE_TMPL = [[

--------------------------------------------------------------------------------
-- Module %s
package.preload[%q] = function(...)
%s
end
-- End of module %s
--------------------------------------------------------------------------------

]]

local MAIN_HEADER = [[

--------------------------------------------------------------------------------
-- Main content
--------------------------------------------------------------------------------

]]

local function get_file_content(path)
local f = assert(io.open(path))
local content = assert(f:read("*a"))
f:close()
return content
end

function merge(output, root, modules)
output:write(FILE_HEADER)
-- insert submodules
for _, m in ipairs(modules) do
local name, file = unpack(m)
output:write(MODULE_TMPL:format(name, name, get_file_content(file), name))
end

-- insert root module
output:write(MAIN_HEADER, get_file_content(root))
end


-- main
local output = ...
output = output and io.open(output, "w") or io.stdout
srcdir = debug.getinfo(1).source:match("@(.+)[/\\]%w+.lua") or "."
merge(output, srcdir .. "/debugger/init.lua", {
{ "debugger.transport.apr", srcdir .. "/debugger/transport/apr.lua" },
{ "debugger.transport.luasocket", srcdir .. "/debugger/transport/luasocket.lua" },
{ "debugger.commands", srcdir .. "/debugger/commands.lua" },
{ "debugger.context", srcdir .. "/debugger/context.lua" },
{ "debugger.dbgp", srcdir .. "/debugger/dbgp.lua" },
{ "debugger.introspection", srcdir .. "/debugger/introspection.lua" },
{ "debugger.platform", srcdir .. "/debugger/platform.lua" },
{ "debugger.util", srcdir .. "/debugger/util.lua" },
{ "debugger.url", srcdir .. "/debugger/url.lua" },
})
if output ~= io.stdout then output:close() end
57 changes: 40 additions & 17 deletions libraries/luadbgpclient/debugger/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,17 @@ function core.previous_context_response(self, reason)
self.previous_context = nil
end

local function cleanup()
coroutine.resume, coroutine.wrap = coresume, cowrap
for _, coro in pairs(core.active_coroutines.from_id) do
debug.sethook(coro)
end
-- to remove hook on the main coroutine, it must be the current one (otherwise, this is a no-op) and this function
-- have to be called adain later on the main thread to finish cleaup
debug.sethook()
core.active_coroutines.from_id, core.active_coroutines.from_coro = { }, { }
end

--- This function handles the debugger commands while the execution is paused. This does not use coroutines because there is no
-- way to get main coro in Lua 5.1 (only in 5.2)
local function debugger_loop(self, async_packet)
Expand All @@ -312,7 +323,13 @@ local function debugger_loop(self, async_packet)

while true do
-- reads packet
local packet = async_packet or assert(dbgp.read_packet(self.skt))
local packet = async_packet or dbgp.read_packet(self.skt)
if not packet then
log("WARNING", "lost debugger connection")
cleanup()
break
end

async_packet = nil
log("DEBUG", packet)
local cmd, args, data = dbgp.cmd_parse(packet)
Expand Down Expand Up @@ -410,9 +427,9 @@ local function debugger_hook(event, line)
end
end

if jit then
if rawget(_G, "jit") then
debugger_hook = function(event, line)
local thread = corunning()
local thread = corunning() or "main"
if event == "call" then
if debug.getinfo(2, "S").what == "C" then return end
stack_levels[thread] = stack_levels[thread] + 1
Expand All @@ -425,7 +442,12 @@ if jit then
stack_levels[thread] = depth - 2
elseif event == "line" then
active_session.coro = util.CurrentThread(corunning())
assert(coresume(line_hook_coro, line))
if active_session.coro[1] == "main" then
line_hook(line)
else
-- run the debugger loop in another thread on the other cases (simplifies stack handling)
assert(coresume(line_hook_coro, line))
end
active_session.coro = nil
end
end
Expand All @@ -436,22 +458,22 @@ local function init(host, port, idekey, transport, executionplatform, workingdir
local host = host or os.getenv "DBGP_IDEHOST" or "127.0.0.1"
local port = port or os.getenv "DBGP_IDEPORT" or "10000"
local idekey = idekey or os.getenv("DBGP_IDEKEY") or "luaidekey"

-- init plaform module
local executionplatform = executionplatform or os.getenv("DBGP_PLATFORM") or nil
local workingdirectory = workingdirectory or os.getenv("DBGP_WORKINGDIR") or nil
platform.init(executionplatform,workingdirectory)

-- get transport layer
local transportpath = transport or os.getenv("DBGP_TRANSPORT") or "debugger.transport.luasocket"
local transport = require(transportpath)

-- install base64 functions into util
util.b64, util.rawb64, util.unb64 = transport.b64, transport.rawb64, transport.unb64

local skt = assert(transport.create())
skt:settimeout(nil)

-- try to connect several times: if IDE launches both process and server at same time, first connect attempts may fail
local ok, err
for i=1, 5 do
Expand All @@ -460,11 +482,11 @@ local function init(host, port, idekey, transport, executionplatform, workingdir
transport.sleep(0.5)
end
if err then error(string.format("Cannot connect to %s:%d : %s", host, port, err)) end

-- get the debugger and transport layer URI
debugger_uri = platform.get_uri(debug.getinfo(1).source)
transportmodule_uri = platform.get_uri(debug.getinfo(transport.create).source)

-- get the root script path (the highest possible stack index)
local source
for i=2, math.huge do
Expand All @@ -473,14 +495,14 @@ local function init(host, port, idekey, transport, executionplatform, workingdir
source = platform.get_uri(info.source) or source
end
if not source then source = "unknown:/" end -- when loaded before actual script (with a command line switch)

-- generate some kind of thread identifier
local thread = corunning() or "main"
stack_levels[thread] = 1 -- the return event will set the counter to 0
local sessionid = tostring(os.time()) .. "_" .. tostring(thread)

dbgp.send_xml(skt, { tag = "init", attr = {
appid = "Lua DBGp",
appid = "Lua DBGp",
idekey = idekey,
session = sessionid,
thread = tostring(thread),
Expand All @@ -489,14 +511,15 @@ local function init(host, port, idekey, transport, executionplatform, workingdir
protocol_version = "1.0",
fileuri = source
} })


--FIXME util.CurrentThread(corunning) => util.CurrentThread(corunning()) WHAT DOES IT FIXES ??
local sess = { skt = skt, state = "starting", id = sessionid, coro = util.CurrentThread(corunning) }
active_session = sess
debugger_loop(sess)

-- set debug hooks
debug.sethook(debugger_hook, "rlc")

-- install coroutine collecting functions.
-- TODO: maintain a list of *all* coroutines can be overkill (for example, the ones created by copcall), make a extension point to
-- customize debugged coroutines
Expand Down
52 changes: 26 additions & 26 deletions libraries/luadbgpclient/debugger/platform.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ local util = require "debugger.util"

local M = { }

-- Get the execution plaform os (could be win or unix)
-- Execution plaform (could be win or unix)
-- Used to manage file path difference between the 2 platform
local platform = nil

-- keep all computed URIs in cache (as they are quite long to compute)
local uri_cache = { }

-- parse a normalized path and return a table of each segment
-- you could precise the path seperator.
local function split(path,sep)
local t = {}
for w in path:gmatch("[^"..(sep or "/").."]+")do
table.insert(t, w)
end
return t
-- parse a normalized path and return a table of each segment
-- you could precise the path seperator.
local function split(path,sep)
local t = {}
for w in path:gmatch("[^"..(sep or "/").."]+")do
table.insert(t, w)
end
return t
end

--- Returns a RFC2396 compliant URI for given source, or false if the mapping failed
Expand All @@ -48,30 +48,30 @@ local function get_abs_file_uri (source)
end
end

--FIXME: as result is cached, changes in package.path that modify the module name are missed
-- (mostly affect main module when Lua interpreter is launched with an absolute path)
--FIXME: as result is cached, changes in package.path that modify the module name are missed
-- (mostly affect main module when Lua interpreter is launched with an absolute path)
local function get_module_uri (source)
if source:sub(1,1) == "@" then -- real source file
local uri
local uri
local sourcepath = source:sub(2)
local normalizedpath = M.normalize(sourcepath)
local luapathtable = split (package.path, ";")
local is_source_absolute = M.is_path_absolute(sourcepath)
local luapathtable = split (package.path, ";")
local is_source_absolute = M.is_path_absolute(sourcepath)
-- workarround : Add always the ?.lua entry to support
-- the case where file was loaded by : "lua myfile.lua"
table.insert(luapathtable,"?.lua")
for i,var in ipairs(luapathtable) do
-- avoid relative patterns matching absolute ones (e.g. ?.lua matches anything)
if M.is_path_absolute(var) == is_source_absolute then
local escaped = string.gsub(M.normalize(var),"[%^%$%(%)%%%.%[%]%*%+%-%?]",function(c) return "%"..c end)
local pattern = string.gsub(escaped,"%%%?","(.+)")
local modulename = string.match(normalizedpath,pattern)
if modulename then
modulename = string.gsub(modulename,"/",".");
-- if we find more than 1 possible modulename return the shorter
if not uri or string.len(uri)>string.len(modulename) then
uri = modulename
end
-- avoid relative patterns matching absolute ones (e.g. ?.lua matches anything)
if M.is_path_absolute(var) == is_source_absolute then
local escaped = string.gsub(M.normalize(var),"[%^%$%(%)%%%.%[%]%*%+%-%?]",function(c) return "%"..c end)
local pattern = string.gsub(escaped,"%%%?","(.+)")
local modulename = string.match(normalizedpath,pattern)
if modulename then
modulename = string.gsub(modulename,"/",".");
-- if we find more than 1 possible modulename return the shorter
if not uri or string.len(uri)>string.len(modulename) then
uri = modulename
end
end
end
end
Expand Down Expand Up @@ -148,7 +148,7 @@ function M.init(executionplatform,workingdirectory)
return false
end

status, iswin = pcall(iswindows)
local status, iswin = pcall(iswindows)
if status and iswin then
platform = "win"
else
Expand Down
2 changes: 1 addition & 1 deletion libraries/luadbgpclient/debugger/transport/apr.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ local apr = require "apr"

-- base 64 wrapping
function b64_wrap(src)
t = {}
local t = {}
local b64_src = mime.b64(src)
for i=1, #b64_src, 76 do t[#t+1] = b64_src:sub(i, i+75).."\r\n" end
return table.concat(t)
Expand Down
72 changes: 44 additions & 28 deletions libraries/luadbgpclient/debugger/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,41 +87,57 @@ local CurrentThreadMT = {
CurrentThreadMT.__index = CurrentThreadMT
function M.CurrentThread(coro) return setmetatable({ coro }, CurrentThreadMT) end

-- Fallback method to inspect running thread (only for main thread in 5.1 or for conditional breakpoints)
--- Gets a script stack level with additional debugger logic added
-- @param l (number) stack level to get for debugged script (0 based)
-- @return real Lua stack level suitable to be passed through deubg functions
local function get_script_level(l)
local hook = debug.gethook()
for i=2, math.huge do
if assert(debug.getinfo(i, "f")).func == hook then
return i + l -- the script to level is just below, but because of the extra call to this function, the level is ok for callee
end
end
end
M.MainThread = {
[1] = "main", -- as the raw thread object is used as table keys, provide a replacement.
getinfo = function(self, level, what) return getinfo(get_script_level(level), what:gsub("t", "", 1)) end,
getlocal = function(self, level, idx) return getlocal(get_script_level(level), idx) end,
setlocal = function(self, level, idx, val) return setlocal(get_script_level(level), idx, val) end,
}

-- Some version dependant functions
if _VERSION == "Lua 5.1" then
local loadstring, getfenv, setfenv, debug_getinfo = loadstring, getfenv, setfenv, debug.getinfo

local loadstring, getfenv, setfenv, debug_getinfo, MainThread =
loadstring, getfenv, setfenv, debug.getinfo, nil

-- in 5.1 "t" flag does not exist and trigger an error so remove it from what
CurrentThreadMT.getinfo = function(self, level, what) return getinfo(self[1], level + 2, what:gsub("t", "", 1)) end
ForeignThreadMT.getinfo = function(self, level, what) return getinfo(self[1], level, what:gsub("t", "", 1)) end

-- If the VM is vanilla Lua 5.1, there is no way to get a reference to the main coroutine, so fall back to direct mode:
-- the debugger loop is started on the top of main thread and the actual level is recomputed each time
if not jit then
-- allow CurrentThread to take a nil parameter
local oldCurrentThread = M.CurrentThread
M.CurrentThread = function(coro) return coro and oldCurrentThread(coro) or M.MainThread end

-- when we're forced to start debug loop on top of program stack (when on main coroutine)
-- this requires some hackery to get right stack level

-- Fallback method to inspect running thread (only for main thread in 5.1 or for conditional breakpoints)
--- Gets a script stack level with additional debugger logic added
-- @param l (number) stack level to get for debugged script (0 based)
-- @return real Lua stack level suitable to be passed through deubg functions
local function get_script_level(l)
local hook = debug.gethook()
for i=2, math.huge do
if assert(debug.getinfo(i, "f")).func == hook then
return i + l -- the script to level is just below, but because of the extra call to this function, the level is ok for callee
end
end
end


if rawget(_G, "jit") then
MainThread = {
[1] = "main", -- as the raw thread object is used as table keys, provide a replacement.
-- LuaJIT completely eliminates tail calls from stack, so get_script_level retunrs wrong result in this case
getinfo = function(self, level, what) return getinfo(get_script_level(level) - 1, what:gsub("t", "", 1)) end,
getlocal = function(self, level, idx) return getlocal(get_script_level(level) - 1, idx) end,
setlocal = function(self, level, idx, val) return setlocal(get_script_level(level) - 1, idx, val) end,
}
else
MainThread = {
[1] = "main",
getinfo = function(self, level, what) return getinfo(get_script_level(level) , what:gsub("t", "", 1)) end,
getlocal = function(self, level, idx) return getlocal(get_script_level(level), idx) end,
setlocal = function(self, level, idx, val) return setlocal(get_script_level(level), idx, val) end,
}
end



-- If the VM is vanilla Lua 5.1 or LuaJIT 2 without 5.2 compatibility, there is no way to get a reference to
-- the main coroutine, so fall back to direct mode: the debugger loop is started on the top of main thread
-- and the actual level is recomputed each time
local oldCurrentThread = M.CurrentThread
M.CurrentThread = function(coro) return coro and oldCurrentThread(coro) or MainThread end

-- load a piece of code alog with its environment
function M.loadin(code, env)
local f = loadstring(code)
Expand Down
Loading