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

Incorrect paths for header files #271

Open
ZedThree opened this issue Jul 12, 2018 · 13 comments
Labels

Comments

@ZedThree
Copy link

@ZedThree ZedThree commented Jul 12, 2018

Header files don't seem to be dealt with correctly:

$ gcovr -j8 -r . --html-details -o gcovr-report/coverage.html -dTraceback (most recent call last):
  File "/home/p/.local/bin/gcovr", line 11, in <module>
    sys.exit(main())
  File "/home/p/.local/lib/python3.6/site-packages/gcovr/__main__.py", line 588, in main
    print_html_report(covdata, options)
  File "/home/p/.local/lib/python3.6/site-packages/gcovr/html_generator.py", line 275, in print_html_report
    errors='replace')
FileNotFoundError: [Errno 2] No such file or directory: 'src/array.hxx'

While --html-details throws an error, just running gcovr counts each header separately for each directory containing a source file that includes it, so I get stuff like:

------------------ ...
File               ...
------------------ ...
src/dir1/array.hxx ...
src/dir2/array.hxx ...
...

If I keep the gcov files, I can see they have the correct names, i.e.:

./^#include#array.hxx.gcov
./src/dir1/^#^#include#array.hxx.gcov
./src/dir2/^#^#include#array.hxx.gcov

This is with gcovr version 4.1 and gcc 8.1

@latk

This comment has been minimized.

Copy link
Member

@latk latk commented Jul 12, 2018

@latk

This comment has been minimized.

Copy link
Member

@latk latk commented Jul 21, 2018

This issue has not seen any progress in a while, so I am closing it for now. If the problem persists, please add further details so it can be re-opened. If someone else is experiencing a similar issue, please create a new issue.

@ZedThree

This comment has been minimized.

Copy link
Author

@ZedThree ZedThree commented Jul 24, 2018

Sorry for the late reply, I went on holiday before I got round to updating this! Apologies for the following information dump...

Here is a zipfile containing a complete toy problem:
gcovr-271.zip

Directory layout is as follows:

├── make.config
├── makefile
├── include
│   ├── makefile
│   ├── sys
│   │   └── foo.hxx
│   └── toy.hxx
├── src
│   ├── makefile
│   ├── sys
│   │   ├── foo.cxx
│   │   └── makefile
│   └── toy.cxx
└── test
    ├── makefile
    └── test.cxx

Also included is compile_commands.json.

Running:

make
make check

should print:

thing is : 4
bar
foo

test

Here is the output of gcovr -r . -k -s:

------------------------------------------------------------------------------
                           GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File                                       Lines    Exec  Cover   Missing
------------------------------------------------------------------------------
src/foo.hxx                                    2       0     0%   9-10
src/sys/foo.cxx                                9       9   100%   
src/toy.cxx                                   10      10   100%   
src/toy.hxx                                    4       4   100%   
test/foo.hxx                                   2       2   100%   
test/test.cxx                                  6       6   100%   
------------------------------------------------------------------------------
TOTAL                                         33      31    93%
------------------------------------------------------------------------------
lines: 93.9% (31 out of 33)
branches: 52.6% (20 out of 38)

Notice that foo.hxx is included twice, once with 0% coverage. Neither header file (foo.hxx, toy.hxx) is under include/. The makefile has a code-coverage-capture target that uses lcov, which reports everything correctly, so I don't think it's something fundamentally wrong with our setup. Unfortunately, lcov automatically cleans up the gcov files it generates, with no option to keep them, so it's not possible to directly compare them. I have a suspicion it's something to do with our recursive-make build system and needing to run gcov in the correct locations, but I'm not certain.

Finally, here is the output of find . -type f -name "*.gcov" | xargs head (after getting rid of the files from system headers):


==> ./src/sys/foo.cxx.gcov <==
        -:    0:Source:foo.cxx
        -:    0:Graph:/home/user/gcovr-271/src/sys/foo.gcno
        -:    0:Data:/home/user/gcovr-271/src/sys/foo.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include "sys/foo.hxx"
        -:    2:
