From 1df5e73f493088b68f24052dcf8f58e9892a23b3 Mon Sep 17 00:00:00 2001 From: mihacooper Date: Sun, 22 Oct 2017 14:08:24 +0300 Subject: [PATCH] Implement stacktrace collecting (#95) --- src/cpp/lua-helpers.cpp | 6 +++--- src/cpp/stored-object.cpp | 2 +- src/cpp/threading.cpp | 44 +++++++++++++++++++++++++++++++-------- src/cpp/threading.h | 8 +++---- tests/lua/thread.lua | 34 ++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/cpp/lua-helpers.cpp b/src/cpp/lua-helpers.cpp index 21a37a9..f1fc19a 100644 --- a/src/cpp/lua-helpers.cpp +++ b/src/cpp/lua-helpers.cpp @@ -11,14 +11,14 @@ std::string luaError(int errCode) { case LUA_ERRRUN: return "Execution error (LUA_ERRRUN)"; case LUA_ERRGCMM: return "Error in __gc method (LUA_ERRGCMM)"; case LUA_ERRERR: return "Recursive error (LUA_ERRERR)"; - default: return "Unknown"; + default: return "Unknown (" + std::to_string(errCode) + ")"; } } int dumpMemoryWriter(lua_State*, const void* batch, size_t batchSize, void* storage) { - if (storage == nullptr || batch == nullptr) + if (storage == nullptr) return 1; - if (batchSize) { + if (batchSize && batch) { std::string& buff = *reinterpret_cast(storage); const char* newData = reinterpret_cast(batch); buff.insert(buff.end(), newData, newData + batchSize); diff --git a/src/cpp/stored-object.cpp b/src/cpp/stored-object.cpp index 6fa790c..3a6810f 100644 --- a/src/cpp/stored-object.cpp +++ b/src/cpp/stored-object.cpp @@ -164,7 +164,7 @@ StoredObject fromSolObject(const SolObject& luaObject) { else if (luaObject.template is()) return std::make_unique>(luaObject); else - throw Exception() << "Unable to store userdata object\n"; + throw Exception() << "Unable to store userdata object"; case sol::type::function: { FunctionObject func = GC::instance().create(luaObject); return std::make_unique(func.handle()); diff --git a/src/cpp/threading.cpp b/src/cpp/threading.cpp index 202c092..367a023 100644 --- a/src/cpp/threading.cpp +++ b/src/cpp/threading.cpp @@ -51,14 +51,26 @@ enum class Command { Pause }; +#if LUA_VERSION_NUM > 501 + +int luaErrorHandler(lua_State* state); +const lua_CFunction luaErrorHandlerPtr = luaErrorHandler; + +#else + +const lua_CFunction luaErrorHandlerPtr = nullptr; + +#endif // LUA_VERSION_NUM > 501 + } // namespace + class ThreadHandle { public: ThreadHandle() : status_(Status::Running) , command_(Command::Run) - , lua_(std::make_unique()) { + , lua_(std::make_unique(luaErrorHandlerPtr)) { luaL_openlibs(*lua_); } @@ -141,6 +153,17 @@ const sol::optional NO_TIMEOUT; static thread_local ThreadHandle* thisThreadHandle = nullptr; +#if LUA_VERSION_NUM > 501 + +int luaErrorHandler(lua_State* state) { + luaL_traceback(state, state, nullptr, 1); + const auto stacktrace = sol::stack::pop(state); + thisThreadHandle->result().emplace_back(createStoredObject(stacktrace)); + throw Exception() << sol::stack::pop(state); +} + +#endif // LUA_VERSION_NUM > 501 + void luaHook(lua_State*, lua_Debug*) { assert(thisThreadHandle); switch (thisThreadHandle->command()) { @@ -198,7 +221,10 @@ void Thread::runThread(Thread thread, thread.handle_->changeStatus(Status::Canceled); } catch (const sol::error& err) { DEBUG << "Failed with msg: " << err.what() << std::endl; - thread.handle_->result().emplace_back(createStoredObject(err.what())); + auto& returns = thread.handle_->result(); + returns.insert(returns.begin(), + { createStoredObject("failed"), + createStoredObject(err.what()) }); thread.handle_->changeStatus(Status::Failed); } } @@ -279,14 +305,14 @@ void Thread::exportAPI(sol::state_view& lua) { sol::stack::pop(lua); } -std::pair Thread::status(const sol::this_state& lua) { - sol::object luaStatus = sol::make_object(lua, statusToString(handle_->status())); - - if (handle_->status() == Status::Failed) { +StoredArray Thread::status(const sol::this_state& lua) { + const auto stat = handle_->status(); + if (stat == Status::Failed) { assert(!handle_->result().empty()); - return std::make_pair(luaStatus, handle_->result()[0]->unpack(lua)); + return handle_->result(); } else { - return std::make_pair(luaStatus, sol::nil); + const sol::object luaStatus = sol::make_object(lua, statusToString(stat)); + return StoredArray({createStoredObject(luaStatus)}); } } @@ -298,7 +324,7 @@ sol::optional toOptionalTime(const sol::optional return sol::optional(); } -std::pair Thread::wait(const sol::this_state& lua, +StoredArray Thread::wait(const sol::this_state& lua, const sol::optional& duration, const sol::optional& period) { handle_->waitForCompletion(toOptionalTime(duration, period)); diff --git a/src/cpp/threading.h b/src/cpp/threading.h index a55cc19..f221c48 100644 --- a/src/cpp/threading.h +++ b/src/cpp/threading.h @@ -23,10 +23,10 @@ public: static void exportAPI(sol::state_view& lua); - std::pair status(const sol::this_state& state); - std::pair wait(const sol::this_state& state, - const sol::optional& duration, - const sol::optional& period); + StoredArray status(const sol::this_state& state); + StoredArray wait(const sol::this_state& state, + const sol::optional& duration, + const sol::optional& period); StoredArray get(const sol::optional& duration, const sol::optional& period); bool cancel(const sol::this_state& state, diff --git a/tests/lua/thread.lua b/tests/lua/thread.lua index 5e33ddd..ba7ca29 100644 --- a/tests/lua/thread.lua +++ b/tests/lua/thread.lua @@ -370,3 +370,37 @@ test.this_thread.pause_on_finished_thread = function () test.is_true(worker_thread:get(2, "s")) test.is_true(effil.thread(call_pause)(worker_thread):get(5, "s")) end + +if LUA_VERSION > 51 then + +test.thread.traceback = function() + local curr_file = debug.getinfo(1,'S').short_src + + local function foo() + function boom() + error("err msg") + end + function bar() + boom() + end + bar() + end + + local status, err, trace = effil.thread(foo)():wait() + print("status: ", status) + print("error: ", err) + print("stacktrace: ", trace) + + test.equal(status, "failed") + -- .lua:: + test.is_not_nil(string.find(err, curr_file .. ":%d+: err msg")) + test.is_not_nil(string.find(trace, ( +[[stack traceback: +%%s%s:%%d+: in function 'boom' +%%s%s:%%d+: in function 'bar' +%%s%s:%%d+: in function <%s:%%d+>]] + ):format(curr_file, curr_file, curr_file, curr_file) + )) +end + +end -- LUA_VERSION > 51 \ No newline at end of file