diff --git a/llvm/include/llvm/ProfileData/GCOV.h b/llvm/include/llvm/ProfileData/GCOV.h index 294782c2c94d..58c454cbba80 100644 --- a/llvm/include/llvm/ProfileData/GCOV.h +++ b/llvm/include/llvm/ProfileData/GCOV.h @@ -46,11 +46,11 @@ enum GCOVVersion { V304, V407, V408, V800, V900 }; /// A struct for passing gcov options between functions. struct Options { - Options(bool A, bool B, bool C, bool F, bool P, bool U, bool L, bool N, - bool T, bool X) + Options(bool A, bool B, bool C, bool F, bool P, bool U, bool I, bool L, + bool N, bool T, bool X) : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), - PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N), - UseStdout(T), HashFilenames(X) {} + PreservePaths(P), UncondBranch(U), Intermediate(I), LongFileNames(L), + NoOutput(N), UseStdout(T), HashFilenames(X) {} bool AllBlocks; bool BranchInfo; @@ -58,6 +58,7 @@ struct Options { bool FuncCoverage; bool PreservePaths; bool UncondBranch; + bool Intermediate; bool LongFileNames; bool NoOutput; bool UseStdout; diff --git a/llvm/lib/ProfileData/GCOV.cpp b/llvm/lib/ProfileData/GCOV.cpp index a9dd53341b01..9527993536ab 100644 --- a/llvm/lib/ProfileData/GCOV.cpp +++ b/llvm/lib/ProfileData/GCOV.cpp @@ -512,6 +512,7 @@ class LineConsumer { StringRef Remaining; public: + LineConsumer() = default; LineConsumer(StringRef Filename) { ErrorOr> BufferOrErr = MemoryBuffer::getFileOrSTDIN(Filename); @@ -615,11 +616,11 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, llvm::sort(Filenames); for (StringRef Filename : Filenames) { - auto AllLines = LineConsumer(Filename); - + auto AllLines = + Options.Intermediate ? LineConsumer() : LineConsumer(Filename); std::string CoveragePath = getCoveragePath(Filename, MainFilename); std::unique_ptr CovStream; - if (Options.NoOutput) + if (Options.NoOutput || Options.Intermediate) CovStream = std::make_unique(); else if (!Options.UseStdout) CovStream = openCoveragePath(CoveragePath); @@ -723,6 +724,51 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, source.coverage = FileCoverage; } + if (Options.Intermediate && !Options.NoOutput) { + // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0 + // (PR GCC/82702). We create just one file. + std::string outputPath(sys::path::filename(MainFilename)); + std::error_code ec; + raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_Text); + if (ec) { + errs() << ec.message() << "\n"; + return; + } + + for (const SourceInfo &source : sources) { + os << "file:" << source.filename << '\n'; + for (const GCOVFunction *f : source.functions) + os << "function:" << f->startLine << ',' << f->getEntryCount() << ',' + << f->Name << '\n'; + const LineData &line = LineInfo[source.filename]; + for (uint32_t lineNum = 0; lineNum != line.LastLine; ++lineNum) { + BlockLines::const_iterator blocksIt = line.Blocks.find(lineNum); + if (blocksIt == line.Blocks.end()) + continue; + const BlockVector &blocks = blocksIt->second; + // GCC 8 (r254259) added third third field for Ada: + // lcount:,, + // We don't need the third field. + os << "lcount:" << (lineNum + 1) << ',' + << GCOVBlock::getLineCount(blocks) << '\n'; + + if (!Options.BranchInfo) + continue; + for (const GCOVBlock *block : blocks) { + if (block->getLastLine() != lineNum + 1 || + block->getNumDstEdges() < 2) + continue; + for (const GCOVArc *arc : block->dsts()) { + const char *type = block->getCount() + ? arc->Count ? "taken" : "nottaken" + : "notexec"; + os << "branch:" << (lineNum + 1) << ',' << type << '\n'; + } + } + } + } + } + if (!Options.UseStdout) { // FIXME: There is no way to detect calls given current instrumentation. if (Options.FuncCoverage) @@ -833,7 +879,7 @@ void FileInfo::printFileCoverage(raw_ostream &OS) const { const GCOVCoverage &Coverage = source.coverage; OS << "File '" << Coverage.Name << "'\n"; printCoverage(OS, Coverage); - if (!Options.NoOutput) + if (!Options.NoOutput && !Options.Intermediate) OS << "Creating '" << source.name << "'\n"; OS << "\n"; } diff --git a/llvm/test/tools/llvm-cov/gcov-8.c b/llvm/test/tools/llvm-cov/gcov-8.c index 8099d22accc5..eef3511e93a7 100644 --- a/llvm/test/tools/llvm-cov/gcov-8.c +++ b/llvm/test/tools/llvm-cov/gcov-8.c @@ -19,10 +19,14 @@ int main() { // GCOV: 1: [[@LINE]]:int // RUN: cp %s %p/Inputs/gcov-8.gc* . /// FIXME Lines executed:100.00% of 12 -// RUN: llvm-cov gcov gcov-8.c | FileCheck %s -// CHECK: File 'gcov-8.c' -// CHECK-NEXT: Lines executed:77.78% of 9 -// CHECK-NEXT: Creating 'gcov-8.c.gcov' +// RUN: llvm-cov gcov gcov-8.c | FileCheck %s --check-prefixes=OUT,OUTFILE +// OUT: File 'gcov-8.c' +// OUT-NEXT: Lines executed:77.78% of 9 +// OUT-B-NEXT: Branches executed:85.71% of 14 +// OUT-B-NEXT: Taken at least once:42.86% of 14 +// OUT-B-NEXT: No calls +// OUTFILE-NEXT: Creating 'gcov-8.c.gcov' +// OUT-EMPTY: // RUN: FileCheck --input-file=%t/gcov-8.c.gcov --check-prefix=HEADER %s // RUN: FileCheck --input-file=%t/gcov-8.c.gcov --check-prefix=GCOV %s @@ -33,3 +37,37 @@ int main() { // GCOV: 1: [[@LINE]]:int // HEADER-NEXT: -: 0:Runs:1{{$}} // HEADER-NEXT: -: 0:Programs:1 // HEADER-NEXT: -: 1:/// Test that llvm-cov + +// RUN: llvm-cov gcov -i gcov-8.c | FileCheck %s --check-prefix=OUT +// RUN: FileCheck %s --check-prefix=I < gcov-8.c.gcov +// RUN: llvm-cov gcov --intermediate-format gcov-8.c +// RUN: FileCheck %s --check-prefix=I < gcov-8.c.gcov + +// RUN: llvm-cov gcov -i -b gcov-8.c | FileCheck %s --check-prefixes=OUT,OUT-B +// RUN: FileCheck %s --check-prefixes=I,I-B < gcov-8.c.gcov + +// I:file:gcov-8.c +// I-NEXT:function:4,1,main +// I-NEXT:lcount:4,1 +// I-NEXT:lcount:6,12 +// I-B-NEXT:branch:6,taken +// I-B-NEXT:branch:6,nottaken +// I-NEXT:lcount:7,11 +// I-B-NEXT:branch:7,taken +// I-B-NEXT:branch:7,nottaken +// I-NEXT:lcount:8,7 +// I-B-NEXT:branch:8,taken +// I-B-NEXT:branch:8,nottaken +// I-NEXT:lcount:9,11 +// I-NEXT:lcount:10,11 +// I-B-NEXT:branch:10,taken +// I-B-NEXT:branch:10,nottaken +// I-NEXT:lcount:11,11 +// I-B-NEXT:branch:11,taken +// I-B-NEXT:branch:11,nottaken +// I-B-NEXT:branch:11,taken +// I-B-NEXT:branch:11,nottaken +// I-NEXT:lcount:12,0 +// I-B-NEXT:branch:12,notexec +// I-B-NEXT:branch:12,notexec +// I-NEXT:lcount:14,0 diff --git a/llvm/test/tools/llvm-cov/gcov-intermediate-format.test b/llvm/test/tools/llvm-cov/gcov-intermediate-format.test new file mode 100644 index 000000000000..56bad2357262 --- /dev/null +++ b/llvm/test/tools/llvm-cov/gcov-intermediate-format.test @@ -0,0 +1,55 @@ +REQUIRES: shell + +RUN: rm -rf %t && mkdir %t && cd %t +RUN: cp %S/Inputs/test.gcno %S/Inputs/test.gcda . + +RUN: llvm-cov gcov -i test.cpp 2> %t.err | FileCheck %s --check-prefixes=OUT +RUN: FileCheck %s --check-prefix=I < test.cpp.gcov +RUN: cp test.cpp.gcov saved.cpp.gcov + +# -i does not read source files. No ENOENT diagnostic. +RUN: count 0 < %t.err + +# -n suppresses the .gcov output. +RUN: llvm-cov gcov -i -n test. | FileCheck %s --check-prefixes=OUT +RUN: not ls test..gcov + +# The output filename is formed by appending ".gcov" to the specifiled filename. +RUN: llvm-cov gcov -i test. | FileCheck %s --check-prefix=OUT +RUN: cmp test..gcov saved.cpp.gcov + +RUN: llvm-cov gcov -i -b test.cpp | FileCheck %s --check-prefixes=OUT,OUT-B +RUN: FileCheck %s --check-prefixes=I,I-B --match-full-lines --strict-whitespace < test.cpp.gcov + +# Many other options are ignored. +RUN: rm -f test.cpp.gcov && llvm-cov gcov -i -a -c -l -p -u -x test.cpp +RUN: cmp test.cpp.gcov saved.cpp.gcov + + OUT:File 'test.cpp' + OUT-NEXT:Lines executed:81.40% of 43 + OUT-B-NEXT:Branches executed:100.00% of 15 + OUT-B-NEXT:Taken at least once:86.67% of 15 + OUT-B-NEXT:No calls + OUT-EMPTY: + OUT-NEXT:File './test.h' + OUT-NEXT:Lines executed:100.00% of 1 + OUT-B-NEXT:No branches + OUT-B-NEXT:No calls + OUT-EMPTY: + + I:file:test.cpp + I:function:10,4294967296,_ZN1A1BEv + I-NEXT:function:12,0,_Z7uselessv + I-NEXT:function:14,0,_Z12more_uselessv + I-NEXT:function:18,1,_Z3foov + I-NEXT:function:23,0,_Z3barv + I-NEXT:function:28,4,_Z6assignii + I-NEXT:function:32,1,_Z15initialize_gridv + I-NEXT:function:38,1,main + I:lcount:10,4294967296 + I:lcount:33,3 +I-B-NEXT:branch:33,taken +I-B-NEXT:branch:33,taken + I:file:./test.h + I-NEXT:function:2,1,_ZN1AC2Ev + I-NEXT:lcount:2,1 diff --git a/llvm/tools/llvm-cov/gcov.cpp b/llvm/tools/llvm-cov/gcov.cpp index 3ebf6a654bd3..4359ff466af0 100644 --- a/llvm/tools/llvm-cov/gcov.cpp +++ b/llvm/tools/llvm-cov/gcov.cpp @@ -105,6 +105,16 @@ int gcovMain(int argc, const char *argv[]) { cl::desc("Show coverage for each function")); cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary)); + // Supported by gcov 4.9~8. gcov 9 (GCC r265587) removed --intermediate-format + // and -i was changed to mean --json-format. We consider this format still + // useful and support -i. + cl::opt Intermediate( + "intermediate-format", cl::init(false), + cl::desc("Output .gcov in intermediate text format")); + cl::alias IntermediateA("i", cl::desc("Alias for --intermediate-format"), + cl::Grouping, cl::NotHidden, + cl::aliasopt(Intermediate)); + cl::opt NoOutput("n", cl::Grouping, cl::init(false), cl::desc("Do not output any .gcov files")); cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); @@ -144,8 +154,8 @@ int gcovMain(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary, - PreservePaths, UncondBranch, LongNames, NoOutput, - UseStdout, HashFilenames); + PreservePaths, UncondBranch, Intermediate, LongNames, + NoOutput, UseStdout, HashFilenames); for (const auto &SourceFile : SourceFiles) reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV,