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 method stop have no effect. The default value for this field is true.
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 the Viewer 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 string file.
setbreak(file, line)
Registers a break point mark in line line of all Lua chucks which source name ends with the string file. 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 called stop, 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 name name to the value of value. If there is no local variable with that name name 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 name name to the value of value. If there is no upvalue with that name name 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 name name to the value of value. If there is no global variable with that name name 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 name name from the default Lua global environment. If both arguments are provided then it changes the value of the global variable with name name to the value of value in the default Lua global environment. If there is no global variable in the default Lua global environment with that name name 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 and goup.
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 parameter kind 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

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

Copyright (C) 2004-2008 Tecgraf, PUC-Rio

This project is currently being maintained by Tecgraf at PUC-Rio.