Code Inspector
loop.debug.Inspector
Class of objects that implement a command-line prompt to access the lexical scope of a point in an application. The command-line prompt also allows to navigate through active co-rotines and access the lexical scope of the point they are currently executing. It is also possible to navigate to inactive functions in order to access the current value of their upvalues. This class is useful for implementation of command-line debugging mechanisms.
Each object instance implements an environment that retrieves the value of variables and upvalues of a given point in the code.
The method stop
sets up the environment to access the lexical scope of the point it was called.
The access is provided by an interactive prompt similar to the one provided by the Lua stand-alone interpreter.
Although it provides additional commands to navigate to different points of the running application like active co-routines.
Behavior
Fields
active
-
Defines if the inspector instance is active or not.
If this field evaluates to
false
then all calls to methodstop
have no effect. The default value for this field istrue
. input
-
Defines the object used to read commands inserted through the command prompt.
The object must provide the method
write
like Lua file handlers. The default value for this field is the standard input (i.e.io.stdin
). viewer
-
Defines the object used to print out messages of the command prompt.
The object must provide the same API of instances of
Viewer
. The default value for this field is theViewer
class itself.
Methods
allbreaks()
- Return an iterator that iterates over all registered break point marks. The iteration variables are the source name pattern and the source code line.
removebreak(file, line)
-
Unregisters the break point marks from line
line
of all Lua chucks which source name ends with the stringfile
. setbreak(file, line)
-
Registers a break point mark in line
line
of all Lua chucks which source name ends with the stringfile
. When the execution of the program reaches a break point mark the inspection console is open in that point. stop([level])
-
Executes the command-line prompt used to inspect the current executing code.
Parameter
level
defines the stack level of the function to be inspected. Level 1 is the function that calledstop
, level 2 is the function above, and so on. The command-line prompt executes Lua code as if the code was executed inside the function being inspected. Additionally it also provides the commands described below. As a shortcut, the commands can be invoked without arguments by typing its name only.see(...)
-
Command that writes the textual representation of all its arguments.
This command uses the object in field
viewer
to generate the textual representations. loc([name, value])
-
Command used to access the local variables avaliable in the inspected scope.
When used with no arguments it writes the textual representation of all local variables avaliable.
If first argument is provided it just returns the value of the local variable with name
name
. If both arguments are provided then it changes the value of the local variable with namename
to the value ofvalue
. If there is no local variable with that namename
the call has no effect. upv([name, value])
-
Command used to access the upvalues avaliable in the inspected scope.
When used with no arguments it writes the textual representation of all upvalues avaliable.
If first argument is provided it just returns the value of the upvalue with name
name
. If both arguments are provided then it changes the value of the upvalue with namename
to the value ofvalue
. If there is no upvalue with that namename
the call has no effect. env([name, value])
-
Command used to access the environment of the inspected scope.
When used with no arguments it writes the textual representation of all global variables avaliable.
If first argument is provided it just returns the value of the global variable with name
name
. If both arguments are provided then it changes the value of the global variable with namename
to the value ofvalue
. If there is no global variable with that namename
a new variable is created with the given value. lua([name, value])
-
Command used to access the default Lua global environment, i.e. the default value of
_G
. When used with no arguments it writes the textual representation of the default Lua global state. If first argument is provided it just returns the value of the global variable with namename
from the default Lua global environment. If both arguments are provided then it changes the value of the global variable with namename
to the value ofvalue
in the default Lua global environment. If there is no global variable in the default Lua global environment with that namename
a new variable is created with the given value. goto(where)
-
Command used to change the inspected scope to the co-routine or function defined by argument
where
. When nagivating to a function, no local variables are accessible. To access local variables you have to nagivate to a co-routine executing the function and go up in the stack level until reach the desired function. It is not possible to navigate to the main thread. goup()
- Command used to change the inspected scope to the function one level up in the stack of the current thread.
back()
- Command used to go back to the previous inspected scope.
hist()
-
Command that list all the scope changes already performed through the commands
goto
andgoup
. curr()
- Command that shows the upper levels of the stack of the current inspected environment.
done()
- Command used to indicate the conclusion of the inspection and continue the execution.
step([kind])
-
Command used to indicate the conclusion of the inspection and re-open the inspection console after execution of one more instruction of the source code.
If the parameter
kind
is"in"
and the next instruction of the source code results in a function call then the inspection console is re-open at the first instruction of the called function's source code. Additionally, if the parameterkind
is"out"
then the rest of the current function is executed and the inspection console is re-open at the first instruction after the current function returned. mkbp(file, line)
-
Command used to emulate method
setbreak
in the inspection console. rmbp(file, line)
-
Command used to emulate method
removebreak
in the inspection console. lsbp()
- Command used to list all the break point marks currently registered.
Meta-Fields
__index = function
- Retrieves the values of the variables and upvalues accessible through the current inspected scope.
__newindex = function
- Changes the values of the variables and upvalues accessible through the current inspected scope.
Remarks
- All names of fields and methods provided by the
Inspector
instance are always binded to their respective values and therefore hide any variables or upvalues with those names. The same is true for the names of the commands provide by the command prompt that are listed in the description of methodstop
. To access variables or upvalues with those names use commandsloc(
name [, value])
,upv(
name [, value])
,env(
name [, value])
andlua(
name [, value])
to access respectively the value of local variables, upvalues global variables of the function's environment, and global variables of the Lua default global environment (i.e._G
).
Examples
Dining philosophers problem analyzer
inspector = require("loop.debug.Inspector")() -- TIPS: -- To check the state of all philosophers, type: -- io.write("Forks = ") see(forks) -- goto(P1) print("--> P1:") loc() -- goto(P2) print("--> P2:") loc() -- goto(P3) print("--> P3:") loc() -- -- To remove deadlock condition, type: -- forks = {true,true,true} -- goto(P1) left,right = false,false -- goto(P2) left,right = false,false -- goto(P3) left,right = false,false -- -- To avoid that philosophers die from starvation, type: -- goto(P1) hunger = 0 goto(P2) hunger = 0 goto(P3) hunger = 0 local forks = { true, true, true } function philo(lfork, rfork) local hunger = 0 local left = false local right = false repeat if hunger == 0 then for i=1, math.random(5) do coroutine.yield("thinking") hunger = hunger + 1 end else hunger = hunger + 1 end if not left then if forks[lfork] then left, forks[lfork] = true, false else coroutine.yield("waiting") end end if not right then if forks[rfork] then right, forks[rfork] = true, false else coroutine.yield("waiting") end end if left and right then while hunger > 0 do coroutine.yield("eating") hunger = hunger - 1 end left, forks[lfork] = false, true right, forks[rfork] = false, true else if hunger > 10 then coroutine.yield("starving") end end until hunger > 10 return "dead" end P1 = coroutine.create(philo) P2 = coroutine.create(philo) P3 = coroutine.create(philo) local locked = 0 local failed math.randomseed(os.time()) repeat locked = 0 for i, philo in pairs{ P1, P2, P3 } do local ok, msg = coroutine.resume(philo, i, (i+1)%3) print("P"..i, msg) if ok then if msg == "starving" then print("STARVATION", "P"..i) inspector:stop() elseif msg == "waiting" then locked = locked + 1 else locked = 0 end else failed = true end if locked >= 3 then print("DEADLOCK") inspector:stop() end end until failed