diff --git a/src/devtools/DebuggingServer.cpp b/src/devtools/DebuggingServer.cpp index 86e2bdae..26a2ecfa 100644 --- a/src/devtools/DebuggingServer.cpp +++ b/src/devtools/DebuggingServer.cpp @@ -165,6 +165,11 @@ bool DebuggingServer::performCommand( return true; } +void DebuggingServer::onHitBreakpoint(dv::value&& stackTrace) { + logger.info() << "hit breakpoint:\n" + << json::stringify(stackTrace, true, " "); +} + void DebuggingServer::setClient(u64id_t client) { this->connection = std::make_unique(engine.getNetwork(), client); diff --git a/src/devtools/DebuggingServer.hpp b/src/devtools/DebuggingServer.hpp index b8b103b8..b379c3e3 100644 --- a/src/devtools/DebuggingServer.hpp +++ b/src/devtools/DebuggingServer.hpp @@ -41,6 +41,7 @@ namespace devtools { ~DebuggingServer(); bool update(); + void onHitBreakpoint(dv::value&& stackTrace); void setClient(u64id_t client); private: diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index b0f0918d..6d04da5c 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -185,4 +185,8 @@ public: const Project& getProject() { return *project; } + + devtools::DebuggingServer* getDebuggingServer() { + return debuggingServer.get(); + } }; diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index b9c25fc6..74be96de 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -1,9 +1,12 @@ #include #include #include +#include #include "libs/api_lua.hpp" #include "debug/Logger.hpp" +#include "engine/Engine.hpp" +#include "devtools/DebuggingServer.hpp" #include "logic/scripting/scripting.hpp" using namespace scripting; @@ -159,6 +162,120 @@ static int l_math_normal_random(lua::State* L) { return lua::pushnumber(L, randomFloats(generator)); } +constexpr inline int MAX_SHORT_STRING_LEN = 50; + +static dv::value create_stack_trace(lua_State* L, int initFrame = 2) { + auto entriesList = dv::list(); + + lua_Debug frame; + int level = initFrame; + + while (lua_getstack(L, level, &frame)) { + auto entry = dv::object(); + if (lua_getinfo(L, "nSlf", &frame) == 0) { + level++; + entriesList.add(std::move(entry)); + continue; + } + if (frame.name) { + entry["function"] = frame.name; + } + if (frame.source) { + const char* src = + (frame.source[0] == '@') ? frame.source + 1 : frame.source; + entry["source"] = src; + entry["line"] = frame.currentline; + } + entry["what"] = frame.what; + + auto locals = dv::object(); + + int localIndex = 1; + const char* name; + while ((name = lua_getlocal(L, &frame, localIndex++))) { + if (name[0] == '(') { + lua::pop(L); + continue; + } + auto local = dv::object(); + + 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); + lua::pop(L); + } + entry["locals"] = std::move(locals); + entriesList.add(std::move(entry)); + level++; + } + return entriesList; +} + +static int l_debug_breakpoint(lua::State* L) { + if (auto server = engine->getDebuggingServer()) { + server->onHitBreakpoint(create_stack_trace(L)); + } + return 0; +} + void initialize_libs_extends(lua::State* L) { if (lua::getglobal(L, "debug")) { lua::pushcfunction(L, lua::wrap); @@ -173,6 +290,9 @@ void initialize_libs_extends(lua::State* L) { lua::pushcfunction(L, lua::wrap); lua::setfield(L, "print"); + lua::pushcfunction(L, lua::wrap); + lua::setfield(L, "breakpoint"); + lua::pop(L); } if (lua::getglobal(L, "math")) {