in Source/CTest/cmCTestCoverageHandler.cxx [851:1248]
int cmCTestCoverageHandler::HandleGCovCoverage(
cmCTestCoverageHandlerContainer* cont)
{
std::string gcovCommand =
this->CTest->GetCTestConfiguration("CoverageCommand");
if (gcovCommand.empty()) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Could not find gcov." << std::endl, this->Quiet);
return 0;
}
std::string gcovExtraFlags =
this->CTest->GetCTestConfiguration("CoverageExtraFlags");
// Immediately skip to next coverage option since codecov is only for Intel
// compiler
if (gcovCommand == "codecov") {
return 0;
}
// Style 1
std::string st1gcovOutputRex1 =
"[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$";
std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";
cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str());
cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str());
// Style 2
std::string st2gcovOutputRex1 = "^File *[`'](.*)'$";
std::string st2gcovOutputRex2 =
"Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$";
std::string st2gcovOutputRex3 = "^(.*)reating [`'](.*\\.gcov)'";
std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$";
std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$";
std::string st2gcovOutputRex6 =
"^(.*):source file is newer than graph file `(.*)'$";
cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str());
cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str());
cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str());
cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str());
cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str());
cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str());
std::vector<std::string> files;
this->FindGCovFiles(files);
if (files.empty()) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" Cannot find any GCov coverage files." << std::endl,
this->Quiet);
// No coverage files is a valid thing, so the exit code is 0
return 0;
}
std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
std::string tempDir = testingDir + "/CoverageInfo";
if (!cmSystemTools::MakeDirectory(tempDir)) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unable to make directory: " << tempDir << std::endl);
cont->Error++;
return 0;
}
cmWorkingDirectory workdir(tempDir);
int gcovStyle = 0;
std::set<std::string> missingFiles;
std::string actualSourceFile;
cmCTestOptionalLog(
this->CTest, HANDLER_OUTPUT,
" Processing coverage (each . represents one file):" << std::endl,
this->Quiet);
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " ", this->Quiet);
int file_count = 0;
// make sure output from gcov is in English!
cmCTestCoverageHandlerLocale locale_C;
static_cast<void>(locale_C);
std::vector<std::string> basecovargs =
cmSystemTools::ParseArguments(gcovExtraFlags);
basecovargs.insert(basecovargs.begin(), gcovCommand);
basecovargs.emplace_back("-o");
// files is a list of *.da and *.gcda files with coverage data in them.
// These are binary files that you give as input to gcov so that it will
// give us text output we can analyze to summarize coverage.
//
for (std::string const& f : files) {
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "." << std::flush,
this->Quiet);
// Call gcov to get coverage data for this *.gcda file:
//
std::string fileDir = cmSystemTools::GetFilenamePath(f);
std::vector<std::string> covargs = basecovargs;
covargs.push_back(fileDir);
covargs.push_back(f);
std::string const command = joinCommandLine(covargs);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
command << std::endl, this->Quiet);
std::string output;
std::string errors;
int retVal = 0;
*cont->OFS << "* Run coverage for: " << fileDir << std::endl;
*cont->OFS << " Command: " << command << std::endl;
int res = this->CTest->RunCommand(covargs, &output, &errors, &retVal,
tempDir.c_str(),
cmDuration::zero() /*this->TimeOut*/);
*cont->OFS << " Output: " << output << std::endl;
*cont->OFS << " Errors: " << errors << std::endl;
if (!res) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Problem running coverage on file: " << f << std::endl);
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Command produced error: " << errors << std::endl);
cont->Error++;
continue;
}
if (retVal != 0) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Coverage command returned: "
<< retVal << " while processing: " << f << std::endl);
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Command produced error: " << cont->Error << std::endl);
}
cmCTestOptionalLog(
this->CTest, HANDLER_VERBOSE_OUTPUT,
"--------------------------------------------------------------"
<< std::endl
<< output << std::endl
<< "--------------------------------------------------------------"
<< std::endl,
this->Quiet);
std::vector<std::string> lines;
cmsys::SystemTools::Split(output, lines);
for (std::string const& line : lines) {
std::string sourceFile;
std::string gcovFile;
cmCTestOptionalLog(this->CTest, DEBUG,
"Line: [" << line << "]" << std::endl, this->Quiet);
if (line.empty()) {
// Ignore empty line; probably style 2
} else if (st1re1.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 1;
}
if (gcovStyle != 1) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e1" << std::endl);
cont->Error++;
break;
}
actualSourceFile.clear();
sourceFile = st1re1.match(2);
} else if (st1re2.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 1;
}
if (gcovStyle != 1) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e2" << std::endl);
cont->Error++;
break;
}
gcovFile = st1re2.match(1);
} else if (st2re1.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 2;
}
if (gcovStyle != 2) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e3" << std::endl);
cont->Error++;
break;
}
actualSourceFile.clear();
sourceFile = st2re1.match(1);
} else if (st2re2.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 2;
}
if (gcovStyle != 2) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e4" << std::endl);
cont->Error++;
break;
}
} else if (st2re3.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 2;
}
if (gcovStyle != 2) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e5" << std::endl);
cont->Error++;
break;
}
gcovFile = st2re3.match(2);
} else if (st2re4.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 2;
}
if (gcovStyle != 2) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e6" << std::endl);
cont->Error++;
break;
}
cmCTestOptionalLog(this->CTest, WARNING,
"Warning: " << st2re4.match(1)
<< " had unexpected EOF" << std::endl,
this->Quiet);
} else if (st2re5.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 2;
}
if (gcovStyle != 2) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e7" << std::endl);
cont->Error++;
break;
}
cmCTestOptionalLog(this->CTest, WARNING,
"Warning: Cannot open file: " << st2re5.match(1)
<< std::endl,
this->Quiet);
} else if (st2re6.find(line)) {
if (gcovStyle == 0) {
gcovStyle = 2;
}
if (gcovStyle != 2) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output style e8" << std::endl);
cont->Error++;
break;
}
cmCTestOptionalLog(this->CTest, WARNING,
"Warning: File: " << st2re6.match(1)
<< " is newer than "
<< st2re6.match(2) << std::endl,
this->Quiet);
} else {
// gcov 4.7 can have output lines saying "No executable lines" and
// "Removing 'filename.gcov'"... Don't log those as "errors."
if (line != "No executable lines" &&
!cmHasLiteralPrefix(line, "Removing ")) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Unknown gcov output line: [" << line << "]"
<< std::endl);
cont->Error++;
// abort();
}
}
// If the last line of gcov output gave us a valid value for gcovFile,
// and we have an actualSourceFile, then insert a (or add to existing)
// SingleFileCoverageVector for actualSourceFile:
//
if (!gcovFile.empty() && !actualSourceFile.empty()) {
cmCTestCoverageHandlerContainer::SingleFileCoverageVector& vec =
cont->TotalCoverage[actualSourceFile];
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" in gcovFile: " << gcovFile << std::endl,
this->Quiet);
cmsys::ifstream ifile(gcovFile.c_str());
if (!ifile) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Cannot open file: " << gcovFile << std::endl);
} else {
std::string nl;
while (cmSystemTools::GetLineFromStream(ifile, nl)) {
// Skip empty lines
if (nl.empty()) {
continue;
}
// Skip unused lines
if (nl.size() < 12) {
continue;
}
// Handle gcov 3.0 non-coverage lines
// non-coverage lines seem to always start with something not
// a space and don't have a ':' in the 9th position
// TODO: Verify that this is actually a robust metric
if (nl[0] != ' ' && nl[9] != ':') {
continue;
}
// Read the coverage count from the beginning of the gcov output
// line
std::string prefix = nl.substr(0, 12);
int cov = atoi(prefix.c_str());
// Read the line number starting at the 10th character of the gcov
// output line
std::string lineNumber = nl.substr(10, 5);
int lineIdx = atoi(lineNumber.c_str()) - 1;
if (lineIdx >= 0) {
while (vec.size() <= static_cast<size_t>(lineIdx)) {
vec.push_back(-1);
}
// Initially all entries are -1 (not used). If we get coverage
// information, increment it to 0 first.
if (vec[lineIdx] < 0) {
if (cov > 0 || prefix.find('#') != std::string::npos) {
vec[lineIdx] = 0;
}
}
vec[lineIdx] += cov;
}
}
}
actualSourceFile.clear();
}
if (!sourceFile.empty() && actualSourceFile.empty()) {
gcovFile.clear();
// Is it in the source dir or the binary dir?
//
if (IsFileInDir(sourceFile, cont->SourceDir)) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" produced s: " << sourceFile << std::endl,
this->Quiet);
*cont->OFS << " produced in source dir: " << sourceFile
<< std::endl;
actualSourceFile = cmSystemTools::CollapseFullPath(sourceFile);
} else if (IsFileInDir(sourceFile, cont->BinaryDir)) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" produced b: " << sourceFile << std::endl,
this->Quiet);
*cont->OFS << " produced in binary dir: " << sourceFile
<< std::endl;
actualSourceFile = cmSystemTools::CollapseFullPath(sourceFile);
}
if (actualSourceFile.empty()) {
if (missingFiles.find(sourceFile) == missingFiles.end()) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Something went wrong" << std::endl,
this->Quiet);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Cannot find file: [" << sourceFile << "]"
<< std::endl,
this->Quiet);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" in source dir: [" << cont->SourceDir << "]"
<< std::endl,
this->Quiet);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" or binary dir: [" << cont->BinaryDir.size()
<< "]" << std::endl,
this->Quiet);
*cont->OFS << " Something went wrong. Cannot find file: "
<< sourceFile << " in source dir: " << cont->SourceDir
<< " or binary dir: " << cont->BinaryDir << std::endl;
missingFiles.insert(sourceFile);
}
}
}
}
file_count++;
if (file_count % 50 == 0) {
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
" processed: " << file_count << " out of "
<< files.size() << std::endl,
this->Quiet);
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " ", this->Quiet);
}
}
return file_count;
}