Bug 16397 – missing coverage from template instances when using separate compilation

Status
NEW
Severity
enhancement
Priority
P4
Component
phobos
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2016-08-17T00:07:30Z
Last change time
2024-12-01T16:27:40Z
Assigned to
No Owner
Creator
Seb
Moved to GitHub: phobos#10195 →

Comments

Comment #0 by greensunny12 — 2016-08-17T00:07:30Z
In #4719 we ran into some troubles with the reporting of the coverage between the test_runner and individual tests, see also: https://github.com/dlang/phobos/pull/4719#issuecomment-239363404 The difference only exists in some module, but for example in std.digest.digest different coverage hits are reported: 1) Output with test_runner (compiled with ENABLE_COVERAGE=1), e.g. generated/linux/debug/64/unittest/test_runner std.digest.digest | body | { 0000000| enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ | "big, check " ~ typeof(this).stringof ~ ".length!"; 0000000| asArray!(digestLength!T)(buf, msg) = _digest.peek(); 0000000| return buf[0 .. digestLength!T]; | } | | @trusted ubyte[] peek() const | { 0000000| enum len = digestLength!T; 0000000| auto buf = new ubyte[len]; 0000000| asArray!(digestLength!T)(buf) = _digest.peek(); 0000000| return buf; | } | } 2) make -f posix.mak std/digest/digest.test | body | { 1| enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ | "big, check " ~ typeof(this).stringof ~ ".length!"; 1| asArray!(digestLength!T)(buf, msg) = _digest.peek(); 1| return buf[0 .. digestLength!T]; | } | | @trusted ubyte[] peek() const | { 1| enum len = digestLength!T; 1| auto buf = new ubyte[len]; 1| asArray!(digestLength!T)(buf) = _digest.peek(); 1| return buf; | }
Comment #1 by code — 2017-07-08T18:04:56Z
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
Comment #2 by code — 2017-07-08T18:20:12Z
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 ----
Comment #3 by github-bugzilla — 2017-07-08T19:40:58Z
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)
Comment #4 by code — 2017-07-09T13:20:59Z
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' ----
Comment #5 by github-bugzilla — 2017-08-16T13:23:42Z
Comment #6 by github-bugzilla — 2018-01-05T13:30:02Z
Comment #7 by robert.schadek — 2024-12-01T16:27:40Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/phobos/issues/10195 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB