Skip to content

Instrument all consteval headers in coverage script - #369

Closed
jbcoe wants to merge 1 commit into
jbcoe-restore-operator-overload-set-guardfrom
jbcoe-instrument-all-consteval-headers
Closed

jbcoe wants to merge 1 commit into
jbcoe-restore-operator-overload-set-guardfrom
jbcoe-instrument-all-consteval-headers

Conversation

@jbcoe

@jbcoe jbcoe commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Fixes a point raised in philipcraig's review of the #336-#355 stack (out of diff on #355): consteval_coverage.py only instruments protocol.hh, which keeps 3 of 21 consteval sites after the detail.hh split, so the coverage job measures almost nothing.

@jbcoe
jbcoe force-pushed the jbcoe-instrument-all-consteval-headers branch from 47286b6 to 861c2d6 Compare September 12, 2026 19:36
@codecov

codecov Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.47%. Comparing base (6ccfb5c) to head (df01d8c).

Additional details and impacted files
@@                              Coverage Diff                              @@
##           jbcoe-restore-operator-overload-set-guard     #369      +/-   ##
=============================================================================
+ Coverage                                      72.79%   73.47%   +0.68%     
=============================================================================
  Files                                             12       15       +3     
  Lines                                            838     1003     +165     
  Branches                                         221      221              
=============================================================================
+ Hits                                             610      737     +127     
- Misses                                            22       60      +38     
  Partials                                         206      206              
Flag Coverage Δ
consteval 78.53% <ø> (-21.47%) ⬇️
runtime 72.39% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jbcoe
jbcoe force-pushed the jbcoe-instrument-all-consteval-headers branch from 861c2d6 to 271d803 Compare September 12, 2026 20:27
@jbcoe
jbcoe added this pull request to stack #362 September 12, 2026 20:29
@jbcoe
jbcoe force-pushed the jbcoe-instrument-all-consteval-headers branch from 271d803 to a831360 Compare September 12, 2026 22:11
@jbcoe
jbcoe removed this pull request from stack #362 September 12, 2026 22:29
@jbcoe
jbcoe force-pushed the jbcoe-instrument-all-consteval-headers branch 5 times, most recently from a7015cd to ff104bb Compare September 13, 2026 01:37
@jbcoe

jbcoe commented Sep 13, 2026

Copy link
Copy Markdown
Owner Author

This check takes ~35 mins to run. I'm not happy with that and am comfortable losing coverage checks on compile time code.

@philipcraig WDYT?

@jbcoe
jbcoe force-pushed the jbcoe-instrument-all-consteval-headers branch from ff104bb to 67a29c8 Compare September 13, 2026 06:33
@philipcraig

Copy link
Copy Markdown
Collaborator

This check takes ~35 mins to run. I'm not happy with that and am comfortable losing coverage checks on compile time code.

@philipcraig WDYT?

I have a threading of this work idea -- stay tuned

@philipcraig

Copy link
Copy Markdown
Collaborator

This check takes ~35 mins to run. I'm not happy with that and am comfortable losing coverage checks on compile time code.
@philipcraig WDYT?

I have a threading of this work idea -- stay tuned

oh! looks like from your last commit you have the same idea

@philipcraig philipcraig left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second-round pass over the whole stack (#336#369). Multi-header instrumentation works, and the per-shard LCOV output only lists that shard's probe points, so the Codecov merge across the matrix is sound. Two things inline.

On the run time: the sharding here may well be enough, and #374 collects the further ideas from this review (keying the trap on header and line so there is one work directory and one verify pass, running the verify compiles through the pool, probing the cheapest translation unit first). Either route needs a look once the timings from this PR's matrix are in, so I will re-review that part later rather than now.

probe_jobs: list[tuple[ProbeConfiguration, ProbePoint]] = []
for header in shard_headers:
header_work_dir = os.path.join(arguments.work_dir, header)
os.makedirs(header_work_dir, exist_ok=True)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exist_ok=True only tolerates an existing directory. After a run on main, build/consteval-coverage/protocol.hh is a regular file written by the single-header layout, so the first run on this branch dies before any compile:

File "scripts/consteval_coverage.py", line 602, in main
FileExistsError: [Errno 17] File exists: '.../build/consteval-coverage/protocol.hh'

CI is unaffected (fresh checkout). A shutil.rmtree(arguments.work_dir, ignore_errors=True) before the loop fixes it.


# Translation units that instantiate protocols and so drive the consteval
# machinery in protocol.hh.
# machinery in the instrumented headers.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name_mangling.h is now instrumented (74 of 177 probe points) but name_mangling_tests.cc is not a default translation unit, and it is the only TU that reaches signed_decimal, integral_decimal, mangle_template_argument_value and the is_value branch. Against the three defaults those probes never fire, so 11 lines that name_mangling_tests.cc does exercise (Tagged<3>, Tagged<-3>, Tagged64<4294967296LL>) land as DA:...,0 on Codecov. Adding it costs about two seconds per compile, and it is the cheapest TU, so probing it first also saves time.

@philipcraig

Copy link
Copy Markdown
Collaborator

Measured the shards: the probe step is 9.3–11.8 min per shard against 36.3 min unsharded, at four times the runner minutes. Each shard still repeats 18–21 serial verify compiles, because round-robin sharding puts every header in every shard.

Let's land this as is, with the two inline fixes, and take the serial and per-probe costs in #374 afterwards. The numbers are on that issue.

@jbcoe
jbcoe force-pushed the jbcoe-restore-operator-overload-set-guard branch from b35abdb to 6ccfb5c Compare September 13, 2026 13:37
@jbcoe
jbcoe force-pushed the jbcoe-instrument-all-consteval-headers branch from 67a29c8 to b306f68 Compare September 13, 2026 13:41
consteval_coverage.py only instrumented protocol.hh, which after the
detail.hh split keeps 3 of the ~21 original consteval sites; the rest
moved to conformance.hh, vtable.hh, member_function_thunks.hh,
operator_thunks.hh, protocol_wrappers.hh and name_mangling.h. The
coverage job was reporting a percentage of almost nothing. Instrument
all seven headers, tagging each trap with a file index alongside the
line number since line numbers collide across headers.

The disarmed sentinel is a single NO_PROBE_ARMED constant reused by
both the generated C++ macro defaults and the Python call sites,
rather than -1 hardcoded independently in three places. ProbePoint
carries its file_index directly instead of recomputing it via
INSTRUMENTED_HEADERS.index() in run_probe, and write_lcov groups
probe_points by header once instead of re-filtering the full list per
header.

Addresses philipcraig's review comment on the #336-#355 stack (out of
diff): consteval_coverage.py only instruments protocol.hh.


