单线程lua程序如何检测死循环

lua底层没有提供检查死循环的接口, 在不影响性能的情况下, 比较可行的方法是检测脚本执行时间, 超过一定的时限, 就认为存在死循环
多线程下, 可以在其他线程中监测是否存在执行时间异常的脚本, 单线程下就会麻烦一点
这里提供一种单线程下的死循环检查方法, 通过lua_sethooksignal来解决死循环问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static void lstop (lua_State * L, lua_Debug * ar) {
(void)ar; /* unused arg. */
lua_sethook(L, NULL, 0, 0); /* reset hook */
// 抛出一个错误
luaL_error(L, "There may be an endless loop !");
}
static void laction (int i) {
signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
/*
设置钩子函数, 当调用一个函数时, 从一个函数中返回时, 执行指定数量的指令时
就会执行一次钩子函数 lstop
*/
lua_sethook(L, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}
void run(const char * path) {
L = luaL_newstate();
luaL_openlibs(L);
luaL_checkversion(L);

int result = luaL_loadfile(L, path);
if (result != 0) {
const char * error = lua_tostring(L, -1);
_EXIT_("Load lua file [%s] failed, error : %s\n", path, error);
}

alarm(5); /*设置闹钟 一个进程只能有一个闹钟时间*/
signal(SIGALRM, laction); /*闹钟触发后, 调用laction*/
result = lua_pcall(L, 0, 0, 0);
alarm(0); /*取消闹钟*/
signal(SIGALRM, SIG_DFL); /*SIGALRM恢复为默认信号处理程序*/

if (result != 0) {
const char * error = lua_tostring(L, -1);
_EXIT_("Call lua failed, error : %s\n", error);
}

_EXIT_("Exit the lua VM\n");
}
1
2
3
4
5
6
function main()
local loop = true
while loop do
end
end
main()

启动测试, 5s后可以看到死循环被中断了

1
Call lua failed, error : script.lua:6: There may be an endless loop !