Implement stacktrace collecting (#95)
This commit is contained in:
parent
e84fbb32f4
commit
1df5e73f49
@ -11,14 +11,14 @@ std::string luaError(int errCode) {
|
|||||||
case LUA_ERRRUN: return "Execution error (LUA_ERRRUN)";
|
case LUA_ERRRUN: return "Execution error (LUA_ERRRUN)";
|
||||||
case LUA_ERRGCMM: return "Error in __gc method (LUA_ERRGCMM)";
|
case LUA_ERRGCMM: return "Error in __gc method (LUA_ERRGCMM)";
|
||||||
case LUA_ERRERR: return "Recursive error (LUA_ERRERR)";
|
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) {
|
int dumpMemoryWriter(lua_State*, const void* batch, size_t batchSize, void* storage) {
|
||||||
if (storage == nullptr || batch == nullptr)
|
if (storage == nullptr)
|
||||||
return 1;
|
return 1;
|
||||||
if (batchSize) {
|
if (batchSize && batch) {
|
||||||
std::string& buff = *reinterpret_cast<std::string*>(storage);
|
std::string& buff = *reinterpret_cast<std::string*>(storage);
|
||||||
const char* newData = reinterpret_cast<const char*>(batch);
|
const char* newData = reinterpret_cast<const char*>(batch);
|
||||||
buff.insert(buff.end(), newData, newData + batchSize);
|
buff.insert(buff.end(), newData, newData + batchSize);
|
||||||
|
|||||||
@ -164,7 +164,7 @@ StoredObject fromSolObject(const SolObject& luaObject) {
|
|||||||
else if (luaObject.template is<Thread>())
|
else if (luaObject.template is<Thread>())
|
||||||
return std::make_unique<GCObjectHolder<Thread>>(luaObject);
|
return std::make_unique<GCObjectHolder<Thread>>(luaObject);
|
||||||
else
|
else
|
||||||
throw Exception() << "Unable to store userdata object\n";
|
throw Exception() << "Unable to store userdata object";
|
||||||
case sol::type::function: {
|
case sol::type::function: {
|
||||||
FunctionObject func = GC::instance().create<FunctionObject>(luaObject);
|
FunctionObject func = GC::instance().create<FunctionObject>(luaObject);
|
||||||
return std::make_unique<FunctionHolder>(func.handle());
|
return std::make_unique<FunctionHolder>(func.handle());
|
||||||
|
|||||||
@ -51,14 +51,26 @@ enum class Command {
|
|||||||
Pause
|
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
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
class ThreadHandle {
|
class ThreadHandle {
|
||||||
public:
|
public:
|
||||||
ThreadHandle()
|
ThreadHandle()
|
||||||
: status_(Status::Running)
|
: status_(Status::Running)
|
||||||
, command_(Command::Run)
|
, command_(Command::Run)
|
||||||
, lua_(std::make_unique<sol::state>()) {
|
, lua_(std::make_unique<sol::state>(luaErrorHandlerPtr)) {
|
||||||
luaL_openlibs(*lua_);
|
luaL_openlibs(*lua_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +153,17 @@ const sol::optional<std::chrono::milliseconds> NO_TIMEOUT;
|
|||||||
|
|
||||||
static thread_local ThreadHandle* thisThreadHandle = nullptr;
|
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<std::string>(state);
|
||||||
|
thisThreadHandle->result().emplace_back(createStoredObject(stacktrace));
|
||||||
|
throw Exception() << sol::stack::pop<std::string>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LUA_VERSION_NUM > 501
|
||||||
|
|
||||||
void luaHook(lua_State*, lua_Debug*) {
|
void luaHook(lua_State*, lua_Debug*) {
|
||||||
assert(thisThreadHandle);
|
assert(thisThreadHandle);
|
||||||
switch (thisThreadHandle->command()) {
|
switch (thisThreadHandle->command()) {
|
||||||
@ -198,7 +221,10 @@ void Thread::runThread(Thread thread,
|
|||||||
thread.handle_->changeStatus(Status::Canceled);
|
thread.handle_->changeStatus(Status::Canceled);
|
||||||
} catch (const sol::error& err) {
|
} catch (const sol::error& err) {
|
||||||
DEBUG << "Failed with msg: " << err.what() << std::endl;
|
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);
|
thread.handle_->changeStatus(Status::Failed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,14 +305,14 @@ void Thread::exportAPI(sol::state_view& lua) {
|
|||||||
sol::stack::pop<sol::object>(lua);
|
sol::stack::pop<sol::object>(lua);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<sol::object, sol::object> Thread::status(const sol::this_state& lua) {
|
StoredArray Thread::status(const sol::this_state& lua) {
|
||||||
sol::object luaStatus = sol::make_object(lua, statusToString(handle_->status()));
|
const auto stat = handle_->status();
|
||||||
|
if (stat == Status::Failed) {
|
||||||
if (handle_->status() == Status::Failed) {
|
|
||||||
assert(!handle_->result().empty());
|
assert(!handle_->result().empty());
|
||||||
return std::make_pair(luaStatus, handle_->result()[0]->unpack(lua));
|
return handle_->result();
|
||||||
} else {
|
} 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<std::chrono::milliseconds> toOptionalTime(const sol::optional<int>
|
|||||||
return sol::optional<std::chrono::milliseconds>();
|
return sol::optional<std::chrono::milliseconds>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<sol::object, sol::object> Thread::wait(const sol::this_state& lua,
|
StoredArray Thread::wait(const sol::this_state& lua,
|
||||||
const sol::optional<int>& duration,
|
const sol::optional<int>& duration,
|
||||||
const sol::optional<std::string>& period) {
|
const sol::optional<std::string>& period) {
|
||||||
handle_->waitForCompletion(toOptionalTime(duration, period));
|
handle_->waitForCompletion(toOptionalTime(duration, period));
|
||||||
|
|||||||
@ -23,10 +23,10 @@ public:
|
|||||||
|
|
||||||
static void exportAPI(sol::state_view& lua);
|
static void exportAPI(sol::state_view& lua);
|
||||||
|
|
||||||
std::pair<sol::object, sol::object> status(const sol::this_state& state);
|
StoredArray status(const sol::this_state& state);
|
||||||
std::pair<sol::object, sol::object> wait(const sol::this_state& state,
|
StoredArray wait(const sol::this_state& state,
|
||||||
const sol::optional<int>& duration,
|
const sol::optional<int>& duration,
|
||||||
const sol::optional<std::string>& period);
|
const sol::optional<std::string>& period);
|
||||||
StoredArray get(const sol::optional<int>& duration,
|
StoredArray get(const sol::optional<int>& duration,
|
||||||
const sol::optional<std::string>& period);
|
const sol::optional<std::string>& period);
|
||||||
bool cancel(const sol::this_state& state,
|
bool cancel(const sol::this_state& state,
|
||||||
|
|||||||
@ -370,3 +370,37 @@ test.this_thread.pause_on_finished_thread = function ()
|
|||||||
test.is_true(worker_thread:get(2, "s"))
|
test.is_true(worker_thread:get(2, "s"))
|
||||||
test.is_true(effil.thread(call_pause)(worker_thread):get(5, "s"))
|
test.is_true(effil.thread(call_pause)(worker_thread):get(5, "s"))
|
||||||
end
|
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")
|
||||||
|
-- <souce file>.lua:<string number>: <error message>
|
||||||
|
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
|
||||||
Loading…
x
Reference in New Issue
Block a user