Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OTP 27 will break coverage support #20

Open
the-mikedavis opened this issue Feb 26, 2024 · 3 comments
Open

OTP 27 will break coverage support #20

the-mikedavis opened this issue Feb 26, 2024 · 3 comments
Assignees
Labels
bug Something isn't working

Comments

@the-mikedavis
Copy link
Member

Before OTP 27, cover worked by inserting code to do counters:add(CRef, LineIndex, 1) around any significant code. The CRef 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 erlang/otp#7856). The compiler now emits executable_line instructions when passed the force_line_counters (or line_coverage option, depending on the system's code:get_coverage_mode/0). The JIT turns that that instruction into an atomic add or literal mov that updates a section of the BeamCodeHeader's coverage buffer (see x86, arm). BeamCodeHeader is a member of each module instance (erl_module_instance) and the BIF that cover 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.

@lhoguin
Copy link

lhoguin commented Feb 27, 2024

we can't turn off native coverage on a JIT-enabled build of OTP.

+JPcover false?

@the-mikedavis
Copy link
Member Author

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).

@the-mikedavis
Copy link
Member Author

I brought this up upstream in erlang/otp#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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants