add 'step' and 'resume' commands

This commit is contained in:
MihailRis 2025-10-07 21:53:46 +03:00
parent 5972bc769b
commit 9372a5226e
6 changed files with 91 additions and 42 deletions

View File

@ -2,8 +2,16 @@
-- may be reused one global ffi buffer per lua_State -- may be reused one global ffi buffer per lua_State
local breakpoints = {} local breakpoints = {}
local dbg_steps_mode = false
local hook_lock = false
debug.sethook(function (e, line) debug.sethook(function (e, line)
if dbg_steps_mode and not hook_lock then
hook_lock = true
debug.breakpoint()
debug.pull_events()
end
hook_lock = false
local bps = breakpoints[line] local bps = breakpoints[line]
if not bps then if not bps then
return return
@ -18,6 +26,9 @@ end, "l")
local DBG_EVENT_SET_BREAKPOINT = 1 local DBG_EVENT_SET_BREAKPOINT = 1
local DBG_EVENT_RM_BREAKPOINT = 2 local DBG_EVENT_RM_BREAKPOINT = 2
local DBG_EVENT_STEP = 3
local DBG_EVENT_STEP_INTO_FUNCTION = 4
local DBG_EVENT_RESUME = 5
local __pull_events = debug.__pull_events local __pull_events = debug.__pull_events
debug.__pull_events = nil debug.__pull_events = nil
@ -31,6 +42,10 @@ function debug.pull_events()
debug.set_breakpoint(event[2], event[3]) debug.set_breakpoint(event[2], event[3])
elseif event[1] == DBG_EVENT_RM_BREAKPOINT then elseif event[1] == DBG_EVENT_RM_BREAKPOINT then
debug.remove_breakpoint(event[2], event[3]) debug.remove_breakpoint(event[2], event[3])
elseif event[1] == DBG_EVENT_STEP then
dbg_steps_mode = true
elseif event[1] == DBG_EVENT_RESUME then
dbg_steps_mode = false
end end
end end
end end

View File

@ -130,25 +130,25 @@ DebuggingServer::~DebuggingServer() {
bool DebuggingServer::update() { bool DebuggingServer::update() {
if (connection == nullptr) { if (connection == nullptr) {
return true; return false;
} }
std::string message = connection->read(); std::string message = connection->read();
if (message.empty()) { if (message.empty()) {
return true; return false;
} }
logger.debug() << "received: " << message; logger.debug() << "received: " << message;
try { try {
auto obj = json::parse(message); auto obj = json::parse(message);
if (!obj.has("type")) { if (!obj.has("type")) {
logger.error() << "missing message type"; logger.error() << "missing message type";
return true; return false;
} }
const auto& type = obj["type"].asString(); const auto& type = obj["type"].asString();
return performCommand(type, obj); return performCommand(type, obj);
} catch (const std::runtime_error& err) { } catch (const std::runtime_error& err) {
logger.error() << "could not to parse message: " << err.what(); logger.error() << "could not to parse message: " << err.what();
} }
return true; return false;
} }
bool DebuggingServer::performCommand( bool DebuggingServer::performCommand(
@ -158,24 +158,38 @@ bool DebuggingServer::performCommand(
engine.quit(); engine.quit();
connection->sendResponse("success"); connection->sendResponse("success");
} else if (type == "detach") { } else if (type == "detach") {
logger.info() << "detach received";
connection->sendResponse("success"); connection->sendResponse("success");
connection.reset(); connection.reset();
engine.detachDebugger();
return false; return false;
} else if (type == "set-breakpoint" || type == "remove-breakpoint") { } else if (type == "set-breakpoint" || type == "remove-breakpoint") {
if (!map.has("source") || !map.has("line")) if (!map.has("source") || !map.has("line"))
return true; return false;
breakpointEvents.push_back(BreakpointEvent { breakpointEvents.push_back(DebuggingEvent {
type[0] == 's' type[0] == 's'
? BreakpointEventType::SET_BREAKPOINT ? DebuggingEventType::SET_BREAKPOINT
: BreakpointEventType::REMOVE_BREAKPOINT, : DebuggingEventType::REMOVE_BREAKPOINT,
map["source"].asString(), BreakpointEventDto {
static_cast<int>(map["line"].asInteger()), map["source"].asString(),
static_cast<int>(map["line"].asInteger()),
}
}); });
} else if (type == "step" || type == "step-into-function") {
breakpointEvents.push_back(DebuggingEvent {
type == "step"
? DebuggingEventType::STEP
: DebuggingEventType::STEP_INTO_FUNCTION,
SignalEventDto {}
});
return true;
} else if (type == "resume") {
breakpointEvents.push_back(DebuggingEvent {
DebuggingEventType::RESUME, SignalEventDto {}});
return true;
} else { } else {
logger.error() << "unsupported command '" << type << "'"; logger.error() << "unsupported command '" << type << "'";
} }
return true; return false;
} }
void DebuggingServer::onHitBreakpoint(dv::value&& stackTrace) { void DebuggingServer::onHitBreakpoint(dv::value&& stackTrace) {
@ -195,6 +209,6 @@ void DebuggingServer::setClient(u64id_t client) {
std::make_unique<ClientConnection>(engine.getNetwork(), client); std::make_unique<ClientConnection>(engine.getNetwork(), client);
} }
std::vector<BreakpointEvent> DebuggingServer::pullBreakpointEvents() { std::vector<DebuggingEvent> DebuggingServer::pullEvents() {
return std::move(breakpointEvents); return std::move(breakpointEvents);
} }

View File

@ -3,6 +3,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <variant>
#include "typedefs.hpp" #include "typedefs.hpp"
@ -36,16 +37,27 @@ namespace devtools {
u64id_t connection; u64id_t connection;
}; };
enum class BreakpointEventType { enum class DebuggingEventType {
SET_BREAKPOINT = 1, SET_BREAKPOINT = 1,
REMOVE_BREAKPOINT, REMOVE_BREAKPOINT,
STEP,
STEP_INTO_FUNCTION,
RESUME,
}; };
struct BreakpointEvent {
BreakpointEventType type; struct BreakpointEventDto {
std::string source; std::string source;
int line; int line;
}; };
struct SignalEventDto {
};
struct DebuggingEvent {
DebuggingEventType type;
std::variant<BreakpointEventDto, SignalEventDto> data;
};
class DebuggingServer { class DebuggingServer {
public: public:
DebuggingServer(Engine& engine, const std::string& serverString); DebuggingServer(Engine& engine, const std::string& serverString);
@ -55,13 +67,15 @@ namespace devtools {
void onHitBreakpoint(dv::value&& stackTrace); void onHitBreakpoint(dv::value&& stackTrace);
void setClient(u64id_t client); void setClient(u64id_t client);
std::vector<BreakpointEvent> pullBreakpointEvents(); std::vector<DebuggingEvent> pullEvents();
private: private:
Engine& engine; Engine& engine;
network::Server& server; network::Server& server;
std::unique_ptr<ClientConnection> connection; std::unique_ptr<ClientConnection> connection;
std::vector<BreakpointEvent> breakpointEvents; std::vector<DebuggingEvent> breakpointEvents;
bool performCommand(const std::string& type, const dv::value& map); bool performCommand(
const std::string& type, const dv::value& map
);
}; };
} }

