diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index b08e8c02..3f732e07 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -587,6 +587,7 @@ function __process_post_runnables() __vc_named_coroutines[name] = nil end + debug.pull_events() network.__process_events() block.__process_register_events() block.__perform_ticks(time.delta()) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 26d5d813..eb3ddbde 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -1,5 +1,57 @@ -- Lua has no parallelizm, also _set_data does not call any lua functions so -- may be reused one global ffi buffer per lua_State + +local breakpoints = {} + +debug.sethook(function (e, line) + local bps = breakpoints[line] + if not bps then + return + end + local source = debug.getinfo(2).source + if not bps[source] then + return + end + debug.breakpoint() + debug.pull_events() +end, "l") + +local DBG_EVENT_SET_BREAKPOINT = 1 +local DBG_EVENT_RM_BREAKPOINT = 2 +local __pull_events = debug.__pull_events +debug.__pull_events = nil + +function debug.pull_events() + local events = __pull_events() + if not events then + return + end + for i, event in ipairs(events) do + if event[1] == DBG_EVENT_SET_BREAKPOINT then + debug.set_breakpoint(event[2], event[3]) + elseif event[1] == DBG_EVENT_RM_BREAKPOINT then + debug.remove_breakpoint(event[2], event[3]) + end + end +end + +function debug.set_breakpoint(source, line) + local bps = breakpoints[line] + if not bps then + bps = {} + breakpoints[line] = bps + end + bps[source] = true +end + +function debug.remove_breakpoint(source, line) + local bps = breakpoints[line] + if not bps then + return + end + bps[source] = nil +end + local canvas_ffi_buffer local canvas_ffi_buffer_size = 0 diff --git a/src/devtools/DebuggingServer.cpp b/src/devtools/DebuggingServer.cpp index 88466dd3..09c8c18f 100644 --- a/src/devtools/DebuggingServer.cpp +++ b/src/devtools/DebuggingServer.cpp @@ -31,12 +31,14 @@ std::string ClientConnection::read() { if (length <= 0) { logger.error() << "invalid message length " << length; } else { + logger.info() << "message length " << length; messageLength = length; } } } else if (connection->available() >= messageLength) { std::string string(messageLength, 0); connection->recv(string.data(), messageLength); + messageLength = 0; return string; } return ""; @@ -156,9 +158,20 @@ bool DebuggingServer::performCommand( engine.quit(); connection->sendResponse("success"); } else if (type == "detach") { + logger.info() << "detach received"; connection->sendResponse("success"); connection.reset(); return false; + } else if (type == "set-breakpoint" || type == "remove-breakpoint") { + if (!map.has("source") || !map.has("line")) + return true; + breakpointEvents.push_back(BreakpointEvent { + type[0] == 's' + ? BreakpointEventType::SET_BREAKPOINT + : BreakpointEventType::REMOVE_BREAKPOINT, + map["source"].asString(), + static_cast(map["line"].asInteger()), + }); } else { logger.error() << "unsupported command '" << type << "'"; } @@ -166,8 +179,6 @@ bool DebuggingServer::performCommand( } void DebuggingServer::onHitBreakpoint(dv::value&& stackTrace) { - logger.info() << "hit breakpoint:\n" - << json::stringify(stackTrace, true, " "); if (connection == nullptr) { return; } @@ -183,3 +194,7 @@ void DebuggingServer::setClient(u64id_t client) { this->connection = std::make_unique(engine.getNetwork(), client); } + +std::vector DebuggingServer::pullBreakpointEvents() { + return std::move(breakpointEvents); +} diff --git a/src/devtools/DebuggingServer.hpp b/src/devtools/DebuggingServer.hpp index b379c3e3..308cee87 100644 --- a/src/devtools/DebuggingServer.hpp +++ b/src/devtools/DebuggingServer.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "typedefs.hpp" @@ -35,6 +36,16 @@ namespace devtools { u64id_t connection; }; + enum class BreakpointEventType { + SET_BREAKPOINT = 1, + REMOVE_BREAKPOINT, + }; + struct BreakpointEvent { + BreakpointEventType type; + std::string source; + int line; + }; + class DebuggingServer { public: DebuggingServer(Engine& engine, const std::string& serverString); @@ -44,10 +55,12 @@ namespace devtools { void onHitBreakpoint(dv::value&& stackTrace); void setClient(u64id_t client); + std::vector pullBreakpointEvents(); private: Engine& engine; network::Server& server; std::unique_ptr connection; + std::vector breakpointEvents; bool performCommand(const std::string& type, const dv::value& map); }; diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 64a89fb5..2a7820a6 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -318,6 +318,7 @@ void Engine::startPauseLoop() { } } while (!isQuitSignal()) { + network->update(); if (!debuggingServer->update()) { debuggingServer.reset(); break; diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index 15749b47..e845f925 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -279,6 +279,33 @@ static int l_debug_breakpoint(lua::State* L) { return 0; } +static int l_debug_pull_events(lua::State* L) { + if (auto server = engine->getDebuggingServer()) { + auto events = server->pullBreakpointEvents(); + if (events.empty()) { + return 0; + } + lua::createtable(L, events.size(), 0); + for (int i = 0; i < events.size(); i++) { + const auto& event = events[i]; + lua::createtable(L, 3, 0); + + lua::pushinteger(L, static_cast(event.type)); + lua::rawseti(L, 1); + + lua::pushstring(L, event.source); + lua::rawseti(L, 2); + + lua::pushinteger(L, event.line); + lua::rawseti(L, 3); + + lua::rawseti(L, i + 1); + } + return 1; + } + return 0; +} + void initialize_libs_extends(lua::State* L) { if (lua::getglobal(L, "debug")) { lua::pushcfunction(L, lua::wrap); @@ -296,6 +323,9 @@ void initialize_libs_extends(lua::State* L) { lua::pushcfunction(L, lua::wrap); lua::setfield(L, "breakpoint"); + lua::pushcfunction(L, lua::wrap); + lua::setfield(L, "__pull_events"); + lua::pop(L); } if (lua::getglobal(L, "math")) {