X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=include%2Fllvm%2FSupport%2FGCOV.h;h=544434f036a4a8fb74a79d3a063db9c7fa0e65aa;hb=4918b66f8428f0d5b4559da8f966e3aa54c3b1ba;hp=ef1a4ebd713871e9522bfb9ce605f6511eb2bb88;hpb=76fa4d629b22903632529e2cb19c86105a0d1247;p=oota-llvm.git diff --git a/include/llvm/Support/GCOV.h b/include/llvm/Support/GCOV.h index ef1a4ebd713..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" @@ -25,17 +27,26 @@ namespace llvm { class GCOVFunction; class GCOVBlock; -class GCOVLines; class FileInfo; namespace GCOV { - enum GCOVFormat { - InvalidGCOV, - GCNO_402, - GCNO_404, - GCDA_402, - GCDA_404 - }; +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 @@ -43,31 +54,57 @@ namespace GCOV { class GCOVBuffer { public: GCOVBuffer(MemoryBuffer *B) : Buffer(B), Cursor(0) {} - - /// 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") - return GCOV::GCNO_404; - else if (Magic == "oncg*204MVLL") - return GCOV::GCNO_402; - else if (Magic == "adcg*404MVLL") - return GCOV::GCDA_404; - else if (Magic == "adcg*204MVLL") - return GCOV::GCDA_402; - - Cursor = 0; - return GCOV::InvalidGCOV; + + /// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer. + bool readGCNOFormat() { + StringRef File = Buffer->getBuffer().slice(0, 4); + if (File != "oncg") { + errs() << "Unexpected file type: " << File << ".\n"; + return false; + } + Cursor = 4; + return true; + } + + /// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer. + bool readGCDAFormat() { + StringRef File = Buffer->getBuffer().slice(0, 4); + if (File != "adcg") { + errs() << "Unexpected file type: " << File << ".\n"; + return false; + } + Cursor = 4; + return true; + } + + /// readGCOVVersion - Read GCOV version. + bool readGCOVVersion(GCOV::GCOVVersion &Version) { + StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4); + if (VersionStr == "*204") { + Cursor += 4; + Version = GCOV::V402; + return true; + } + if (VersionStr == "*404") { + Cursor += 4; + Version = GCOV::V404; + return true; + } + if (VersionStr == "*704") { + Cursor += 4; + Version = GCOV::V704; + return true; + } + errs() << "Unexpected version: " << VersionStr << ".\n"; + return false; } /// 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; @@ -77,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; @@ -90,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; @@ -103,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; @@ -116,10 +150,21 @@ 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; + return true; + } + + /// 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') { return false; } Cursor += 4; @@ -129,40 +174,54 @@ 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; return true; } - uint32_t readInt() { - uint32_t Result; - StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor+4); - assert (Str.empty() == false && "Unexpected memory buffer end!"); + bool readInt(uint32_t &Val) { + 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); Cursor += 4; - Result = *(const uint32_t *)(Str.data()); - return Result; + Val = *(const uint32_t *)(Str.data()); + return true; } - uint64_t readInt64() { - uint64_t Lo = readInt(); - uint64_t Hi = readInt(); - uint64_t Result = Lo | (Hi << 32); - return Result; + bool readInt64(uint64_t &Val) { + uint32_t Lo, Hi; + if (!readInt(Lo) || !readInt(Hi)) + return false; + Val = ((uint64_t)Hi << 32) | Lo; + return true; } - StringRef readString() { - uint32_t Len = readInt() * 4; - StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor+Len); + bool readString(StringRef &Str) { + 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"; + return false; + } + Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first; Cursor += Len; - return Str.split('\0').first; + return true; } uint64_t getCursor() const { return Cursor; } + void advanceCursor(uint32_t n) { Cursor += n * 4; } + private: MemoryBuffer *Buffer; uint64_t Cursor; @@ -172,75 +231,215 @@ private: /// (.gcno and .gcda). class GCOVFile { public: - GCOVFile() : Functions(), ProgramCount(0) {} - ~GCOVFile(); - bool read(GCOVBuffer &Buffer); - void dump(); + 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: - SmallVector Functions; + bool GCNOInitialized; + GCOV::GCOVVersion Version; + uint32_t Checksum; + 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) {} + + GCOVBlock &Src; + GCOVBlock &Dst; + uint64_t Count; +}; + /// GCOVFunction - Collects function information. class GCOVFunction { public: - GCOVFunction() : Ident(0), LineNumber(0) {} - ~GCOVFunction(); - bool read(GCOVBuffer &Buffer, GCOV::GCOVFormat Format); - void dump(); + 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, 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: - GCOVBlock(uint32_t N) : Number(N), Counter(0) {} + typedef SmallVectorImpl::const_iterator EdgeIterator; + + GCOVBlock(GCOVFunction &P, uint32_t N) + : Parent(P), Number(N), Counter(0), DstEdgesAreSorted(true), SrcEdges(), + DstEdges(), Lines() {} ~GCOVBlock(); - void addEdge(uint32_t N) { Edges.push_back(N); } - void addLine(StringRef Filename, uint32_t LineNo); - void addCount(uint64_t N) { Counter += N; } - size_t getNumEdges() { return Edges.size(); } - void dump(); + 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 + SrcEdges.push_back(Edge); + } + void addDstEdge(GCOVEdge *Edge) { + 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); + } + 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; - SmallVector Edges; - StringMap Lines; + bool DstEdgesAreSorted; + SmallVector SrcEdges; + SmallVector DstEdges; + SmallVector Lines; }; -/// GCOVLines - A wrapper around a vector of int to keep track of line nos. -class GCOVLines { -public: - ~GCOVLines() { Lines.clear(); } - void add(uint32_t N) { Lines.push_back(N); } - void collectLineCounts(FileInfo &FI, StringRef Filename, uint64_t Count); - void dump(); +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; -private: - SmallVector Lines; -}; + 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; + }; -typedef DenseMap LineCounts; -class FileInfo { public: - void addLineCount(StringRef Filename, uint32_t Line, uint64_t Count) { - LineInfo[Filename][Line-1] += Count; + FileInfo(const GCOV::Options &Options) + : Options(Options), LineInfo(), RunCount(0), ProgramCount(0) {} + + void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *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 setProgramCount(uint32_t PC) { ProgramCount = PC; } - void print(StringRef gcnoFile, StringRef gcdaFile); + 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); + private: - StringMap LineInfo; + 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