lunarmodules / luacov

LuaCov is a simple coverage analyzer for Lua code.
http://lunarmodules.github.io/luacov/
MIT License
300 stars 68 forks source link

How to work with coroutines? #38

Closed daurnimator closed 8 years ago

daurnimator commented 9 years ago

How can/should I use luacov with coroutine heavy programs? (In PUC-Rio Lua, debug hooks are per-coroutine)

mpeterv commented 9 years ago

luacov patches global coroutine.create and coroutine.wrap so that coroutines they produce have the debug hook on. So it should just work unless a module uses these functions before luacov module is loaded or coroutines are created using C API.

daurnimator commented 9 years ago

Thats good to know.

However, in my case, the coroutines are being created in C. What should I do?

mpeterv commented 9 years ago

Assuming you control coroutine creation or can access all coroutines created from C, you could get the debug hook luacov uses using debug.gethook on the main thread, then set it on each coroutine. Perhaps luacov should expose a function for that...

daurnimator commented 9 years ago

To be more specific, I'm trying to profile some code that uses cqueues. The cq:wrap method takes a function, turns it into a coroutine, adds it to the scheduler cq. https://github.com/wahern/cqueues/blob/faa63dc4593c6ce8c7774a6f0e8bc583b18ebbd4/src/cqueues.c#L2331 How could/should this function be changed to be "luacov friendly"?

hishamhm commented 9 years ago

In PUC-Rio Lua, debug hooks are per-coroutine

And it isn't it so in LuaJIT? (I wasn't aware!) Maybe the simplest thing is just to test coverage using that instead?

daurnimator commented 9 years ago

And it isn't it so in LuaJIT? (I wasn't aware!)

From http://luajit.org/status.html: "The Lua debug API is missing a couple of features (return hooks for non-Lua functions) and shows slightly different behavior in LuaJIT (no per-coroutine hooks, no tail call counting)."

Maybe the simples thing is just to test coverage using that instead?

Not when my code uses 5.3 features :)

mpeterv commented 9 years ago

@daurnimator you could get debug hook in the main chunk and set it in the beginning of functions passed to cq:wrap():

local hook, mask = debug.gethook()
...
cq:wrap(function() debug.sethook(hook, mask) ... end)
daurnimator commented 9 years ago

@daurnimator you could get debug hook in the main chunk and set it in the beginning of functions passed to cq:wrap():

Interesting..... but hard to place into a library.

I'm wondering if there's a reasonable place to put something like this:

    if not jit then -- LuaJIT does not support per-coroutine debug hooks
        local wrap; wrap = cqueues.interpose("wrap", function (self, func, ...)
            local hook, mask, count = debug.gethook()
            return wrap(self, function (...)
                debug.sethook(hook, mask, count)
                return func(...)
            end, ...)
        end)
    end

(interpose is a method provided on cqueues objects for extending the class)

BTW, this whole issue is something I brought up with Roberto in stockholm: having something like luai_userstatethread that you can provide via the debug library.

mpeterv commented 8 years ago

@daurnimator a minor issue with that approach is that the debug.sethook call itself isn't covered. Perhaps luacov should expose a wrapper function instead, e.g.

if not jit then -- LuaJIT does not support per-coroutine debug hooks
   local wrap; wrap = cqueues.interpose("wrap", function(self, func, ...)
      func = luacov.runner.with_luacov(func)
      return wrap(self, func, ...)
   end)
end
daurnimator commented 8 years ago

Perhaps the luajit check should be in luacov?

mpeterv commented 8 years ago

@daurnimator sure, that would make sense

mpeterv commented 8 years ago

@daurnimator under LuaJIT on master branch luacov now doesn't patch coroutine functions and in #42 with_luacov() doesn't call debug.sethook.

daurnimator commented 8 years ago

Thanks. that's helpful.