chapter 24 The Debug Library

来源:互联网 发布:mac文明 编辑:程序博客网 时间:2024/05/17 01:17
The debug library does not give you a debugger for Lua,Lua 没Debuger?, but it offers all the primitives
that you need for writing your own debugger. For performance reasons,
the official interface to these primitives is through the C API. The debug library

in Lua is a way to access them directly within Lua code.


Unlike the other libraries, you should use the debug library with parsimony.(异常俭省)
First, some of its functionality is not exactly famous for performance. Second,
it breaks some sacred truths of the language, such as that you cannot access
a local variable from outside its lexical scope. Frequently, you may not want to
open this library in your final version of a product, or else you may want to erase
it.


The debug library comprises two kinds of functions:introspective functions
and hooks. Introspective functions allow us to inspect several aspects of the
running program, such as its stack of active functions, current line of execution,
and values and names of local variables. Hooks allow us to trace the execution
of a program
.


An important concept in the debug library is the stack level. A stack level
is a number that refers to a particular function that is active at that moment:
the function calling the debug library has level 1, the function that called it has
level 2, and so on,离debug call 远的level 大
.


function TestMain()

function TestDebug()   ---level1 call the debug function

   Debug.CallXXXXX  ----statck level 1

    

end

end

TestMain(); --TestMain() is level 2

              --main level3



24.1 Introspective Facilities

The main introspective function in the debug library is thedebug.getinfo function.
Its first parameter can be a function or a stack level. When you call
debug.getinfo(foo) for some function foo, you get a table with some data about
this function. The table can have the following fields:


source: where the function was defined. If the function was defined in a string
(through loadstring), source is this string. If the function was defined in
a file, source is the file name prefixed with a ‘@’.

short_src: a short version of source (up to 60 characters), useful for error
messages.

linedefined: number of the first line of the source where the function was
defined.

lastlinedefined: number of the last line of the source where the function was
defined.

what: what this function is. Options are “Lua” if foo is a regular Lua function,
“C” if it is a C function, or “main” if it is the main part of a Lua chunk.

name: a reasonable name for the function.

namewhat: what the previous field means. This field can be “global”, “local”,
“method”, “field”, or “” (the empty string). The empty string means that
Lua did not find a name for the function.


nups: number of upvalues of that function.

activelines: a table representing the set of active lines of the function. An
active line is a line with some code, as opposed to empty lines or lines
containing only comments. (A typical use of this information is for setting
breakpoints. Most debuggers do not allow you to set a breakpoint outside
an active line, as it would be unreachable.)


func: the function itself; see later.


When foo is a C function, Lua does not have much data about it. For such
functions, only the fields what, name, and namewhat are relevant
.


When you call debug.getinfo(n) for some number n, you get data about
the function active at that stack level. For instance, if n is 1, you get data
about the function doing the call. (When n is 0, you get data about getinfo
itself, a C function.) If n is larger than the number of active functions in the
stack, debug.getinfo returns 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.



The field name is tricky. Remember that, because functions are first-class
values in Lua, a function may not have a name, or may have several names.

Lua tries to find a name for a function by looking into the code that called the
function, to see how it was called. This method works only when we call getinfo
with a number, that is, when we ask information about a particular invocation.


The getinfo function is not efficient. Lua keeps debug information in a form
that does not impair program execution; efficient retrieval is a secondary goal

here. To achieve better performance, getinfo has an optional second parameter
that selects what information to get. In this way, the function does not waste
time collecting data that the user does not need. The format of this parameter
is a string, where each letter selects a group of fields, according to the following
table:


‘n’ selects name and namewhat
‘f’ selects func
‘S’ selects source, short_src, what, linedefined, and lastlinedefined
‘l’ selects currentline
‘L’ selects activelines
‘u’ selects nup


The following function illustrates the use of debug.getinfo. It prints a
 primitive traceback of the active stack:
    function traceback ()
      for level = 1, math.huge 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
          end
end


It is not difficult to improve this function, by including more data from getinfo.
Actually, the debug library offers such an improved version, the traceback
function. Unlike our version, debug.traceback does not print its result; instead,
it returns a (usually long) string with the traceback.


