Featured image of post Lua traceback 打印变量

Lua traceback 打印变量

通过修改 Lua 源码中 lauxlib.c:luaL_traceback 的实现, 在 Lua traceback 中加上函数参数和局部变量的打印。

在通过打印查 bug,和修复其他环境(其他同学上报以及线上环境)的 tb 时,可以有更多的信息进行问题的查证。

1 效果

  1. 测试文件 test.lua:
Lua
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
local function func1(arg1, arg2, ...)
    local local_str = "local1"
    local local_int = 1
    local local_tab = {1, 2, 3}
    print(debug.traceback())
end

local function func2()
    func1("arg1", 2, "arg3", 4, {5, 6, 7})
end

func2()
  1. 测试效果:
Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
~$ ./lua test.lua
stack traceback:
    ../Lua/test.lua:6: in upvalue 'func1'
        => arg arg1 [string] = arg1
        => arg arg2 [number] = 2
        => (vararg) [string] = arg3
        => (vararg) [number] = 4
        => (vararg) [table] = table: 0x563dba3f8a30
        => local local_str [string] = local1
        => local local_int [number] = 1
        => local local_tab [table] = table: 0x563dba3f79a0
    ../Lua/test.lua:10: in local 'func2'
    ../Lua/test.lua:13: in main chunk
        => local func1 [function] = function: 0x563dba3f8970
        => local func2 [function] = function: 0x563dba3f9180
    [C]: in ?

2 实现方式

实现思路参考自知乎文章——《Lua:一个实用的 traceback 函数》1

适用版本:lua-5.4.3,其他版本可能有所小改动。

  1. 在 lauxlib.c 中新增 _parse_variablesparse_func_variables 函数,用于打印当前调用栈的变量。
C
 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
static void _parse_variables (lua_State *L, lua_Debug *ar, luaL_Buffer *B,
                              int start, int step, int count,
                              int need_anonymous_var, const char *fmt) {
  const char *var_name, *type_name, *value_str;
  while (count-- > 0 && (var_name = lua_getlocal(L, ar, start)) != NULL) {
    type_name = luaL_typename(L, -1);
    value_str = luaL_tolstring(L, -1, NULL);
    lua_pop(L, 1);
    if (need_anonymous_var > 0 || var_name[0] != '(') {
      lua_pushfstring(B->L, fmt, var_name, type_name, value_str);
      luaL_addvalue(B);
    }
    start += step;
  }
}

static void parse_func_variables (lua_State *L, lua_State *L1, lua_Debug ar) {
  luaL_Buffer b;
  luaL_buffinit(L, &b);
  lua_getinfo(L1, "u", &ar);
  // func args
  _parse_variables(L1, &ar, &b, 1, 1, ar.nparams, 0, "\n\t    => arg %s [%s] = %s");
  // func varargs
  _parse_variables(L1, &ar, &b, -1, -1, INT_MAX, 1, "\n\t    => %s [%s] = %s");
  // func locals
  _parse_variables(L1, &ar, &b, ar.nparams + 1, 1, INT_MAX, 0, "\n\t    => local %s [%s] = %s");
  luaL_pushresult(&b);
}
  1. 修改 lauxlib.c 的 luaL_traceback​ 实现,加上 parse_func_variables​ 函数的调用。
Diff
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    else {
      lua_getinfo(L1, "Slnt", &ar);
      if (ar.currentline <= 0)
        lua_pushfstring(L, "\n\t%s: in ", ar.short_src);
      else
        lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline);
      luaL_addvalue(&b);
      pushfuncname(L, &ar);
      luaL_addvalue(&b);
+     parse_func_variables(L, L1, ar);
+     luaL_addvalue(&b);
      if (ar.istailcall)
        luaL_addstring(&b, "\n\t(...tail calls...)");
    }
Licensed under CC BY-NC-SA 4.0
最后更新于 2024-10-05T12:22:53 +0800
本博客已稳定运行
发表了 2 篇文章 · 总计 0.92K 字
总访问量 0 次 · 总访客数 0