快速掌握Lua 5.3 —— 调试库 (2)

Q:如何调试”Closure”的”upvalue”信息?

A:

--[[ debug.getupvalue(f, up)
     返回函数("Closure")"f"的第"up"个"upvalue"的名字和值。
     Lua按照"upvalues"在匿名函数中出现的顺序对其编号。如果指定的"up"索引越界,则返回"nil"。
     以‘(‘开头的变量名表示没有名字的变量(比如是循环控制用到的控制变量,或是去除了调试信息的代码块)。

     debug.setupvalue(f, up, value)
     与"debug.setupvalue()"的功能相对,将函数"f"("Closure")的第"up"个"upvalue"的值设置为"value"。
     函数返回被设置的"upvalue"的名字。如果指定的"up"索引越界,则返回"nil"。

     注:获取与设置"upvalue"与"Closure"是否被调用(是否在调用栈上)无关。]]
-- "Closure"。
function newCounter ()
    local n = 0
    local k = 0
    return function ()
        k = n
        n = n + 1
        return n
    end
end

counter = newCounter()
print(counter())
print(counter())
-- 此时"k"是1,"n"是2。

local i = 1
repeat
    name, val = debug.getupvalue(counter, i)
    if name then
        print ("index", i, name, "=", val)    -- 依次输出两个"upvalues"的名字和值。
        if(name == "n") then
            debug.setupvalue (counter, 2, 10)    -- 设置"n"的值为10。
        end
        i = i + 1
    end
until not name
-- 此时"n"的值被设置为10。
print(counter())
-- 在此调用后"n"的值被加1,变为11。
--[[ results:
     1
     2
     index    1    k    =    1
     index    2    n    =    2
     11
]]

--[[ debug.upvaluejoin(f1, n1, f2, n2)
     让"Closure""f1"的第"n1"个"upvalue"引用"Closure""f2"的第"n2"个"upvalue"。

     debug.upvalueid(f, n)
     返回指定"Closure""f"的第"n"个"upvalue"的标识符
     (一个轻量用户数据,每个"upvalue"的标识符唯一)。
     这个标识符可以让程序检查两个不同的"Closure"是否共享了相同的"upvalue(s)"。 ]]
function newCounter()
    local n = 0
    local k = 0
    return function ()
        k = n
        n = n + 1
        return n
    end
end
counter = newCounter()

function newCounter1()
    local n = 0
    local k = 0
    return function ()
        k = n
        n = n + 1
        return n
    end
end
counter1 = newCounter1()

-- 每个"upvalue"都有自己独有的ID。
print(debug.upvalueid(counter, 1))    --> userdata: 00559300
print(debug.upvalueid(counter, 2))    --> userdata: 00559348
print(debug.upvalueid(counter1, 1))    --> userdata: 005593D8
print(debug.upvalueid(counter1, 2))    --> userdata: 00559420

-- 让"counter"的第一个"upvalue"引用"counter1"的第二个"upvalue"。
debug.upvaluejoin(counter, 1, counter1, 2)

-- "counter"的第一个"upvalue"与"counter1"的第二个"upvalue"的ID相同。
print(debug.upvalueid(counter, 1))    --> userdata: 00559420
print(debug.upvalueid(counter, 2))    --> userdata: 00559348
print(debug.upvalueid(counter1, 1))    --> userdata: 005593D8
print(debug.upvalueid(counter1, 2))    --> userdata: 00559420

Q:如何追踪程序的运行?

A:

--[[ debug.sethook([thread,] hook, mask [, count])
     将函数"hook"设置为线程"thread"的钩子函数。
     "mask"决定钩子函数何时被触发,"count"决定何时额外的调用一次钩子函数。
     "thread"默认为当前线程。"count"默认为0,
     钩子函数将在每运行"count"条指令时额外的调用一次钩子函数,向钩子函数传递事件"count"。
     "mask"可以指定为如下值的一个或多个:
         ‘c‘: 每当Lua调用一个函数时,调用钩子函数,向钩子函数传递事件"call"或"tail call";
         ‘r‘: 每当Lua从一个函数内返回时,调用钩子函数,向钩子函数传递事件"return";
         ‘l‘: 每当Lua进入新的一行时,调用钩子函数,向钩子函数传递事件"line"。
     当钩子函数被调用时,第一个参数是触发这次调用的事件。对于"line"事件,有第二个参数,为当前行号。
     函数不传参,则为关闭钩子函数。]]
debug.sethook(print, "crl")

function foo()
    local a = 1
end

local x = 1
foo()
local y = 1
--[[ results:
     return  nil
     line    5
     line    3
     line    7
     line    8
     call    nil
     line    4
     line    5
     return  nil
     line    9
     return  nil
     return  nil
]]

--[[ debug.gethook([thread])
     返回钩子函数的内存地址,钩子函数的掩码,"debug.sethook()"为钩子函数设置的"count"。]]
debug.sethook(print, "l", 9)
print(debug.gethook())
debug.sethook()    -- 关闭钩子函数。
print(debug.gethook())    -- 没有钩子函数就什么都获取不到了。
--[[ results:
     line    2
     function: 013D1A70    l    9
     line    3
     nil     0
]]

Q:如何查看Lua的注册表信息?

A:

--[[ debug.getregistry()
     函数返回Lua的"registry"。]]

Q:如何创建一个程序分析器?

A:调式库除了用于调式以外还可以用于完成其他任务,这种常见的任务就是分析。对于一个实时的分析来说,最好使用C接口来完成。对于每一个钩子函数其使用的Lua调用代价太大,并且通常会导致测量的结果不准确。然而,对于计数分析来说,Lua可以很好的胜任。

-- 一个记录程序中函数被调用次数的小型基本分析器。
local Counters = {}    -- key-value: 函数-计数
local Names = {}    -- key-value:函数-函数名

local function hook()
    local f = debug.getinfo(2, "f").func    -- 获取被调用的函数本身。
    if Counters[f] == nil then    -- 如果是第一次被调用。
        Counters[f] = 1
        Names[f] = debug.getinfo(2, "Sn")    -- 获取函数信息。
    else    -- 如果之前被记录过,这里只是增加其计数。
        Counters[f] = Counters[f] + 1
    end
end

local f = assert(load("print(‘Hello World!‘)"))
debug.sethook(hook, "c")    -- 当函数被调用时调用钩子函数。
f()
debug.sethook()    -- 关闭钩子函数。

-- 获取结果。
function getname(func)
    local n = Names[func]
    if n.what == "C" then    -- 如果是C函数,只返回其名字。
        return n.name
    end
    -- 如果不是C函数,返回"[file]:line"的形式。
    local loc = string.format("[%s]:%s", n.short_src, n.linedefined)
    if n.namewhat ~= "" then    -- 如果不是匿名函数,返回一个合理的名字,"[file]:line (name)"。
        return string.format("%s (%s)", loc, n.name)
    else    -- 否则只返回"[file]:line"的形式。
        return string.format("%s", loc)
    end
end

for func, count in pairs(Counters) do
    print(getname(func), count)
end
--[[ results:
     Hello World!
     [[string "print(‘Hello World!‘)"]]:0 (f)    1
     print    1
     sethook    1
     nil    1    <-- 这个不知道是什么函数。
]]

附加:

1、在钩子函数内,你可以调用”debug.getinfo()”,指定栈级别为2, 来获得正在运行的函数的详细信息(”debug.getinfo()”的栈级别为0,钩子函数的栈级别为1)。

2、一个打印文件名及行号的精致的追踪器,

function trace(event, line)
    local s = debug.getinfo(2).short_src
    print(s .. ":" .. line)
end

debug.sethook(trace, "l")
时间: 2024-11-08 08:15:30

快速掌握Lua 5.3 —— 调试库 (2)的相关文章

快速掌握Lua 5.3 —— 调试库 (1)

Q:什么是活动函数? A:程序中被调用但还未执行完成的函数. function g() --[[ 此时函数"g"被调用但还未执行完成,是活动函数.所以这里获取的是函数"g"的信息. "debug.getinfo(2)"获取的才是函数"f"的信息.]] local x = debug.getinfo(1, "n") for k, v in pairs(x) do print(k, v) end end fun

快速掌握Lua 5.3 —— 字符串库 (3)

Q:什么情况下"pattern"会匹配空串? A:要小心的使用*和-,因为它们可以匹配零次. -- 如果你打算用"%a*"匹配单词,你会发现到处都是单词. print(string.find(";$% **#$hello13", "%a*")) --> 1 0 print(string.find(";$% **#$hello13", "%a*", 6)) --> 6 5 --

快速掌握Lua 5.3 —— 字符串库 (2)

Q:模式匹配字符串的相关函数? A: --[[ string.find(s, pattern [, init [, plain]]) 在字符串"s"中查找第一个与匹配模式"pattern"相匹配的子串, 函数返回子串的开始位置和终止位置.如果未找到返回"nil". 如果在"pattern"中定义了捕获,捕获物也会在之后依次返回. "init"可指定从字符串"s"的什么位置开始查找,默认为

快速掌握Lua 5.3 —— 编写提供给Lua使用的C库函数的技巧 (2)

Q:什么是"registry"? A:有时候,我们需要在程序中使用一些非局部的变量.在C中我们可以使用全局变量或是静态变量来实现,而在为Lua编写C库的过程中,使用以上类型的变量并不是一个好的方式.首先,这些变量中无法存储Lua的值.其次,这些变量如果在多个Lua状态机中被使用,则很可能造成非预期的结果. 一个替代方案是,将这些值存储在Lua的全局变量中.这种方式解决了上面提到的两个问题,Lua全局变量可以存储任何Lua的值,同时每一个Lua状态机都有自己独立的一套全局变量.但这依旧不

快速掌握Lua 5.3 —— 资源管理

Q:Lua的"finalizer"? A:在我们之前看到的使用"userdata"的例子中,我们只关心如何创建并使用"userdata",从未关心何时以及如何释放我们创建的"userdata",因为这些事都由Lua的垃圾回收器帮我们处理.然而很多时候,程序并不会这么简单,有可能在其中还会涉及到文件句柄,窗口句柄等,此时这些资源就需要创建者进行管理. 一些面向对象语言提供了析够器用来帮助用户管理这些资源,Lua同样提供了类似的机

Lua的C++绑定库(一)

C++是一门非常复杂的语言,然而更可怕的是,这门语言的各种奇葩特性还在继续增加,很多C++的程序员都会觉得这完全是在给自己添堵嘛,都已经这么复杂了,何必还要使劲往里面塞东西呢,明明C++03或者说是C++98的标准就已经完全够用了.我个人的看法呢,其实后续的标准还是很有必要的,因为这里面的很多新标准,对于一些写底层库的作者来说,真的是非常需要,换句话说,如果没有type_traits.右值语义.可变参数模板这些特性,那我就不会重新造这个轮子,也就不会写这篇文章了,正是因为C++11的标准里面引入

ubuntu 调试库

1.安装带有调试信息的libc: sudo apt-get install libc6-dbg 2.下载libc源码 a.选定一个放置源码的目录并进入,如 /home/kent/dev-os/libc6-source b.执行sudo apt-get source libc6, 会把源码下载到当前目录中. 最后源码目录大概是:/home/kent/dev-os/libc6-source/eglibc-2.15 www.2cto.com 3.运行gdb时指定libc源码目录:gdb `find /

快速掌握Lua 5.3 —— I/O库 (2)

Q:什么是"Complete Model"? A:所有的文件操作都基于明确指定的文件句柄,可以同时打开多个文件句柄.这就意味着同一时间可以操作多个文件,对于每一个文件读或写均可.文件句柄等同于C语言中的"FILE*",它代表一个被打开文件的当前读取位置.io.open()可以指定打开的文件,并返回其文件句柄, --[[ io.open(filename [, mode]) 以"mode"模式打开文件"filename",返回其

Lua中的基本函数库

基本函数库为Lua内置的函数库,不需要额外装载 assert (v [, message])功能:相当于C的断言,参数:v:当表达式v为nil或false将触发错误,message:发生错误时返回的信息,默认为"assertion failed!" -------------------------------------------------------------------------------- collectgarbage (opt [, arg])功能:是垃圾收集器的通