[compiler-rt] e3200da - [gcov] Support .gcno/.gcda in gcov 8, 9 or 10 compatible formats

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 7 11:27:56 PDT 2020


Author: Fangrui Song
Date: 2020-06-07T11:27:49-07:00
New Revision: e3200dab606cf038338d05ba545cb54f734ef2e9

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

LOG: [gcov] Support .gcno/.gcda in gcov 8, 9 or 10 compatible formats

Added: 
    

Modified: 
    compiler-rt/lib/profile/GCDAProfiling.c
    compiler-rt/test/profile/gcov-basic.c
    llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
index cea7eb8ec954..d15540d7c18a 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -545,10 +545,15 @@ void llvm_gcda_summary_info() {
     }
 
     val = read_32bit_value(); /* length */
-    read_32bit_value();
-    if (gcov_version < 90)
+    uint32_t prev_runs;
+    if (gcov_version < 90) {
       read_32bit_value();
-    uint32_t prev_runs = read_32bit_value();
+      read_32bit_value();
+      prev_runs = read_32bit_value();
+    } else {
+      prev_runs = read_32bit_value();
+      read_32bit_value();
+    }
     for (uint32_t i = gcov_version < 90 ? 3 : 2; i < val; ++i)
       read_32bit_value();
     /* Add previous run count to new counter, if not already counted before. */

diff  --git a/compiler-rt/test/profile/gcov-basic.c b/compiler-rt/test/profile/gcov-basic.c
index f29dfd49b0a7..e00cebf4b781 100644
--- a/compiler-rt/test/profile/gcov-basic.c
+++ b/compiler-rt/test/profile/gcov-basic.c
@@ -2,19 +2,33 @@
 
 /// gcov 3.4 redesigned the format and changed the extension from .da to .gcda
 // RUN: %clang --coverage -Xclang -coverage-version='304*' %s -o %t
-// RUN: rm -f gcov-basic.gcda && %run %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
 // RUN: llvm-cov gcov -t gcov-basic.gcno | FileCheck %s
 
 /// r173147: split checksum into cfg checksum and line checksum.
 // RUN: %clang --coverage -Xclang -coverage-version='407*' %s -o %t
-// RUN: rm -f gcov-basic.gcda && %run %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
 // RUN: llvm-cov gcov -t gcov-basic.gcno | FileCheck %s
 
 /// r189778: the exit block moved from the last to the second.
 // RUN: %clang --coverage -Xclang -coverage-version='408*' %s -o %t
-// RUN: rm -f gcov-basic.gcda && %run %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
 // RUN: llvm-cov gcov -t gcov-basic.gcno
 
