8000 --html-details cannot locate sources · Issue #368 · gcovr/gcovr · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

--html-details cannot locate sources #368

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

Closed
DexterMagnific opened this issue Apr 28, 2020 · 52 comments
Closed

--html-details cannot locate sources #368

DexterMagnific opened this issue Apr 28, 2020 · 52 comments

Comments

@DexterMagnific
Copy link
DexterMagnific commented Apr 28, 2020

Hi,

I have a problem with the --html-details options. It seems like gcovr tries to search for sources inside the build dir.

Here is my dir structure:

root/
  src/
    lib/
    test/
    build/debug/{lib|test}

the build dir is inside the src/ dir

gcda and gcno files are located inside src/build/debug/{lib|test}

with --html-details options, gcovr seems to search for source files inside src/build/debug/{lib|test}.

I have the following exception:

Traceback (most recent call last):
  File "C:\Python37\Scripts\gcovr-script.py", line 11, in <module>
    load_entry_point('gcovr==4.2', 'console_scripts', 'gcovr')()
  File "c:\python37\lib\site-packages\gcovr\__main__.py", line 254, in main
    print_reports(covdata, options, logger)
  File "c:\python37\lib\site-packages\gcovr\__main__.py", line 373, in print_reports
    generator(covdata, output.value, options)
  File "c:\python37\lib\site-packages\gcovr\html_generator.py", line 249, in print_html_report
    errors='replace') as INPUT:
FileNotFoundError: [Errno 2] No such file or directory: 'src\\build\\lib\\debug\\myfile.cpp'

the command line run from root/:
'''
gcovr.exe --html --html-details -r . -o coverage.html --gcov-executable /c/Qt/Tools/mingw730_32/bin/gcov.exe -v
'''

Can you please help with this ?

Thanks

@steven-ro
Copy link
steven-ro commented May 5, 2020

The problem occurs for me as well, if I have my source directory at /src with other paths e.g. /workingdir or /home/username/src it works

