Lua的debug.traceback

新的traceback:

local function toStringEx(v , len)
    if len == nil then
        len = 0;
    end
    local pre = string.rep('\t',len);
    local ret = "";
    if type(v) == "table" then
        if len > 5 then
            return "\t{ ... }";
        end
        local t = "";
        for k,v1 in pairs(v) do
            t = t .. "\n\t" .. pre .. tostring(k) .. ":";
            t = t .. tostring(v1 , len + 1);
        end
        if t == "" then
            ret = ret .. pre .. "{ }\t{" .. tostring(v) .. "}";
        else
            if  len > 0 then
                ret = ret .. "\t{" .. tostring(v) .. "}\n";
            end
            ret = ret .. pre .. "{" .. t .. "\n" .. pre .."}";
        end
    else
        ret = ret .. pre .. tostring(v) .. "\t{" .. type(v) .. "}";
    end
    return ret;
end

local function tracebackex(baseLevel)
    local ret = "";
    local level = baseLevel;
    ret = ret .. "stack traceback:\n";
    while true do
        local info = debug.getinfo(level,"Sln");
        if not info then
            break ;
        end
        if info.what == "C" then
            ret = ret .. tostring(level - baseLevel + 1) .. "\tC function " ..info.name .. " \n";
        else
            ret = ret .. tostring(level - baseLevel + 1) .. string.format("\t[string \"%s\"]:%d in function %s \n",info.source,info.currentline,info.name or info.what);
        end

        --        local i =  1;
        --        while true do
        --            local name , value = debug.getlocal(level,i);
        --            if not name then
        --                break;
        --            end
        --            ret = ret .. "\t\t" .. name .. " =\t" .. toStringEx(value , 3) .. "\n";
        --            i = i + 1;
        --        end
        level = level + 1;
    end
    return ret;
end


分析:

Debug库中的一个重要的思想是栈级别(stack level)。一个栈级别就是一个指向在当前时刻正在活动的特殊函数的数字,也就是说,这个函数正在被调用但还没有返回。调用debug库的函数级别为1,调用他(他指调用debug库的函数)的函数级别为2,以此类推。
23.1 自省(Introspective)
在debug库中主要的自省函数是debug.getinfo。他的第一个参数可以是一个函数或者栈级别。对于函数foo调用debug.getinfo(foo),将返回关于这个函数信息的一个表。这个表有下列一些域:
ü source,标明函数被定义的地方。如果函数在一个字符串内被定义(通过loadstring),source就是那个字符串。如果函数在一个文件中定义,source是@加上文件名。
ü short_src,source的简短版本(最多60个字符),记录一些有用的错误信息。
ü linedefined,source中函数被定义之处的行号。
ü what,标明函数类型。如果foo是一个普通得Lua函数,结果为 "Lua";如果是一个C函数,结果为 "C";如果是一个Lua的主chunk,结果为 "main"。
ü name,函数的合理名称。
ü namewhat,上一个字段代表的含义。这个字段的取值可能为:W"global"、"local"、"method"、"field",或者 ""(空字符串)。空字符串意味着Lua没有找到这个函数名。
ü nups,函数的upvalues的个数。
ü func,函数本身;详细情况看后面。
当foo是一个C函数的时候,Lua无法知道很多相关的信息,所以对这种函数,只有what、name、namewhat这几个域的值可用。
以数字 n调用debug.getinfo(n)时,返回在n级栈的活动函数的信息数据。比如,如果n=1,返回的是正在进行调用的那个函数的信息。(n=0表示C函数getinfo本身)如果n比栈中活动函数的个数大的话,debug.getinfo返回nil。当你使用数字n调用debug.getinfo查询活动函数的信息的时候,返回的结果table中有一个额外的域:currentline,即在那个时刻函数所在的行号。另外,func表示指定n级的活动函数。
字段名的写法有些技巧。记住:因为在Lua中函数是第一类值,所以一个函数可能有多个函数名。查找指定值的函数的时候,Lua会首先在全局变量中查找,如果没找到才会到调用这个函数的代码中看它是如何被调用的。后面这种情况只有在我们使用数字调用getinfo的时候才会起作用,也就是这个时候我们能够获取调用相关的详细信息。
函数getinfo 的效率并不高。Lua以不消弱程序执行的方式保存debug信息(Lua keeps debug information in a form that does not impair program execution),效率被放在第二位。为了获取比较好地执行性能,getinfo可选的第二个参数可以用来指定选取哪些信息。指定了这个参数之后,程序不会浪费时间去收集那些用户不关心的信息。这个参数的格式是一个字符串,每一个字母代表一种类型的信息,可用的字母的含义如下:
'n'


