• 《Programming in Lua 3》读书笔记(二十)


    日期:2014.7.29
    PartⅢ The Standard Libraries
    24 The Debug Library

    Lua的debug库并不是提供一个调试器,而是提供一些供你写调试器的一些操作。Lua实现这些功能是以C API来实现的,因此这个库相当于提供使用Lua代码访问C API,这是从性能上考虑的。
    Lua的debug库包含两类函数:introspective function和hooks(啥东西?)。前面一个函数允许我们对调试正在运行的程序(当前运行到的行数、局部变量的名字和值等);hook则允许我们回溯程序的运行过程。
    Debug库一个重要的概念是:stack level。指的是一个代表当前阶段一个特定函数的活跃值:访问debug库的函数其level为1,调用该函数的函数其level为2。

    24.1 Introspective Facilities
           debug库中主要的内省函数是debug.getinfo。该函数的第一个参数可以是一个函数也可以是一个stack level。当参数是一个函数的时候,该函数返回一个table包含该函数的一些信息:
           当参数函数为一个C函数的时候,返回信息仅包括函数类型(Lua C)、名字、函数名的前一个字段(global local 等)
           当以数值作为参数调用该函数的时候,该数值代表一个stack level。此时会得到一个满足该level的函数的信息。如当n为1的时候,会得到进行执行调用的函数,当为0的时候,得到getinfo这个函数本身,当n大于stack中活跃函数的数量的时候,返回nil。when you query an active function,calling debug.getinfo with a number,the result table has an extra field ,currentline,with the line where the function is at that moment.Moreover,func has the function that is active at that level.
            table中的字段name需要谨慎对待,因为lua中的函数可以没有名字,也可以有多个名字。
            作者也提到,getinfo函数不怎么高效。lua以一种不损害程序运行的形式保存debug的信息,efficient retrieval is a secondary goal here.getinfo函数接受一个第二参数用来选择需要得到的信息以提高效率,此时函数不会去收集用户不需要的信息。该参数的类型是一个字符串型,一个字母收集一类字段的信息:
    'n'     收集 name 和 namewhat
    'f'      收集 func
    'S'     收集source,short_src,what,linedefined,和lastlinedefined
    'l'      收集 currentline
    'L'      收集 activeline
    'u'     收集 nup
    下面的例子介绍了debug.getinfo函数的使用:

     
    Accessing local variables
           可以使用debug.getlocal 函数查看任意活跃函数中的局部变量,这个函数有两个参数:访问的函数的stack level和访问变量的index值。该函数返回两个值:访问变量的名字和值。如果参数index值大于函数中总的变量的值,那么getlocal函数返回nil。如果stack level值不符合要求,则会引起错误(此时的技巧是通过getinfo函数来先验证stack level)

            函数中的局部变量依据其出现的顺序进行排序,最先出现的变量其index值最小,而且只会对当前函数有效区间内的变量进行计数,举例来看:
    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,29)
    打印出来的值为:
    a     10
    b     29
    x     nil
    a     4
           函数中首先出现的局部变量是a,其index为1,其次是b,index为2,接着是x,index为3,最后为函数体内部的局部变量a,其index为4.此时要注意的是,函数体内的局部变量c,有其自身的有效范围,此时已经在函数体的有效范围之外了。(局部变量只会在其初始化代码之后可见)。
           自Lua5.2开始,负数作为参数会得到函数的一些额外信息:index -1代表第一个额外信息,此时这个name总是"(*vararg)".---这段内容还有待考证
           也可以通过函数debug.setlocal来改变局部变量的值,该函数的前两个参数分别为:stack level 和变量的index值,第三个参数则为要设定的新的值。如果index超出了范围,则该函数会返回nil值

     
    Accessing non-local variables
           使用一个lua函数:getupvalue 可以使我们访问非局部变量。与局部变量不同的是,非局部变量是当函数非active的时候才会存在(这也是所谓的闭包),因此该函数的第一个参数不是一个stack level ,而是一个函数(准确的说是一个闭包),第二个参数是变量的index值,其顺序和局部变量一致都是先出现的index值越小。(函数不能以同一个名字访问两个非局部变量)
           使用setupvalue 可以用来改变非局部变量的值,与setlocal一样,函数也是三个参数,只不过第一个参数是一个闭包。

     
    Accessing other coroutines
           dubug库中的所有内省函数都接受一个可选参数--一个协同程序作为其第一参数,因此我们也可以从外部查看协同程序,例如:
    co = coroutine.create(function ( ... )
         local x = 10
    
         coroutine.yield()
    
         error("some error")
    
    end)
    
    
    coroutine.resume(co)
    
    print(debug.traceback(co))
    运行该段代码打印出:
    stack traceback:
         [C]: in function 'yield'
           此时在yield函数内部

           回溯不会执行resume,因为协同程序和主程序不在同一个栈中。
           当协同程序引发错误的时候,不会存在与当前的这个栈中。这就意味着我们可以在其引发的错误之后查看该协同程序,继续上述的例子,假如我们此时再一次resume协同程序,这次便会引发错误:
    e.g.
    
    coroutine.resume(co)
    
    print(debug.traceback(co))
    
    print(coroutine.resume(co))
    --打印
    stack traceback:
         [C]: in function 'yield'
    false     some error

           而此时再一次回溯:
    e.g.
    
    coroutine.resume(co)
    
    print(debug.traceback(co))
    
    print(coroutine.resume(co))
    
    print(debug.traceback(co))
    --打印
    stack traceback:
         [C]: in function 'yield'
    false    some error
    stack traceback:
         [C]: in function 'error'
           此时便会到error函数内部了

           我们甚至可以再函数产生错误之后再范围协同程序内部的局部变量
    print(debug.getlocal(co,1,1))
    --打印
    x 10           --这个是协同程序内部的局部变量x的值

     
    24.2 Hooks
            Lua debug库的hook机制允许我们注册一个函数的调用,然后在程序运行的某个特定时间点调用我们注册的那个函数。这里有四个时间点(events 事件?)可以触发一个hook:
    call events           每次lua调用一个函数的时候就触发这个事件
    return events        每次函数返回值的时候触发这个事件
    line events           每当lua开始执行新的一行代码的时候触发这个事件
    count events          进过给定数量的指令后触发这个事件
            Lua通常以一个参数调用hooks,这个参数是字符串型,代表某个事件:"call"("tail call"),"return","line","count".当参数是line的时候,函数也接受新的一行的行数作为第二个参数。而如果需要得到更多关于hook内部的信息,则需要使用函数:debug.getinfo.
            使用函数debug.sethook 可以注册一个hook,参数为两个可选第三个参数:第一个参数是要注册的函数;第二个参数是一个string字符串型,代表需要触发的事件('c','r','l'); 可选的第三参数代表我们想获得count event事件的频率;call,return,line事件通过第二个参数控制,而可选的第三个参数用来控制count事件。关掉一个hook,则以无参数调用sethook。
         


    24.3 Profiles
    Lua的debug库不仅可以用来做debug,还可以用来(profiling??)
  • 相关阅读:
    类和对象
    关联查询
    重点函数
    三大范式
    主外键
    软件开发的项目周期
    什么是事务
    索引
    视图
    数据库对象
  • 原文地址:https://www.cnblogs.com/zhong-dev/p/4044565.html
Copyright © 2020-2023  润新知