How to create gcov files for a project in a different dir?

18,578

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
Share:
18,578

Related videos on Youtube

BlackCat
Author by

BlackCat

Updated on September 18, 2022

Comments

  • BlackCat
    BlackCat over 1 year

    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
    
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' almost 9 years
    @BlackCat Sorry for the delay. I hadn't realized that gcov always writes to the current directory. I've updated my answer.
  • Scott - Слава Україні
    Scott - Слава Україні about 4 years
    I'm not sure how this is different from what the OP already knows how to do (and said so in the question).  I don't see how this addresses the issue "There are many folders and subfolders." and "I want to ... make the whole process as automatic as possible."
  • Kurt Stewart
    Kurt Stewart about 4 years
    just add them to a shell script file and call it compciv.org/recipes/cli/basic-shell-scripts