-int main() {       // CHECK:      1: [[@LINE]]:int main
-  return 0;        // CHECK-NEXT: 1: [[@LINE]]:
+/// PR gcov-profile/48463
+// RUN: %clang --coverage -Xclang -coverage-version='800*' %s -o %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
+// RUN: llvm-cov gcov -t gcov-basic.gcno
+
+/// PR gcov-profile/84846, r269678
+// RUN: %clang --coverage -Xclang -coverage-version='900*' %s -o %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
+// RUN: llvm-cov gcov -t gcov-basic.gcno
+
+// CHECK: Runs:2
+
+int main(int argc, char *argv[]) { // CHECK:      2: [[@LINE]]:int main
+  if (argc > 1)                    // CHECK-NEXT: 2: [[@LINE]]:
+    puts("hello");                 // CHECK-NEXT: 1: [[@LINE]]:
+  return 0;                        // CHECK-NEXT: 2: [[@LINE]]:
 }

diff  --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index ccd9d26222a9..8c4ba2bb4edf 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -62,6 +62,12 @@ static cl::opt<std::string> DefaultGCOVVersion("default-gcov-version",
                                                cl::init("408*"), cl::Hidden,
                                                cl::ValueRequired);
 
+// Returns the number of words which will be used to represent this string.
+static unsigned wordsOfString(StringRef s) {
+  // Length + NUL-terminated string + 0~3 padding NULs.
+  return (s.size() / 4) + 2;
+}
+
 GCOVOptions GCOVOptions::getDefault() {
   GCOVOptions Options;
   Options.EmitNotes = true;
@@ -87,6 +93,18 @@ class GCOVProfiler {
   runOnModule(Module &M,
               std::function<const TargetLibraryInfo &(Function &F)> GetTLI);
 
+  void write(uint32_t i) {
+    char Bytes[4];
+    endian::write32(Bytes, i, Endian);
+    os->write(Bytes, 4);
+  }
+  void writeString(StringRef s) {
+    write(wordsOfString(s) - 1);
+    os->write(s.data(), s.size());
+    os->write_zeros(4 - s.size() % 4);
+  }
+  void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); }
+
 private:
   // Create the .gcno files for the Module based on DebugInfo.
   void emitProfileNotes();
@@ -120,6 +138,8 @@ class GCOVProfiler {
   std::string mangleName(const DICompileUnit *CU, GCovFileType FileType);
 
   GCOVOptions Options;
+  support::endianness Endian;
+  raw_ostream *os;
 
   // Checksum, produced by hash of EdgeDestinations
   SmallVector<uint32_t, 4> FileChecksums;
@@ -196,37 +216,13 @@ static SmallString<128> getFilename(const DISubprogram *SP) {
 namespace {
   class GCOVRecord {
   protected:
-    support::endianness Endian;
-
-    GCOVRecord(support::endianness Endian) : Endian(Endian) {}
-
-    void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); }
-
-    void write(uint32_t i) {
-      char Bytes[4];
-      endian::write32(Bytes, i, Endian);
-      os->write(Bytes, 4);
-    }
-
-    // Returns the length measured in 4-byte blocks that will be used to
-    // represent this string in a GCOV file
-    static unsigned lengthOfGCOVString(StringRef s) {
-      // A GCOV string is a length, followed by a NUL, then between 0 and 3 NULs
-      // padding out to the next 4-byte word. The length is measured in 4-byte
-      // words including padding, not bytes of actual string.
-      return (s.size() / 4) + 1;
-    }
-
-    void writeGCOVString(StringRef s) {
-      uint32_t Len = lengthOfGCOVString(s);
-      write(Len);
-      writeBytes(s.data(), s.size());
+    GCOVProfiler *P;
 
-      // Write 1 to 4 bytes of NUL padding.
-      writeBytes("\0\0\0\0", 4 - (s.size() % 4));
-    }
+    GCOVRecord(GCOVProfiler *P) : P(P) {}
 
-    raw_ostream *os;
+    void write(uint32_t i) { P->write(i); }
+    void writeString(StringRef s) { P->writeString(s); }
+    void writeBytes(const char *Bytes, int Size) { P->writeBytes(Bytes, Size); }
   };
 
   class GCOVFunction;
@@ -243,23 +239,20 @@ namespace {
     }
 
     uint32_t length() const {
-      // Here 2 = 1 for string length + 1 for '0' id#.
-      return lengthOfGCOVString(Filename) + 2 + Lines.size();
+      return 1 + wordsOfString(Filename) + Lines.size();
     }
 
     void writeOut() {
       write(0);
-      writeGCOVString(Filename);
+      writeString(Filename);
       for (int i = 0, e = Lines.size(); i != e; ++i)
         write(Lines[i]);
     }
 
-    GCOVLines(StringRef F, raw_ostream *os, support::endianness Endian)
-        : GCOVRecord(Endian), Filename(std::string(F)) {
-      this->os = os;
-    }
+    GCOVLines(GCOVProfiler *P, StringRef F)
+        : GCOVRecord(P), Filename(std::string(F)) {}
 
-   private:
+  private:
     std::string Filename;
     SmallVector<uint32_t, 32> Lines;
   };
@@ -271,8 +264,7 @@ namespace {
   class GCOVBlock : public GCOVRecord {
    public:
     GCOVLines &getFile(StringRef Filename) {
-      return LinesByFile.try_emplace(Filename, Filename, os, Endian)
-          .first->second;
+      return LinesByFile.try_emplace(Filename, P, Filename).first->second;
     }
 
     void addEdge(GCOVBlock &Successor) {
@@ -312,10 +304,8 @@ namespace {
    private:
     friend class GCOVFunction;
 
-    GCOVBlock(uint32_t Number, raw_ostream *os, support::endianness Endian)
-        : GCOVRecord(Endian), Number(Number) {
-      this->os = os;
-    }
+    GCOVBlock(GCOVProfiler *P, uint32_t Number)
+        : GCOVRecord(P), Number(Number) {}
 
     uint32_t Number;
     StringMap<GCOVLines> LinesByFile;
@@ -327,21 +317,18 @@ namespace {
   // object users can construct, the blocks and lines will be rooted here.
   class GCOVFunction : public GCOVRecord {
   public:
-    GCOVFunction(const DISubprogram *SP, Function *F, raw_ostream *os,
-                 support::endianness Endian, uint32_t Ident,
-                 bool UseCfgChecksum, bool ExitBlockBeforeBody)
-        : GCOVRecord(Endian), SP(SP), Ident(Ident),
-          UseCfgChecksum(UseCfgChecksum), ReturnBlock(1, os, Endian) {
-      this->os = os;
-
+    GCOVFunction(GCOVProfiler *P, Function *F, const DISubprogram *SP,
+                 unsigned EndLine, uint32_t Ident, int Version)
+        : GCOVRecord(P), SP(SP), EndLine(EndLine), Ident(Ident),
+          Version(Version), ReturnBlock(P, 1) {
       LLVM_DEBUG(dbgs() << "Function: " << getFunctionName(SP) << "\n");
-
+      bool ExitBlockBeforeBody = Version >= 48;
       uint32_t i = 0;
       for (auto &BB : *F) {
         // Skip index 1 if it's assigned to the ReturnBlock.
         if (i == 1 && ExitBlockBeforeBody)
           ++i;
-        Blocks.insert(std::make_pair(&BB, GCOVBlock(i++, os, Endian)));
+        Blocks.insert(std::make_pair(&BB, GCOVBlock(P, i++)));
       }
       if (!ExitBlockBeforeBody)
         ReturnBlock.Number = i;
@@ -380,26 +367,45 @@ namespace {
     void writeOut(uint32_t CfgChecksum) {
       write(GCOV_TAG_FUNCTION);
       SmallString<128> Filename = getFilename(SP);
-      uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) +
-                          1 + lengthOfGCOVString(Filename) + 1;
-      if (UseCfgChecksum)
-        ++BlockLen;
+      uint32_t BlockLen =
+          2 + (Version >= 47) + wordsOfString(getFunctionName(SP));
+      if (Version < 80)
+        BlockLen += wordsOfString(Filename) + 1;
+      else
+        BlockLen += 1 + wordsOfString(Filename) + 3 + (Version >= 90);
+
       write(BlockLen);
       write(Ident);
       write(FuncChecksum);
-      if (UseCfgChecksum)
+      if (Version >= 47)
         write(CfgChecksum);
-      writeGCOVString(getFunctionName(SP));
-      writeGCOVString(Filename);
-      write(SP->getLine());
+      writeString(getFunctionName(SP));
+      if (Version < 80) {
+        writeString(Filename);
+        write(SP->getLine());
+      } else {
+        write(SP->isArtificial()); // artificial
+        writeString(Filename);
+        write(SP->getLine()); // start_line
+        write(0);             // start_column
+        // EndLine is the last line with !dbg. It is not the } line as in GCC,
+        // but good enough.
+        write(EndLine);
+        if (Version >= 90)
+          write(0); // end_column
+      }
 
       // Emit count of blocks.
       write(GCOV_TAG_BLOCKS);
-      write(Blocks.size() + 1);
-      for (int i = 0, e = Blocks.size() + 1; i != e; ++i) {
-        write(0);  // No flags on our blocks.
+      if (Version < 80) {
+        write(Blocks.size() + 1);
+        for (int i = Blocks.size() + 1; i; --i)
+          write(0);
+      } else {
+        write(1);
+        write(Blocks.size() + 1);
       }
-      LLVM_DEBUG(dbgs() << Blocks.size() << " blocks.\n");
+      LLVM_DEBUG(dbgs() << (Blocks.size() + 1) << " blocks\n");
 
       // Emit edges between blocks.
       Function *F = Blocks.begin()->first->getParent();
@@ -425,9 +431,10 @@ namespace {
 
   private:
     const DISubprogram *SP;
+    unsigned EndLine;
     uint32_t Ident;
     uint32_t FuncChecksum;
-    bool UseCfgChecksum;
+    int Version;
     DenseMap<BasicBlock *, GCOVBlock> Blocks;
     GCOVBlock ReturnBlock;
   };
@@ -572,9 +579,10 @@ PreservedAnalyses GCOVProfilerPass::run(Module &M,
   return PreservedAnalyses::none();
 }
 
-static bool functionHasLines(Function &F) {
+static bool functionHasLines(const Function &F, unsigned &EndLine) {
   // Check whether this function actually has any source lines. Not only
   // do these waste space, they also can crash gcov.
+  EndLine = 0;
   for (auto &BB : F) {
     for (auto &I : BB) {
       // Debug intrinsic locations correspond to the location of the
@@ -587,6 +595,7 @@ static bool functionHasLines(Function &F) {
 
       // Artificial lines such as calls to the global constructors.
       if (Loc.getLine() == 0) continue;
+      EndLine = std::max(EndLine, Loc.getLine());
 
       return true;
     }
@@ -689,6 +698,15 @@ void GCOVProfiler::emitProfileNotes() {
   NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu");
   if (!CU_Nodes) return;
 
+  int Version;
+  {
+    uint8_t c3 = Options.Version[0];
+    uint8_t c2 = Options.Version[1];
+    uint8_t c1 = Options.Version[2];
+    Version = c3 >= 'A' ? (c3 - 'A') * 100 + (c2 - '0') * 10 + c1 - '0'
+                        : (c3 - '0') * 10 + c1 - '0';
+  }
+
   for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) {
     // Each compile unit gets its own .gcno file. This means that whether we run
     // this pass over the original .o's as they're produced, or run it after
@@ -711,14 +729,14 @@ void GCOVProfiler::emitProfileNotes() {
 
     std::string EdgeDestinations;
 
-    auto Endian = M->getDataLayout().isLittleEndian()
-                      ? support::endianness::little
-                      : support::endianness::big;
+    Endian = M->getDataLayout().isLittleEndian() ? support::endianness::little
+                                                 : support::endianness::big;
     unsigned FunctionIdent = 0;
     for (auto &F : M->functions()) {
       DISubprogram *SP = F.getSubprogram();
+      unsigned EndLine;
       if (!SP) continue;
-      if (!functionHasLines(F) || !isFunctionInstrumented(F))
+      if (!functionHasLines(F, EndLine) || !isFunctionInstrumented(F))
         continue;
       // TODO: Functions using scope-based EH are currently not supported.
       if (isUsingScopeBasedEH(F)) continue;
@@ -731,11 +749,8 @@ void GCOVProfiler::emitProfileNotes() {
         ++It;
       EntryBlock.splitBasicBlock(It);
 
-      bool UseCfgChecksum = strncmp(Options.Version, "407", 3) >= 0;
-      bool ExitBlockBeforeBody = strncmp(Options.Version, "408", 3) >= 0;
-      Funcs.push_back(
-          std::make_unique<GCOVFunction>(SP, &F, &out, Endian, FunctionIdent++,
-                                         UseCfgChecksum, ExitBlockBeforeBody));
+      Funcs.push_back(std::make_unique<GCOVFunction>(this, &F, SP, EndLine,
+                                                     FunctionIdent++, Version));
       GCOVFunction &Func = *Funcs.back();
 
       // Add the function line number to the lines of the entry block
@@ -785,7 +800,9 @@ void GCOVProfiler::emitProfileNotes() {
     }
 
     char Tmp[4];
-    FileChecksums.push_back(hash_value(EdgeDestinations));
+    os = &out;
+    auto Stamp = static_cast<uint32_t>(hash_value(EdgeDestinations));
+    FileChecksums.push_back(Stamp);
     if (Endian == support::endianness::big) {
       out.write("gcno", 4);
       out.write(Options.Version, 4);
@@ -794,13 +811,17 @@ void GCOVProfiler::emitProfileNotes() {
       std::reverse_copy(Options.Version, Options.Version + 4, Tmp);
       out.write(Tmp, 4);
     }
-    endian::write32(Tmp, FileChecksums.back(), Endian);
-    out.write(Tmp, 4);
+    write(Stamp);
+    if (Version >= 90)
+      writeString(""); // unuseful current_working_directory
+    if (Version >= 80)
+      write(0); // unuseful has_unexecuted_blocks
 
     for (auto &Func : Funcs)
-      Func->writeOut(FileChecksums.back());
+      Func->writeOut(Stamp);
 
-    out.write("\0\0\0\0\0\0\0\0", 8);  // EOF
+    write(0);
+    write(0);
     out.close();
   }
 }
@@ -814,8 +835,9 @@ bool GCOVProfiler::emitProfileArcs() {
     SmallVector<std::pair<GlobalVariable *, MDNode *>, 8> CountersBySP;
     for (auto &F : M->functions()) {
       DISubprogram *SP = F.getSubprogram();
+      unsigned EndLine;
       if (!SP) continue;
-      if (!functionHasLines(F) || !isFunctionInstrumented(F))
+      if (!functionHasLines(F, EndLine) || !isFunctionInstrumented(F))
         continue;
       // TODO: Functions using scope-based EH are currently not supported.
       if (isUsingScopeBasedEH(F)) continue;


        


More information about the llvm-commits mailing list