From d23c759c0fa10b7fb244b4d2911b49b95138a7e1 Mon Sep 17 00:00:00 2001
From: Yuchen Wu <yuchenericwu@hotmail.com>
Date: Wed, 20 Nov 2013 04:15:05 +0000
Subject: [PATCH] llvm-cov: Added file checksum to gcno and gcda files.

Instead of permanently outputting "MVLL" as the file checksum, clang
will create gcno and gcda checksums by hashing the destination block
numbers of every arc. This allows for llvm-cov to check if the two gcov
files are synchronized.

Regenerated the test files so they contain the checksum. Also added
negative test to ensure error when the checksums don't match.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@195191 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/llvm/Support/GCOV.h                   |  15 ++-
 lib/IR/GCOV.cpp                               |   7 +
 .../Instrumentation/GCOVProfiling.cpp         | 124 ++++++++++++------
 test/Transforms/GCOVProfiling/version.ll      |   4 +-
 test/tools/llvm-cov/Inputs/test.gcda          | Bin 824 -> 824 bytes
 test/tools/llvm-cov/Inputs/test.gcno          | Bin 3112 -> 3112 bytes
 .../Inputs/test_checksum_mismatch.gcda        | Bin 0 -> 825 bytes
 .../tools/llvm-cov/Inputs/test_read_fail.gcno | Bin 111 -> 72 bytes
 test/tools/llvm-cov/llvm-cov.test             |   2 +
 9 files changed, 102 insertions(+), 50 deletions(-)
 create mode 100644 test/tools/llvm-cov/Inputs/test_checksum_mismatch.gcda