Accessing local variables

We can inspect the local variables of any active function with debug.getlocal.
This function has two parameters: the stack level of the function you are querying
and a variable index. It returns two values: the name and the current value
of this variable.
If the variable index is larger than the number of active variables,
getlocal returns nil. If the stack level is invalid, it raises an error. (We
can use debug.getinfo to check the validity of the stack level.)


local function TestVariable()
   local var1=5;
   local var2=10;
   local var3=100;
 print(debug.getlocal(1,1));    -- var1    5   --

 print(debug.getlocal(2,1));   --TestVariable    function: 017DB550
end
TestVariable();
print(debug.getlocal(1,1));   --TestVariable    function: 017DB550 ,结果跟上面 print(debug.getlocal(2,1));  一样


var1    5   --
TestVariable    function: 017DB550  -- main 里面  TestVariable    is a function variable.

TestVariable    function: 017DB550  -- main 里面 TestVariable     is a function variable.


============full version of lession24.lua

local function TestVariable(first,second)
   local var1=5;
   local var2=10;
   local var3=100;

   do
      local outscopttest=1;
      local outspcetest2=2;  -- already out scope, can be captured by debug.getlocal
   end

   for i=1,math.huge do
    local name,value= debug.getlocal(1,i);
    if name ==nil then break end;
    print(name,value);   
   end

end

local a,b=10,11

TestVariable(a,b);
for k=1,math.huge do
  t=debug.getinfo(k);
  if t==nil then break end;
  print(t.source,t.what,t.currentline);
end


===============

frist   10
second  11

var1    5
var2    10
var3    100
(for index)     4
(for limit)     1.#INF
(for step)      1
i       7       =====我想说的是,如果函数没有执行的,我们是没办法在函数外面获得这个函数里面的local variable的

(Remember that local variables are only
visible after their initialization code.so   local name,valuedebug.getlocal(1,i); 没有被获取到)

@lession24.lua  main    15
=[C]    C       -1
=stdin  main    1
=[C]    C       -1


Starting with Lua 5.2, negative indices get information about the extra
arguments of the function: index -1 refers to the first extra argument. The
name in this case is always “(*vararg)”.

local function testExtrac(first,second,...)
    print(...);
    for i=-1,-10,-1 do  --each time 步涨-1
     local name,value= debug.getlocal(1,i);
       print(name,value);   
       end
   
end
testExtrac(10,11,12,13,14,15);

(*vararg)       12  is always “(*vararg)”.
(*vararg)       13  is always “(*vararg)”.
(*vararg)       14
(*vararg)       15
nil     nil
nil     nil
nil     nil
nil     nil
nil     nil
nil     nil



You can also change the values of local variables, with debug.setlocal. Its
first two parameters are a stack level and a variable index, like in getlocal. Its
third parameter is the new value for this variable. It returns the variable name
or nil if the variable index is out of scope.


Accessing non-local variables

The debug library also allows us to access the non-local variables used by a
Lua function, with getupvalue. Unlike local variables, the non-local variables
referred by a function exist even when the function is not active (this is what
closures are about, after all). Therefore, the first argument for getupvalue is not
a stack level
, but a function (a closure, more precisely). The second argument is
the variable index. Lua numbers non-local variables in the order they are first
referred in a function, but this order is not relevant, because a function cannot
access two non-local variables with the same name.


You can also update non-local variables, with debug.setupvalue. As you
might expect, it has three parameters: a closure, a variable index, and the new
value. Like setlocal, it returns the name of the variable, or nil if the variable
index is out of range.


local function Factory(first,second,third)
  local  var1=10;
  local var2=11;
  local var3=12;
  do
    local var4=200;
    var5=6;
  end
  return function ()
       print("mygod....",first,second,var1,var2,var3,var4,var5);
  end
end

local mygod=Factory(1,2,3); --  return a closure
 for i=1,math.huge do
    local name,value= debug.getupvalue(mygod,i);
    if name ==nil then break end;
    print(name,value);   
   end

