Skip to content
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
17 changes: 16 additions & 1 deletion docs/luadebug/hookmgr.lua
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,24 @@ end
---
---@param co thread
---@return thread
---获取coroutine调用方
---获取coroutine调用方。手动指定的父协程优先,其次才是resume关系。
---
function hookmgr.coroutine_from(co)
end

---
---@param co lightuserdata
---@param parent lightuserdata|nil
---手动指定co的父协程,传nil表示清除。co是协程标识(见rdebug.threadptr)。
---
function hookmgr.coroutine_setparent(co, parent)
end

---
---@param co lightuserdata
---通知调试器co已结束,清掉它的映射以及所有指向它的映射。
---
function hookmgr.coroutine_dead(co)
end

return hookmgr
40 changes: 30 additions & 10 deletions extension/script/backend/worker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ end

local function coroutineFrom(L)
if hookmgr.coroutine_from then
return coroutineTree[L] or hookmgr.coroutine_from(L)
-- 手动指定的父协程优先,其次是native推导的,最后才是Lua兜底表
return hookmgr.coroutine_from(L) or coroutineTree[L]
end
return coroutineTree[L]
end
Expand Down Expand Up @@ -270,11 +271,17 @@ function CMD.stackTrace(pkg)
start = start + skipFrame
local L = baseL
local coroutineId = 0
local visited = {}
local finish
repeat
hookmgr.sethost(L)
local curL = L
L = coroutineFrom(curL)
-- 手动指定的父协程可能构成环
if visited[curL] then
break
end
visited[curL] = true
if stackFrame[curL] == nil then
local n;
finish, n = stackTrace(res, coroutineId, start, levels)
Expand Down Expand Up @@ -947,16 +954,19 @@ function event.thread(co, type)
if not debuggeeReady() then return end
-- L是触发事件的协程,即co的调用方(父协程)
local L = hookmgr.gethost()
if co then
if co and rdebug.threadptr then
-- 退出事件也可能只是yield,要确认协程真的结束了
local dead = type == 1 and rdebug.costatus(co) == "dead"
-- co是调试目标里的协程,转成地址才能在调试器侧标识它
co = rdebug.threadptr and rdebug.threadptr(co)
if co then
if type == 0 then
coroutineTree[co] = L
hookmgr.updatehookmask(co)
return
elseif type == 1 then
coroutineTree[co] = nil
co = rdebug.threadptr(co)
if type == 0 then
Comment on lines +961 to +962
coroutineTree[co] = L
hookmgr.updatehookmask(co)
return
elseif type == 1 then
coroutineTree[co] = nil
if dead and hookmgr.coroutine_dead then
hookmgr.coroutine_dead(co)
end
end
end
Expand All @@ -972,6 +982,16 @@ function event.setThreadName(name)
sendToMaster 'setThreadName' (name)
end

function event.setCoroutineParent(co, parent)
if not debuggeeReady() then return end
if not (rdebug.threadptr and hookmgr.coroutine_setparent) then return end
-- co和parent是调试目标里的协程,转成地址才能在调试器侧标识它们
co = rdebug.threadptr(co)
if co then
hookmgr.coroutine_setparent(co, parent and rdebug.threadptr(parent))
end
end

function event.exit()
sendToMaster 'exitWorker' {}
channel.destroy(WorkerChannel)
Expand Down
21 changes: 21 additions & 0 deletions extension/script/debugger.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ if debug.getregistry()["lua-debug"] then
return self
end

function empty:set_coroutine_parent(co, parent)
dbg:set_coroutine_parent(co, parent)
return self
end

function empty:setup_patch()
return self
end
Expand Down Expand Up @@ -219,6 +224,12 @@ function dbg:set_wait(name, f)
return self
end

-- 指定协程co的父协程,堆栈窗口里会把parent的堆栈拼在co后面。parent为nil表示清除。
function dbg:set_coroutine_parent(co, parent)
self:event("setCoroutineParent", co, parent)
return self
end

function dbg:setup_patch()
local ERREVENT_ERRRUN = 0x02
local rawxpcall = xpcall
Expand Down Expand Up @@ -260,6 +271,16 @@ function dbg:setup_patch()
end
end

-- 被close的协程也是结束了,不然它的父链接会一直留着
local rawcoroutineclose = coroutine.close
if rawcoroutineclose then
function coroutine.close(co)
local ok, err = rawcoroutineclose(co)
self:event("thread", co, 1)
Comment on lines +274 to +279
return ok, err
end
end

return self
end

