Open the-mikedavis opened 5 months ago
we can't turn off native coverage on a JIT-enabled build of OTP.
+JPcover false
?
I believe that +JPcover
exists so you can compile modules from erlc
with native coverage/counters without having to go through cover
. false
is the default so that sources aren't compiled with the extra counters by default.
cover
ignores the global coverage mode that +JPcover
sets (code:get_coverage_mode/0
) with a new force_line_counters
compile
option when re-compiling modules through any of its cover:compile*
functions: https://github.com/erlang/otp/blob/b41cfe1da34665ef429615ee1de5305cd507b3de/lib/tools/src/cover.erl#L2072-L2075.
The BIF it's using to decide when to use native or emulator coverage code:coverage_support/0
is a wrapper around -ifdef BEAMASM
, which is set by make when the building the JIT flavor: https://github.com/erlang/otp/blob/7b7fc1ddd1aafae02cf22213ad84cac362fb1ac2/erts/emulator/Makefile.in#L274-L277. cover
is using that flag to decide when to patch code and whether to read out of counters
or use the new code:get_coverage/2
BIF (https://github.com/erlang/otp/blob/7b7fc1ddd1aafae02cf22213ad84cac362fb1ac2/lib/tools/src/cover.erl#L2233-L2238).
I brought this up upstream in https://github.com/erlang/otp/issues/8214
But it's not trivial to make native coverage configurable. Even if we can configure it, we would need to also modify rebar to be able to configure how rebar calls cover:compile_beam/1
, or at least modify its application env. Being able to change how cover works at runtime can also break some assumptions within cover - it assumes that it's either using native or EMU coverage per node. Using native coverage for some modules and EMU for others can break assumptions if we don't make larger changes to cover.
So for now I will put this down since I don't think it's worth the time investment. We can always call the original functions used in Khepri transactions in unit tests for coverage if needed. We will just need to gate the coverage tests when OTP 27 is released.
Before OTP 27,
cover
worked by inserting code to docounters:add(CRef, LineIndex, 1)
around any significant code. TheCRef
is a counter for the original module's coverage, so Horus only needed to preserve those instructions in the standalone module in order to have the function's coverage count for the original module.OTP 27 introduces "native" coverage (introduced in https://github.com/erlang/otp/pull/7856). The compiler now emits
executable_line
instructions when passed theforce_line_counters
(orline_coverage
option, depending on the system'scode:get_coverage_mode/0
). The JIT turns that that instruction into an atomic add or literal mov that updates a section of theBeamCodeHeader
'scoverage
buffer (see x86, arm).BeamCodeHeader
is a member of each module instance (erl_module_instance
) and the BIF thatcover
uses to read coverage information when native coverage is enabled reads from this new buffer from the current module. (As a consequence, old coverage is now no longer accessible after upgrading/reloading a module.)The old
cover
code is still around for architectures that use the emulator but we can't turn off native coverage on a JIT-enabled build of OTP.Since coverage information is now attached to module instances, I don't believe we can trick
cover
into counting executions of horus functions anymore.