add 'get-value' command

This commit is contained in:
MihailRis 2025-10-08 21:35:37 +03:00
parent aa42fb1e39
commit 8f56969997
6 changed files with 195 additions and 69 deletions

View File

@ -1,6 +1,3 @@
-- 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 = {} local breakpoints = {}
local dbg_steps_mode = false local dbg_steps_mode = false
local dbg_step_into_func = false local dbg_step_into_func = false
@ -9,6 +6,7 @@ local current_func
local current_func_stack_size local current_func_stack_size
local _debug_getinfo = debug.getinfo local _debug_getinfo = debug.getinfo
local _debug_getlocal = debug.getlocal
-- 'return' hook not called for some functions -- 'return' hook not called for some functions
-- todo: speedup -- todo: speedup
@ -64,8 +62,11 @@ local DBG_EVENT_RM_BREAKPOINT = 2
local DBG_EVENT_STEP = 3 local DBG_EVENT_STEP = 3
local DBG_EVENT_STEP_INTO_FUNCTION = 4 local DBG_EVENT_STEP_INTO_FUNCTION = 4
local DBG_EVENT_RESUME = 5 local DBG_EVENT_RESUME = 5
local DBG_EVENT_GET_VALUE = 6
local __pull_events = debug.__pull_events local __pull_events = debug.__pull_events
local __sendvalue = debug.__sendvalue
debug.__pull_events = nil debug.__pull_events = nil
debug.__sendvalue = nil
function debug.pull_events() function debug.pull_events()
local events = __pull_events() local events = __pull_events()
@ -86,6 +87,16 @@ function debug.pull_events()
elseif event[1] == DBG_EVENT_RESUME then elseif event[1] == DBG_EVENT_RESUME then
dbg_steps_mode = false dbg_steps_mode = false
dbg_step_into_func = false dbg_step_into_func = false
elseif event[1] == DBG_EVENT_GET_VALUE then
local _, value = _debug_getlocal(event[2] + 3, event[3])
for _, key in ipairs(event[4]) do
if value == nil then
value = "error: index nil value"
break
end
value = value[key]
end
__sendvalue(value, event[2], event[3], event[4])
end end
end end
end end
@ -107,6 +118,8 @@ function debug.remove_breakpoint(source, line)
bps[source] = nil bps[source] = nil
end end
-- 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 canvas_ffi_buffer local canvas_ffi_buffer
local canvas_ffi_buffer_size = 0 local canvas_ffi_buffer_size = 0

View File