diff --git a/include/llvm/Support/GCOV.h b/include/llvm/Support/GCOV.h
index 0fbe86c3c5a..7ba4acb1aef 100644
--- a/include/llvm/Support/GCOV.h
+++ b/include/llvm/Support/GCOV.h
@@ -45,15 +45,15 @@ public:
   
   /// readGCOVFormat - Read GCOV signature at the beginning of buffer.
   GCOV::GCOVFormat readGCOVFormat() {
-    StringRef Magic = Buffer->getBuffer().slice(0, 12);
-    Cursor = 12;
-    if (Magic == "oncg*404MVLL")
+    StringRef Magic = Buffer->getBuffer().slice(0, 8);
+    Cursor = 8;
+    if (Magic == "oncg*404")
       return GCOV::GCNO_404;
-    else if (Magic == "oncg*204MVLL")
+    else if (Magic == "oncg*204")
       return GCOV::GCNO_402;
-    else if (Magic == "adcg*404MVLL")
+    else if (Magic == "adcg*404")
       return GCOV::GCDA_404;
-    else if (Magic == "adcg*204MVLL")
+    else if (Magic == "adcg*204")
       return GCOV::GCDA_402;
     
     Cursor = 0;
@@ -193,12 +193,13 @@ private:
 /// (.gcno and .gcda).
 class GCOVFile {
 public:
-  GCOVFile() : Functions(), RunCount(0), ProgramCount(0) {}
+  GCOVFile() : Checksum(0), Functions(), RunCount(0), ProgramCount(0) {}
   ~GCOVFile();
   bool read(GCOVBuffer &Buffer);
   void dump() const;
   void collectLineCounts(FileInfo &FI);
 private:
+  uint32_t Checksum;
   SmallVector<GCOVFunction *, 16> Functions;
   uint32_t RunCount;
   uint32_t ProgramCount;
diff --git a/lib/IR/GCOV.cpp b/lib/IR/GCOV.cpp
index 25a4ff669d2..622c4f7851a 100644
--- a/lib/IR/GCOV.cpp
+++ b/lib/IR/GCOV.cpp
@@ -46,6 +46,7 @@ bool GCOVFile::read(GCOVBuffer &Buffer) {
     return false;
 
   if (isGCNOFile(Format)) {
+    if (!Buffer.readInt(Checksum)) return false;
     while (true) {
       if (!Buffer.readFunctionTag()) break;
       GCOVFunction *GFun = new GCOVFunction();
@@ -55,6 +56,12 @@ bool GCOVFile::read(GCOVBuffer &Buffer) {
     }
   }
   else if (isGCDAFile(Format)) {
+    uint32_t Checksum2;
+    if (!Buffer.readInt(Checksum2)) return false;
+    if (Checksum != Checksum2) {
+      errs() << "File checksum does not match.\n";
+      return false;
+    }
     for (size_t i = 0, e = Functions.size(); i < e; ++i) {
       if (!Buffer.readFunctionTag()) {
         errs() << "Unexpected number of functions.\n";
diff --git a/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index 206bffbb274..03c63557bce 100644
--- a/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -18,6 +18,7 @@
 
 #include "llvm/Transforms/Instrumentation.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/StringExtras.h"
@@ -62,32 +63,35 @@ GCOVOptions GCOVOptions::getDefault() {
 }
 
 namespace {
+  class GCOVFunction;
+
   class GCOVProfiler : public ModulePass {
   public:
     static char ID;
     GCOVProfiler() : ModulePass(ID), Options(GCOVOptions::getDefault()) {
-      ReversedVersion[0] = Options.Version[3];
-      ReversedVersion[1] = Options.Version[2];
-      ReversedVersion[2] = Options.Version[1];
-      ReversedVersion[3] = Options.Version[0];
-      ReversedVersion[4] = '\0';
-      initializeGCOVProfilerPass(*PassRegistry::getPassRegistry());
+      init();
     }
     GCOVProfiler(const GCOVOptions &Options) : ModulePass(ID), Options(Options){
       assert((Options.EmitNotes || Options.EmitData) &&
              "GCOVProfiler asked to do nothing?");
-      ReversedVersion[0] = Options.Version[3];
-      ReversedVersion[1] = Options.Version[2];
-      ReversedVersion[2] = Options.Version[1];
-      ReversedVersion[3] = Options.Version[0];
-      ReversedVersion[4] = '\0';
-      initializeGCOVProfilerPass(*PassRegistry::getPassRegistry());
+      init();
+    }
+    ~GCOVProfiler() {
+      DeleteContainerPointers(Funcs);
     }
     virtual const char *getPassName() const {
       return "GCOV Profiler";
     }
 
   private:
+    void init() {
+      ReversedVersion[0] = Options.Version[3];
+      ReversedVersion[1] = Options.Version[2];
+      ReversedVersion[2] = Options.Version[1];
+      ReversedVersion[3] = Options.Version[0];
+      ReversedVersion[4] = '\0';
+      initializeGCOVProfilerPass(*PassRegistry::getPassRegistry());
+    }
     bool runOnModule(Module &M);
 
     // Create the .gcno files for the Module based on DebugInfo.
@@ -131,9 +135,12 @@ namespace {
 
     // Reversed, NUL-terminated copy of Options.Version.
     char ReversedVersion[5];  
+    // Checksum, produced by hash of EdgeDestinations
+    uint32_t FileChecksum;
 
     Module *M;
     LLVMContext *Ctx;
+    SmallVector<GCOVFunction *, 16> Funcs;
   };
 }
 
@@ -145,7 +152,7 @@ ModulePass *llvm::createGCOVProfilerPass(const GCOVOptions &Options) {
   return new GCOVProfiler(Options);
 }
 
-static std::string getFunctionName(DISubprogram SP) {
+static StringRef getFunctionName(DISubprogram SP) {
   if (!SP.getLinkageName().empty())
     return SP.getLinkageName();
   return SP.getName();
@@ -302,7 +309,8 @@ namespace {
   class GCOVFunction : public GCOVRecord {
    public:
     GCOVFunction(DISubprogram SP, raw_ostream *os, uint32_t Ident,
-                 bool UseCfgChecksum) {
+                 bool UseCfgChecksum) :
+        SP(SP), Ident(Ident), UseCfgChecksum(UseCfgChecksum), CfgChecksum(0) {
       this->os = os;
 
       Function *F = SP.getFunction();
@@ -312,20 +320,6 @@ namespace {
         Blocks[BB] = new GCOVBlock(i++, os);
       }
       ReturnBlock = new GCOVBlock(i++, os);
-
-      writeBytes(FunctionTag, 4);
-      uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) +
-          1 + lengthOfGCOVString(SP.getFilename()) + 1;
-      if (UseCfgChecksum)
-        ++BlockLen;
-      write(BlockLen);
-      write(Ident);
-      write(0);  // lineno checksum
-      if (UseCfgChecksum)
-        write(0);  // cfg checksum
-      writeGCOVString(getFunctionName(SP));
-      writeGCOVString(SP.getFilename());
-      write(SP.getLineNumber());
     }
 
     ~GCOVFunction() {
@@ -341,7 +335,37 @@ namespace {
       return *ReturnBlock;
     }
 
+    std::string getEdgeDestinations() {
+      std::string EdgeDestinations;
+      raw_string_ostream EDOS(EdgeDestinations);
+      Function *F = Blocks.begin()->first->getParent();
+      for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) {
+        GCOVBlock &Block = *Blocks[I];
+        for (int i = 0, e = Block.OutEdges.size(); i != e; ++i)
+          EDOS << Block.OutEdges[i]->Number;
+      }
+      return EdgeDestinations;
+    }
+
+    void setCfgChecksum(uint32_t Checksum) {
+      CfgChecksum = Checksum;
+    }
+
     void writeOut() {
+      writeBytes(FunctionTag, 4);
+      uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) +
+          1 + lengthOfGCOVString(SP.getFilename()) + 1;
+      if (UseCfgChecksum)
+        ++BlockLen;
+      write(BlockLen);
+      write(Ident);
+      write(0);  // lineno checksum
+      if (UseCfgChecksum)
+        write(CfgChecksum);
+      writeGCOVString(getFunctionName(SP));
+      writeGCOVString(SP.getFilename());
+      write(SP.getLineNumber());
+
       // Emit count of blocks.
       writeBytes(BlockTag, 4);
       write(Blocks.size() + 1);
@@ -375,6 +399,10 @@ namespace {
     }
 
    private:
+    DISubprogram SP;
+    uint32_t Ident;
+    bool UseCfgChecksum;
+    uint32_t CfgChecksum;
     DenseMap<BasicBlock *, GCOVBlock *> Blocks;
     GCOVBlock *ReturnBlock;
   };
@@ -427,9 +455,7 @@ void GCOVProfiler::emitProfileNotes() {
     std::string ErrorInfo;
     raw_fd_ostream out(mangleName(CU, "gcno").c_str(), ErrorInfo,
                        sys::fs::F_Binary);
-    out.write("oncg", 4);
-    out.write(ReversedVersion, 4);
-    out.write("MVLL", 4);
+    std::string EdgeDestinations;
 
     DIArray SPs = CU.getSubprograms();
     for (unsigned i = 0, e = SPs.getNumElements(); i != e; ++i) {
@@ -441,17 +467,19 @@ void GCOVProfiler::emitProfileNotes() {
 
       Function *F = SP.getFunction();
       if (!F) continue;
-      GCOVFunction Func(SP, &out, i, Options.UseCfgChecksum);
+      GCOVFunction *Func =
+        new GCOVFunction(SP, &out, i, Options.UseCfgChecksum);
+      Funcs.push_back(Func);
 
       for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
-        GCOVBlock &Block = Func.getBlock(BB);
+        GCOVBlock &Block = Func->getBlock(BB);
         TerminatorInst *TI = BB->getTerminator();
         if (int successors = TI->getNumSuccessors()) {
           for (int i = 0; i != successors; ++i) {
-            Block.addEdge(Func.getBlock(TI->getSuccessor(i)));
+            Block.addEdge(Func->getBlock(TI->getSuccessor(i)));
           }
         } else if (isa<ReturnInst>(TI)) {
-          Block.addEdge(Func.getReturnBlock());
+          Block.addEdge(Func->getReturnBlock());
         }
 
         uint32_t Line = 0;
@@ -467,8 +495,18 @@ void GCOVProfiler::emitProfileNotes() {
           Lines.addLine(Loc.getLine());
         }
       }
-      Func.writeOut();
+      EdgeDestinations += Func->getEdgeDestinations();
     }
+
+    FileChecksum = hash_value(EdgeDestinations);
+    out.write("oncg", 4);
+    out.write(ReversedVersion, 4);
+    out.write(reinterpret_cast<char*>(&FileChecksum), 4);
+
+    for (SmallVectorImpl<GCOVFunction *>::iterator I = Funcs.begin(),
+           E = Funcs.end(); I != E; ++I)
+      (*I)->writeOut();
+
     out.write("\0\0\0\0\0\0\0\0", 8);  // EOF
     out.close();
   }
@@ -666,6 +704,7 @@ Constant *GCOVProfiler::getStartFileFunc() {
   Type *Args[] = {
     Type::getInt8PtrTy(*Ctx),  // const char *orig_filename
     Type::getInt8PtrTy(*Ctx),  // const char version[4]
+    Type::getInt32Ty(*Ctx),    // uint32_t checksum
   };
   FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false);
   return M->getOrInsertFunction("llvm_gcda_start_file", FTy);
@@ -683,10 +722,11 @@ Constant *GCOVProfiler::getIncrementIndirectCounterFunc() {
 }
 
 Constant *GCOVProfiler::getEmitFunctionFunc() {
-  Type *Args[3] = {
+  Type *Args[] = {
     Type::getInt32Ty(*Ctx),    // uint32_t ident
     Type::getInt8PtrTy(*Ctx),  // const char *function_name
     Type::getInt8Ty(*Ctx),     // uint8_t use_extra_checksum
+    Type::getInt32Ty(*Ctx),    // uint32_t cfg_checksum
   };
   FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false);
   return M->getOrInsertFunction("llvm_gcda_emit_function", FTy);
@@ -760,17 +800,19 @@ Function *GCOVProfiler::insertCounterWriteout(
     for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) {
       DICompileUnit CU(CU_Nodes->getOperand(i));
       std::string FilenameGcda = mangleName(CU, "gcda");
-      Builder.CreateCall2(StartFile,
+      Builder.CreateCall3(StartFile,
                           Builder.CreateGlobalStringPtr(FilenameGcda),
-                          Builder.CreateGlobalStringPtr(ReversedVersion));
+                          Builder.CreateGlobalStringPtr(ReversedVersion),
+                          Builder.getInt32(FileChecksum));
       for (unsigned j = 0, e = CountersBySP.size(); j != e; ++j) {
         DISubprogram SP(CountersBySP[j].second);
-        Builder.CreateCall3(
+        Builder.CreateCall4(
             EmitFunction, Builder.getInt32(j),
             Options.FunctionNamesInData ?
               Builder.CreateGlobalStringPtr(getFunctionName(SP)) :
               Constant::getNullValue(Builder.getInt8PtrTy()),
-            Builder.getInt8(Options.UseCfgChecksum));
+            Builder.getInt8(Options.UseCfgChecksum),
+            Builder.getInt32(FileChecksum));
 
         GlobalVariable *GV = CountersBySP[j].first;
         unsigned Arcs =
diff --git a/test/Transforms/GCOVProfiling/version.ll b/test/Transforms/GCOVProfiling/version.ll
index 15bcb052acf..409954c0d9e 100644
--- a/test/Transforms/GCOVProfiling/version.ll
+++ b/test/Transforms/GCOVProfiling/version.ll
@@ -1,11 +1,11 @@
 ; RUN: echo '!9 = metadata !{metadata !"%T/version.ll", metadata !0}' > %t1
 ; RUN: cat %s %t1 > %t2
 ; RUN: opt -insert-gcov-profiling -disable-output < %t2
-; RUN: head -c12 %T/version.gcno | grep '^oncg\*204MVLL$'
+; RUN: head -c8 %T/version.gcno | grep '^oncg\*204'
 ; RUN: rm %T/version.gcno
 ; RUN: not opt -insert-gcov-profiling -default-gcov-version=asdfasdf -disable-output < %t2
 ; RUN: opt -insert-gcov-profiling -default-gcov-version=407* -disable-output < %t2
-; RUN: head -c12 %T/version.gcno | grep '^oncg\*704MVLL$'
+; RUN: head -c8 %T/version.gcno | grep '^oncg\*704'
 ; RUN: rm %T/version.gcno
 
 define void @test() {
diff --git a/test/tools/llvm-cov/Inputs/test.gcda b/test/tools/llvm-cov/Inputs/test.gcda
index 23d03bdd1fd2e4da1ed7ea957db0477b88c0eeda..a62c069c9a7b5d2c1536b09812bb7b4357d93ad8 100644
GIT binary patch
delta 21
ccmdnNwu6l)F(o-&%gDfFR`!(_8+mk?0a0iNg#Z8m

delta 21
ccmdnNwu6l)F(o-&%gDgQH_XRpBaaR<07x+f{{R30

diff --git a/test/tools/llvm-cov/Inputs/test.gcno b/test/tools/llvm-cov/Inputs/test.gcno
index 6162604e7449ce85feb8bb6cd6438556c21ab1ee..218398548efee0828fe4762c70776d7349277c8c 100644
GIT binary patch
delta 21
ccmZ1>u|k3;KQB35%gDfFR`!(_8+l}S08;Y@ng9R*

delta 21
ccmZ1>u|k3;KQB35%gDgQH_XRpBaaLZ07<+C6aWAK

diff --git a/test/tools/llvm-cov/Inputs/test_checksum_mismatch.gcda b/test/tools/llvm-cov/Inputs/test_checksum_mismatch.gcda
new file mode 100644
index 0000000000000000000000000000000000000000..2fcf33dd21b2bd2a46728a8067903eb6d555b737
GIT binary patch
literal 825
zcmZ`%%TB{E5R56Waz=avrzWN442WCd$RQWY&@{5tCQ=L>_?wC!WykgqW0jF&@6OJS
zH*w7Rt-Y>hH%~9CRZ$e`R2E_)fv%s+yYl|=)02C36pL~9dKI~#zx{B{ws8&}qdrR1
zxdik$kyzJdwd;pQCtZj7poKqxeqGNueLtQ-Pbn#AkL3ZP_SFodHdOrT=Z78$^Dm8a
zw(YDHCd`kSBh*v^ek1HM|H5|m-I}faYIHl;b@)`$dnI+wmCge%=Jd|<oM)YWV&UKO
z3}yoGJ7YVa$l0^Vg*>cDuP2yaMiKM;Y<jTH_s?e{&wl!HXFvQ9=lKEN-w*wM|Hp^&
c&;OYCcs=e281>bE`aQMKCq77Xe5b_452mLw+W-In

literal 0
HcmV?d00001

diff --git a/test/tools/llvm-cov/Inputs/test_read_fail.gcno b/test/tools/llvm-cov/Inputs/test_read_fail.gcno
index 63b5d71e69519a8111b721b493acac3e257c59ab..bcc20a65eee483a161ac140ee0aa6e4dec99dc6d 100644
GIT binary patch
delta 20
bcmd1z;K|QRPS-LrFqxHo<;6rEYep^rNbm;F

delta 59
qcmeas=gH4YPS-LrF!2rZ@tMeDt-*i*&Wy}Z4kKL9m5~$7<^lj4z6J*X

diff --git a/test/tools/llvm-cov/llvm-cov.test b/test/tools/llvm-cov/llvm-cov.test
index 28738a78d16..d95af9b40f9 100644
--- a/test/tools/llvm-cov/llvm-cov.test
+++ b/test/tools/llvm-cov/llvm-cov.test
@@ -7,4 +7,6 @@ RUN:   | diff -aub test.cpp.gcov -
 
 RUN: not llvm-cov -gcno=test_read_fail.gcno -gcda=test.gcda
 
+RUN: not llvm-cov -gcno=test.gcno -gcda=test_checksum_mismatch.gcda
+
 XFAIL: powerpc64, s390x