3

I want to analyze code coverage datas. I want to create gcov files from OpenSSL (and from other projects), but I can only create them in the same directory of the project, and only for the files in the current folder.

I want to create them in a different directory, preserve the source original directory structure, and make the whole process as automatic as possible.

source: ~/mygcovproject/projects/openssl-1.0.0

output: ~/mygcovproject/gcovdata/openssl-1.0.0

Currently I can create the files only in this way:

$ cd ~/mygcovproject/projects/openssl-1.0.0
$ make clean
$ export CC="gcc -fprofile-arcs -ftest-coverage"; ./config
$ make
$ make tests

$ cd test
$ gcov *.c
$ mv *.gcov ~/mygcovproject/gcovdata/openssl-1.0.0/test/
$ cd ..

$ cd apps
$ gcov *.c
$ mv *.gcov ~/mygcovproject/gcovdata/openssl-1.0.0/apps/
$ cd ..

$cd crypto
... (for all the folders)

But there is 2 big problem with this method:

1) There are many folders and subfolders.

2) I have to move the files manually.

How should I do this? Can you help me please?

Upd:

Thanks Gilles, it helped me a lot, but I still struggle with the last part. I get error messages from gcov:

$ cat dothemagic.sh 
#!/bin/bash
shopt -s globstar
gcov_data_dir="../../gcovdata/${PWD##*/}"
mkdir -p "$gcov_data_dir"
#make
#make tests
for x in ./**/*.c; do
  gcov "$gcov_data_dir/${x%/*}/$x"
done
exit

$ ./dothemagic.sh 
../../gcovdata/openssl-1.0.0/./apps/./apps/app_rand.gcno:cannot open notes file
../../gcovdata/openssl-1.0.0/./apps/./apps/apps.gcno:cannot open notes file
../../gcovdata/openssl-1.0.0/./apps/./apps/asn1pars.gcno:cannot open notes file
../../gcovdata/openssl-1.0.0/./apps/./apps/ca.gcno:cannot open notes file
../../gcovdata/openssl-1.0.0/./apps/./apps/ciphers.gcno:cannot open notes file
../../gcovdata/openssl-1.0.0/./apps/./apps/cms.gcno:cannot open notes file
../../gcovdata/openssl-1.0.0/./apps/./apps/crl2p7.gcno:cannot open notes file
...

I tried this too, but it did not work, i get errors:

for x in ./**/*.c; do
  echo $x
  gcov $x
done


$ ./run_tests.sh openssl-1.0.0

./apps/app_rand.c
File 'app_rand.c'
Lines executed:37.50% of 40
Creating 'app_rand.c.gcov'
Cannot open source file app_rand.c

./apps/apps.c
File 'apps.c'
Lines executed:33.76% of 939
Creating 'apps.c.gcov'
Cannot open source file apps.c

...

I tried a single command:

$ gcov ./apps/app_rand.c
File 'app_rand.c'
Lines executed:37.50% of 40
Creating 'app_rand.c.gcov'
Cannot open source file app_rand.c

Looks like I can only run gcov on the files in the same folder. How should I solve this? Should I cd in the directories in the loop, then move the files? Or am I doing something wrong?

I tried in the folders with the -o options, but it did not worked:

$ pwd
/home/blackcat/gcov_project/projects/openssl-1.0.0/test
$ ls bftest.*
bftest.c  bftest.c.gcov  bftest.gcda  bftest.gcno  bftest.o
$ gcov -o ~/gcov_project/gcov/ bftest.c
/home/blackcat/gcov_project/gcov/bftest.gcno:cannot open notes file
$ gcov bftest.c
File 'bftest.c'
Lines executed:47.52% of 101
Creating 'bftest.c.gcov'

File '/usr/include/x86_64-linux-gnu/bits/stdio2.h'
Lines executed:100.00% of 1
Creating 'stdio2.h.gcov'

File '/usr/include/x86_64-linux-gnu/bits/string3.h'
Lines executed:100.00% of 2
Creating 'string3.h.gcov'

Upd2: Starting from Gilles solution I created a working code. Thanks. In the end I put all of them in the same directory, but I created a prefix from their path.

### Generate and copy gcov files ###

cd "$TARGET_DIR"
mkdir -p "$OUTPUT_DIR"

CDIR=""
for x in **/*.c; do
  if [ "$CDIR" != "$TARGET_DIR/${x%/*}" ]; then
    CDIR="$TARGET_DIR/${x%/*}"
    cd $CDIR
    gcov -p *.c

    SUBDIR="${x%/*}"
    PREFIX="#${SUBDIR/\//\#}"

    for f in *.gcov; do
        if [[ $f == \#* ]] ;
        then
           cp $f "$OUTPUT_DIR/$f"
        else
           cp $f "$OUTPUT_DIR/$PREFIX#$f"
        fi
    done
  fi
done
2

You have the commands, so put them in a script!

To run a bunch of commands on different data, put the changing data in a variable.

To run gcov and mv on all the files, there are several possible methods, including:

  • Run gcov on all files, then move them.
  • Run gcov on one file, then move its output.
  • Run gconv on the files in a directory, then move them.

The first approach doesn't work because gcov needs to be executed in the directory containing the source files. The third directory-based approach is in fact the most complicated of the three: the simplest method would be to run gcov on one file at a time.

In bash, you can enumerate all the C files in a directory and its subdirectories recursively with the wildcard pattern **/*.c. The ** wildcard needs to be enabled with the globstar option. To iterate over the files, use a for loop.

To change into a directory just to run one command, run cd and that command in a subshell: (cd … && gcov …).

You need one more type of shell construct: a bit of manipulation of file names to extract the directory part. The parameter expansion construct ${x%/*} expands to the value of the variable x with the shortest suffix matching the pattern /* removed. In other words, that's the directory part of the file name stored in x. This wouldn't work if x consisted only of a file name with no directory part (i.e. foo as opposed to bar/foo); it so happens that there's no .c file at the root of the OpenSSL source tree, but a simple way to make sure the file name starts with ./, which designates the current directory.

Invoke this script at the root of the OpenSSL source tree, after running ./config with your desired options.

#!/bin/bash
shopt -s globstar
gcov_data_dir="../../gcovdata/${PWD##*/}"
make
make tests
for x in ./**/*.c; do
  mkdir -p "$gcov_data_dir/${x%/*}"
  (cd "${x%/*}" && gcov "${x##*/}") &&
  mv "$x.gcov" "$gcov_data_dir/${x%/*}"
done

To avoid having to move the .gcov files, an alternative approach would be to create a forest of symbolic links to the compilation directory, and run gcov in the gcovdata directory. With GNU coreutils (i.e. on non-embedded Linux or Cygwin), you can do that with cp -al.

cp -al openssl-1.0.0 gcovdata
cd gcovdata
for x in ./**/*.c; do
  (cd "${x%/*}" && gcov "${x##*/}")
done
  • Thanks Gilles, it helped me a lot, but I still struggle with the last part. I've updated my question. – BlackCat Jul 26 '15 at 11:32
  • @BlackCat Sorry for the delay. I hadn't realized that gcov always writes to the current directory. I've updated my answer. – Gilles Aug 3 '15 at 1:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.