View File

@ -284,12 +284,14 @@ void Engine::postUpdate() {
scripting::process_post_runnables(); scripting::process_post_runnables();
if (debuggingServer) { if (debuggingServer) {
if (!debuggingServer->update()) { debuggingServer->update();
debuggingServer.reset();
}
} }
} }
void Engine::detachDebugger() {
debuggingServer.reset();
}
void Engine::updateFrontend() { void Engine::updateFrontend() {
double delta = time.getDelta(); double delta = time.getDelta();
updateHotkeys(); updateHotkeys();
@ -317,10 +319,9 @@ void Engine::startPauseLoop() {
input->toggleCursor(); input->toggleCursor();
} }
} }
while (!isQuitSignal()) { while (!isQuitSignal() && debuggingServer) {
network->update(); network->update();
if (!debuggingServer->update()) { if (debuggingServer->update()) {
debuggingServer.reset();
break; break;
} }
if (isHeadless()) { if (isHeadless()) {

View File

@ -190,4 +190,6 @@ public:
devtools::DebuggingServer* getDebuggingServer() { devtools::DebuggingServer* getDebuggingServer() {
return debuggingServer.get(); return debuggingServer.get();
} }
void detachDebugger();
}; };

View File

@ -280,30 +280,33 @@ static int l_debug_breakpoint(lua::State* L) {
} }
static int l_debug_pull_events(lua::State* L) { static int l_debug_pull_events(lua::State* L) {
if (auto server = engine->getDebuggingServer()) { auto server = engine->getDebuggingServer();
auto events = server->pullBreakpointEvents(); if (!server) {
if (events.empty()) { return 0;
return 0; }
} auto events = server->pullEvents();
lua::createtable(L, events.size(), 0); if (events.empty()) {
for (int i = 0; i < events.size(); i++) { return 0;
const auto& event = events[i]; }
lua::createtable(L, 3, 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<int>(event.type)); lua::pushinteger(L, static_cast<int>(event.type));
lua::rawseti(L, 1); lua::rawseti(L, 1);
lua::pushstring(L, event.source); if (auto dto = std::get_if<devtools::BreakpointEventDto>(&event.data)) {
lua::pushstring(L, dto->source);
lua::rawseti(L, 2); lua::rawseti(L, 2);
lua::pushinteger(L, event.line); lua::pushinteger(L, dto->line);
lua::rawseti(L, 3); lua::rawseti(L, 3);
lua::rawseti(L, i + 1);
} }
return 1;
lua::rawseti(L, i + 1);
} }
return 0; return 1;
} }
void initialize_libs_extends(lua::State* L) { void initialize_libs_extends(lua::State* L) {