[llvm] def2156 - [gcov] Add -i --intermediate-format

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 16 14:14:41 PDT 2020


Author: Fangrui Song
Date: 2020-06-16T14:14:28-07:00
New Revision: def21563895d757c7769de41d4ee8d9cb99714db

URL: https://github.com/llvm/llvm-project/commit/def21563895d757c7769de41d4ee8d9cb99714db
DIFF: https://github.com/llvm/llvm-project/commit/def21563895d757c7769de41d4ee8d9cb99714db.diff

LOG: [gcov] Add -i --intermediate-format

Between gcov 4.9~8, `gcov -i $file` prints coverage information to
$file.gcov in an intermediate text format (single file, instead of
$source.gcov for each source file).

lcov newer than 2019-05-24 detects -i support and uses it to increase
processing speed.  gcov 9 (GCC r265587) removed --intermediate-format
and -i was changed to mean --json-format. However, we consider this
format still useful and support it. geninfo (part of lcov) supports this
format even if we announce that we are compatible with gcov 9.0.0

Added: 
    llvm/test/tools/llvm-cov/gcov-intermediate-format.test

Modified: 
    llvm/include/llvm/ProfileData/GCOV.h
    llvm/lib/ProfileData/GCOV.cpp
    llvm/test/tools/llvm-cov/gcov-8.c
    llvm/tools/llvm-cov/gcov.cpp

Removed: 
    


################################################################################
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<std::unique_ptr<MemoryBuffer>> 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<raw_ostream> CovStream;
-    if (Options.NoOutput)
+    if (Options.NoOutput || Options.Intermediate)
       CovStream = std::make_unique<raw_null_ostream>();
     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:<line>,<count>,<has_unexecuted_blocks>
+        // 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<bool> 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<bool> 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,


        


More information about the llvm-commits mailing list