同过这个例子,我们又一窥closure 的implement 了,,,first see the print result:

_ENV    table: 00571A18   -- the closure will auto include the _ENV global table
first   1
second  2
var1    10
var2    11
var3    12

---Lua 自能的排除了third, 因为我们的闭包里面没有用到third,Factory 里面的do ..end block 在调用时候执行完后其实已经outof scope,so will not include in the closure, but for  var5, it exactly in the _ENV table.


Listing 24.1. Getting the value of a variable:

function getvarvalue (name, level)
  local value
   local found = false
    level = (level or 1) + 1
    -- try local variables
    for i = 1, math.huge do
    local n, v = debug.getlocal(level, i)
       if not n then break end
       if n == name then
         value = v
       found = true
     end
     end
     if found then return value end
-- try non-local variables

local func = debug.getinfo(level, "f").func -- through "f",get the function in the level.
for i = 1, math.huge do
local n, v = debug.getupvalue(func, i)
if not n then break end
if n == name then return v end
end
-- not found; get value from the environment
local env = getvarvalue("_ENV", level)
return env[name]
end



Accessing other coroutines


All introspective functions from the debug library accept an optional coroutine
as their first argument, so that we can inspect the coroutine from outside. For
instance, consider the next example:


co = coroutine.create(function ()
   local x = 10
   coroutine.yield()
    error("some error")
end)
coroutine.resume(co)
print(debug.traceback(co))
The call to traceback will work on coroutine co, resulting in something like this:

stack traceback:
[C]: in function 'yield'
temp:3: in function <temp:1>

The trace does not go through the call to resume, because the coroutine and the
main program run in different stacks.

see my implement:

   local function testCompare()
    print("in main function call");
     print(debug.traceback(1));
   end
   testCompare();
   
   local co=coroutine.create(function ()
    print("in cotoutin");
     print(debug.traceback(1));
    coroutine.yield();
    error("test error appear");
   end);
   coroutine.resume(co);
   print(debug.traceback(co));
    coroutine.resume(co);
     print(debug.traceback(co));


======see the print result

in main function call
1
stack traceback:
        lession24.lua:57: in function 'testCompare'
        lession24.lua:59: in main chunk
        [C]: in function 'dofile'
        stdin:1: in main chunk
        [C]: in ?   -- in normal function call,可以traceback 到main chunk ....
in cotoutin
1
stack traceback:
        lession24.lua:63: in function <lession24.lua:61>
stack traceback:
        [C]: in function 'yield'
        lession24.lua:64: in function <lession24.lua:61>
stack traceback:
        [C]: in function 'error'  --当再次resume 的时候,上次的 stack 其实已经没有了,,,,
        lession24.lua:65: in function <lession24.lua:61>


We can also inspect local variables from a coroutine, even after an error:
print(debug.getlocal(co, 1, 1)) --> x 10



24.2 Hooks

The hook mechanism of the debug library allows us to register a function to be
called at specific events as a program runs. There are four kinds of events that
can trigger a hook:


 1.call        events happen every time Lua calls a function;
 2.return    events happen every time a function returns;
 3. line       events happen when Lua starts executing a new line of code;
 4.count     events happen after a given number of instructions.


Lua calls hooks with a single argument, a string describing the event that
generated the call: “call” (or “tail call”), “return”, “line”, or “count”.相当于回调函数,参数

就是调用这个hook 的类型 For   line events, it also passes a second argument, the new line number. To get more
information inside a hook we must call debug.getinfo.


To register a hook, we call debug.sethook with two or three arguments: the
first argument is the hook function; the second argument is a mask string, which
describes the events we want to monitor; and the optional third argument is
a number that describes at what frequency we want to get count events. To
monitor the call, return, and line events, we add their first letters (‘c’, ‘r’, or ‘l’)
in the mask string. To monitor the count event, we simply supply a counter as
the third argument. To turn off hooks, we call sethook with no arguments.


As a simple example, the following code installs a primitive tracer, which
prints each line the interpreter executes:

       debug.sethook(print, "l")

This call simply installs print as the hook function and instructs Lua to call it
only at line events.


