add 'get-value' command
This commit is contained in:
parent
aa42fb1e39
commit
8f56969997
@ -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 dbg_steps_mode = false
|
||||
local dbg_step_into_func = false
|
||||
@ -9,6 +6,7 @@ local current_func
|
||||
local current_func_stack_size
|
||||
|
||||
local _debug_getinfo = debug.getinfo
|
||||
local _debug_getlocal = debug.getlocal
|
||||
|
||||
-- 'return' hook not called for some functions
|
||||
-- todo: speedup
|
||||
@ -64,8 +62,11 @@ local DBG_EVENT_RM_BREAKPOINT = 2
|
||||
local DBG_EVENT_STEP = 3
|
||||
local DBG_EVENT_STEP_INTO_FUNCTION = 4
|
||||
local DBG_EVENT_RESUME = 5
|
||||
local DBG_EVENT_GET_VALUE = 6
|
||||
local __pull_events = debug.__pull_events
|
||||
local __sendvalue = debug.__sendvalue
|
||||
debug.__pull_events = nil
|
||||
debug.__sendvalue = nil
|
||||
|
||||
function debug.pull_events()
|
||||
local events = __pull_events()
|
||||
@ -86,6 +87,16 @@ function debug.pull_events()
|
||||
elseif event[1] == DBG_EVENT_RESUME then
|
||||
dbg_steps_mode = 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
|
||||
@ -107,6 +118,8 @@ function debug.remove_breakpoint(source, line)
|
||||
bps[source] = nil
|
||||
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_size = 0
|
||||
|
||||
|
||||
@ -186,6 +186,27 @@ bool DebuggingServer::performCommand(
|
||||
breakpointEvents.push_back(DebuggingEvent {
|
||||
DebuggingEventType::RESUME, SignalEventDto {}});
|
||||
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 {
|
||||
logger.error() << "unsupported command '" << type << "'";
|
||||
}
|
||||
@ -204,6 +225,30 @@ void DebuggingServer::onHitBreakpoint(dv::value&& stackTrace) {
|
||||
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) {
|
||||
this->connection =
|
||||
std::make_unique<ClientConnection>(engine.getNetwork(), client);
|
||||
|
||||
@ -43,6 +43,7 @@ namespace devtools {
|
||||
STEP,
|
||||
STEP_INTO_FUNCTION,
|
||||
RESUME,
|
||||
GET_VALUE,
|
||||
};
|
||||
|
||||
struct BreakpointEventDto {
|
||||
@ -53,9 +54,17 @@ namespace devtools {
|
||||
struct SignalEventDto {
|
||||
};
|
||||
|
||||
using ValuePath = std::vector<std::variant<std::string, int>>;
|
||||
|
||||
struct GetValueEventDto {
|
||||
int frame;
|
||||
int localIndex;
|
||||
ValuePath path;
|
||||
};
|
||||
|
||||
struct DebuggingEvent {
|
||||
DebuggingEventType type;
|
||||
std::variant<BreakpointEventDto, SignalEventDto> data;
|
||||
std::variant<BreakpointEventDto, SignalEventDto, GetValueEventDto> data;
|
||||
};
|
||||
|
||||
class DebuggingServer {
|
||||
@ -65,6 +74,9 @@ namespace devtools {
|
||||
|
||||
bool update();
|
||||
void onHitBreakpoint(dv::value&& stackTrace);
|
||||
void pause();
|
||||
|
||||
void sendValue(dv::value&& value, int frame, int local, ValuePath&& path);
|
||||
|
||||
void setClient(u64id_t client);
|
||||
std::vector<DebuggingEvent> pullEvents();
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "devtools/DebuggingServer.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
|
||||
using namespace devtools;
|
||||
using namespace scripting;
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
auto locals = dv::object();
|
||||
auto locals = dv::list();
|
||||
|
||||
int localIndex = 1;
|
||||
const char* name;
|
||||
@ -175,68 +220,13 @@ static dv::value collect_locals(lua::State* L, lua_Debug& frame) {
|
||||
continue;
|
||||
}
|
||||
auto local = dv::object();
|
||||
local["name"] = name;
|
||||
local["index"] = localIndex - 1;
|
||||
|
||||
int type = lua::type(L, -1);
|
||||
switch (type) {
|
||||
case LUA_TNIL:
|
||||
local["short"] = "nil";
|
||||
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);
|
||||
local["type"] = lua::type_name(L, type);
|
||||
local["short"] = get_short_value(L, -1, type);
|
||||
locals.add(std::move(local));
|
||||
lua::pop(L);
|
||||
}
|
||||
return locals;
|
||||
@ -279,6 +269,51 @@ static int l_debug_breakpoint(lua::State* L) {
|
||||
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) {
|
||||
auto server = engine->getDebuggingServer();
|
||||
if (!server) {
|
||||
@ -296,12 +331,30 @@ static int l_debug_pull_events(lua::State* L) {
|
||||
lua::pushinteger(L, static_cast<int>(event.type));
|
||||
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::rawseti(L, 2);
|
||||
|
||||
lua::pushinteger(L, dto->line);
|
||||
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);
|
||||
@ -329,6 +382,9 @@ void initialize_libs_extends(lua::State* L) {
|
||||
lua::pushcfunction(L, lua::wrap<l_debug_pull_events>);
|
||||
lua::setfield(L, "__pull_events");
|
||||
|
||||
lua::pushcfunction(L, lua::wrap<l_debug_sendvalue>);
|
||||
lua::setfield(L, "__sendvalue");
|
||||
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getglobal(L, "math")) {
|
||||
|
||||
@ -249,7 +249,7 @@ namespace lua {
|
||||
inline lua::Number tonumber(lua::State* L, int idx) {
|
||||
#ifndef NDEBUG
|
||||
if (lua_type(L, idx) != LUA_TNUMBER && !lua_isnoneornil(L, idx)) {
|
||||
throw std::runtime_error("integer expected");
|
||||
throw std::runtime_error("number expected");
|
||||
}
|
||||
#endif
|
||||
return lua_tonumber(L, idx);
|
||||
|
||||
@ -50,8 +50,8 @@ namespace lua {
|
||||
inline int type(lua::State* L, int idx) {
|
||||
return lua_type(L, idx);
|
||||
}
|
||||
inline const char* type_name(lua::State* L, int idx) {
|
||||
return lua_typename(L, idx);
|
||||
inline const char* type_name(lua::State* L, int tp) {
|
||||
return lua_typename(L, tp);
|
||||
}
|
||||
inline int rawget(lua::State* L, int idx = -2) {
|
||||
lua_rawget(L, idx);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user