X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=include%2Fllvm%2FSupport%2FGCOV.h;h=544434f036a4a8fb74a79d3a063db9c7fa0e65aa;hb=abb30d1a458e4b68274a6e56fe3d1e5575845d81;hp=d80913717a34ed0366ce133c779a29c6503c1582;hpb=812ad83e58f94c006034b942b9482ec1331ae3a2;p=oota-llvm.git diff --git a/include/llvm/Support/GCOV.h b/include/llvm/Support/GCOV.h index d80913717a3..544434f036a 100644 --- a/include/llvm/Support/GCOV.h +++ b/include/llvm/Support/GCOV.h @@ -1,4 +1,4 @@ -//===-- llvm/Support/GCOV.h - LLVM coverage tool ----------------*- C++ -*-===// +//===- GCOV.h - LLVM coverage tool ----------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This header provides the interface to read and write coverage files that +// This header provides the interface to read and write coverage files that // use 'gcov' format. // //===----------------------------------------------------------------------===// @@ -16,8 +16,10 @@ #define LLVM_SUPPORT_GCOV_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/iterator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" @@ -28,10 +30,23 @@ class GCOVBlock; class FileInfo; namespace GCOV { - enum GCOVVersion { - V402, - V404 - }; +enum GCOVVersion { V402, V404, V704 }; + +/// \brief 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) + : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), + PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N) {} + + bool AllBlocks; + bool BranchInfo; + bool BranchCount; + bool FuncCoverage; + bool PreservePaths; + bool UncondBranch; + bool LongFileNames; + bool NoOutput; +}; } // end GCOV namespace /// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific @@ -39,7 +54,7 @@ namespace GCOV { class GCOVBuffer { public: GCOVBuffer(MemoryBuffer *B) : Buffer(B), Cursor(0) {} - + /// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer. bool readGCNOFormat() { StringRef File = Buffer->getBuffer().slice(0, 4); @@ -64,7 +79,7 @@ public: /// readGCOVVersion - Read GCOV version. bool readGCOVVersion(GCOV::GCOVVersion &Version) { - StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor+4); + StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4); if (VersionStr == "*204") { Cursor += 4; Version = GCOV::V402; @@ -75,6 +90,11 @@ public: Version = GCOV::V404; return true; } + if (VersionStr == "*704") { + Cursor += 4; + Version = GCOV::V704; + return true; + } errs() << "Unexpected version: " << VersionStr << ".\n"; return false; } @@ -82,10 +102,9 @@ public: /// readFunctionTag - If cursor points to a function tag then increment the /// cursor and return true otherwise return false. bool readFunctionTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); - if (Tag.empty() || - Tag[0] != '\0' || Tag[1] != '\0' || - Tag[2] != '\0' || Tag[3] != '\1') { + StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || + Tag[3] != '\1') { return false; } Cursor += 4; @@ -95,10 +114,9 @@ public: /// readBlockTag - If cursor points to a block tag then increment the /// cursor and return true otherwise return false. bool readBlockTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); - if (Tag.empty() || - Tag[0] != '\0' || Tag[1] != '\0' || - Tag[2] != '\x41' || Tag[3] != '\x01') { + StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x41' || + Tag[3] != '\x01') { return false; } Cursor += 4; @@ -108,10 +126,9 @@ public: /// readEdgeTag - If cursor points to an edge tag then increment the /// cursor and return true otherwise return false. bool readEdgeTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); - if (Tag.empty() || - Tag[0] != '\0' || Tag[1] != '\0' || - Tag[2] != '\x43' || Tag[3] != '\x01') { + StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x43' || + Tag[3] != '\x01') { return false; } Cursor += 4; @@ -121,10 +138,9 @@ public: /// readLineTag - If cursor points to a line tag then increment the /// cursor and return true otherwise return false. bool readLineTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); - if (Tag.empty() || - Tag[0] != '\0' || Tag[1] != '\0' || - Tag[2] != '\x45' || Tag[3] != '\x01') { + StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x45' || + Tag[3] != '\x01') { return false; } Cursor += 4; @@ -134,10 +150,9 @@ public: /// readArcTag - If cursor points to an gcda arc tag then increment the /// cursor and return true otherwise return false. bool readArcTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); - if (Tag.empty() || - Tag[0] != '\0' || Tag[1] != '\0' || - Tag[2] != '\xa1' || Tag[3] != '\1') { + StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\xa1' || + Tag[3] != '\1') { return false; } Cursor += 4; @@ -147,10 +162,9 @@ public: /// readObjectTag - If cursor points to an object summary tag then increment /// the cursor and return true otherwise return false. bool readObjectTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); - if (Tag.empty() || - Tag[0] != '\0' || Tag[1] != '\0' || - Tag[2] != '\0' || Tag[3] != '\xa1') { + StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || + Tag[3] != '\xa1') { return false; } Cursor += 4; @@ -160,10 +174,9 @@ public: /// readProgramTag - If cursor points to a program summary tag then increment /// the cursor and return true otherwise return false. bool readProgramTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); - if (Tag.empty() || - Tag[0] != '\0' || Tag[1] != '\0' || - Tag[2] != '\0' || Tag[3] != '\xa3') { + StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || + Tag[3] != '\xa3') { return false; } Cursor += 4; @@ -171,11 +184,11 @@ public: } bool readInt(uint32_t &Val) { - if (Buffer->getBuffer().size() < Cursor+4) { - errs() << "Unexpected end of memory buffer: " << Cursor+4 << ".\n"; + if (Buffer->getBuffer().size() < Cursor + 4) { + errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n"; return false; } - StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor+4); + StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4); Cursor += 4; Val = *(const uint32_t *)(Str.data()); return true; @@ -183,26 +196,32 @@ public: bool readInt64(uint64_t &Val) { uint32_t Lo, Hi; - if (!readInt(Lo) || !readInt(Hi)) return false; + if (!readInt(Lo) || !readInt(Hi)) + return false; Val = ((uint64_t)Hi << 32) | Lo; return true; } bool readString(StringRef &Str) { - uint32_t Len; - if (!readInt(Len)) return false; + uint32_t Len = 0; + // Keep reading until we find a non-zero length. This emulates gcov's + // behaviour, which appears to do the same. + while (Len == 0) + if (!readInt(Len)) + return false; Len *= 4; - if (Buffer->getBuffer().size() < Cursor+Len) { - errs() << "Unexpected end of memory buffer: " << Cursor+Len << ".\n"; + if (Buffer->getBuffer().size() < Cursor + Len) { + errs() << "Unexpected end of memory buffer: " << Cursor + Len << ".\n"; return false; } - Str = Buffer->getBuffer().slice(Cursor, Cursor+Len).split('\0').first; + Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first; Cursor += Len; return true; } uint64_t getCursor() const { return Cursor; } - void advanceCursor(uint32_t n) { Cursor += n*4; } + void advanceCursor(uint32_t n) { Cursor += n * 4; } + private: MemoryBuffer *Buffer; uint64_t Cursor; @@ -212,103 +231,215 @@ private: /// (.gcno and .gcda). class GCOVFile { public: - GCOVFile() : gcnoInitialized(false), Checksum(0), Functions(), RunCount(0), - ProgramCount(0) {} - ~GCOVFile(); + GCOVFile() + : GCNOInitialized(false), Checksum(0), Functions(), RunCount(0), + ProgramCount(0) {} bool readGCNO(GCOVBuffer &Buffer); bool readGCDA(GCOVBuffer &Buffer); + uint32_t getChecksum() const { return Checksum; } void dump() const; void collectLineCounts(FileInfo &FI); + private: - bool gcnoInitialized; + bool GCNOInitialized; GCOV::GCOVVersion Version; uint32_t Checksum; - SmallVector Functions; + SmallVector, 16> Functions; uint32_t RunCount; uint32_t ProgramCount; }; +/// GCOVEdge - Collects edge information. struct GCOVEdge { - GCOVEdge(GCOVBlock *S, GCOVBlock *D): Src(S), Dst(D), Count(0) {} + GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D), Count(0) {} - GCOVBlock *Src; - GCOVBlock *Dst; + GCOVBlock &Src; + GCOVBlock &Dst; uint64_t Count; }; /// GCOVFunction - Collects function information. class GCOVFunction { public: - GCOVFunction() : Ident(0), LineNumber(0) {} - ~GCOVFunction(); + typedef pointee_iterator>::const_iterator> BlockIterator; + + GCOVFunction(GCOVFile &P) : Parent(P), Ident(0), LineNumber(0) {} bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version); bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version); + StringRef getName() const { return Name; } StringRef getFilename() const { return Filename; } + size_t getNumBlocks() const { return Blocks.size(); } + uint64_t getEntryCount() const; + uint64_t getExitCount() const; + + BlockIterator block_begin() const { return Blocks.begin(); } + BlockIterator block_end() const { return Blocks.end(); } + iterator_range blocks() const { + return make_range(block_begin(), block_end()); + } + void dump() const; void collectLineCounts(FileInfo &FI); + private: + GCOVFile &Parent; uint32_t Ident; + uint32_t Checksum; uint32_t LineNumber; StringRef Name; StringRef Filename; - SmallVector Blocks; - SmallVector Edges; + SmallVector, 16> Blocks; + SmallVector, 16> Edges; }; /// GCOVBlock - Collects block information. class GCOVBlock { + struct EdgeWeight { + EdgeWeight(GCOVBlock *D) : Dst(D), Count(0) {} + + GCOVBlock *Dst; + uint64_t Count; + }; + + struct SortDstEdgesFunctor { + bool operator()(const GCOVEdge *E1, const GCOVEdge *E2) { + return E1->Dst.Number < E2->Dst.Number; + } + }; + public: typedef SmallVectorImpl::const_iterator EdgeIterator; - GCOVBlock(GCOVFunction &P, uint32_t N) : - Parent(P), Number(N), Counter(0), SrcEdges(), DstEdges(), Lines() {} + GCOVBlock(GCOVFunction &P, uint32_t N) + : Parent(P), Number(N), Counter(0), DstEdgesAreSorted(true), SrcEdges(), + DstEdges(), Lines() {} ~GCOVBlock(); + const GCOVFunction &getParent() const { return Parent; } + void addLine(uint32_t N) { Lines.push_back(N); } + uint32_t getLastLine() const { return Lines.back(); } + void addCount(size_t DstEdgeNo, uint64_t N); + uint64_t getCount() const { return Counter; } + void addSrcEdge(GCOVEdge *Edge) { - assert(Edge->Dst == this); // up to caller to ensure edge is valid + assert(&Edge->Dst == this); // up to caller to ensure edge is valid SrcEdges.push_back(Edge); } void addDstEdge(GCOVEdge *Edge) { - assert(Edge->Src == this); // up to caller to ensure edge is valid + assert(&Edge->Src == this); // up to caller to ensure edge is valid + // Check if adding this edge causes list to become unsorted. + if (DstEdges.size() && DstEdges.back()->Dst.Number > Edge->Dst.Number) + DstEdgesAreSorted = false; DstEdges.push_back(Edge); } - void addLine(uint32_t N) { Lines.push_back(N); } - void addCount(size_t DstEdgeNo, uint64_t N); - uint64_t getCount() const { return Counter; } size_t getNumSrcEdges() const { return SrcEdges.size(); } size_t getNumDstEdges() const { return DstEdges.size(); } + void sortDstEdges(); EdgeIterator src_begin() const { return SrcEdges.begin(); } EdgeIterator src_end() const { return SrcEdges.end(); } + iterator_range srcs() const { + return make_range(src_begin(), src_end()); + } + EdgeIterator dst_begin() const { return DstEdges.begin(); } EdgeIterator dst_end() const { return DstEdges.end(); } + iterator_range dsts() const { + return make_range(dst_begin(), dst_end()); + } void dump() const; void collectLineCounts(FileInfo &FI); + private: GCOVFunction &Parent; uint32_t Number; uint64_t Counter; + bool DstEdgesAreSorted; SmallVector SrcEdges; SmallVector DstEdges; SmallVector Lines; }; -typedef SmallVector BlockVector; -typedef DenseMap LineData; class FileInfo { + // It is unlikely--but possible--for multiple functions to be on the same + // line. + // Therefore this typedef allows LineData.Functions to store multiple + // functions + // per instance. This is rare, however, so optimize for the common case. + typedef SmallVector FunctionVector; + typedef DenseMap FunctionLines; + typedef SmallVector BlockVector; + typedef DenseMap BlockLines; + + struct LineData { + LineData() : LastLine(0) {} + BlockLines Blocks; + FunctionLines Functions; + uint32_t LastLine; + }; + + struct GCOVCoverage { + GCOVCoverage(StringRef Name) + : Name(Name), LogicalLines(0), LinesExec(0), Branches(0), + BranchesExec(0), BranchesTaken(0) {} + + StringRef Name; + + uint32_t LogicalLines; + uint32_t LinesExec; + + uint32_t Branches; + uint32_t BranchesExec; + uint32_t BranchesTaken; + }; + public: + FileInfo(const GCOV::Options &Options) + : Options(Options), LineInfo(), RunCount(0), ProgramCount(0) {} + void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) { - LineInfo[Filename][Line-1].push_back(Block); + if (Line > LineInfo[Filename].LastLine) + LineInfo[Filename].LastLine = Line; + LineInfo[Filename].Blocks[Line - 1].push_back(Block); + } + void addFunctionLine(StringRef Filename, uint32_t Line, + const GCOVFunction *Function) { + if (Line > LineInfo[Filename].LastLine) + LineInfo[Filename].LastLine = Line; + LineInfo[Filename].Functions[Line - 1].push_back(Function); } void setRunCount(uint32_t Runs) { RunCount = Runs; } void setProgramCount(uint32_t Programs) { ProgramCount = Programs; } - void print(StringRef gcnoFile, StringRef gcdaFile) const; + void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile, + StringRef GCDAFile); + private: + std::string getCoveragePath(StringRef Filename, StringRef MainFilename); + std::unique_ptr openCoveragePath(StringRef CoveragePath); + void printFunctionSummary(raw_ostream &OS, const FunctionVector &Funcs) const; + void printBlockInfo(raw_ostream &OS, const GCOVBlock &Block, + uint32_t LineIndex, uint32_t &BlockNo) const; + void printBranchInfo(raw_ostream &OS, const GCOVBlock &Block, + GCOVCoverage &Coverage, uint32_t &EdgeNo); + void printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo, + uint64_t Count) const; + + void printCoverage(raw_ostream &OS, const GCOVCoverage &Coverage) const; + void printFuncCoverage(raw_ostream &OS) const; + void printFileCoverage(raw_ostream &OS) const; + + const GCOV::Options &Options; StringMap LineInfo; uint32_t RunCount; uint32_t ProgramCount; -}; + typedef SmallVector, 4> FileCoverageList; + typedef MapVector FuncCoverageMap; + + FileCoverageList FileCoverages; + FuncCoverageMap FuncCoverages; +}; } #endif