lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

> I've seen clever proposals from Dan Tull  ... looking for something 
> a bit simpler and possibly slightly more generic. 
It's worth noting that because the opcode patching associates a
separate function pointer per breakpoint, it's actually quite 
versatile. I actually used it to implement near zero overhead code
coverage (using self-clearing breakpoints) and very low overhead 
dynamic probes for profiling instrumentation.

As an aside if anybody has used that patch (or a derivative), let 
me know. I found a couple of bugs that could occur. They only 
happen in very obscure circumstances, but can result in a crash.

The patch is more of a proof of concept of the core mechanism
that a complete solution.

> The breakpoint support helps with breakpoints (obviously), but 
> requires additional work to support "step over" and "step out"...
We accept the overhead of the line hook overhead temporarily
to implement stepping, though honestly once I had performant
breakpoints I don't use manual stepping as often.

> It seems like most of the time for debug hook processing is 
> spent in luaD_callhook/luaD_hook functions...
> still slower than the opcode processing...
I actually tried something quite similar to the technique you're
describing for breakpoints and that's what led me to an opcode
patching approach.

Essentially, the problem I had was that the existing hook
forces an O(N) algorithm (where N is the average number of
opcodes executing that do not hook) and patching is the only
way I found to make that O(1). It's possible to improve  a hook 
approach by a constant factor, but for large N, it'll still hurt.

I ran some experiments and for breakpoints that are not in a 
high traffic zone, N can be very large. The 5.1 dispatch loop can 
spin at 60-160 million iterations per second depending on your
mix of opcodes and processor.

Also worth noting, I made the "HALT" opcode the highest one
and patched string.dump to make sure they are not persisted so 
it doesn't break on disk bytecode compatibility.

Still, I can understand wanting a mechanism that's less invasive
and has a cleaner API (though the API could easily be made
cleaner than the prototype patch I shared while using patching
under the hood). A 5.2 port should be easy. LuaJIT is a bit more
involved, though the technique should be applicable.

> One tricky part would be storing hook callbacks as the current
> mechanism allows for only one hook.
Yes, I still have to wrestle with this a bit. Our main hook function 
winds being overloaded as a de-multiplexer for common events which 
can be awkward at times to avoid being stomped by hook changes.

> I'd be also interested in having a "timepoint" hook, which would be
> called after a specified number of seconds...
I built something to serve this need and initially thought this is
what I wanted as well, but what worked (much) better was to have
a trigger API that could be safely* invoked from another thread.

The C trigger API performs an atomic increment on a counter on the
global state structure and passes that counter value to the hook,
which will be called as soon as the hook check notices it is non-zero.

I used it originally for a very low overhead sampling profiler (by
having another thread fire signals based a timer), but I also use to 
do near instant pausing of multiple independent Lua states running 
on multiple threads. By instant here, I don't mean instant in time 
terms, but in instruction terms. In practice, the Lua state hooks on 
the next executed opcode after a trigger. This is how I solved the
problem of setting breakpoints when the VM is actively running
without incurring the count or line hook tax.

It's proven a very versatile mechanism like the opcode patching.

It has very low overhead when hooks are enabled and no extra 
overhead if the hookmask is zero (due to short circuit in the 
hook condition at the top of the dispatch loop).


[1] lua_sethook was not quite safe enough for my needs and
has the problem of being Lua state (coroutine) specific, too: