• 还原Lua调用栈


    Lua数据类型

    类型 大类型 类型细分 _tt(类型)
    nil #define LUA_TNIL 0

    0
    布尔 #define LUA_TBOOLEAN 1

    int

    1

    number

    #define LUA_TNUMBER 

    double

    3

    int

    19(0x13)

    字符串

    #define LUA_TSTRING 4

    短字符串

    68(0x44)

    长字符串

    84(0x54)

    function

    #define LUA_TFUNCTION 6

    lua_CFunction

    /* typedef int (*lua_CFunction) (lua_State *L); */

    22(0x16)

    CClosure

    102(0x66)

    LClosure

    70(0x46)

    #define LUA_TTABLE 5

    Table

    69(0x45)

    user data

    #define LUA_TLIGHTUSERDATA 2

    void*

    /* 轻量级用户数据(light userdata)*/

    2

    #define LUA_TUSERDATA 7

    Udata

    /* 完全用户数据(full userdata)*/

    71(0x47)
    Lua栈

    #define LUA_TTHREAD 8

    Lua_State

    72(0x48)

    CallInfo结构

    Lua_State结构

    C++中还原lua调用栈

    Lua代码:

    1 -- 
    2 -- This is a test lua
    3 --
    4 function TestFunc(a)
    5     return square(a);
    6 end
    7 
    8 TestFunc(5);

    C++代码:

    当前c++调用栈:

    lua调用栈解析:

    # 表达式 结果 类型
    Frame 1 L->ci->func->tt_ 22  // lua_CFunction类型 int
    L->ci->func->value_.gc 0x00007ff6cd3f0050 {LuaTest.exe!Square(lua_State *)} GCObject *
    ((CClosure*)L->ci->func->value_.gc)->f 0xccccccccb8000000 int(*)(lua_State *)
    (L->ci->func+1)->tt_ 19  // int   为传入的第一个参数 int
    (int*)(&(L->ci->func+1)->value_) 0x0000024417590650 {5} int *
    Frame 2 L->ci->previous->func->tt_ 70  // LClosure int
    (char*)((LClosure*)L->ci->previous->func->value_.gc)->p->source + sizeof(TString) 0x0000024417589628 "@I:\pubcode\LuaTest\LuaCode\Test4.lua" char *
    L->ci->previous->u.l.savedpc -  ((LClosure*) L->ci->previous->func->value_.gc)->p->code 3 __int64
    ((LClosure*) L->ci->previous->func->value_.gc)->p->lineinfo [ 3 -1] 5 int
    Frame 3 L->ci->previous->previous->func->tt_ 70  // LClosure int
    (char*)((LClosure*)L->ci->previous->previous->func->value_.gc)->p->source + sizeof(TString) 0x0000024417589628 "@I:\pubcode\LuaTest\LuaCode\Test4.lua" char *
    L->ci->previous->previous->u.l.savedpc -  ((LClosure*) L->ci->previous->previous->func->value_.gc)->p->code 5 __int64
    ((LClosure*) L->ci->previous->func->value_.gc)->p->lineinfo [ 5 -1] 8 int
    Frame 4 L->ci->previous->previous->previous->func->tt_ 0  // nil int

    使用c++函数来还原,实现如下:

    #include <cstring>
    #include <string>
    #include <vector>
    
    extern "C"
    {
    #include "lua.h"
    #include "lua.hpp"
    #include "lualib.h"
    #include "lauxlib.h"
    #include "luaconf.h"
    #include "lobject.h"
    #include "lstate.h"
    }
    
    const TValue luaO_nilobject_ = { NILCONSTANT };
    
    /* corresponding test */
    #define isvalid(o)    ((o) != luaO_nilobject)
    /* value at a non-valid index */
    #define NONVALIDVALUE        cast(TValue *, luaO_nilobject)
    /* test for pseudo index */
    #define ispseudo(i)        ((i) <= LUA_REGISTRYINDEX)
    
    static TValue* index2addr(lua_State* L, int idx) {
        CallInfo* ci = L->ci;
        if (idx > 0) {
            TValue* o = ci->func + idx;
            api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
            if (o >= L->top) return NONVALIDVALUE;
            else return o;
        }
        else if (!ispseudo(idx)) {  /* negative index */
            api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
            return L->top + idx;
        }
        else if (idx == LUA_REGISTRYINDEX)
            return &G(L)->l_registry;
        else {  /* upvalues */
            idx = LUA_REGISTRYINDEX - idx;
            api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
            if (ttislcf(ci->func))  /* light C function? */
                return NONVALIDVALUE;  /* it has no upvalues */
            else {
                CClosure* func = clCvalue(ci->func);
                return (idx <= func->nupvalues) ? &func->upvalue[idx - 1] : NONVALIDVALUE;
            }
        }
    }
    
    void GetLuaTValue(lua_State* L, StkId o, int t, int ad, int count, char* szOutput)
    {
        int i = count + ad + 1;
        switch (t) {
        case LUA_TSTRING:
            sprintf_s(szOutput, 255, "%d[%d]: '%s'", i, ad, lua_tostring(L, i));
            break;
        case LUA_TBOOLEAN:
            sprintf_s(szOutput, 255, "%d[%d]: %s", i, ad, lua_toboolean(L, i) ? "true" : "false");
            break;
        case LUA_TNUMBER:
            if (ttisinteger(o))
                sprintf_s(szOutput, 255, "%d[%d]: int %d", i, ad, (int)lua_tointeger(L, i));
            else
                sprintf_s(szOutput, 255, "%d[%d]: double %lf", i, ad, lua_tonumber(L, i));
            break;
        case LUA_TFUNCTION:
            if (ttislcf(o)) {
                sprintf_s(szOutput, 255, "%d[%d]: c 0x%p", i, ad, fvalue(o)); // lua_CFunction
            }
            else if (ttisCclosure(o))
            {
                sprintf_s(szOutput, 255, "%d[%d]: c closure 0x%p FucAdr:0x%p", i, ad, clCvalue(o), clCvalue(o)->f); // CClosure
            }
            else if (ttisLclosure(o))
            {
                Proto* pinfo = clLvalue(o)->p;
                sprintf_s(szOutput, 255, "%d[%d]: lua closure 0x%p %s[%d,%d]", i, ad, clLvalue(o), getstr(pinfo->source), pinfo->linedefined, pinfo->lastlinedefined);
            }
            break;
        case LUA_TTABLE:
            sprintf_s(szOutput, 255, "%d[%d]: table:0x%p", i, ad, hvalue(o));
            break;
        case LUA_TLIGHTUSERDATA:
            sprintf_s(szOutput, 255, "%d[%d]: light userdata:0x%p", i, ad, pvalue(o));
            break;
        case LUA_TUSERDATA:
            sprintf_s(szOutput, 255, "%d[%d]: full userdata:0x%p", i, ad, uvalue(o));
            break;
        case LUA_TTHREAD:
            sprintf_s(szOutput, 255, "%d[%d]: thread:0x%p", i, ad, thvalue(o));
            break;
        default: sprintf_s(szOutput, 255, "%d[%d]: %s", i, ad, lua_typename(L, t)); break;
        }
    }
    
    const char* VSDumpLuaStateTValue(lua_State* L, int n)
    {
        static char s_szBuffer[256] = { 0 };
        memset(s_szBuffer, 0, 256);
    
        int ad = -1;
        int count = lua_gettop(L);
        int i = count;
        while (i) {
            if ((n < 0 ? ad : i) == n)
            {
                StkId o = index2addr(L, i);
                int t = (isvalid(o) ? ttnov(o) : LUA_TNONE);
                GetLuaTValue(L, o, t, ad, count, s_szBuffer);
    
                return s_szBuffer;
            }
    
            i--; ad--;
        }
    
        return "Lua Frame not found!";
    }
    
    std::vector<std::string> VSDumpLuaState(lua_State* L)
    {
        std::vector<std::string> Msg;
    
        char szBuffer[256] = { 0 };
        memset(szBuffer, 0, 256);
    
        int ad = -1;
        int count = lua_gettop(L);
        int i = count;
    
        sprintf_s(szBuffer, 255, "----------------  Lua State Dump ----------------");
        Msg.push_back(szBuffer);
        while (i) {
            StkId o = index2addr(L, i);
            int t = (isvalid(o) ? ttnov(o) : LUA_TNONE);
            GetLuaTValue(L, o, t, ad, count, szBuffer);
    
            Msg.push_back(szBuffer);
            i--; ad--;
        }
        sprintf_s(szBuffer, 255, "---------------------------------------------");
        Msg.push_back(szBuffer);
    
        return Msg;
    }
    
    std::vector<std::string> VSDumpLuaCallStack(lua_State* L)
    {
        std::vector<std::string> Msg;
    
        char szBuffer[256] = { 0 };
        memset(szBuffer, 0, 256);
    
        sprintf_s(szBuffer, 255, "----------------  Lua Call Stack Dump ----------------");
        Msg.push_back(szBuffer);
    
        CallInfo* curCi = L->ci;
        while (curCi != NULL)
        {
            StkId func = curCi->func;
    
            if (ttislcf(func)) {
                sprintf_s(szBuffer, 255, "++ CallStack: c 0x%p", fvalue(func)); // lua_CFunction
            }
            else if (ttisCclosure(func))
            {
                sprintf_s(szBuffer, 255, "++ CallStack: c closure 0x%p FucAdr:0x%p", clCvalue(func), clCvalue(func)->f); // CClosure
            }
            else if (ttisLclosure(func))
            {
                Proto* pinfo = clLvalue(func)->p;
                int linenum = pinfo->lineinfo[curCi->u.l.savedpc - pinfo->code];
                sprintf_s(szBuffer, 255, "++ CallStack: lua closure 0x%p %s:%d", clLvalue(func), getstr(pinfo->source), linenum);
            }
            else
            {
                int funct = (isvalid(func) ? ttnov(func) : LUA_TNONE);
                sprintf_s(szBuffer, 255, "++ CallStack: %s", lua_typename(L, funct));
            }
            Msg.push_back(szBuffer);
    
            StkId locals = ((L->top < curCi->top) ? L->top : curCi->top) - 1;
            int ad = -1;
            int count = (int)(locals - func);
            while (locals > func)
            {
                StkId o = locals;
                int t = (isvalid(o) ? ttnov(o) : LUA_TNONE);
                GetLuaTValue(L, o, t, ad, count, szBuffer);
                Msg.push_back(szBuffer);
                --locals;
                --ad;
            }
    
            if (curCi == &(L->base_ci))
            {
                break;
            }
    
            curCi = curCi->previous;
        }
    
    
        sprintf_s(szBuffer, 255, "---------------------------------------------");
        Msg.push_back(szBuffer);
    
        return Msg;
    }

    在C++代码中(在业务逻辑或lua虚拟机里面)调用LuaCall(为以下4个函数),会导致luaD_call函数的调用,其参数StkId func为当前要执行的function(可能为LClosurelua_CFunctionCClosure注:StkIdTValue*类型

    例如:上面c++栈上的第4行luaD_call即为I:\pubcode\LuaTest\LuaCode\Test4.luaLClosure(Lua闭包)

    /*****************   lua.h  *****************/
    LUA_API void  (lua_callk) (lua_State *L, int nargs, int nresults,
                               lua_KContext ctx, lua_KFunction k);
    #define lua_call(L,n,r)        lua_callk(L, (n), (r), 0, NULL)
    
    LUA_API int   (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
                                lua_KContext ctx, lua_KFunction k);
    #define lua_pcall(L,n,r,f)    lua_pcallk(L, (n), (r), (f), 0, NULL)

     调用关系如下:

    lua_pcall() / lua_pcallk() / lua_call() / lua_callk
      |- ... ... 
      |-luaD_call(lua_State *L, StkId func, int nResults)
      |-if (!luaD_precall(L, func, nResults))  /* 返回0表示为LClosure */ 注:① 通过next_ci宏创建新的CallInfo栈帧  ② lua_CFunction或CClosure在该函数中直接执行  ③ LClosure会在该函数中参数不够时进行nil补全,为送入VM执行LClosure做好准备
         |-- luaV_execute(L);  /* 虚拟机执行LClosure */
             |-... ...
             |-newframe: /* jump tag */
             |-... ...
             |-for (;;)
               |-... ...
               |-vmcase(OP_CALL)  /* call指令 */
                    |-if (luaD_precall(L, ra, nresults))
                      |---... ...
                    |-else
                      |---... ...
                      |---goto newframe; 
               |-vmcase(OP_TAILCALL)  /* tailcall指令 */
                    |-if (luaD_precall(L, ra, nresults))
                      |---... ...
                    |-else
                      |---... ...
                      |---goto newframe;
               |-... ...

    Lua中还原lua调用栈

    lua中的使用debug.traceback函数打印当前的lua调用栈

    Lua代码:

     1 -- 
     2 -- This is a test lua
     3 --
     4 function TestFunc(a)
     5     SubFunc(a);
     6 end
     7 
     8 function SubFunc(a)
     9     local b = a + 100
    10     print(debug.traceback());
    11 end
    12 
    13 TestFunc(5);

    输出结果如下:

    stack traceback:
    	I:pubcodeLuaTestLuaCode/test4.lua:10: in function 'SubFunc'
    	I:pubcodeLuaTestLuaCode/test4.lua:5: in function 'TestFunc'
    	I:pubcodeLuaTestLuaCode/test4.lua:13: in main chunk
    	[C]: in ?

    它在c++中的绑定函数为db_tracebackdb_traceback最后调用luaL_traceback函数来实现lua栈的打印

    LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
                                    const char *msg, int level) {
      lua_Debug ar;
      int top = lua_gettop(L);
      int last = lastlevel(L1);
      int n1 = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1;
      if (msg)
        lua_pushfstring(L, "%s
    ", msg);
      luaL_checkstack(L, 10, NULL);
      lua_pushliteral(L, "stack traceback:");
      while (lua_getstack(L1, level++, &ar)) {
        if (n1-- == 0) {  /* too many levels? */
          lua_pushliteral(L, "
    	...");  /* add a '...' */
          level = last - LEVELS2 + 1;  /* and skip to last ones */
        }
        else {
          lua_getinfo(L1, "Slnt", &ar);
          lua_pushfstring(L, "
    	%s:", ar.short_src);
          if (ar.currentline > 0)
            lua_pushfstring(L, "%d:", ar.currentline);
          lua_pushliteral(L, " in ");
          pushfuncname(L, &ar);
          if (ar.istailcall)
            lua_pushliteral(L, "
    	(...tail calls...)");
          lua_concat(L, lua_gettop(L) - top);
        }
      }
      lua_concat(L, lua_gettop(L) - top);
    }

  • 相关阅读:
    mingW与cygwin
    Ruby on Rails 和 J2EE:两者能否共存?
    嵌入式Linux学习笔记(一) 启航、计划和内核模块初步体验
    嵌入式Linux学习笔记(六) 上位机QT界面实现和通讯实现
    嵌入式Linux问题总结(一) Ubuntu常用命令和编译问题解决方法
    嵌入式Linux学习笔记(五) 通讯协议制定和下位机代码实现
    嵌入式Linux学习笔记(四) 设备树和UART驱动开发
    嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发
    嵌入式Linux学习笔记(二) 交叉编译环境和Linux系统编译、下载
    记录嵌入式面试的流程
  • 原文地址:https://www.cnblogs.com/kekec/p/14232897.html
Copyright © 2020-2023  润新知