Expand Down
84 changes: 74 additions & 10 deletions src/luadebug/rdebug_hookmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,54 @@ struct hookmgr {
// thread
//
int thread_mask = 0;
// 自动推导的调用方:child -> resumer,只有补丁Lua才会有内容
bee::flatmap<lua_State*, lua_State*> coroutine_tree;
// 手动指定的父协程:child -> parent
bee::flatmap<lua_State*, lua_State*> coroutine_parent;

// 协程结束后清掉它的映射,以及所有指向它的映射,否则会留下悬垂的lua_State*
void coroutine_dead(lua_State* co) {
coroutine_tree.erase(co);
coroutine_parent.erase(co);
if (coroutine_parent.empty()) {
return;
}
bee::dynarray<lua_State*> children(coroutine_parent.size());
size_t n = 0;
for (auto [child, parent] : coroutine_parent) {
if (parent == co) {
children[n++] = child;
}
}
for (size_t i = 0; i < n; ++i) {
coroutine_parent.erase(children[i]);
}
}

void coroutine_setparent(lua_State* co, lua_State* parent) {
if (parent) {
coroutine_parent.insert_or_assign(co, parent);
} else {
coroutine_parent.erase(co);
}
}

lua_State* coroutine_from(lua_State* co) {
auto p = coroutine_parent.find(co);
if (p) {
return *p;
}
#if defined(LUA_HOOKTHREAD)
auto r = coroutine_tree.find(co);
if (!r) {
return nullptr;
}
return *r;
#else
return nullptr;
#endif
}

#if defined(LUA_HOOKTHREAD)
void thread_hookmask(lua_State* hL, int mask) {
if (thread_mask != mask) {
Expand All @@ -360,18 +407,16 @@ struct hookmgr {
if (type == 0) {
coroutine_tree.insert_or_assign(co, from);
} else if (type == 1) {
coroutine_tree.erase(from);
if (lua_status(from) == LUA_YIELD) {
// 只是让出,自动链接下次resume会重来,手动指定的父协程要保留
coroutine_tree.erase(from);
} else {
coroutine_dead(from);
}
}
}
updatehookmask(co);
}
lua_State* coroutine_from(lua_State* co) {
auto r = coroutine_tree.find(co);
if (!r) {
return nullptr;
}
return *r;
}
#endif

//
Expand Down Expand Up @@ -760,6 +805,7 @@ static int thread_open(luadbg_State* L) {
hookmgr::get_self(L)->thread_open(luadebug::debughost::get(L), luadbg_toboolean(L, 1));
return 0;
}
#endif
static int coroutine_from(luadbg_State* L) {
luadbgL_checktype(L, 1, LUA_TLIGHTUSERDATA);
lua_State* from = hookmgr::get_self(L)->coroutine_from((lua_State*)luadbg_touserdata(L, 1));
Expand All @@ -769,7 +815,23 @@ static int coroutine_from(luadbg_State* L) {
luadbg_pushlightuserdata(L, from);
return 1;
}
#endif
static int coroutine_setparent(luadbg_State* L) {
luadbgL_checktype(L, 1, LUA_TLIGHTUSERDATA);
lua_State* co = (lua_State*)luadbg_touserdata(L, 1);
lua_State* parent = nullptr;
int t = luadbg_type(L, 2);
if (t != LUADBG_TNIL && t != LUADBG_TNONE) {
luadbgL_checktype(L, 2, LUA_TLIGHTUSERDATA);
parent = (lua_State*)luadbg_touserdata(L, 2);
}
hookmgr::get_self(L)->coroutine_setparent(co, parent);
return 0;
}
static int coroutine_dead(luadbg_State* L) {
luadbgL_checktype(L, 1, LUA_TLIGHTUSERDATA);
hookmgr::get_self(L)->coroutine_dead((lua_State*)luadbg_touserdata(L, 1));
return 0;
}

LUADEBUG_FUNC
int luaopen_luadebug_hookmgr(luadbg_State* L) {
Expand Down Expand Up @@ -812,8 +874,10 @@ int luaopen_luadebug_hookmgr(luadbg_State* L) {
#endif
#if defined(LUA_HOOKTHREAD)
{ "thread_open", thread_open },
{ "coroutine_from", coroutine_from },
#endif
{ "coroutine_from", coroutine_from },
{ "coroutine_setparent", coroutine_setparent },
{ "coroutine_dead", coroutine_dead },
{ NULL, NULL },
};
luadbgL_setfuncs(L, lib, 1);
Expand Down
Loading