Repository navigation
feat(coverage): Go-side line coverage built on the debug hook (LCOV) - #35
Merged
Merged
Conversation
Adds line and count debug hooks, the primitive test-coverage tooling
(LuaCov-style, LCOV output) is built on. debug.sethook/gethook were already
declared in the type stub (compiler/stdlib/debug.go) but had no runtime
implementation, so any coverage instrumentation was impossible.
- hook.go: sethook/gethook + the callHook/fireHook engine. All state lives on
LState (hook, hookMask, hookCount, hookCounter, hookLastLine, inHook) — the
hot callFrame struct is left untouched, so there is no per-frame size cost on
the call path.
- vm.go: one guarded call at the dispatch-loop head; when no hook is installed
it is a single uint8 compare. Measured (benchstat, n=10, paired vs main):
every VM benchmark within noise, geomean -0.10% — no measurable overhead.
- Line events fire when the current source line changes (tracked on LState).
This is a coverage-oriented simplification of PUC's per-frame oldpc tracking:
every executed line is reported at least once; it does not re-fire a line on
a same-frame loop back-edge. Happy to switch to full per-frame semantics if
preferred.
- Reentrancy is blocked via LState.inHook so a hook cannot trigger itself; the
register top is saved/restored around the call.
- Call/return ('c'/'r') masks are rejected explicitly rather than silently
ignored — line/count only for now.
hook_test.go covers coverage collection, count firing, gethook round-trip,
nested calls, reentrancy, sethook(nil) clearing, and c/r rejection.
Full suite (2199 tests) green, go vet clean. No generated files touched.
Coverage is a consumer of the debug hook (this branch is based on the sethook/gethook branch), not a separate instrumentation path: - WIPPY_COVERAGE arms every LState's line hook via covArm (hookMask |= HookMaskLine) in newLState — no debug.sethook call needed, no separate dispatch-loop guard; - the hook's line-event path in callHook records each executing source line (numerator), reusing the hook's hookLastLine dedup; - CompileWithOptions registers every prototype's coverable lines (walking nested FunctionPrototypes' DbgSourcePositions) = exact denominator incl. never-run functions; - WriteCoverageLCOV / CoverageSummary emit a standard LCOV tracefile. No-op when WIPPY_COVERAGE is unset (covArm and covRegisterProto both bail). Full suite green, go vet clean; smoke confirms covArm arms the hook and hits are recorded with no explicit sethook.
Contributor
|
we can keep it for v1 for now since v2 already have coverage, will be reviewed, thank you |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds opt-in Go-side line coverage through the existing VM hook dispatcher, with deterministic LCOV output and a process-wide, mutex-protected collector shared by states and coroutines. The denominator comes from prototype debug positions, including unexecuted nested functions; both existing precompiled-prototype loaders register restored code, so warm bytecode does not disappear from reports.
Depends on debug hooks #34. Current main and its reviewed hook repair are integrated. Coverage consumes dispatches independently of the user's installed hook mask and callback reentrancy: clearing or replacing a debug callback cannot silently disable collection, and callback bodies themselves are recorded. The former constructor-specific arming path is removed. Normal debug mask/gethook behavior stays intact. The dispatcher checks the opt-in coverage flag; no zero-overhead performance claim is made without current measurements.
The added regressions reproduce lost hits for equal-number lines in distinct chunks, disabled collection after
debug.sethook(nil), and absent restored-bytecode sources. Fixed controls also verify real callback body lines, zero credit for an unexecuted function, and preservation of the user's count-only hook mask. Test source identities are isolated across repeated executions. All hook/coverage controls pass three race-detector repetitions withWIPPY_COVERAGE=1. The existing CI test matrix explicitly enables coverage for these opt-in tests; default-mode skips do not supply passing credit.Reporting snapshots are captured under the existing collector lock. Caller filters and file I/O run after it is released, so reentrant reporting cannot deadlock or block VM recording on file writes. Reentrant filter controls fail on the original implementation; the repaired controls and concurrent execution/reporting checks pass three race repetitions.
Debug-hook dependency #34 is merged as bfc0f00. The exact current head is
b2728c443a1a630ddd296ecb4169b90d86698802; full CI 41239692484 is running. The isolated release candidate is5c2f4918f199a21bf8fbaaa36409503f2ccc7c09(CI 41239756455), based on published v1.6.12 with only the fifteen reviewed feature/test/CI files. The 98 unrelated main compiler/type-checker files are excluded. Every selected file matches this reviewed head byte for byte. No tag or release is created until full checks pass.Disabled-instrumentation microbenchmarks, interleaved against v1.6.12 on the same pinned CPU with ten samples, show +0.74% geomean, individual results from -6.22% to +4.60%. These limited VM microbenchmarks are not a zero-overhead or production-throughput claim. No Runtime #553 implementation or dependency adoption is included.
Landed release receipt
Merged and released in Go-Lua v1.7.0, from isolated stable commit
5c2f4918f199a21bf8fbaaa36409503f2ccc7c09. All twelve hosted checks passed on both the final coverage PR and the stable candidate, including both explicitly enabled coverage-regression steps. The release's fifteen selected files were independently downloaded from the public Go module proxy and verified byte for byte against the tested source. No unrelated main compiler/type-checker changes or Runtime dependency adoption are included.