2

If a local variable in some unknown scope gets set to 256, how can I know that it happened?

I'd like to be able to look for one value at a time, assuming that's possible.

I have access to the debug API.

skwerlman
  • 143
  • 1
  • 5
  • You want to scan the entire variable space for any variable with a value of 256? – Etan Reisner Apr 27 '15 at 23:37
  • You can't without making some simplifying assumptions and/or flow control analysis, or adding tracing to the VM code. The debug library breaks at most every line and function call or return. A single line could set it to 256 and set it to something else between breaks. So, do you have any simplifying assumptions? – Tom Blodget Apr 27 '15 at 23:55
  • If possible, I'd like to know which variable is 256 at the time that it gets assigned. At worst, I need to know the values of each local by the time it's scope ends, so breaking at each return would be sufficient (but not optimal). – skwerlman Apr 28 '15 at 01:44
  • But a local's scope can end well before the function it is created in returns. – Tom Blodget Apr 28 '15 at 02:28
  • 2
    What problem are you really trying to solve? – Tom Blodget Apr 28 '15 at 02:28

1 Answers1

3

You can loop over all local variables at the current scope inside debug hook and check which one has the value you need:

do
  local seen = {}
  debug.sethook(function(ev, line)
      local level = 2
      local target = 256
      local i = 1
      while true do
        local name, value = debug.getlocal(level, i)
        if not name then break end
        if value == target and string.sub(name, 1, 1) ~= '(' and not seen[name] then
          print("at line", line, "variable", name, value)
          seen[name] = true
        elseif seen[name] and value ~= target then
          seen[name] = nil
        end
        i = i + 1
      end
    end, "l")
end

local a = 256
local b = 11
a = 13
a, b = 256, 256
print("done")

This prints the following for me:

at line 23  variable    a   256
at line 26  variable    a   256
at line 26  variable    b   256
done

This only applies to local variables. For global variables you can iterate over _G or _ENV tables and compare the values.

Note that the lines printed are the lines of the next statement and not the lines on which the change happens (as the hook stops before the line is executed).

There are two other options to track variable changes (with some limitations): (1) using metamethods and a proxy table, and (2) using a debugger.

Community
  • 1
  • 1
Paul Kulchenko
  • 25,884
  • 3
  • 38
  • 56
  • Thanks for the reply. Unfortunately, I don't know when the value will be assigned, nor where, so I can't know when to run that snippet nor on what. I need something that will be automatically run when the value is assigned to a variable. – skwerlman Apr 28 '15 at 01:47
  • 1
    You don't need to know that; you can simply set it up at the beginning of your script and it will be triggered on every line (which will have performance impact) and will point you to the line where the value is changed. – Paul Kulchenko Apr 28 '15 at 02:04
  • 2
    You can't do it in general with a variable, but you can detect if the value of a table field is changed using [`__newindex` and a proxy table](http://www.lua.org/pil/13.4.4.html). Since global variables are fields in a table, you can track their changes, but not local (simple) variables. – Paul Kulchenko Apr 28 '15 at 02:06
  • After rereading and subsequently using this code, it solves my problem. Thanks. Though I could do with an explanation of how it works, since I've never touched hooks before. – skwerlman Apr 29 '15 at 21:31
  • Also, is there a way to tell which file the assignment occurred in? – skwerlman Apr 29 '15 at 21:37
  • 1
    Yes, you can try `debug.getinfo(2, "S").source`. See the documentation for `debug.getinfo`. – Paul Kulchenko Apr 29 '15 at 23:28