Home | History | Annotate | Download | only in Support
      1 //===- GCOV.h - LLVM coverage tool ------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This header provides the interface to read and write coverage files that
     11 // use 'gcov' format.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #ifndef LLVM_SUPPORT_GCOV_H
     16 #define LLVM_SUPPORT_GCOV_H
     17 
     18 #include "llvm/ADT/DenseMap.h"
     19 #include "llvm/ADT/MapVector.h"
     20 #include "llvm/ADT/SmallVector.h"
     21 #include "llvm/ADT/StringMap.h"
     22 #include "llvm/ADT/StringRef.h"
     23 #include "llvm/ADT/iterator.h"
     24 #include "llvm/ADT/iterator_range.h"
     25 #include "llvm/Support/MemoryBuffer.h"
     26 #include "llvm/Support/raw_ostream.h"
     27 #include <cassert>
     28 #include <cstddef>
     29 #include <cstdint>
     30 #include <memory>
     31 #include <string>
     32 #include <utility>
     33 
     34 namespace llvm {
     35 
     36 class GCOVFunction;
     37 class GCOVBlock;
     38 class FileInfo;
     39 
     40 namespace GCOV {
     41 
     42 enum GCOVVersion { V402, V404, V704 };
     43 
     44 /// \brief A struct for passing gcov options between functions.
     45 struct Options {
     46   Options(bool A, bool B, bool C, bool F, bool P, bool U, bool L, bool N)
     47       : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F),
     48         PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N) {}
     49 
     50   bool AllBlocks;
     51   bool BranchInfo;
     52   bool BranchCount;
     53   bool FuncCoverage;
     54   bool PreservePaths;
     55   bool UncondBranch;
     56   bool LongFileNames;
     57   bool NoOutput;
     58 };
     59 
     60 } // end namespace GCOV
     61 
     62 /// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific
     63 /// read operations.
     64 class GCOVBuffer {
     65 public:
     66   GCOVBuffer(MemoryBuffer *B) : Buffer(B) {}
     67 
     68   /// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
     69   bool readGCNOFormat() {
     70     StringRef File = Buffer->getBuffer().slice(0, 4);
     71     if (File != "oncg") {
     72       errs() << "Unexpected file type: " << File << ".\n";
     73       return false;
     74     }
     75     Cursor = 4;
     76     return true;
     77   }
     78 
     79   /// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
     80   bool readGCDAFormat() {
     81     StringRef File = Buffer->getBuffer().slice(0, 4);
     82     if (File != "adcg") {
     83       errs() << "Unexpected file type: " << File << ".\n";
     84       return false;
     85     }
     86     Cursor = 4;
     87     return true;
     88   }
     89 
     90   /// readGCOVVersion - Read GCOV version.
     91   bool readGCOVVersion(GCOV::GCOVVersion &Version) {
     92     StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4);
     93     if (VersionStr == "*204") {
     94       Cursor += 4;
     95       Version = GCOV::V402;
     96       return true;
     97     }
     98     if (VersionStr == "*404") {
     99       Cursor += 4;
    100       Version = GCOV::V404;
    101       return true;
    102     }
    103     if (VersionStr == "*704") {
    104       Cursor += 4;
    105       Version = GCOV::V704;
    106       return true;
    107     }
    108     errs() << "Unexpected version: " << VersionStr << ".\n";
    109     return false;
    110   }
    111 
    112   /// readFunctionTag - If cursor points to a function tag then increment the
    113   /// cursor and return true otherwise return false.
    114   bool readFunctionTag() {
    115     StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    116     if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
    117         Tag[3] != '\1') {
    118       return false;
    119     }
    120     Cursor += 4;
    121     return true;
    122   }
    123 
    124   /// readBlockTag - If cursor points to a block tag then increment the
    125   /// cursor and return true otherwise return false.
    126   bool readBlockTag() {
    127     StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    128     if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x41' ||
    129         Tag[3] != '\x01') {
    130       return false;
    131     }
    132     Cursor += 4;
    133     return true;
    134   }
    135 
    136   /// readEdgeTag - If cursor points to an edge tag then increment the
    137   /// cursor and return true otherwise return false.
    138   bool readEdgeTag() {
    139     StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    140     if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x43' ||
    141         Tag[3] != '\x01') {
    142       return false;
    143     }
    144     Cursor += 4;
    145     return true;
    146   }
    147 
    148   /// readLineTag - If cursor points to a line tag then increment the
    149   /// cursor and return true otherwise return false.
    150   bool readLineTag() {
    151     StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    152     if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x45' ||
    153         Tag[3] != '\x01') {
    154       return false;
    155     }
    156     Cursor += 4;
    157     return true;
    158   }
    159 
    160   /// readArcTag - If cursor points to an gcda arc tag then increment the
    161   /// cursor and return true otherwise return false.
    162   bool readArcTag() {
    163     StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    164     if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\xa1' ||
    165         Tag[3] != '\1') {
    166       return false;
    167     }
    168     Cursor += 4;
    169     return true;
    170   }
    171 
    172   /// readObjectTag - If cursor points to an object summary tag then increment
    173   /// the cursor and return true otherwise return false.
    174   bool readObjectTag() {
    175     StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    176     if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
    177         Tag[3] != '\xa1') {
    178       return false;
    179     }
    180     Cursor += 4;
    181     return true;
    182   }
    183 
    184   /// readProgramTag - If cursor points to a program summary tag then increment
    185   /// the cursor and return true otherwise return false.
    186   bool readProgramTag() {
    187     StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    188     if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
    189         Tag[3] != '\xa3') {
    190       return false;
    191     }
    192     Cursor += 4;
    193     return true;
    194   }
    195 
    196   bool readInt(uint32_t &Val) {
    197     if (Buffer->getBuffer().size() < Cursor + 4) {
    198       errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n";
    199       return false;
    200     }
    201     StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4);
    202     Cursor += 4;
    203     Val = *(const uint32_t *)(Str.data());
    204     return true;
    205   }
    206 
    207   bool readInt64(uint64_t &Val) {
    208     uint32_t Lo, Hi;
    209     if (!readInt(Lo) || !readInt(Hi))
    210       return false;
    211     Val = ((uint64_t)Hi << 32) | Lo;
    212     return true;
    213   }
    214 
    215   bool readString(StringRef &Str) {
    216     uint32_t Len = 0;
    217     // Keep reading until we find a non-zero length. This emulates gcov's
    218     // behaviour, which appears to do the same.
    219     while (Len == 0)
    220       if (!readInt(Len))
    221         return false;
    222     Len *= 4;
    223     if (Buffer->getBuffer().size() < Cursor + Len) {
    224       errs() << "Unexpected end of memory buffer: " << Cursor + Len << ".\n";
    225       return false;
    226     }
    227     Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first;
    228     Cursor += Len;
    229     return true;
    230   }
    231 
    232   uint64_t getCursor() const { return Cursor; }
    233   void advanceCursor(uint32_t n) { Cursor += n * 4; }
    234 
    235 private:
    236   MemoryBuffer *Buffer;
    237   uint64_t Cursor = 0;
    238 };
    239 
    240 /// GCOVFile - Collects coverage information for one pair of coverage file
    241 /// (.gcno and .gcda).
    242 class GCOVFile {
    243 public:
    244   GCOVFile() = default;
    245 
    246   bool readGCNO(GCOVBuffer &Buffer);
    247   bool readGCDA(GCOVBuffer &Buffer);
    248   uint32_t getChecksum() const { return Checksum; }
    249   void print(raw_ostream &OS) const;
    250   void dump() const;
    251   void collectLineCounts(FileInfo &FI);
    252 
    253 private:
    254   bool GCNOInitialized = false;
    255   GCOV::GCOVVersion Version;
    256   uint32_t Checksum = 0;
    257   SmallVector<std::unique_ptr<GCOVFunction>, 16> Functions;
    258   uint32_t RunCount = 0;
    259   uint32_t ProgramCount = 0;
    260 };
    261 
    262 /// GCOVEdge - Collects edge information.
    263 struct GCOVEdge {
    264   GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D) {}
    265 
    266   GCOVBlock &Src;
    267   GCOVBlock &Dst;
    268   uint64_t Count = 0;
    269 };
    270 
    271 /// GCOVFunction - Collects function information.
    272 class GCOVFunction {
    273 public:
    274   using BlockIterator = pointee_iterator<SmallVectorImpl<
    275       std::unique_ptr<GCOVBlock>>::const_iterator>;
    276 
    277   GCOVFunction(GCOVFile &P) : Parent(P) {}
    278 
    279   bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
    280   bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
    281   StringRef getName() const { return Name; }
    282   StringRef getFilename() const { return Filename; }
    283   size_t getNumBlocks() const { return Blocks.size(); }
    284   uint64_t getEntryCount() const;
    285   uint64_t getExitCount() const;
    286 
    287   BlockIterator block_begin() const { return Blocks.begin(); }
    288   BlockIterator block_end() const { return Blocks.end(); }
    289   iterator_range<BlockIterator> blocks() const {
    290     return make_range(block_begin(), block_end());
    291   }
    292 
    293   void print(raw_ostream &OS) const;
    294   void dump() const;
    295   void collectLineCounts(FileInfo &FI);
    296 
    297 private:
    298   GCOVFile &Parent;
    299   uint32_t Ident = 0;
    300   uint32_t Checksum;
    301   uint32_t LineNumber = 0;
    302   StringRef Name;
    303   StringRef Filename;
    304   SmallVector<std::unique_ptr<GCOVBlock>, 16> Blocks;
    305   SmallVector<std::unique_ptr<GCOVEdge>, 16> Edges;
    306 };
    307 
    308 /// GCOVBlock - Collects block information.
    309 class GCOVBlock {
    310   struct EdgeWeight {
    311     EdgeWeight(GCOVBlock *D) : Dst(D) {}
    312 
    313     GCOVBlock *Dst;
    314     uint64_t Count = 0;
    315   };
    316 
    317   struct SortDstEdgesFunctor {
    318     bool operator()(const GCOVEdge *E1, const GCOVEdge *E2) {
    319       return E1->Dst.Number < E2->Dst.Number;
    320     }
    321   };
    322 
    323 public:
    324   using EdgeIterator = SmallVectorImpl<GCOVEdge *>::const_iterator;
    325 
    326   GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N) {}
    327   ~GCOVBlock();
    328 
    329   const GCOVFunction &getParent() const { return Parent; }
    330   void addLine(uint32_t N) { Lines.push_back(N); }
    331   uint32_t getLastLine() const { return Lines.back(); }
    332   void addCount(size_t DstEdgeNo, uint64_t N);
    333   uint64_t getCount() const { return Counter; }
    334 
    335   void addSrcEdge(GCOVEdge *Edge) {
    336     assert(&Edge->Dst == this); // up to caller to ensure edge is valid
    337     SrcEdges.push_back(Edge);
    338   }
    339 
    340   void addDstEdge(GCOVEdge *Edge) {
    341     assert(&Edge->Src == this); // up to caller to ensure edge is valid
    342     // Check if adding this edge causes list to become unsorted.
    343     if (DstEdges.size() && DstEdges.back()->Dst.Number > Edge->Dst.Number)
    344       DstEdgesAreSorted = false;
    345     DstEdges.push_back(Edge);
    346   }
    347 
    348   size_t getNumSrcEdges() const { return SrcEdges.size(); }
    349   size_t getNumDstEdges() const { return DstEdges.size(); }
    350   void sortDstEdges();
    351 
    352   EdgeIterator src_begin() const { return SrcEdges.begin(); }
    353   EdgeIterator src_end() const { return SrcEdges.end(); }
    354   iterator_range<EdgeIterator> srcs() const {
    355     return make_range(src_begin(), src_end());
    356   }
    357 
    358   EdgeIterator dst_begin() const { return DstEdges.begin(); }
    359   EdgeIterator dst_end() const { return DstEdges.end(); }
    360   iterator_range<EdgeIterator> dsts() const {
    361     return make_range(dst_begin(), dst_end());
    362   }
    363 
    364   void print(raw_ostream &OS) const;
    365   void dump() const;
    366   void collectLineCounts(FileInfo &FI);
    367 
    368 private:
    369   GCOVFunction &Parent;
    370   uint32_t Number;
    371   uint64_t Counter = 0;
    372   bool DstEdgesAreSorted = true;
    373   SmallVector<GCOVEdge *, 16> SrcEdges;
    374   SmallVector<GCOVEdge *, 16> DstEdges;
    375   SmallVector<uint32_t, 16> Lines;
    376 };
    377 
    378 class FileInfo {
    379   // It is unlikely--but possible--for multiple functions to be on the same
    380   // line.
    381   // Therefore this typedef allows LineData.Functions to store multiple
    382   // functions
    383   // per instance. This is rare, however, so optimize for the common case.
    384   using FunctionVector = SmallVector<const GCOVFunction *, 1>;
    385   using FunctionLines = DenseMap<uint32_t, FunctionVector>;
    386   using BlockVector = SmallVector<const GCOVBlock *, 4>;
    387   using BlockLines = DenseMap<uint32_t, BlockVector>;
    388 
    389   struct LineData {
    390     LineData() = default;
    391 
    392     BlockLines Blocks;
    393     FunctionLines Functions;
    394     uint32_t LastLine = 0;
    395   };
    396 
    397   struct GCOVCoverage {
    398     GCOVCoverage(StringRef Name) : Name(Name) {}
    399 
    400     StringRef Name;
    401 
    402     uint32_t LogicalLines = 0;
    403     uint32_t LinesExec = 0;
    404 
    405     uint32_t Branches = 0;
    406     uint32_t BranchesExec = 0;
    407     uint32_t BranchesTaken = 0;
    408   };
    409 
    410 public:
    411   FileInfo(const GCOV::Options &Options) : Options(Options) {}
    412 
    413   void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) {
    414     if (Line > LineInfo[Filename].LastLine)
    415       LineInfo[Filename].LastLine = Line;
    416     LineInfo[Filename].Blocks[Line - 1].push_back(Block);
    417   }
    418 
    419   void addFunctionLine(StringRef Filename, uint32_t Line,
    420                        const GCOVFunction *Function) {
    421     if (Line > LineInfo[Filename].LastLine)
    422       LineInfo[Filename].LastLine = Line;
    423     LineInfo[Filename].Functions[Line - 1].push_back(Function);
    424   }
    425 
    426   void setRunCount(uint32_t Runs) { RunCount = Runs; }
    427   void setProgramCount(uint32_t Programs) { ProgramCount = Programs; }
    428   void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile,
    429              StringRef GCDAFile);
    430 
    431 private:
    432   std::string getCoveragePath(StringRef Filename, StringRef MainFilename);
    433   std::unique_ptr<raw_ostream> openCoveragePath(StringRef CoveragePath);
    434   void printFunctionSummary(raw_ostream &OS, const FunctionVector &Funcs) const;
    435   void printBlockInfo(raw_ostream &OS, const GCOVBlock &Block,
    436                       uint32_t LineIndex, uint32_t &BlockNo) const;
    437   void printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
    438                        GCOVCoverage &Coverage, uint32_t &EdgeNo);
    439   void printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
    440                              uint64_t Count) const;
    441 
    442   void printCoverage(raw_ostream &OS, const GCOVCoverage &Coverage) const;
    443   void printFuncCoverage(raw_ostream &OS) const;
    444   void printFileCoverage(raw_ostream &OS) const;
    445 
    446   const GCOV::Options &Options;
    447   StringMap<LineData> LineInfo;
    448   uint32_t RunCount = 0;
    449   uint32_t ProgramCount = 0;
    450 
    451   using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>;
    452   using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>;
    453 
    454   FileCoverageList FileCoverages;
    455   FuncCoverageMap FuncCoverages;
    456 };
    457 
    458 } // end namespace llvm
    459 
    460 #endif // LLVM_SUPPORT_GCOV_H
    461