[gcov] Refactor llvm-cov gcov and add SourceInfo
authorFangrui Song <maskray@google.com>
Tue, 16 Jun 2020 20:53:16 +0000 (13:53 -0700)
committerFangrui Song <maskray@google.com>
Tue, 16 Jun 2020 21:14:26 +0000 (14:14 -0700)
llvm/include/llvm/ProfileData/GCOV.h
llvm/lib/ProfileData/GCOV.cpp
llvm/test/tools/llvm-cov/llvm-cov.test
llvm/tools/llvm-cov/gcov.cpp

index d5dc88e..294782c 100644 (file)
@@ -192,6 +192,9 @@ public:
   void dump() const;
   void collectLineCounts(FileInfo &FI);
 
+  std::vector<std::string> filenames;
+  StringMap<unsigned> filenameToIdx;
+
 private:
   bool GCNOInitialized = false;
   GCOV::GCOVVersion Version;
@@ -201,6 +204,11 @@ private:
   std::map<uint32_t, GCOVFunction *> IdentToFunction;
   uint32_t RunCount = 0;
   uint32_t ProgramCount = 0;
+
+  using iterator = pointee_iterator<
+      SmallVectorImpl<std::unique_ptr<GCOVFunction>>::const_iterator>;
+  iterator begin() const { return iterator(Functions.begin()); }
+  iterator end() const { return iterator(Functions.end()); }
 };
 
 struct GCOVArc {
@@ -220,10 +228,10 @@ public:
   using BlockIterator = pointee_iterator<
       SmallVectorImpl<std::unique_ptr<GCOVBlock>>::const_iterator>;
 
-  GCOVFunction(GCOVFile &P) {}
+  GCOVFunction(GCOVFile &file) : file(file) {}
 
   StringRef getName() const { return Name; }
-  StringRef getFilename() const { return Filename; }
+  StringRef getFilename() const;
   size_t getNumBlocks() const { return Blocks.size(); }
   uint64_t getEntryCount() const;
   uint64_t getExitCount() const;
@@ -238,6 +246,7 @@ public:
   void dump() const;
   void collectLineCounts(FileInfo &FI);
 
+  GCOVFile &file;
   uint32_t ident = 0;
   uint32_t linenoChecksum;
   uint32_t cfgChecksum = 0;
@@ -247,7 +256,7 @@ public:
   uint32_t endColumn = 0;
   uint8_t artificial = 0;
   StringRef Name;
-  StringRef Filename;
+  unsigned srcIdx;
   SmallVector<std::unique_ptr<GCOVBlock>, 0> Blocks;
   SmallVector<std::unique_ptr<GCOVArc>, 0> arcs, treeArcs;
 };
@@ -312,6 +321,28 @@ public:
   SmallVector<uint32_t, 16> Lines;
 };
 
+struct GCOVCoverage {
+  GCOVCoverage() = default;
+  GCOVCoverage(StringRef Name) : Name(Name) {}
+
+  StringRef Name;
+
+  uint32_t LogicalLines = 0;
+  uint32_t LinesExec = 0;
+
+  uint32_t Branches = 0;
+  uint32_t BranchesExec = 0;
+  uint32_t BranchesTaken = 0;
+};
+
+struct SourceInfo {
+  StringRef filename;
+  std::string name;
+  std::vector<GCOVFunction *> functions;
+  GCOVCoverage coverage;
+  SourceInfo(StringRef filename) : filename(filename) {}
+};
+
 class FileInfo {
 protected:
   // It is unlikely--but possible--for multiple functions to be on the same
@@ -332,20 +363,8 @@ protected:
     uint32_t LastLine = 0;
   };
 
-  struct GCOVCoverage {
-    GCOVCoverage(StringRef Name) : Name(Name) {}
-
-    StringRef Name;
-
-    uint32_t LogicalLines = 0;
-    uint32_t LinesExec = 0;
-
-    uint32_t Branches = 0;
-    uint32_t BranchesExec = 0;
-    uint32_t BranchesTaken = 0;
-  };
-
 public:
+  friend class GCOVFile;
   FileInfo(const GCOV::Options &Options) : Options(Options) {}
 
   void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) {
@@ -364,7 +383,7 @@ public:
   void setRunCount(uint32_t Runs) { RunCount = Runs; }
   void setProgramCount(uint32_t Programs) { ProgramCount = Programs; }
   void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile,
-             StringRef GCDAFile, GCOV::GCOVVersion Version);
+             StringRef GCDAFile, GCOVFile &file);
 
 protected:
   std::string getCoveragePath(StringRef Filename, StringRef MainFilename);
@@ -386,11 +405,10 @@ protected:
   uint32_t RunCount = 0;
   uint32_t ProgramCount = 0;
 
-  using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>;
   using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>;
 
-  FileCoverageList FileCoverages;
   FuncCoverageMap FuncCoverages;
+  std::vector<SourceInfo> sources;
 };
 
 } // end namespace llvm
index 2e0d320..a9dd533 100644 (file)
@@ -68,18 +68,23 @@ bool GCOVFile::readGCNO(GCOVBuffer &buf) {
       if (Version >= GCOV::V407)
         fn->cfgChecksum = buf.getWord();
       buf.readString(fn->Name);
+      StringRef filename;
       if (Version < GCOV::V800) {
-        buf.readString(fn->Filename);
+        filename = buf.getString();
         fn->startLine = buf.getWord();
       } else {
         fn->artificial = buf.getWord();
-        fn->Filename = buf.getString();
+        filename = buf.getString();
         fn->startLine = buf.getWord();
         fn->startColumn = buf.getWord();
         fn->endLine = buf.getWord();
         if (Version >= GCOV::V900)
           fn->endColumn = buf.getWord();
       }
+      auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size());
+      if (r.second)
+        filenames.emplace_back(filename);
+      fn->srcIdx = r.first->second;
       IdentToFunction[fn->ident] = fn;
     } else if (tag == GCOV_TAG_BLOCKS && fn) {
       if (Version < GCOV::V800) {
@@ -224,8 +229,8 @@ bool GCOVFile::readGCDA(GCOVBuffer &buf) {
 }
 
 void GCOVFile::print(raw_ostream &OS) const {
-  for (const auto &FPtr : Functions)
-    FPtr->print(OS);
+  for (const GCOVFunction &f : *this)
+    f.print(OS);
 }
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -235,16 +240,23 @@ LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); }
 
 /// collectLineCounts - Collect line counts. This must be used after
 /// reading .gcno and .gcda files.
-void GCOVFile::collectLineCounts(FileInfo &FI) {
-  for (const auto &FPtr : Functions)
-    FPtr->collectLineCounts(FI);
-  FI.setRunCount(RunCount);
-  FI.setProgramCount(ProgramCount);
+void GCOVFile::collectLineCounts(FileInfo &fi) {
+  assert(fi.sources.empty());
+  for (StringRef filename : filenames)
+    fi.sources.emplace_back(filename);
+  for (GCOVFunction &f : *this) {
+    f.collectLineCounts(fi);
+    fi.sources[f.srcIdx].functions.push_back(&f);
+  }
+  fi.setRunCount(RunCount);
+  fi.setProgramCount(ProgramCount);
 }
 
 //===----------------------------------------------------------------------===//
 // GCOVFunction implementation.
 
+StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; }
+
 /// getEntryCount - Get the number of times the function was called by
 /// retrieving the entry block's count.
 uint64_t GCOVFunction::getEntryCount() const {
@@ -258,7 +270,7 @@ uint64_t GCOVFunction::getExitCount() const {
 }
 
 void GCOVFunction::print(raw_ostream &OS) const {
-  OS << "===== " << Name << " (" << ident << ") @ " << Filename << ":"
+  OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":"
      << startLine << "\n";
   for (const auto &Block : Blocks)
     Block->print(OS);
@@ -279,7 +291,7 @@ void GCOVFunction::collectLineCounts(FileInfo &FI) {
 
   for (const auto &Block : Blocks)
     Block->collectLineCounts(FI);
-  FI.addFunctionLine(Filename, startLine, this);
+  FI.addFunctionLine(getFilename(), startLine, this);
 }
 
 //===----------------------------------------------------------------------===//
@@ -596,8 +608,7 @@ FileInfo::openCoveragePath(StringRef CoveragePath) {
 
 /// print -  Print source files with collected line count information.
 void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
-                     StringRef GCNOFile, StringRef GCDAFile,
-                     GCOV::GCOVVersion Version) {
+                     StringRef GCNOFile, StringRef GCDAFile, GCOVFile &file) {
   SmallVector<StringRef, 4> Filenames;
   for (const auto &LI : LineInfo)
     Filenames.push_back(LI.first());
@@ -619,7 +630,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
     CovOS << "        -:    0:Graph:" << GCNOFile << "\n";
     CovOS << "        -:    0:Data:" << GCDAFile << "\n";
     CovOS << "        -:    0:Runs:" << RunCount << "\n";
-    if (Version < GCOV::V900)
+    if (file.getVersion() < GCOV::V900)
       CovOS << "        -:    0:Programs:" << ProgramCount << "\n";
 
     const LineData &Line = LineInfo[Filename];
@@ -707,7 +718,9 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
         }
       }
     }