Fix header instrumentation not reaching transitively-included headers

Per-header work dirs only copied the target header's instrumented copy;
every other instrumented header still lived in SOURCE_ROOT, so a header
reached only transitively (e.g. name_mangling.h via protocol.hh) resolved
back to its uninstrumented original and was silently reported as 0%
covered. Each header's work dir now also gets plain copies of every other
instrumented header, so quote-includes among them stay local. Updates the
stale protocol.hh-only doc comments this broke.

Shard consteval-coverage probing across a CI matrix

Compiling every probe point once per header, single-threaded on a 4-core
runner, takes ~38 minutes once all 21 consteval sites are probed instead of
the original 3. Adds --shard-count/--shard-index to consteval_coverage.py,
round-robining probe points (not whole headers, whose sizes vary too much
to balance) across shards, and wires up a 4-way matrix in coverage.yml.
@jbcoe
jbcoe force-pushed the jbcoe-instrument-all-consteval-headers branch from b306f68 to df01d8c Compare September 13, 2026 13:52
@jbcoe

jbcoe commented Sep 13, 2026

Copy link
Copy Markdown
Owner Author

This is too slow and @philipcraig has a better PR Closing..

@jbcoe jbcoe closed this Sep 13, 2026
philipcraig added a commit that referenced this pull request Sep 13, 2026
Closes #374.

Replaces one compile per probe point with batched rounds, and
instruments every library header instead of just `protocol.hh`.

## Timings

The CI probe step for the same 177 probe points, single runner unless
stated:

| Run | Probe points | Probe step |
|---|---|---|
| main (`protocol.hh` only) | 83 | 10.1 min |
| #369 unsharded | 177 | 36.3 min |
| #369 four shards | 44 each | 9.3–11.8 min per shard, 4× runner minutes
|
| this PR | 177 | 3.1 min |

The whole job now takes 5 minutes, and `timeout-minutes` drops from 90
to 30.

Locally, against the one-compile-per-probe script from #369 with
`name_mangling_tests.cc` added:

| Script | Cores | Wall | Rounds |
|---|---|---|---|
| #369, one compile per probe | 24 | 419 s | – |
| this PR | 4 | 94 s | 23 |
| this PR, contiguous instead of interleaved batches | 4 | 155 s | 42 |

Both scripts report 150/177 probe points covered, with identical
per-header LCOV records.

## How

- Traps are keyed on `(header index, __LINE__)`, so all headers are
instrumented in one work directory and one `-include` file arms any
subset of them.
- Each round arms every probe point not yet covered and compiles a
translation unit once per batch. GCC reports every independent constant
evaluation that throws, so one compile finds many points; a point masked
by an earlier trap on its path surfaces in the next round once that trap
is disarmed. Rounds stop when one finds nothing new (from jbcoe's
prototype on the closed #369 branch).
- Batches are interleaved rather than contiguous, so neighbouring probe
points, which usually share an evaluation path, do not mask each other.
- Translation units are probed cheapest first (`name_mangling_tests.cc`
is now included), so `protocol_test.cc` only ever arms what the cheaper
ones left uncovered.
- Unarmed traps are `if constexpr` no-ops, so they cost the constant
evaluator nothing.
- A compile that fails with no trap fired is reported as an instrumenter
bug, which replaces the separate unarmed verify pass.
- The CI job is back to a single runner.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants