Libtool prefixes objects but gcov requires them without prefix - c

I need to perform some test coverage with gcov on a shared library I am working on.
The problem is libtool renames the object files from my-name.c to libmylib_la-my-name.lo and gcov is unable to handle that conversion. Everytime I run it, the error cannot open notes file is generated.
If I manually rename my-name.c to libmylib_la-my-name.c after the build gcov works fine, so there is no other problem apart the filename mangling.
Addendum
Trying to provide a minimal working example I discovered the filename mangling happens only when lib..._la_CFLAGS is set (and also when it is set to an empty value).
cat <<EOT > configure.ac
AC_INIT(sample,0.0.1)
AC_CONFIG_SRCDIR(configure.ac)
AM_INIT_AUTOMAKE(foreign)
LT_INIT
AC_PROG_CC
AC_CONFIG_FILES(Makefile)
AC_OUTPUT
EOT
cat <<EOT > Makefile.am
lib_LTLIBRARIES=libsample.la
libsample_la_SOURCES=sample.c
# The following line triggers the filename mangling (libsample_la-sample.lo instead of sample.lo)
libsample_la_CFLAGS=
EOT
touch sample.c && autoreconf -if && ./configure && make
Is there a way to avoid the filename mangling operated by libtool or to let gcov understand the filename mangling scheme?

Gcov gcda and gcno files are named after object files. You can run gcov from source directory directly on object file or you can use -o option of gcov to specify the object file and corresponding gcov files.
For example I have a small project that builds a shared library. I pass gcov flags to make command:
make CFLAGS="-O0 --coverage" LDFLAGS=--coverage
Object files and corresponding gcno files are created in src/.libs folder:
$ ls -la src/.libs
libtest_la-test.o
libtest_la-test.gcno
The source file is in src folder
$ ls src/
test.c
Next I run my test suite and gcda files are created:
$ ls -la src/.libs
libtest_la-test.o
libtest_la-test.gcno
libtest_la-test.gcda
Now I can enter src directory and run gcov, specifiying object file name:
$ gcov -o .libs/libtest_la-test.o test.c
File ‘test.c’
Lines executed:27.08% of 96
Creating ‘test.c.gcov'
It is also possible to just run gcov on object file:
$ gcov .libs/libtest_la-test.o
File ’test.c’
Lines executed:27.08% of 96
Creating ’test.c.gcov'
Or even just specifying base name of object file and gcov files:
$ gcov .libs/libtest_la-test
File ’test.c’
Lines executed:27.08% of 96
Creating ’test.c.gcov'
But I would suggest another automated approach that works very well for me, using lcov. I invoke it from top directory specifying paths to source files and object files:
$ lcov --base-directory src --directory src/.libs/ --capture --output-file gcov.info
Capturing coverage data from src/.libs/
Found gcov version: 4.8.2
Scanning src/.libs/ for .gcda files ...
Found 10 data files in src/.libs/
Processing .libs/test_la-test.gcda
[…]
Finished .info-file creation
$ genhtml -o html/coverage gcov.info
Reading data file gcov.info
Found 10 entries.
Found common filename prefix "/usr/src/libtest”
Writing .css and .png files.
Generating output.
Processing file src/test.c
[…]
Writing directory view page.
Overall coverage rate:
lines......: 56.1% (2098 of 3737 lines)
functions..: 68.8% (139 of 202 functions)
Now html/coverage directory contains html files that can be easily analyzed in a web browser.

Libtool shouldn't change .c file names. However, it does change .o file names; this is because it needs to compile libraries twice on some platforms (once to create position-independent code (PIC) for .so (shared) libraries, once to create code which is not PIC for .a (static) libraries).
What you may be seeing is the fact that gcov has issues with shared libraries. See "can gcov deal with shared object?" for details.
If that doesn't fix it, I'll have to agree with Brett that you ned to supply more info.

Related

On `gcc` 11, how to change the prefix of `.gcno` files when using `--coverage` so they match what `gcov` expects?

I'm adding code coverage on a C project of mine. The process I'm following is this:
# compile a .c file, expecting two new files "a.out" and "main.gcno"
gcc --coverage main.c
# run executable to get coverage data, expecting to create a new "main.gcda" file
./a.out
# use `gcov` to get formatted reports
gcov main.c
When I'm using gcc on version 10.3.0, this all works as expected without issues. When using gcc with version 11.1.0 however, both the main.gcno and main.gcda files have a different name, which breaks the flow as explained below.
Using gcc --coverage main.c produces two files, a.out and a-main.gcno. Then running the executable with ./a.out creates a new file a-main.gcda. Notice the prefix a- on the coverage files. When running the next command gcov main.c, I get the following error:
main.gcno:cannot open notes file
main.gcda:cannot open data file, assuming not executed
Because it is looking for the files main.gcno and main.gcda, but it can't find them.
Why does the new version of gcc do that? Everything I read online assumes that the output of the compiler should be in sync with gcov when run on the same source file. I couldn't find anywhere a way to change the output name of coverage files.
In case it helps, I also noticed the a- prefix depends on the output name. So if an output name is specified (gcc --coverage main.c -o test) then the coverage files will have that as prefix (test-main.gcno and test-main.gcda).
I've also tried manually renaming the files to remove the prefix, and gcov seems happy with that. The problem is that I'm trying to automate the process, and I'd like a more robust way than trying to guess what output name the compiler uses for coverage files.
For reference, I'm also using gcov on version 11.1.0. It's not clear to me if that's related to the gcc being the same version.
Why does the new version of gcc do that?
See the release notes for GCC 11, specifically the third bullet of the Caveats which starts "Naming and location of auxiliary and dump output files changed".
Everything I read online assumes that the output of the compiler should be in sync with gcov when run on the same source file. I couldn't find anywhere a way to change the output name of coverage files.
As it says in the release notes, the -dumpbase option is what you want. To remove any prefix, use -dumpbase ''
I'm not sure whether gcov should have been updated, or if the gcc-11 change should not have affected the coverage files. Edit: This is behaving as expected.
Another solution is to compile and link in two separate steps:
gcc --coverage main.c -c
gcc --coverage main.o
This way the coverage data files do not get the prefix, and gcov finds them as expected.

Compiling dynamically linked library in a makefile

I am trying to run my library using a make file. I currently have a dynamic library called libname.so which I created by linking the object files of some of my C files. This library works correctly when I run the following lines of code in my linux shell :
gcc -L. main1.c -lname -o out
LD_LIBRARY_PATH=.
export LD_LIBRARY_PATH
But when I copy these exact lines of code in to a make file and name the make file title for this function 'names' and then run 'make names' in linux shell, I get the following error:
./out: error while loading shared libraries: libname.so: cannot open shared object file: No such file or directory
Then once again when I run the final two lines of code shown at the end of the makefile function again then run the out file, it is fixed and the program works again.
I just need to figure out how to make it work directly from the makefile.
LD_LIBRARY_PATH=.
export LD_LIBRARY_PATH
These two lines do not influence the creation of the program in any way because you type the lines after creating the program.
These lines are not used for building your program, but they influence running the program (by typing ./out).
If you compile your program using gcc directly (not using make) and open a new terminal, you also have to type these two lines (again) before you run the program.
It does not matter how you build the program (by typing gcc manually or by running make):
After opening a new terminal, you will need to type these two lines before you run the program.
However, the dynamic linker does not only use the path information from LD_LIBRARY_PATH but also from the DT_RUNPATH information in the executable.
Unlike the LD_LIBRARY_PATH variable which is set on one console (or terminal) only, the DT_RUNPATH information is stored directly in the executable file.
As described in another question on this site, you can set the DT_RUNPATH information using the -Wl,-rpath=<value> switch:
gcc -L. main1.c -lname -o out -Wl,-rpath=.
If you do this, the dynamic linker will search your library (libname.so, if I understand correctly) in the current directory.
Note:
. really means: In the current directory; it does not mean: In the same directory as the executable file!
If your program is stored in the directory ./somedir and you type somedir/out, the file ./libname.so is searched, not the file ./somedir/libname.so.
This is both the case for the -Wl,-rpath= method and for the LD_LIBRARY_PATH= mehtod.

GCovr generate a 0% coverage report, if cpp,gcno and gcda files are not present at same folder