---> debug.sethook(print,"l");
---> a=b
line    1
---> a=1   --每次发生line event 的时候,都会call print 函数,参数就是"line" and the linenumber.
line    1
---> b=2
line    1




A more elaborated tracer can use getinfo to add the current   file name to the trace:
function trace (event, line)
local s = debug.getinfo(2).short_src
print(s .. ":" .. line)
end
debug.sethook(trace, "l") --

但似乎我们没办法针对某一函数 install hook, the sethook ,is hook for all call,all line,all return,,,那样好像用处也不大阿,,,


A useful function to use with hooks is debug.debug enter debug model,但到底怎么用,用在什么地方,还没体会到. This simple function  prints a prompt that executes arbitrary Lua commands. It is roughly equivalent
to the following code:

--相当于下面的实现,,,,

function debug1 ()
while true do
io.write("debug> ")
local line = io.read()
if line == "cont" then break end
assert(load(line))()
end
end

When the user enters the “command” cont, the function returns. The standard
implementation is very simple and runs the commands in the global environment,
outside the scope of the code being debugged. Exercise 24.5 discusses a
better implementation.


24.3 Profiles

Despite its name, the debug library is useful also for tasks other than debugging.
A common such task is profiling. For a profile with timing, it is better to use

the C interface: the overhead of a Lua call for each hook is too high and may
invalidate any measure. However, for counting profiles, Lua code does a decent
job. In this section, we will develop a rudimentary profiler that lists the number
of times each function in a program is called in a run


The main data structures of our program are two tables: one associates functions
to their call counters, and the other associates functions to their names.
The indices to both tables are the functions themselves.


local Counters = {}
local Names = {}


Now we define the hook function. Its job is to get the function being called
and increment the corresponding counter; it also collects the function name:



local function hook ()
    local f = debug.getinfo(2, "f").func
    local count = Counters[f]
    if count == nil then -- first time 'f' is called?
    Counters[f] = 1
    Names[f] = debug.getinfo(2, "Sn")
     else -- only increment the counter
     Counters[f] = count + 1
    end
end


The next step is to run the program with this hook. We will assume that the
main chunk of the program is in a file and that the user gives this file name as
an argument to the profiler, like this:
% lua profiler main-prog
With this scheme, the profiler can get the file name in arg[1], turn on the hook,
and run the file:
local f = assert(loadfile(arg[1]))
debug.sethook(hook, "c") -- turn on the hook for calls,f 虽然没有执行,但hook 可以找到的
f() -- run the main program
debug.sethook() -- turn off the hook


。。。。。。。。。。。。。。。 be continue in future,,,,,,P264


The last step is to show the results. Function getname in Listing 24.2 produces
a name for a function. Because function names in Lua are so uncertain, we
add to each function its location, given as a pair file:line. If a function has no
name, then we use only its location. If a function is a C function, we use only its
name (as it has no location). After that definition, we print each function with
its counter:
for func, count in pairs(Counters) do
print(getname(func), count)
end


Listing 24.2. Getting the name of a function:

function getname (func)
local n = Names[func]
if n.what == "C" then
return n.name
end
local lc = string.format("[%s]:%d", n.short_src, n.linedefined)
if n.what ~= "main" and n.namewhat ~= "" then
return string.format("%s (%s)", lc, n.name)
else
return lc
end
end


If we apply our profiler to the Markov example that we developed in Section
10.3, we get a result like this:
[markov.lua]:4 884723
write 10000
[markov.lua]:0 1
read 31103
sub 884722
[markov.lua]:1 (allwords) 1
[markov.lua]:20 (prefix) 894723
find 915824
[markov.lua]:26 (insert) 884723
random 10000
sethook 1
insert 884723
This result means that the anonymous function at line 4 (which is the iterator
function defined inside allwords) was called 884723 times, write (io.write) was
called 10000 times, and so on.
There are several improvements that you can make to this profiler, such as
to sort the output, to print better function names, and to embellish the output
format. Nevertheless, this basic profiler is already useful as it is, and can be
used as a base for more advanced tools.






 












































































0 0
原创粉丝点击