3

I'm trying to optimise my LuaJIT code, and I'm wondering, if there is a debug tool, or if I can write one, to check how many times my script accessed global variables/tables/functions?

  • Just put a metatable on `_G` which registers the `__index` and `__newindex` functions. This is also explain in “Programming in Lua” https://www.lua.org/pil/14.2.html – Henri Menke Sep 18 '19 at 23:26

2 Answers2

1

You can use a proxy table to store globals and divert any access to the global table to this proxy, embellished with tracing functionality.

local globals = {}
setmetatable(_G, {
    __newindex = function (_, k, v)
        print(debug.traceback("Setting global variable " .. k, 2))
        rawset(globals, k, v)
    end,
    __index = function (_, k)
        print(debug.traceback("Getting global variable " .. k, 2))
        return rawget(globals, k)
    end,
})

a = 1
a = 2

print(a)

Sample output:

Setting global variable a
stack traceback:
    prog.lua:15: in main chunk
    [C]: at 0x00404960
Setting global variable a
stack traceback:
    prog.lua:16: in main chunk
    [C]: at 0x00404960
Getting global variable a
stack traceback:
    prog.lua:18: in main chunk
    [C]: at 0x00404960
2

Live example on Wandbox

DarkWiiPlayer
  • 6,871
  • 3
  • 23
  • 38
Henri Menke
  • 10,705
  • 1
  • 24
  • 42
  • Thank You, works like a charm! And for the wandbox link too. I didn't even know, that there was a site, where I could run code in that many languages. Upvoted and accepted, but no reputation –  Sep 19 '19 at 08:58
  • Well, one more thing. This works with stuff, that I declared, but doesn't get triggered by any built in functions/tables (math module, type function, etc.) Is there a way for it to be triggered by those too? –  Sep 19 '19 at 09:36
  • @MorsMortium - Replace `local globals = {}` with `local globals=_G; _G={}; setfenv(1, _G); setfenv(0, _G)` – Egor Skriptunoff Sep 19 '19 at 10:09
  • That gives me: attempt to call global 'setfenv' (a nil value) –  Sep 19 '19 at 10:53
  • Yes I am. How could I access a global variable, if I just set _G to {} –  Sep 19 '19 at 11:01
  • @MorsMortium `globals.setfenv` – Henri Menke Sep 19 '19 at 11:33
  • That works, but if i write globals.setfenv(1, _G), that sets _G to nil, so globals.setfenv(0, _G) will throw an error, that it's nil. If I leave out the (1, _G) version, then the print functions in setmetatable will cause stack overflow, i had to replace that with io.write, but now it works! Thank You guys! –  Sep 19 '19 at 12:33
  • @EgorSkriptunoff your code is completely out of order; if you first reset `_G` then obviously `setfenv` and `getfenv` will "disappear". – DarkWiiPlayer Sep 19 '19 at 13:42
  • @DarkWiiPlayer - Yes. Please post an answer with correct code. – Egor Skriptunoff Sep 19 '19 at 13:46
0

So, if anyone is wondering, this is the final code, that I wrote, combined the comments and the original answer. This writes out user defined and built in global variable accesses. gnumber and snumber are just counters, which can be printed to get the total number of times a global variable got set or retrieved

local globals = _G
_G = {}
local gnumber, snumber = 0, 0
globals.setfenv(0, _G)

setmetatable(_G, {
    __newindex = function (_, k, v)
        snumber = snumber + 1
        io.write("Setting global variable ", k, "\n")
        io.write(debug.traceback(), "\n")
        rawset(globals, k, v)
    end,
    __index = function (_, k)
        gnumber = gnumber + 1
        io.write("Getting global variable ", k, "\n")
        io.write(debug.traceback(), "\n")
        return rawget(globals, k)
    end,
})