I have two folder inside same folder as below :-
1.src (it contains my cpp file)
2.linux (where I am running g++ and executing o file)
now I am running commands as below
cd linux
g++ --coverage ../src/example1.cpp -o example1
./example1
cd ..
/opt/gcovr-3.2//scripts/gcovr -v -r .
I got output as, with 0% coverage
Scanning directory . for gcda/gcno files...
Found 2 files (and will process 1)
Running gcov: 'gcov /opt/gcovr-3.2/doc/examples/example1/linux/example1.gcda --branch-counts --branch-probabilities --preserve-paths --object-directory /opt/gcovr-3.2/doc/examples/example1/linux' in '/opt/gcovr-3.2/doc/examples/example1/linux'
Parsing coverage data for file /opt/gcovr-3.2/doc/examples/src/example1.cpp
Filtering coverage data for file /opt/gcovr-3.2/doc/examples/src/example1.cpp
Gathered coveraged data for 0 files
To debug further, I goto 'linux' folder as run below command
gcov /opt/gcovr-3.2/doc/examples/example1/linux/example1.gcno --branch-counts --branch-probabilities --preserve-paths --object-directory /opt/gcovr-3.2/doc/examples/example1/linux
I got output as, with valid coverage
/opt/gcovr-3.2/doc/examples/example1/linux
File '../src/example1.cpp'
Lines executed:85.71% of 7
Branches executed:100.00% of 2
Taken at least once:50.00% of 2
Calls executed:100.00% of 1
../src/example1.cpp:creating '..#src#example1.cpp.gcov'
Now I want to know what wrong I am doing? my project is complex, so I don't want to do copy all cpp files from respective 'src' folder to respective 'linux' folder. I tried with --object-directory then also same result.
gcovr uses .gcov files for analysis.
I also faced similar issue and overcame it in 2 steps by manually generating .gcov file
In the folder which contains gcno files run gcov -b -l -p -c *.gcno
This will generate gcov files with all details from gcno and gcda files.
Go to Project Root Folder and run gcovr -g -k -r . --html --html-details -o tp.html or any gcovr command with -g option, -g option tells gcovr to process gcov output files
Hope this solution Helps..
I have been working with gcovr and gcov since few months now.
The only thing that's different from what I am doing is that you are not specifying the path properly in "-r" flag.
You should specify the complete path always.
It's fine even if it's not canonical but path should be complete.
And always specify an extra slash "/" or "\" for linux and windows respectively after the root directory name.
For example
-r "/path/to/root/directory/"
It seems that this extra slash is important otherwise it gives problem.
Don't know the exact problem, but I assumed it doesn't search recursively without the end slash.
Om MacOS/Darwin with Homebrew gcc/gcovr installed, it turned out that the gcov used by gcovr was /usr/bin/gcov which is the Apple/clang version which is incompatible.
I don't understand why there is no /usr/local/bin/gcov but creating that as a link to gcov-9 solved it for me.
If applicable, turn off ccache.
If ccache ever does its thing (let's say you wiped the build directory), it will happily restore the object files from cache while not restoring the *.gcno files, because it doesn't know about those.

Difference in commands to create executables from shared library

What is the difference between the launch and launch1 executable created by the following
commands:
gcc main.o ./my_lib/libshared_secure.so -o launch
and
gcc main.o -L ./my_lib -lshared_secure -o launch1
Here main.o is the object code of the main function and libshared_secure.so is a shared
library. I expected that both launch and launch1 would be same but I was incorrect. Why
are the two executables different and which part of the above commands causes these
differences?
The difference was that in executing launch I didn't have to set and export LD_LIBRARY_PATH variable to the path of libshared_secure.so but I had to do that in executing launch1.
readelf -d launch | grep libshared_secure.so
Will report [./my_lib/libshared_secure.so]. However, for launch1 it will be [libshared_secure.so]. Linker will try to load given libraries in system directories and in directories relative to your current working directory (so, if you'll launch binary from some other place (not ./launch but e.g. ../launch from subdirectory) - it will not find library without LD_LIBRARY_PATH).
Other functionality is rpath - linker could take directory to look into, and write it into ELF header, without need to specify LD_LIBRARY_PATH. This avoids problem with current working directory because you could use paths relative to binary directory, not your current one. E.g. gcc -Wl,-rpath='$ORIGIN/my_lib' -Lmy_lib -lshared_secure main.o will link your binary with libshared_secure.so but will add relative entry to rpath.

How to collect all dependencies of a large Makefile project?

In a large C project, I have a top Makefile and many sub-Makefiles in different subdirectories. I need to collect all dependencies of the compilation. For that, I add -MMD to CFLAGS and get a bunch of .d dependency files.
These .d files are scattered in the subdirectories. Also, the dependencies are written sometimes as absolute paths, sometimes as paths relevant to the compilation directory, and sometimes containing symbolic links. I have written a script which finds all .d files, traverses their directories, and resolves all found paths. This works, but with tens of thousands of dependency files this dependency collection lasts about the same time as the compilation! (which is too long to wait :) )
Is there a faster way to get all dependencies in a single file? This is ANSI C, GCC and Linux if that matters. Thanks in advance.
Instead of -MMD, you can use -MM, which sends the dependencies to standard output.
You can then collect all the output to some dependency file in the top level directory with
gcc -MM ... file.c >>$(top)/all.d
If post processing is the only reason for collecting the output in one file, you can filter the output with a pipe
gcc -MM ... file.c | sh filter.sh >file.d
and keep the dependency files separate.
If the path to some local include file (defs.h) or the main source is important, you can force gcc to include a path by giving the appropriate -I option, e.g.
gcc -MM -I$(top)/path/to ... $(top)/path/to/file.c >>$(top)/all.d
or
gcc -MM -I$(top)/path/to ... $(top)/path/to/file.c | sh filter.sh >file.d
Instead of
file.o: file.c defs.h
gcc will emit
file.o: /absolute/path/to/file.c /absolute/path/to/defs.h
This works with relative paths as well, of course.
You can create the dependency files along with the first compile run.
During the first run, the objects do not exist yet, so the compiler will be invoked anyway. Create empty dependency files first, then update them while compiling.
It should be possible to extend the minimal Makefile for a single-directory C++ project to work with subdirectories.

Resources