Merge remote-tracking branch 'myk002/myk_enable_luacov' into develop

develop
lethosor 2021-03-28 00:38:36 -04:00
commit 2332009825
No known key found for this signature in database
GPG Key ID: 76A269552F4F58C1
4 changed files with 82 additions and 4 deletions

@ -535,6 +535,10 @@ on UNIX-like systems:
- ``DFHACK_LOG_MEM_RANGES`` (macOS only): if set, logs memory ranges to - ``DFHACK_LOG_MEM_RANGES`` (macOS only): if set, logs memory ranges to
``stderr.log``. Note that `devel/lsmem` can also do this. ``stderr.log``. Note that `devel/lsmem` can also do this.
- ``DFHACK_ENABLE_LUACOV``: if set, enables coverage analysis of Lua scripts.
Use the `devel/luacov` script to generate coverage reports from the collected
metrics.
Other (non-DFHack-specific) variables that affect DFHack: Other (non-DFHack-specific) variables that affect DFHack:
- ``TERM``: if this is set to ``dumb`` or ``cons25`` on \*nix, the console will - ``TERM``: if this is set to ``dumb`` or ``cons25`` on \*nix, the console will

@ -1186,7 +1186,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
force = true; force = true;
} }
if (!Lua::Interrupt(force)) if (!Lua::Interrupt(force))
con.printerr("Failed to register hook - use 'kill-lua force' to force\n"); {
con.printerr(
"Failed to register hook. This can happen if you have"
" lua profiling or coverage monitoring enabled. Use"
" 'kill-lua force' to force, but this may disable"
" profiling and coverage monitoring.\n");
}
} }
else if (builtin == "script") else if (builtin == "script")
{ {

@ -554,6 +554,17 @@ static int dfhack_error(lua_State *L)
return lua_error(L); return lua_error(L);
} }
// replaces os.exit with a thrown exception
static int dfhack_exit_error(lua_State *L)
{
int exit_code = luaL_checkint(L, 1);
lua_pop(L, 1);
lua_pushfstring(L,
"prevented execution of os.exit(%d); please use error()"
" or qerror() instead.", exit_code);
return dfhack_error(L);
}
static int dfhack_exception_tostring(lua_State *L) static int dfhack_exception_tostring(lua_State *L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
@ -775,7 +786,22 @@ static int dfhack_cowrap (lua_State *L) {
return 1; return 1;
} }
static void add_luacov_coroutine_wrapper(lua_State *L)
{
color_ostream *out = Lua::GetOutput(L);
if (!Lua::PushModulePublic(*out, L, "luacov_helper", "with_luacov"))
{
out->printerr("Failed to load luacov_helper.with_luacov.\n");
return;
}
lua_swap(L);
if (!Lua::SafeCall(*out, L, 1, 1))
out->printerr("Failed to set luacov monitoring wrapper.\n");
}
lua_State *DFHack::Lua::NewCoroutine(lua_State *L) { lua_State *DFHack::Lua::NewCoroutine(lua_State *L) {
if (Lua::IsCoreContext(L) && getenv("DFHACK_ENABLE_LUACOV"))
add_luacov_coroutine_wrapper(L);
lua_State *NL = lua_newthread(L); lua_State *NL = lua_newthread(L);
lua_swap(L); lua_swap(L);
lua_xmove(L, NL, 1); /* move function from L to NL */ lua_xmove(L, NL, 1); /* move function from L to NL */
@ -1328,6 +1354,11 @@ static const luaL_Reg dfhack_coro_funcs[] = {
{ NULL, NULL } { NULL, NULL }
}; };
static const luaL_Reg dfhack_os_funcs[] = {
{ "exit", dfhack_exit_error },
{ NULL, NULL }
};
/************ /************
* Events * * Events *
************/ ************/
@ -1600,7 +1631,7 @@ void DFHack::Lua::Notification::bind(lua_State *state, const char *name)
void OpenDFHackApi(lua_State *state); void OpenDFHackApi(lua_State *state);
namespace DFHack { namespace Lua { namespace Core { namespace DFHack { namespace Lua { namespace Core {
static void InitCoreContext(); static void InitCoreContext(color_ostream &);
}}} }}}
lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
@ -1701,6 +1732,11 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
luaL_setfuncs(state, dfhack_coro_funcs, 0); luaL_setfuncs(state, dfhack_coro_funcs, 0);
lua_pop(state, 1); lua_pop(state, 1);
// replace some os functions
lua_getglobal(state, "os");
luaL_setfuncs(state, dfhack_os_funcs, 0);
lua_pop(state, 1);
// split the global environment // split the global environment
lua_newtable(state); lua_newtable(state);
lua_newtable(state); lua_newtable(state);
@ -1714,7 +1750,7 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
// Init core-context specific stuff before loading dfhack.lua // Init core-context specific stuff before loading dfhack.lua
if (IsCoreContext(state)) if (IsCoreContext(state))
Lua::Core::InitCoreContext(); Lua::Core::InitCoreContext(out);
// load dfhack.lua // load dfhack.lua
if (!Require(out, state, "dfhack")) if (!Require(out, state, "dfhack"))
@ -1901,7 +1937,7 @@ bool DFHack::Lua::Core::Init(color_ostream &out)
return (Lua::Open(out, State) != NULL); return (Lua::Open(out, State) != NULL);
} }
static void Lua::Core::InitCoreContext() static void Lua::Core::InitCoreContext(color_ostream &out)
{ {
lua_newtable(State); lua_newtable(State);
lua_rawsetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); lua_rawsetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
@ -1918,6 +1954,23 @@ static void Lua::Core::InitCoreContext()
lua_setfield(State, -2, "timeout_active"); lua_setfield(State, -2, "timeout_active");
lua_pop(State, 1); lua_pop(State, 1);
if (getenv("DFHACK_ENABLE_LUACOV"))
{
// reads config from .luacov or uses defaults if file doesn't exist.
// note that luacov overrides the debug hook installed by
// interrupt_init() above.
if (Lua::PushModulePublic(out, State, "luacov.runner", "init") &&
Lua::SafeCall(out, State, 0, 0))
{
out.print("Initialized luacov coverage monitoring\n");
}
else
{
out.printerr("Failed to initialize luacov coverage monitoring\n");
// non-fatal error
}
}
} }
void DFHack::Lua::Core::Reset(color_ostream &out, const char *where) void DFHack::Lua::Core::Reset(color_ostream &out, const char *where)

@ -0,0 +1,15 @@
-- Luacov helper functions. Note that this is not a dfhack module since it can't
-- depend on dfhack.lua.
local runner = require('luacov.runner')
-- Called by LuaTools.cpp to set the debug hook for new threads. We could do
-- this in C++, but that's complicated and scary.
function with_luacov(f)
return function(...)
debug.sethook(runner.debug_hook, "l")
return f(...)
end
end
return _ENV