lua debug.sethook 使用

debug.getinfo简介

因为debug.sethook和debug.getinfo一般都是组合起来使用的,在使用之前我们先来介绍一下debug.getinfo

getinfo(optional thread, function or stack level, optional flag):

该函数返回有关功能的信息表。可以直接给该函数,或者可以给一个号码作为函数的值,这意味着该函数在给定线程的调用堆栈的水平仪功能运行:0级是当前功能(程序getinfo本身); 1级是所谓getInfo函数等等。如果函数比当前功能的数大一些,然后getinfo返回nil。下面一段代码:

[lua]
local funcInfo = debug.getinfo(1)
for k,v in pairs(funcInfo) do
print("key:"..tostring(k).."——value:"..tostring(v))
end

[/lua]

输出:
key:nups——value:0
key:what——value:main
key:func——value:function: 0074BCF0
key:lastlinedefined——value:0
key:source——value:@test_commad.lua
key:currentline——value:1
key:namewhat——value:
key:linedefined——value:0
key:short_src——value:test_commad.lua

返回信息表的字段含义是:

  1. source:标明函数被定义的地方。如果函数在一个字符串内被定义(通过loadstring),source就是那个字符串。如果函数在一个文件中定义,source是@加上文件名。
  2. short_src:source的简短版本(最多60个字符),记录一些有用的错误信息。
  3. linedefined,source中函数被定义之处的行号。
  4. what:标明函数类型。如果foo是一个普通得Lua函数,结果为 “Lua”;如果是一个C函数,结果为 “C”;如果是一个Lua的主chunk,结果为 “main”。
  5. name:函数的合理名称。
  6. namewhat:上一个字段代表的含义。这个字段的取值可能为:W”global”、”local”、”method”、”field”,或者 “”(空字符串)。空字符串意味着Lua没有找到这个函数名。
  7. nups:函数的upvalues的个数。
  8. func:函数本身
  9. currentline:当前函数行号

这个函数的效率比较低,所以尽量使用时只获取我们需要的字段,我们可以通过第二个参数来控制

  • ‘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

这个参数是字符串,每一个字母代表一种类型的信息,可以组合在一起使用列如:

[lua]
local funcInfo = debug.getinfo(1,"fl")
for k,v in pairs(funcInfo) do
print("key:"..tostring(k).."——value:"..tostring(v))
end
[/lua]

输出:
key:currentline——value:1
key:func——value:function: 0059BD10

下面我们研究debug.sethook

debug.sethook的使用

而debug.sethook在lua程序设计一书中是这样描述的:debug库的hook是这样一种机制:注册一个函数,用来在程序运行中某一事件到达时被调用。有四种可以触发一个hook的事件:当Lua调用一个函数的时候call事件发生;每次函数返回的时候,return事件发生;Lua开始执行代码的新行时候,line事件发生;运行指定数目的指令之后,count事件发 生。Lua使用单个参数调用hooks,参数为一个描述产生调用的事件:”call”、”return”、”line” 或 “count”。另外,对于line事件,还可以传递第二个参数:新行号。我们在一个hook内总是可以使用debug.getinfo获取更多的信息。

使用带有两个或者三个参数的debug.sethook 函数来注册一个hook:第一个参数是hook函数;第二个参数是一个描述我们打算监控的事件的字符串;可选的第三个参数是一个数字,描述我们打算获取count事件的频率。为了监控call、return和line事件,可以将他们的第一个字母(’c’、’r’ 或 ‘l’)组合成一个mask字符串即可。要想关掉hooks,只需要不带参数地调用sethook即可

[lua]
function trace (event, line)
local s = debug.getinfo(2).short_src
print(s .. ":" .. line)
end
debug.sethook(trace, "l")
print("hi")
[/lua]

输出:
test_commad.lua:8
hi

我们注册一个trace函数来监听执行新行的事件,当print(“hi”)执行时就会触发trace函数打印出print的行号,如果是简体call或者是return没有第二参数的

利用这个特性我们监控任何一个函数的执行情况

[lua]
local funcTab= {}
function testf(event,exarg)
–print("event—->"..event)
local func = debug.getinfo(2).func
if funcTab[func] then
funcTab[func].callcount = funcTab[func].callcount +1
end
end

function myfunction ()
print()
return 10
end
funcTab[myfunction] = {}
funcTab[myfunction]["name"] = "myfunction"
funcTab[myfunction]["callcount"] = 0
debug.sethook(testf,"c")–注册hook
myfunction()
myfunction()
debug.sethook(nil)–关闭hook
for k,v in pairs(funcTab) do
print(v.name)
print(v.callcount)
end
[/lua]

输出:
myfunction
2

表示myfunction被执行了两次

funcTab是用来记录我们要监视的函数的列表,利用这个特性,我们很容易就扩展出一个profiler工具,代码就不解释了。