2016年7月19日 星期二

使用 Valgrind 偵測 Lua 陷阱

這個只有 40 行左右的小程式(luatest.cpp)看得出哪裡有問題嗎?


#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <vector>

static int Foo(lua_State *state);

static const luaL_Reg LuaMyLib[] = {
    {"Foo", Foo}
};

int main()
{
    lua_State *state = luaL_newstate();
    for(int i = 0; i < sizeof(LuaMyLib)/sizeof(LuaMyLib[0]); ++i)
        lua_register(state, LuaMyLib[i].name, LuaMyLib[i].func);
    const char *script = "Foo()";
    if(0 != luaL_loadbuffer(state, script, strlen(script), 0))
    {
        printf("%s\n", luaL_checkstring(state, -1));
        lua_close(state);
        return 0;
    }
    
    if (0 != lua_pcall(state, 0, LUA_MULTRET, 0))
        printf("%s\n", luaL_checkstring(state, -1));
    
    lua_close(state);
    return 0;
}

int Foo(lua_State *L)
{
    std::vector<char> buf;
    buf.resize(1000);
    //do something
    //...
    //shit happen!
    luaL_error(L, "Foo() error!");
    return 0;
}

廢話不多說,編譯後馬上用 valgrind 檢查...


疑!?在 luatest.cpp:35 出現了 memory leak?WTF!?這是為什麼呢?

因為呼叫 luaL_error() 處理錯誤時,Lua 使用 longjmp 產生類似 C++ exception 的效果脫離 VM,但 longjmp 並不會遵從 C++ stack unwinding 正確處理物件解構,也就是 luatest.cpp:34 產生的 vector 在離開 Foo() 時沒有呼叫 vector::~vector(),於是就遺失了 1000bytes!

要解決這個問題,用 C++ 的方式編譯 Lua 即可(把所有的 .c 改名為 .cpp),也由此可知 OS 幫我們安裝的 Lua lib 是 C 版本。

如果這個程式是放在 server side 整天不停執行,可預見的是在一段時間後一定會因為記憶體被消光導致系統崩潰,由此可見使用 valgrind 定期健檢的重要性!

沒有留言:

張貼留言