-    FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage));
+    SourceInfo &source = sources[file.filenameToIdx.find(Filename)->second];
+    source.name = CoveragePath;
+    source.coverage = FileCoverage;
   }
 
   if (!Options.UseStdout) {
@@ -816,13 +829,12 @@ void FileInfo::printFuncCoverage(raw_ostream &OS) const {
 
 // printFileCoverage - Print per-file coverage info.
 void FileInfo::printFileCoverage(raw_ostream &OS) const {
-  for (const auto &FC : FileCoverages) {
-    const std::string &Filename = FC.first;
-    const GCOVCoverage &Coverage = FC.second;
+  for (const SourceInfo &source : sources) {
+    const GCOVCoverage &Coverage = source.coverage;
     OS << "File '" << Coverage.Name << "'\n";
     printCoverage(OS, Coverage);
     if (!Options.NoOutput)
-      OS << "Creating '" << Filename << "'\n";
+      OS << "Creating '" << source.name << "'\n";
     OS << "\n";
   }
 }
index 79e51b4..4e9ef08 100644 (file)
@@ -131,18 +131,18 @@ RUN: FileCheck %s --check-prefixes=H,H-A,H-B --match-full-lines --strict-whitesp
  OUT-FB-NEXT:Taken at least once:81.82% of 11
  OUT-FB-NEXT:No calls
  OUT-F-EMPTY:
-         OUT:File './test.h'
-    OUT-NEXT:Lines executed:100.00% of 1
-  OUT-B-NEXT:No branches
-  OUT-B-NEXT:No calls
-OUTFILE-NEXT:Creating 'test.h.gcov'
-   OUT-EMPTY:
-    OUT-NEXT:File 'test.cpp'
+         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
 OUTFILE-NEXT:Creating 'test.cpp.gcov'
+   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
+OUTFILE-NEXT:Creating 'test.h.gcov'
 
 # Summarize unconditional branches too.
 RUN: llvm-cov gcov test.c -a -b -u | FileCheck %s --check-prefixes=OUT,OUTFILE,OUT-B
@@ -158,13 +158,13 @@ H-C: unconditional  0 taken 1
 RUN: llvm-cov gcov test.c -gcda=no_such_gcda_file | FileCheck %s --check-prefix=NO-GCDA
 RUN: diff -aub test_no_gcda.cpp.gcov test.cpp.gcov
 RUN: diff -aub test_no_gcda.h.gcov test.h.gcov
-NO-GCDA:       File './test.h'
-NO-GCDA-NEXT:  Lines executed:0.00% of 1
-NO-GCDA-NEXT:  Creating 'test.h.gcov'
-NO-GCDA-EMPTY:
-NO-GCDA-NEXT:  File 'test.cpp'
+NO-GCDA:       File 'test.cpp'
 NO-GCDA-NEXT:  Lines executed:0.00% of 43
 NO-GCDA-NEXT:  Creating 'test.cpp.gcov'
+NO-GCDA-EMPTY:
+NO-GCDA-NEXT:  File './test.h'
+NO-GCDA-NEXT:  Lines executed:0.00% of 1
+NO-GCDA-NEXT:  Creating 'test.h.gcov'
 
 # Invalid gcno file.
 RUN: llvm-cov gcov test.c -gcno=test_read_fail.gcno
index bd3b6be..3ebf6a6 100644 (file)
@@ -77,7 +77,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
 
   FileInfo FI(Options);
   GF.collectLineCounts(FI);
-  FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF.getVersion());
+  FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF);
 }
 
 int gcovMain(int argc, const char *argv[]) {