Builtin Library
README.md
probe.shared
The probe registry, and the one path a hit takes to reach you.
The three arm verbs attach by three different mechanisms — a wrapped function,
a patched line, a value read each frame — and then all hand the hit to
M.fire. The condition, the limit, the cooldown, the body and the notice live
there and nowhere else, so a hit reads the same whichever verb armed it and a
change to how probes report is a change in one place.
Surface
| Function | Answers |
|---|---|
M.register(kind, source, opts) | A new probe record, or nil, error. Refuses without opts.notice. |
M.fire(probe, ctx) | Takes one hit. Returns whether the world should freeze here. |
M.cancel(id) / M.cancelAll() | Detach and forget, by whatever mechanism attached. |
M.all() / M.get(id) | The registry, sorted by id / one probe. |
M.row(probe) | One probe as a probe.list row. |
M.drain(probe) | The recorded hits, emptying the ring. |
What a mechanism owes the registry
Two things, and the registry handles the rest.
- A
sourcestring at registration: what this probe is attached to, in the probe's own words. It is written here rather than by the agent, because which of several armed probes fired is exactly what the agent cannot supply about its own probe. - A
detachfunction on the probe record, socancelis one path no matter how the probe attached.
local shared = require(".shared")
local probe, err = shared.register("line", "Ticker:9", opts)
if probe == nil then return { ok = false, error = err } end
probe.detach = function() debugger.removeBreakpoint(bpId) end
-- ...and at the moment of the hit:
shared.fire(probe, { locals = debugger.getLocals(1) })
What fire guarantees
whenis tested first, so a filtered hit costs nothing but the test.- The cooldown suppresses rather than drops silently: suppressed hits are
counted and reported in
probe.list. - The body runs inside a protected call. Three consecutive errors disarm the probe and say so, because a body that throws every time is not reporting anything, it is only costing the frame.
- Whatever the body returns is what the notice carries. A table becomes the values, a string becomes the line, nothing still reports the hit.
- Reaching the limit disarms the probe and posts a final notice naming that it stopped. A probe that goes quiet without saying so is worse than one that never fired, because silence reads as "it never happened".
Compiling
when and run are compiled once, at arm time, so a bad snippet is an error
the agent sees while arming rather than a silence it discovers much later. Both
see the hit's context unpacked as bare names — args, ret, locals,
upvalues, value, previous, entity, component, frame, time, hit,
probe — so a condition reads ret.killed rather than ctx.ret.killed.