gcov and openMP
Asked Answered
A

0

6

I'm using gcov for unit testing coverage analysis in my C++ project, which includes regions parallelized using OpenMP. Upon reviewing the gcov results, I've noticed that the lines parallelized with OpenMP directives are excluded from the analysis. Is there a way to include these parallelized regions in the coverage analysis?

I attach a minimal example code and how I run gcov.

File example.cpp:

#include <cmath>
#include <omp.h>

int main(int argc, char* argv[]){
  #pragma omp parallel for
  for(int i=0;i<200;i++){
    double a=std::sqrt(5+i);
  }
  return 0;
}

I compile the code using the following command:

g++ -fprofile-arcs -ftest-coverage -fopenmp example.cpp -o example

And then execute gcov as follows:

./example && gcov -w example.cpp

The output file example.cpp.gcov generated is:

    -:    0:Source:example.cpp
    -:    0:Graph:example.gcno
    -:    0:Data:example.gcda
    -:    0:Runs:1
    -:    1:#include <cmath>
    -:    2:#include <omp.h>
    -:    3:
    1:    4:int main(int argc, char* argv[]){
    -:    5:  #pragma omp parallel for
    -:    6:  for(int i=0;i<200;i++){
    -:    7:    double a=std::sqrt(5+i);
    -:    8:  }
    1:    9:  return 0;
    -:   10:}

EDIT:

I just tried with a slightly more complicated example code to discard the compiler optimized away useless code lines, and the result is the same as before:

#include <cmath>
#include <omp.h>
#include <iostream>

int main(int argc, char* argv[]){
  double a=0.;
  #pragma omp parallel for
  for(int i=0;i<200;i++){
    #pragma omp critical
    {
    a=a+std::sqrt(5+i);
    }
  }
  std::cout << "a: " << a << std::endl;
  return 0;
}
Amaurosis answered 24/4 at 14:55 Comment(9)
The .gcov file shows that GCC considers lines 5–8 to not contain any executable code, e.g. because they have been optimized away. The lines are marked as - (noncode), not as 0 (uncovered). I cannot reproduce this exact behaviour on any GCC version, all that I tested also attach debug info to the #pragma and sqrt lines. I'm also surprised that you use gcov -w but the output doesn't contain any info about basic blocks. What exact versions are you using?Sincerity
I tested with two g++ and gcov versions: 9.4.0 and 11.4.0. Both results are exactly as posted.Amaurosis
If you look at the compiled code godbolt.org/z/d73xEsrjq you can see that g++ is generating calls to the profiling routines (at least with the latest release) and hasn't (as you can see here godbolt.org/z/n945PnceM ) that it would with optimisation enabled...Beaune
Interesting but the OP reported its command and there is no optimization in it. Is this a mistake of the OP? It looks like the generation is also done with older versions of GCC (at least all > 4.9, including GCC 9.4).Froggy
I just tried with -O0 flag and I get exactly the same results.Amaurosis
-O0 should be the default config. Maybe it has been overrided somehow on your machine (or with some environment variable) or you use a non-standard GCC?Froggy
I use normal gcc. I meant using -O0 still does not show me coverage info for the lines inside the parallel forAmaurosis
The primary purpose of #pragma omp parallel for is to parallelize the loop iterations across multiple threads. Focusing on individual iteration coverage within a parallel loop might not be as relevant. Maybe unrolling loops help you a bit.Hemingway
I can reproduce the problem. Even though GCC emits useful debug information, and the generated code increments gcov counters (which led me to claim "no repro" in the initial comment), it doesn't seem to emit useful profiling metadata that could tie the counters back to the source code? Keeping the same compilation options but commenting out the pragma works as expected (makes it possible to attribute coverage data to the loop contents), so it seems there might be a GCC bug in the interplay between OpenMP codegen and gcno file output.Sincerity

© 2022 - 2024 — McMap. All rights reserved.