selects fields name and namewhat
'f'


selects field func
'S'


selects fields source, short_src, what, and linedefined
'l'


selects field currentline
'u'


selects field nup
下面的函数阐明了debug.getinfo的使用,函数打印一个活动栈的原始跟踪信息(traceback):
function traceback ()
local level = 1
while true do
local info = debug.getinfo(level, "Sl")
if not info then break end
if info.what == "C" then -- is a C function?
print(level, "C function")
else -- a Lua function
print(string.format("[%s]:%d",
info.short_src, info.currentline))
end
level = level + 1
end
end
不难改进这个函数,使得getinfo获取更多的数据,实际上debug库提供了一个改善的版本debug.traceback,与我们上面的函数不同的是,debug.traceback并不打印结果,而是返回一个字符串。
23.1.1访问局部变量
调用debug库的getlocal函数可以访问任何活动状态的局部变量。这个函数由两个参数:将要查询的函数的栈级别和变量的索引。函数有两个返回值:变量名和变量当前值。如果指定的变量的索引大于活动变量个数,getlocal返回nil。如果指定的栈级别无效,函数会抛出错误。(你可以使用debug.getinfo检查栈级别的有效性)
Lua对函数中所出现的所有局部变量依次计数,只有在当前函数的范围内是有效的局部变量才会被计数。比如,下面的代码
function foo (a,b)
local x
do local c = a - b end
local a = 1
while true do
local name, value = debug.getlocal(1, a)
if not name then break end
print(name, value)
a = a + 1
end
end


foo(10, 20)
结果为:
a 10
b 20
x nil
a 4
索引为1的变量是a,2是b,3是x,4是另一个a。在getlocal被调用的那一点,c已经超出了范围,name和value都不在范围内。(记住:局部变量仅仅在他们被初始化之后才可见)也可以使用debug.setlocal修改一个局部变量的值,他的前两个参数是栈级别和变量索引,第三个参数是变量的新值。这个函数返回一个变量名或者nil(如果变量索引超出范围)
23.1.2访问Upvalues
我们也可以通过debug库的getupvalue函数访问Lua函数的upvalues。和局部变量不同的是,即使函数不在活动状态他依然有upvalues(这也就是闭包的意义所在)。所以,getupvalue的第一个参数不是栈级别而是一个函数(精确的说应该是一个闭包),第二个参数是upvalue的索引。Lua按照upvalue在一个函数中被引用(refer)的顺序依次编号,因为一个函数不能有两个相同名字的upvalues,所以这个顺序和upvalue并没什么关联(relevant)。
可以使用函数ebug.setupvalue修改upvalues。也许你已经猜到,他有三个参数:一个闭包,一个upvalues索引和一个新的upvalue值。和setlocal类似,这个函数返回upvalue的名字,或者nil(如果upvalue索引超出索引范围)。
下面的代码显示了,在给定变量名的情况下,如何访问一个正在调用的函数的任意的给定变量的值:
function getvarvalue (name)
local value, found


-- try local variables
local i = 1
while true do
local n, v = debug.getlocal(2, i)
if not n then break end
if n == name then
value = v
found = true
end
i = i + 1
end
if found then return value end


-- try upvalues
local func = debug.getinfo(2).func
i = 1
while true do
local n, v = debug.getupvalue(func, i)
if not n then break end
if n == name then return v end
i = i + 1
end


-- not found; get global
return getfenv(func)[name]
end
首先,我们尝试这个变量是否为局部变量:如果对于给定名字的变量有多个变量,我们必须访问具有最高索引的那一个,所以我们总是需要遍历整个循环。如果在局部变量中找不到指定名字的变量,我们尝试这个变量是否为upvalues:首先,我们使用debug.getinfo(2).func获取调用的函数,然后遍历这个函数的upvalues,最后如果我们找到给定名字的变量,我们在全局变量中查找。注意调用debug.getlocal和debug.getinfo的参数2(用来访问正在调用的函数)的用法。

你可能感兴趣的:(lua)