Actually I think it occurs, when you have a repetition of your source folder name in in the file path, at least this seems to be the problem in my case. /src/path/to/lib/src/something.cpp as it seems to replace the /src in the path (but that's only a first guess, I did not look into it)

@latk
Copy link
Member
latk commented May 6, 2020

I fully believe that something is broken here, because gcovr's path handling depends a lot on guesses and heuristics. And those guesses often get it wrong. So thank you for adding these useful data points :)

What might help here is running gcovr from your build dir, especially if you are using cmake:

cd src/build/debug; gcovr -r ../.. --html-details -o ../../../coverage.html ...

Do you at least get the correct files in other report formats, e.g. the default text report?

@DexterMagnific
Copy link
Author

Running gcovr from the build dir did not fix the problem.

As for the text report, I do get a summary (as for the HTML report), but as you can't browse the source file, I do not know if it is relevant.

Maybe I did not specify that I do get an HTML report with per-file coverage statistics. The only problem is that I cannot browse the files because the paths are not correct.

@latk
Copy link
Member
latk commented May 6, 2020

I think I have a hunch. Assuming that you are using gcovr 4.2, could you try editing the file gcovr/html_generator.py around line 248:

         data['ROWS'] = []
-        currdir = os.getcwd()
-        os.chdir(options.root_dir)
-        with io.open(data['FILENAME'], 'r', encoding=options.source_encoding,
+        with io.open(f, 'r', encoding=options.source_encoding,
                      errors='replace') as INPUT:
             for ctr, line in enumerate(INPUT, 1):
                 data['ROWS'].append(
                     source_row(ctr, line.rstrip(), cdata.lines.get(ctr))
                 )
-        os.chdir(currdir)

Background: the data['FILENAME'] variable is set to options.root_filter.sub('', f), which just chops off the root path from the front. This should usually be fine, but there's no good reason to do this for a filename that we have to open. In particular, there's no strong guarantee that the root_filter only matches at the start of the filename – this needs to be checked in more detail. This part of gcovr still carries a bit of technical debt :)

Even if this isn't the exact problem, I think the root cause has to be nearby.

It would be helpful to have a small test case that reproduces the problem (hello world style source code plus a script or makefile to run gcc & gcovr), but if this is too Windows-specific that might be difficult.

@DexterMagnific
Copy link
Author

Unfortunately no change, no matter if gcovr is run from the root dir or the build dir.

Before 8000 the patch:

FileNotFoundError: [Errno 2] No such file or directory: 'build\\lib\\debug\\MyFile.cpp'

After the patch:

FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\XXX\MyProject\\src\\build\\lib\\debug\\MyFile.cpp'

So the file is still searched in the build dir. the difference is only on the stripped 'root' prefix

@latk
Copy link
Member
latk commented May 7, 2020

Thank you for helping investigate this, that was really helpful in ruling out some problem areas, although I still don't know what the actual problem is. If you can reproduce the issue in a small sample project this would make it possible to do deeper debugging, but I won't promise any timeline.

If you need a workaround: use gcovr to output JSON data, write a script to fix the path names in the JSON, and then use gcovr to turn the JSON into the HTML report. Alternatively, please consider other coverage tools such as lcov.

@latk latk added the Gcov label May 7, 2020
@k10blogger
Copy link

I use a two step process for a workaround for this issue. The first step uses the 'gcov' to map the data to the c file and then at root i use the gcovr to generate HTML from that data. This is possible because during my build process i isolate the files and create a batch file for the same. This happens automatically for me.

What i fail to understand is how is gcovr guessing the path and from where?
Cant we have a separate path list for sources and objects, so that we can append the sources path to one and append the object path to other.
The gcovr just needs to take each object in object path and look for the source in source path. Instead of it guessing the path?

@latk
Copy link
Member
latk commented May 8, 2020

Hi @k10blogger! Running the gcov tool manually tends to work very well, except when multiple object files contain coverage data for the same source file, e.g. inline functions in header files.

Gcovr needs to figure out from which directory the gcov tool should be executed. Generally, this should be exactly the directory from which the compiler (gcc) was invoked. I'm not entirely sure why this is necessary, but it seems that the gcno files contain relative pathnames to the source files. Gcovr uses a brute-force approach and just tries out lots of directories until gcov successfully produces output, see the process_datafile() function.

def process_datafile(filename, covdata, options, toerase, workdir):

This issue indicates that although the gcov tool produces correct output, gcovr misunderstands the source file name as reported by gcov. This is likely an error at the gcov–gcovr interface, which won't be fixed by giving a source path to gcovr. GCC 9 added working-directory metadata to the gcov reports which might fix the issue, but gcovr doesn't make use of it yet.

@thoni56
Copy link
thoni56 commented May 10, 2020

I have something similar. In my case I have proper sources in the root directory and unittests in a subdirectory. I'm sending objects to a subdirectory .objects in both directories.

In the textual output I get references to source files from the unittests but with the .objects directory inserted, like:

unittests/.objs/yylex_tests.c

Those do not exists, so that's probably why the HTML output fails.

Small example:
gcovr-issue-368.zip

From this example you can see that gcovr thinks there are two source files in the subdirectory, booth the actual source file (with coverage 0%) , and then another in the .objects subdirectory of that subdirectory (with coverage 100%).

I'm running gcovr 4.2 on WSL 4.4.0-18362-Microsoft.

@Spacetown
Copy link
Member

@thoni56 You are using two different root directories for compiling but only one for running gcovr. The files produced by gcov contains the path used when compiling and the guessing of the path use the wrong root.

gcovr/gcovr/gcov.py

Lines 186 to 211 in 39f5a82

def guess_source_file_name_heuristics(
gcovname, currdir, root_dir, starting_dir, source_fname):
# gcov writes filenames with '/' path seperators even if the OS
# separator is different, so we replace it with the correct separator
gcovname = gcovname.replace('/', os.sep)
# 0. Try using the current working directory as the source directory
fname = os.path.join(currdir, gcovname)
if os.path.exists(fname):
return fname
# 1. Try using the path to common prefix with the root_dir as the source directory
fname = os.path.join(root_dir, gcovname)
if os.path.exists(fname):
return fname
# 2. Try using the starting directory as the source directory
fname = os.path.join(starting_dir, gcovname)
if os.path.exists(fname):
return fname
# 3. Try using the path to the gcda file as the source directory
source_fname_dir = os.path.dirname(source_fname)
fname = os.path.join(source_fname_dir, os.path.basename(gcovname))
return fname

If you don't change to the sub directory for compiling unit but give the files with the sub directory everything is fine.

@thoni56
Copy link
thoni56 commented Jun 6, 2020

@Spacetown Fair enough, is there any other way to merge the two data sets? I really don't want to bring up all my unittest compilation into the top level Makefile...

@Spacetown
Copy link
Member

You can create a JSON for each executable using the Sam working directory as the compiler. This JSONs can be merged to one HTML report.

@thoni56
Copy link
thoni56 commented Jun 6, 2020

Say what?!?

I'm guessing that by "using the Sam working directory as the compiler" you mean that I should create two separate Json reports in the two directories where the compiles are run, right? Then those JSON files can be merged into a single report? Ok, I'lll try that.

@Spacetown
Copy link
Member

Sorry for my bad English but you got it.

@f18m
Copy link
f18m commented Dec 11, 2020

I'm hitting the same problem of original reporter of this bug... since indeed I have a similar tree structure for my project...

@f18m
Copy link
f18m commented Dec 11, 2020

@Spacetown any help we can provide on fixing this problem? What's the status? is there a work-in-progress MR perhaps? Any hint on where to start the debugging? I would like to help if I can... Thanks a lot!

@Spacetown
Copy link
Member

@f18m Can someone check if the generation of two separate JSON files and merging thme together will work?

@f18m
Copy link
f18m commented Dec 11, 2020

@f18m Can someone check if the generation of two separate JSON files and merging thme together will work?

Hi @Spacetown I tried something like that: instead of running

gcovr -r . --verbose --print-summary --html-details $OUTDIR/coverage.html

I tried using:

gcovr -r . --json >$OUTDIR/coverage.json gcovr --add-tracefile $OUTDIR/coverage.json --html-details $OUTDIR/coverage.html

but it fails as well - I think the coverage.json file contains wrong source file paths and thus the html output generator will keep looking for the wrong source files also in this case.

Spacetown added a commit to Spacetown/gcovr that referenced this issue Dec 16, 2020
@Spacetown
Copy link
Member

I've created a test, there you can see the commands and from which directory the have to be called.
All gcovr calls have to use the same working directory. The second one has a different root directory (last argument). This is needed because the paths in the JSON are relative to the working directory of gcovr.
If this is the solution for your problem I'll create a PR for this test.

@f18m
Copy link
f18m commented Dec 17, 2020

Hi @Spacetown ,

I've created a test, there you can see the commands and from which directory the have to be called.
All gcovr calls have to use the same working directory. The second one has a different root directory (last argument). This is needed because the paths in the JSON are relative to the working directory of gcovr.

Sorry it's not clear to me what you mean. You're saying that I should invoke gcovr in every single subfolder of my project that contains source code? that's going to be quite cumbersome... also I think that --html-details should work out of the box in a single run, without the need to combine together multiple --json output files, right?

Also gcov >= 9.1 has a "current_working_directory" JSON entry in gcov JSON output (https://gcc.gnu.org/onlinedocs/gcc/Invoking-Gcov.html#Invoking-Gcov) that could be used to avoid such issue, right?

If this is the solution for your problem I'll create a PR for this test.

But just to understand: in your feature branch you did not change gcovr internal path handling but just added a test to show that gcovr works if it's invoked multiple times from different folders, right?

Thanks!!

@Spacetown
Copy link
Member

Sorry it's not clear to me what you mean. You're saying that I should invoke gcovr in every single subfolder of my project that contains source code? that's going to be quite cumbersome... also I think that --html-details should work out of the box in a single run, without the need to combine together multiple --json output files, right?

Not in each source directory. You have to call gcovr for each executable from a common base directory giving the root directory used for compiling the executable.
Reason:
The JSON file contains paths relative to the working directory of gcovr and the gcov output contains paths relativ to the compiler working directory.

Also gcov >= 9.1 has a "current_working_directory" JSON entry in gcov JSON output (https://gcc.gnu.org/onlinedocs/gcc/Invoking-Gcov.html#Invoking-Gcov) that could be used to avoid such issue, right?

We use the text output and support also old versions of gcc. At work we use 4.5 and 4.8.

But just to understand: in your feature branch you did not change gcovr internal path handling but just added a test to show that gcovr works if it's invoked multiple times from different folders, right?

Yes

@ribtoks
Copy link
ribtoks commented Jul 7, 2021

I'm also hitting the same issue. --html works, but --html-details fails with FileNotFoundError: [Errno 2] No such file or directory.

Any tips how to overcome this?

@Spacetown
Copy link
Member

@ribtoks Run gcovr in the same directory than GCC. The --html-details fails because with this option the source files are read and added to for the report.

@ribtoks
Copy link
ribtoks commented Jul 12, 2021
6D40

@Spacetown Thanks for the suggestion, but it does not work.

I have a following codebase structure:

src/
  - corelib/
    - models/
      - myclass.h
      - myclass.cpp

When I'm running cd src && gcovr -r ./ , the report looks like this:

Directory: ./
------------------------------------------------------------------------------
File                                       Lines    Exec  Cover   Missing
------------------------------------------------------------------------------
src/corelib/mymodel.cpp

Note: missing /models/ component.

After I try: gcovr -r ./ --html-details -o coverage/ and I get the error (surprise-surprise!) FileNotFoundError: [Errno 2] No such file or directory: 'corelib/mymodel.cpp'

Of course it's not found, it's missing /models/ subdirectory. And there's no way I can fix it, so it seems to me it's a bug!

@Spacetown
Copy link
Member

Sorry, --verbose was the correct one. Do you run the compiler with qmake and gcovr with make?
For me it seems that qmake runs the compiler not in src but in the sub directory corelib. Can you check this?

@ribtoks
Copy link
ribtoks commented Jul 13, 2021

@Spacetown , qmake only generates the Makefile, but yes, gcc runs inside corelib directory. Thanks! Do I now have to launch gcovr from every subdirectory separately to generate a separate reports? Is there a normal way to launch it only once?

@Spacetown
Copy link
Member

@ribtoks Yes, you have to run gcovr for each directory in the src directory and generate a trace file. This trace files can be merged together with a separate call.

Or can you configure qmake to keep the working directory?

@ribtoks
Copy link
ribtoks commented Jul 13, 2021

@Spacetown No, I doubt I can tweak qmake behavior. Can you please provide some pointers to documentation on how to generate and merge trace files?

@Spacetown
Copy link
Member

@ribtoks
Copy link
ribtoks commented Jul 19, 2021

For anybody looking for solution in future: I did not come up with anything convenient. Manually searching for correct directories (with find), calling gcovr inside them and then merging relevant coverage files is nuts leads to a lot of boilerplate and copy-paste across projects. Hopefully this will be supported in the software itself.

@Spacetown
Copy link
Member

@ribtoks Do you have a solution how to guess the correct working directory of the gcc used by qmake or a self written make? The only idea I have is to throw the ball back to the user and add a option to replace the code of the guessing function

def guess_source_file_name_heuristics(

@ribtoks
Copy link F438
ribtoks commented Jul 20, 2021

@Spacetown no, this I don't know. But maybe you can use a file search from root directory and hope that there will be no similarly named files in the same file tree.

It does not necessarily have to be a solution that covers 100% of cases of everybody. If it would cover 90%, already would make life better for many people.

@Spacetown
Copy link
Member

I've prepared a quick test branch (https://github.com/Spacetown/gcovr/tree/recursive_search_file), please can you check if this is working?

@ribtoks
Copy link
ribtoks commented Jul 26, 2021

@Spacetown My gcovr setup runs in CI/CD. I will try your branch, but it will take me a bit to replicate the setup locally. Thanks a lot for your help and prompt changes! I will be back sometime soon.

@blackliner
Copy link

I just added CCACHE_BASEDIR=/home/user/.conan/data/package/....some_hashed_dir/ and ended up with all of my sourcefiles not being found anymore 🤔

Is gcov or gcovr relying on information stored in the built files by the compiler or something similar?

@latk
Copy link
Member
latk commented Aug 12, 2021

@blackliner Yes, GCC creates .gcno files along the .o object files. Information in the .gcno is used to associate coverage data with source code locations. Changing the location of these files will break gcov/gcovr, though it might be possible to work around some of the issues with gcov's “cross-profiling” feature (aka GCOV_PREFIX). See also #481 (comment).

@blackliner
Copy link
blackliner commented Aug 12, 2021

But I did not change any location, just ccache makes some paths that start with CCACHE_BASEDIR from absolute to relative.

Ah, you mean that could lead to a different invocation of gcc and thus gcov is confused... damn, but will try the GCOV_PREFIX!

@blackliner
Copy link

Seems my issue relates to ccache/ccache#219

@ocroquette
Copy link
ocroquette commented Jan 14, 2022

We just hit this bug too. The weird thing is that it happened by updating CMake 3.20 to 3.22. We use CMake and ninja to instrument the compiler to generate coverage information and then call gcovr. With CMake 3.20, --html-details works fine. With CMake 3.22, we get the same error, because gcov is looking for cpp files in the build tree instead of the source tree. The --root option is correctly set but this doesn't help. It is still a mistery why the bug is triggered.
As a (bad) workaround, we removed --html-details for now.

@Spacetown
Copy link
Member

@ocroquette Which version of gcovr are you using?

@ocroquette
Copy link

Hi @Spacetown I can reproduce the issue with 4.2 and 5.0

@Spacetown
Copy link
Member

In cmake 3.21 there is one change mentioned which maybe change the behaviour of gcc:

The Ninja Generators now pass source files and include directories to the compiler using absolute paths. This makes diagnostic messages and debug symbols more consistent, and matches the Makefile Generators.

If you use latest master branch, can you upload the build log where gcovr with the additional option --verbose? Then we can see the call of the compiler, the gcovr call and the search of the files?

@Spacetown
Copy link
Member

Canc you try the LATEST master branch because we changed with #525 the heuristic to find the correct working director. See #525 (comment) for details.

@ocroquette
Copy link

Quick update...

  • Using the master branch of gcovr does not help
  • Updating Ninja to 1.10.2 does not help

Related CMake changes:

I cannot send a log file for now sorry, I need to create a minimal example without internal information first.

@Spacetown
Copy link
Member

Is it possible to strip the log to the gcov file which reference the file which isn't found? Maybe you can also send the file via mail if this is possible for you. You can also try the option -fprofile-abs-pathof gcc version 9 or newer.
In our docker tests we use cmake version 3.22.1 and there is no problem.

@ocroquette
Copy link

@Spacetown I sent the log files to your GMX address. -fprofile-abs-path does not help, gcovr still fails with the same error.

@ocroquette
Copy link
ocroquette commented Jan 16, 2022

@Spacetown found the issue. We were setting "--source-prefix" without a good reason, which, combined with the CMake update, led to the issue. Everything is fine without "--source-prefix", even with 4.2.

Now I wonder if this Github issue should still be open?

@Spacetown
Copy link
Member

Can this be checked with the branch of #597?

@Spacetown
Copy link
Member

No progress for a while. If the problem persist, please reopen.

@ddomnik
Copy link
ddomnik commented Mar 30, 2023

Hello @Spacetown, I think I face the same or a very similar problem on Windows using Cygwin for my catch2 UnitTests.
Folder structure looks like this:

myComponent/
- myComponent.c
- myComponent.h
- unit_test
   - myComponent.test.cpp
   - CMakeLists
   - build

I run cmake .. and then make from inside the build dir. For gcovr, it does not seem to help from which directory I run the command, it will always fail.

It searches for the source files in this directory: CMakeFiles/myComponent.dir/cygdrive/c/Users/USERNAME/Documents/esp/project/myComponent/myComponent.c
this ofc will not work, as in this folder are only the compiled .c.o, .c.o.d, .c.gcda, .c.dcno files.

How to tell gcovr which dir it should use?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

0