function _ZN3Foo4pushENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE called 3 returned 100% blocks executed 100%
        3:    3:void Foo::push(std::string str) {
        3:    4:  stack.push_back(std::move(str));

==> ./src/toy.cxx.gcov <==
        -:    0:Source:toy.cxx
        -:    0:Graph:/home/user/gcovr-271/src/toy.gcno
        -:    0:Data:/home/user/gcovr-271/src/toy.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include "toy.hxx"
        -:    2:#include "sys/foo.hxx"
        -:    3:
        -:    4:#include <iostream>
        -:    5:

==> ./src/^#include#sys#foo.hxx.gcov <==
        -:    0:Source:../include/sys/foo.hxx
        -:    0:Graph:/home/user/gcovr-271/src/toy.gcno
        -:    0:Data:/home/user/gcovr-271/src/toy.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#ifndef __FOO_H__
        -:    2:#define __FOO_H__
        -:    3:
        -:    4:#include <string>
        -:    5:#include <vector>

==> ./src/^#include#toy.hxx.gcov <==
        -:    0:Source:../include/toy.hxx
        -:    0:Graph:/home/user/gcovr-271/src/toy.gcno
        -:    0:Data:/home/user/gcovr-271/src/toy.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#ifndef __TOY_H__
        -:    2:#define __TOY_H__
        -:    3:
        -:    4:#include <iostream>
        -:    5:

==> ./test/test.cxx.gcov <==
        -:    0:Source:test.cxx
        -:    0:Graph:/home/user/gcovr-271/test/test.gcno
        -:    0:Data:/home/user/gcovr-271/test/test.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include "toy.hxx"
        -:    2:#include "sys/foo.hxx"
        -:    3:
function main called 1 returned 100% blocks executed 67%
        1:    4:int main() {

==> ./test/^#include#sys#foo.hxx.gcov <==
        -:    0:Source:../include/sys/foo.hxx
        -:    0:Graph:/home/user/gcovr-271/test/test.gcno
        -:    0:Data:/home/user/gcovr-271/test/test.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#ifndef __FOO_H__
        -:    2:#define __FOO_H__
        -:    3:
        -:    4:#include <string>
        -:    5:#include <vector>

==> ./test.cxx.gcov <==
        -:    0:Source:test.cxx
        -:    0:Graph:/home/user/gcovr-271/test/test.gcno
        -:    0:Data:/home/user/gcovr-271/test/test.gcda
        -:    0:Runs:1
        -:    0:Programs:1

==> ./^#include#sys#foo.hxx.gcov <==
        -:    0:Source:../include/sys/foo.hxx
        -:    0:Graph:/home/user/gcovr-271/src/toy.gcno
        -:    0:Data:/home/user/gcovr-271/src/toy.gcda
        -:    0:Runs:1
        -:    0:Programs:1

==> ./toy.cxx.gcov <==
        -:    0:Source:toy.cxx
        -:    0:Graph:/home/user/gcovr-271/src/toy.gcno
        -:    0:Data:/home/user/gcovr-271/src/toy.gcda
        -:    0:Runs:1
        -:    0:Programs:1

==> ./^#include#toy.hxx.gcov <==
        -:    0:Source:../include/toy.hxx
        -:    0:Graph:/home/user/gcovr-271/src/toy.gcno
        -:    0:Data:/home/user/gcovr-271/src/toy.gcda
        -:    0:Runs:1
        -:    0:Programs:1

==> ./foo.cxx.gcov <==
        -:    0:Source:foo.cxx
        -:    0:Graph:/home/user/gcovr-271/src/sys/foo.gcno
        -:    0:Data:/home/user/gcovr-271/src/sys/foo.gcda
        -:    0:Runs:1
        -:    0:Programs:1
@latk

This comment has been minimized.

Copy link
Member

@latk latk commented Jul 24, 2018

@latk latk removed the more info needed label Jul 24, 2018
@latk latk reopened this Jul 24, 2018
@lisongmin

This comment has been minimized.

Copy link
Contributor

@lisongmin lisongmin commented Aug 4, 2018

It may cause by -I../include option, and we should try real source file relative to the source directory.

  currdir      /tmp/abc
  gcov_fname   /tmp/tmpu47t14wr/^#include#toy.hxx.gcov
               ['        -', '    0', 'Source', '../include/toy.hxx\n']
  source_fname /tmp/abc/src/toy.gcda
  root         /tmp/abc
  fname        /tmp/abc/src/toy.hxx

now fname is dirname(source_fname) + basename(gcov_fname)

  • fname /tmp/abc/src/toy.hxx

which should be realpath(dirname(source_fname) + gcov_fname)

  • fname /tmp/abc/include/toy.hxx

without realpath test/../include/toy.hxx and src/../include/toy.hxx may consider as two different files.

@@ -196,8 +198,13 @@ def guess_source_file_name_heuristics(
     if os.path.exists(fname):
         return fname

-    # 3. Try using the path to the gcda file as the source directory
+    # 3. Try using the path relative to source directory
     source_fname_dir = os.path.dirname(source_fname)
+    fname = os.path.join(source_fname_dir, gcovname)
+    if os.path.exists(fname):
+        return os.path.realpath(fname)
+
+    # 4. Try using the path to the gcda file as the source directory
     fname = os.path.join(source_fname_dir, os.path.basename(gcovname))
     return fname
------------------------------------------------------------------------------
                           GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File                                       Lines    Exec  Cover   Missing
------------------------------------------------------------------------------
include/sys/foo.hxx                            2       2   100%
include/toy.hxx                                4       4   100%
src/sys/foo.cxx                                9       9   100%
src/toy.cxx                                   10      10   100%
test/test.cxx                                  6       6   100%
------------------------------------------------------------------------------
TOTAL                                         31      31   100%
------------------------------------------------------------------------------
@latk

This comment has been minimized.

Copy link
Member

@latk latk commented Aug 5, 2018

Thank you @lisongmin, you are absolutely right that we should use realpath() here – in fact, we should use realpath() for all the heuristics in that function.

I am not quite sure whether it is correct to resolve the source code path relative to the gcda file (confusingly called source_fname in the code). I think the source code paths should be interpreted relative to the original GCC working directory. In this particular example they are the same because of an in-source build, but not in general. Gcovr's guess for the GCC working directory is called chdir in run_gcov_and_process_files().

During the last few weeks I've been spending some time on gcovr's path heuristics. There's a lot of weird code around there and I hope to clean some of it up soon. Gcovr tends to make some assumptions about the build process that are not always a good fit in the real world, e.g. either GCC is invoked somewhere in the build directory or from the --root. This recursive-Makefile approach combined with a separate build directory would probably break the heuristics (haven't tested it yet).

I am super thankful for this issue because it exposes some of these bad assumptions!

@lisongmin

This comment has been minimized.

Copy link
Contributor

@lisongmin lisongmin commented Aug 6, 2018

Be aware symlink may changed via realpath(). This may affect to filter in some case.

@darkmattercoder

This comment has been minimized.

Copy link

@darkmattercoder darkmattercoder commented Dec 3, 2018

Hi,

is there anything that I can help with? I also experience those things. Shall I post outputs similar to the above?

@dquist

This comment has been minimized.

Copy link

@dquist dquist commented Dec 5, 2018

Also experiencing the same error

Traceback (most recent call last):
  File "/usr/local/bin/gcovr", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python2.7/dist-packages/gcovr/__main__.py", line 588, in main
    print_html_report(covdata, options)
  File "/usr/local/lib/python2.7/dist-packages/gcovr/html_generator.py", line 275, in print_html_report
    errors='replace')
IOError: [Errno 2] No such file or directory: 'test/cmake/../..Utility/Flags.h'

Any other info I can give?

Edit: Everything works fine when using standard gcov, but I get the error above when using
--gcov-executable 'llvm-cov gcov' option

Edit 2: At least in my instance, this exception only seems to appear when I am including a file via a compiler include path, not relative to the file being covered.

ie

// Include relative path
#include "../../mylib/include/my-header.h" // <-- Works

// Include using predefined compiler include path
#include "my-header.h"  // <-- Exception

Edit 3: Yeah interestingly the xml report works fine, it's just the html generator that throws an error. The xml report does report the same file multiple times, each with a different path, so this does seem to be a path issue. Interestingly I only get this issue when using clang/llvm-cov. Everything works fine when building with gcc and gcov.

@latk

This comment has been minimized.

Copy link
Member

@latk latk commented Dec 6, 2018

I now have a bit of time to look at issues again since I completed an important project today.

The core problem is that I don't understand what gcov's relative Source paths mean precisely: what directory are they relative to? The gcda file? The source file? The compiler invocation directory? The gcov invocation directory? Could this be solved if gcovr is also given any -I options?

I'll try looking at the gcov source code and at how lcov handles this, sometime over the next few weeks. If someone is quicker than me and can post an explanation here (with sources/references), that would also be most welcome. I'll also try turning @ZedThree's excellent example into an xfail test case.

Since the HTML details report has to open the source files in order to display the details, it is the only report that will die due to this issue.
Unfortunately the tests currently only run with GCC 5, not more recent versions (#206) and not LLVM (#134). So it's possible that small incompatibilities have been missed.

@dquist

This comment has been minimized.

Copy link

@dquist dquist commented Dec 7, 2018

Thank you for looking into this @latk!

Judging from the fact I can hardly find any online resources using gcovr with llvm, I must assume it's not a well used feature. I'm not surprised something got broken by mistake.

@latk latk referenced this issue Dec 13, 2018
@thefifo

This comment has been minimized.

Copy link

@thefifo thefifo commented May 10, 2019

+1 Seeing the issue still with GCC 8.2
I'd also appreciate a hint regarding a workaround for this issue.

@z3bu

This comment has been minimized.

Copy link

@z3bu z3bu commented May 13, 2019

Same issue with gcc 7.4.0 (gcov alone works)

$ gcc --version
gcc.exe (Rev1, Built by MSYS2 project) 7.4.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.