@ -186,6 +186,27 @@ bool DebuggingServer::performCommand(
breakpointEvents.push_back(DebuggingEvent { breakpointEvents.push_back(DebuggingEvent {
DebuggingEventType::RESUME, SignalEventDto {}}); DebuggingEventType::RESUME, SignalEventDto {}});
return true; return true;
} else if (type == "get-value") {
if (!map.has("frame") || !map.has("local") || !map.has("path"))
return false;
int frame = map["frame"].asInteger();
int localIndex = map["local"].asInteger();
ValuePath path;
for (const auto& segment : map["path"]) {
if (segment.isString()) {
path.emplace_back(segment.asString());
} else {
path.emplace_back(static_cast<int>(segment.asInteger()));
}
}
breakpointEvents.push_back(DebuggingEvent {
DebuggingEventType::GET_VALUE, GetValueEventDto {
frame, localIndex, std::move(path)
}
});
return true;
} else { } else {
logger.error() << "unsupported command '" << type << "'"; logger.error() << "unsupported command '" << type << "'";
} }
@ -204,6 +225,30 @@ void DebuggingServer::onHitBreakpoint(dv::value&& stackTrace) {
engine.startPauseLoop(); engine.startPauseLoop();
} }
void DebuggingServer::sendValue(
dv::value&& value, int frame, int local, ValuePath&& path
) {
auto pathValue = dv::list();
for (const auto& segment : path) {
if (auto string = std::get_if<std::string>(&segment)) {
pathValue.add(*string);
} else {
pathValue.add(std::get<int>(segment));
}
}
connection->send(dv::object({
{"type", std::string("value")},
{"frame", frame},
{"local", local},
{"path", std::move(pathValue)},
{"value", std::move(value)},
}));
}
void DebuggingServer::pause() {
engine.startPauseLoop();
}
void DebuggingServer::setClient(u64id_t client) { void DebuggingServer::setClient(u64id_t client) {
this->connection = this->connection =
std::make_unique<ClientConnection>(engine.getNetwork(), client); std::make_unique<ClientConnection>(engine.getNetwork(), client);

View File

@ -43,6 +43,7 @@ namespace devtools {
STEP, STEP,
STEP_INTO_FUNCTION, STEP_INTO_FUNCTION,
RESUME, RESUME,
GET_VALUE,
}; };
struct BreakpointEventDto { struct BreakpointEventDto {
@ -53,9 +54,17 @@ namespace devtools {
struct SignalEventDto { struct SignalEventDto {
}; };
using ValuePath = std::vector<std::variant<std::string, int>>;
struct GetValueEventDto {
int frame;
int localIndex;
ValuePath path;
};
struct DebuggingEvent { struct DebuggingEvent {
DebuggingEventType type; DebuggingEventType type;
std::variant<BreakpointEventDto, SignalEventDto> data; std::variant<BreakpointEventDto, SignalEventDto, GetValueEventDto> data;
}; };
class DebuggingServer { class DebuggingServer {
@ -65,6 +74,9 @@ namespace devtools {
bool update(); bool update();
void onHitBreakpoint(dv::value&& stackTrace); void onHitBreakpoint(dv::value&& stackTrace);
void pause();
void sendValue(dv::value&& value, int frame, int local, ValuePath&& path);
void setClient(u64id_t client); void setClient(u64id_t client);
std::vector<DebuggingEvent> pullEvents(); std::vector<DebuggingEvent> pullEvents();

View File

@ -9,6 +9,7 @@
#include "devtools/DebuggingServer.hpp" #include "devtools/DebuggingServer.hpp"
#include "logic/scripting/scripting.hpp" #include "logic/scripting/scripting.hpp"
using namespace devtools;
using namespace scripting; using namespace scripting;
static debug::Logger logger("lua-debug"); static debug::Logger logger("lua-debug");
@ -164,8 +165,52 @@ static int l_math_normal_random(lua::State* L) {
constexpr inline int MAX_SHORT_STRING_LEN = 50; constexpr inline int MAX_SHORT_STRING_LEN = 50;
static std::string get_short_value(lua::State* L, int idx, int type) {
switch (type) {
case LUA_TNIL:
return "nil";
case LUA_TBOOLEAN:
return lua::toboolean(L, idx) ? "true" : "false";
case LUA_TNUMBER: {
std::stringstream ss;
ss << lua::tonumber(L, idx);
return ss.str();
}
case LUA_TSTRING: {
const char* str = lua::tostring(L, idx);
if (strlen(str) > MAX_SHORT_STRING_LEN) {
return std::string(str, MAX_SHORT_STRING_LEN);
} else {
return str;
}
}
case LUA_TTABLE:
return "{...}";
case LUA_TFUNCTION: {
std::stringstream ss;
ss << "function: 0x" << std::hex << lua::topointer(L, idx);
return ss.str();
}
case LUA_TUSERDATA: {
std::stringstream ss;
ss << "userdata: 0x" << std::hex << lua::topointer(L, idx);
return ss.str();
}
case LUA_TTHREAD: {
std::stringstream ss;
ss << "thread: 0x" << std::hex << lua::topointer(L, idx);
return ss.str();
}
default: {
std::stringstream ss;
ss << "cdata: 0x" << std::hex << lua::topointer(L, idx);
return ss.str();
}
}
}
static dv::value collect_locals(lua::State* L, lua_Debug& frame) { static dv::value collect_locals(lua::State* L, lua_Debug& frame) {
auto locals = dv::object(); auto locals = dv::list();
int localIndex = 1; int localIndex = 1;
const char* name; const char* name;
@ -175,68 +220,13 @@ static dv::value collect_locals(lua::State* L, lua_Debug& frame) {
continue; continue;
} }
auto local = dv::object(); auto local = dv::object();
local["name"] = name;
local["index"] = localIndex - 1;
int type = lua::type(L, -1); int type = lua::type(L, -1);
switch (type) { local["type"] = lua::type_name(L, type);
case LUA_TNIL: local["short"] = get_short_value(L, -1, type);
local["short"] = "nil"; locals.add(std::move(local));
local["type"] = "nil";
break;
case LUA_TBOOLEAN:
local["short"] = lua::toboolean(L, -1) ? "true" : "false";
local["type"] = "boolean";
break;
case LUA_TNUMBER: {
std::stringstream ss;
ss << lua::tonumber(L, -1);
local["short"] = ss.str();
local["type"] = "number";
break;
}
case LUA_TSTRING: {
const char* str = lua::tostring(L, -1);
if (strlen(str) > MAX_SHORT_STRING_LEN) {
local["short"] = std::string(str, MAX_SHORT_STRING_LEN);
} else {
local["short"] = str;
}
local["type"] = "string";
break;
}
case LUA_TTABLE:
local["short"] = "{...}";
local["type"] = "table";
break;
case LUA_TFUNCTION: {
std::stringstream ss;
ss << "function: 0x" << std::hex << lua::topointer(L, -1);
local["short"] = ss.str();
local["type"] = "function";
break;
}
case LUA_TUSERDATA: {
std::stringstream ss;
ss << "userdata: 0x" << std::hex << lua::topointer(L, -1);
local["short"] = ss.str();
local["type"] = "userdata";
break;
}
case LUA_TTHREAD: {
std::stringstream ss;
ss << "thread: 0x" << std::hex << lua::topointer(L, -1);
local["short"] = ss.str();
local["type"] = "thread";
break;
}
default: {
std::stringstream ss;
ss << "cdata: 0x" << std::hex << lua::topointer(L, -1);
local["short"] = ss.str();
local["type"] = "cdata";
break;
}
}
locals[name] = std::move(local);
lua::pop(L); lua::pop(L);
} }
return locals; return locals;
@ -279,6 +269,51 @@ static int l_debug_breakpoint(lua::State* L) {
return 0; return 0;
} }
static int l_debug_sendvalue(lua::State* L) {
auto server = engine->getDebuggingServer();
if (!server) {
return 0;
}
int frame = lua::tointeger(L, 2);
int local = lua::tointeger(L, 3);
ValuePath path;
int pathSectors = lua::objlen(L, 4);
for (int i = 0; i < pathSectors; i++) {
lua::rawgeti(L, i + 1, 4);
if (lua::isstring(L, -1)) {
path.emplace_back(lua::tostring(L, -1));
} else {
path.emplace_back(static_cast<int>(lua::tointeger(L, -1)));
}
lua::pop(L);
}
dv::value value = nullptr;
if (lua::istable(L, 1)) {
auto table = dv::object();
lua::pushnil(L);
while (lua::next(L, 1)) {
auto key = lua::tolstring(L, -2);
int type = lua::type(L, -1);
table[std::string(key)] = dv::object({
{"type", std::string(lua::type_name(L, type))},
{"short", get_short_value(L, -1, type)},
});
lua::pop(L);
}
lua::pop(L);
value = std::move(table);
} else {
value = lua::tovalue(L, 1);
}
server->sendValue(std::move(value), frame, local, std::move(path));
return 0;
}
static int l_debug_pull_events(lua::State* L) { static int l_debug_pull_events(lua::State* L) {
auto server = engine->getDebuggingServer(); auto server = engine->getDebuggingServer();
if (!server) { if (!server) {
@ -296,12 +331,30 @@ static int l_debug_pull_events(lua::State* L) {
lua::pushinteger(L, static_cast<int>(event.type)); lua::pushinteger(L, static_cast<int>(event.type));
lua::rawseti(L, 1); lua::rawseti(L, 1);
if (auto dto = std::get_if<devtools::BreakpointEventDto>(&event.data)) { if (auto dto = std::get_if<BreakpointEventDto>(&event.data)) {
lua::pushstring(L, dto->source); lua::pushstring(L, dto->source);
lua::rawseti(L, 2); lua::rawseti(L, 2);
lua::pushinteger(L, dto->line); lua::pushinteger(L, dto->line);
lua::rawseti(L, 3); lua::rawseti(L, 3);
} else if (auto dto = std::get_if<GetValueEventDto>(&event.data)) {
lua::pushinteger(L, dto->frame);
lua::rawseti(L, 2);
lua::pushinteger(L, dto->localIndex);
lua::rawseti(L, 3);
lua::createtable(L, dto->path.size(), 0);
for (int i = 0; i < dto->path.size(); i++) {
const auto& segment = dto->path[i];
if (auto string = std::get_if<std::string>(&segment)) {
lua::pushstring(L, *string);
} else {
lua::pushinteger(L, std::get<int>(segment));
}
lua::rawseti(L, i + 1);
}
lua::rawseti(L, 4);
} }
lua::rawseti(L, i + 1); lua::rawseti(L, i + 1);
@ -329,6 +382,9 @@ void initialize_libs_extends(lua::State* L) {
lua::pushcfunction(L, lua::wrap<l_debug_pull_events>); lua::pushcfunction(L, lua::wrap<l_debug_pull_events>);
lua::setfield(L, "__pull_events"); lua::setfield(L, "__pull_events");
lua::pushcfunction(L, lua::wrap<l_debug_sendvalue>);
lua::setfield(L, "__sendvalue");
lua::pop(L); lua::pop(L);
} }
if (lua::getglobal(L, "math")) { if (lua::getglobal(L, "math")) {

View File

@ -249,7 +249,7 @@ namespace lua {
inline lua::Number tonumber(lua::State* L, int idx) { inline lua::Number tonumber(lua::State* L, int idx) {
#ifndef NDEBUG #ifndef NDEBUG
if (lua_type(L, idx) != LUA_TNUMBER && !lua_isnoneornil(L, idx)) { if (lua_type(L, idx) != LUA_TNUMBER && !lua_isnoneornil(L, idx)) {
throw std::runtime_error("integer expected"); throw std::runtime_error("number expected");
} }
#endif #endif
return lua_tonumber(L, idx); return lua_tonumber(L, idx);

View File

@ -50,8 +50,8 @@ namespace lua {
inline int type(lua::State* L, int idx) { inline int type(lua::State* L, int idx) {
return lua_type(L, idx); return lua_type(L, idx);
} }
inline const char* type_name(lua::State* L, int idx) { inline const char* type_name(lua::State* L, int tp) {
return lua_typename(L, idx); return lua_typename(L, tp);
} }
inline int rawget(lua::State* L, int idx = -2) { inline int rawget(lua::State* L, int idx = -2) {
lua_rawget(L, idx); lua_rawget(L, idx);