Thread overview
[Issue 16397] missing coverage from template instances when using separate compilation
[Issue 16397] test_runner in coverage mode doesn't match with individual test (covers less)
Jul 08, 2017
Martin Nowak
Jul 08, 2017
Martin Nowak
Jul 08, 2017
Martin Nowak
Jul 09, 2017
Martin Nowak
Dec 17, 2022
Iain Buclaw
July 08, 2017
https://issues.dlang.org/show_bug.cgi?id=16397

Martin Nowak <code@dawg.eu> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |code@dawg.eu

--- Comment #1 from Martin Nowak <code@dawg.eu> ---
Turns out to be a rather trivial but inherent problem of how coverage and
linkers work.
If a template methods is instantiated in two separately compiled modules (say
std.digest.crc and std.digest.digest) the definition from the first object in
the linker argument order is picked.
Here we have our problem std.digest.digest instantiates WrapperDigest!CRC which
gets emitted with coverage information, but std.digest.crc also instantiates
WrapperDigest!CRC without coverage information (since it's in a foreign module)
the one that gets picked is the latter.

Just like with any other template behavior and -unittest I think dmd should
always emit coverage instrumentation, even if it's for functions in foreign
modules, since eventually any instance might get picked.
This requires to mangle __bcoverage and __coverage and make them linkable from
other modules, at the moment they are private symbols, only accessible from
within a single object file.

The logic that prevents increments of "foreign" modules is here in dmd. https://github.com/dlang/dmd/blob/ce96c01d271f914cd9acb6100f7fa9f4d494ee8c/src/ddmd/toir.d#L67

--
July 08, 2017
https://issues.dlang.org/show_bug.cgi?id=16397

Martin Nowak <code@dawg.eu> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
            Summary|test_runner in coverage     |missing coverage from
                   |mode doesn't match with     |template instances when
                   |individual test (covers     |using separate compilation
                   |less)                       |

--
July 08, 2017
https://issues.dlang.org/show_bug.cgi?id=16397

--- Comment #2 from Martin Nowak <code@dawg.eu> ---
cat > a.d << CODE
module a;
import b;

unittest
{
    foo!int();
}
CODE

cat > b.d << CODE
module b;

void foo(T)()
{
    return; // line is covered
}

unittest
{
    foo!int();
}
CODE

dmd -c -cov -unittest a.d
dmd -c -cov -unittest b.d
dmd -main a.o b.o -ofbug
./bug

cat a.lst b.lst
----
       |module a;
       |import b;
       |
       |unittest
       |{
      1|    foo!int();
       |}
a.d is 100% covered
       |module b;
       |
       |void foo(T)()
       |{
0000000|    return; // line is covered
       |}
       |
       |unittest
       |{
      1|    foo!int();
       |}
b.d is 50% covered
----

With different linker order, both calls use the foo instantiation with coverage instrumentation.

dmd -main b.o a.o -ofbug
./bug

cat a.lst b.lst
----
       |module a;
       |import b;
       |
       |unittest
       |{
      1|    foo!int();
       |}
a.d is 100% covered
       |module b;
       |
       |void foo(T)()
       |{
      2|    return; // line is covered
       |}
       |
       |unittest
       |{
      1|    foo!int();
       |}
b.d is 100% covered
➜  D cat a.lst b.lst
                                             20:18:21
       |module a;
       |import b;
       |
       |unittest
       |{
      1|    foo!int();
       |}
a.d is 100% covered
       |module b;
       |
       |void foo(T)()
       |{
      2|    return; // line is covered
       |}
       |
       |unittest
       |{
      1|    foo!int();
       |}
b.d is 100% covered
----

--
July 08, 2017
https://issues.dlang.org/show_bug.cgi?id=16397

--- Comment #3 from github-bugzilla@puremagic.com ---
Commit pushed to master at https://github.com/dlang/phobos

https://github.com/dlang/phobos/commit/1f7e59980539fe0f9ba352b258e8266db6bdff01 fix test coverage

- use single tests as workaround for Issue 16397
- fix single tests (broken sed command)

--
July 09, 2017
https://issues.dlang.org/show_bug.cgi?id=16397

--- Comment #4 from Martin Nowak <code@dawg.eu> ---
Comment from Rainer https://github.com/dlang/phobos/pull/5579#issuecomment-313905465

> Always generating coverage instrumentation of templated code will only work if > you cover all linked libraries, though. For example, coverage from druntime
> modules might leak into the report, just as well as that it might provide
> template instances without coverage instrumention.

Yes, but that's what we do with everything else as well, e.g. -unittest or -profile, so it seems sensible to use this here as well.

That's also what C++ and gcov do.

----
cat > cova.cc << CODE
#include "covb.h"

int main()
{
    foo<int>(1, 2);
}
CODE
cat > covb.h << CODE
template <typename T>
T foo(T a, T b)
{
    return a + b;
}
CODE
g++ -fprofile-arcs -ftest-coverage cova.cc -o cova
./cova
gcov cova.cc
----
File 'covb.h'
Lines executed:100.00% of 2
Creating 'covb.h.gcov'

File 'cova.cc'
Lines executed:100.00% of 3
Creating 'cova.cc.gcov'
----

--
August 16, 2017
https://issues.dlang.org/show_bug.cgi?id=16397

--- Comment #5 from github-bugzilla@puremagic.com ---
Commit pushed to stable at https://github.com/dlang/phobos

https://github.com/dlang/phobos/commit/1f7e59980539fe0f9ba352b258e8266db6bdff01 fix test coverage

--
January 05, 2018
https://issues.dlang.org/show_bug.cgi?id=16397

--- Comment #6 from github-bugzilla@puremagic.com ---
Commit pushed to dmd-cxx at https://github.com/dlang/phobos

https://github.com/dlang/phobos/commit/1f7e59980539fe0f9ba352b258e8266db6bdff01 fix test coverage

--
December 17, 2022
https://issues.dlang.org/show_bug.cgi?id=16397

Iain Buclaw <ibuclaw@gdcproject.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Priority|P1                          |P4

--