-//===- GCOVr.cpp - LLVM coverage tool -------------------------------------===//
+//===- GCOV.cpp - LLVM coverage tool --------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryObject.h"
#include "llvm/Support/system_error.h"
+#include <algorithm>
using namespace llvm;
//===----------------------------------------------------------------------===//
if (!Buffer.readInt(Length)) return false;
if (!Buffer.readInt(Dummy)) return false; // checksum
if (!Buffer.readInt(Dummy)) return false; // num
- if (!Buffer.readInt(RunCount)) return false;;
+ if (!Buffer.readInt(RunCount)) return false;
Buffer.advanceCursor(Length-3);
}
while (Buffer.readProgramTag()) {
uint32_t BlockNo;
if (!Buff.readInt(BlockNo)) return false;
if (BlockNo >= BlockCount) {
- errs() << "Unexpected block number (in " << Name << ").\n";
+ errs() << "Unexpected block number: " << BlockNo << " (in " << Name
+ << ").\n";
return false;
}
for (uint32_t i = 0, e = EdgeCount; i != e; ++i) {
uint32_t BlockNo;
if (!Buff.readInt(BlockNo)) return false;
if (BlockNo >= BlockCount) {
- errs() << "Unexpected block number (in " << Name << ").\n";
+ errs() << "Unexpected block number: " << BlockNo << " (in " << Name
+ << ").\n";
return false;
}
GCOVBlock *Block = Blocks[BlockNo];
while (Buff.getCursor() != (EndPos - 4)) {
StringRef F;
if (!Buff.readString(F)) return false;
- if (F != Filename) {
- errs() << "Multiple sources for a single basic block (in "
- << Name << ").\n";
+ if (Filename != F) {
+ errs() << "Multiple sources for a single basic block: " << Filename
+ << " != " << F << " (in " << Name << ").\n";
return false;
}
if (Buff.getCursor() == (EndPos - 4)) break;
Block.addCount(EdgeNo, ArcCount);
--Count;
}
+ Block.sortDstEdges();
}
return true;
}
+/// getEntryCount - Get the number of times the function was called by
+/// retrieving the entry block's count.
+uint64_t GCOVFunction::getEntryCount() const {
+ return Blocks.front()->getCount();
+}
+
+/// getExitCount - Get the number of times the function returned by retrieving
+/// the exit block's count.
+uint64_t GCOVFunction::getExitCount() const {
+ return Blocks.back()->getCount();
+}
+
/// dump - Dump GCOVFunction content to dbgs() for debugging purposes.
void GCOVFunction::dump() const {
dbgs() << "===== " << Name << " @ " << Filename << ":" << LineNumber << "\n";
for (SmallVectorImpl<GCOVBlock *>::iterator I = Blocks.begin(),
E = Blocks.end(); I != E; ++I)
(*I)->collectLineCounts(FI);
+ FI.addFunctionLine(Filename, LineNumber, this);
}
//===----------------------------------------------------------------------===//
DstEdges[DstEdgeNo]->Dst->Counter += N;
}
+/// sortDstEdges - Sort destination edges by block number, nop if already
+/// sorted. This is required for printing branch info in the correct order.
+void GCOVBlock::sortDstEdges() {
+ if (!DstEdgesAreSorted) {
+ SortDstEdgesFunctor SortEdges;
+ std::stable_sort(DstEdges.begin(), DstEdges.end(), SortEdges);
+ }
+}
+
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVBlock::collectLineCounts(FileInfo &FI) {
//===----------------------------------------------------------------------===//
// FileInfo implementation.
+// Safe integer division, returns 0 if numerator is 0.
+static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
+ if (!Numerator)
+ return 0;
+ return Numerator/Divisor;
+}
+
+// This custom division function mimics gcov's branch ouputs:
+// - Round to closest whole number
+// - Only output 0% or 100% if it's exactly that value
+static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) {
+ if (!Numerator)
+ return 0;
+ if (Numerator == Divisor)
+ return 100;
+
+ uint8_t Res = (Numerator*100+Divisor/2) / Divisor;
+ if (Res == 0)
+ return 1;
+ if (Res == 100)
+ return 99;
+ return Res;
+}
+
+struct formatBranchInfo {
+ formatBranchInfo(const GCOVOptions &Options, uint64_t Count,
+ uint64_t Total) :
+ Options(Options), Count(Count), Total(Total) {}
+
+ void print(raw_ostream &OS) const {
+ if (!Total)
+ OS << "never executed";
+ else if (Options.BranchCount)
+ OS << "taken " << Count;
+ else
+ OS << "taken " << branchDiv(Count, Total) << "%";
+ }
+
+ const GCOVOptions &Options;
+ uint64_t Count;
+ uint64_t Total;
+};
+
+static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) {
+ FBI.print(OS);
+ return OS;
+}
+
/// print - Print source files with collected line count information.
-void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) const {
+void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) {
for (StringMap<LineData>::const_iterator I = LineInfo.begin(),
E = LineInfo.end(); I != E; ++I) {
StringRef Filename = I->first();
}
StringRef AllLines = Buff->getBuffer();
- std::string CovFilename = Filename.str() + ".llcov";
+ std::string CovFilename = Filename.str() + ".gcov";
std::string ErrorInfo;
raw_fd_ostream OS(CovFilename.c_str(), ErrorInfo);
if (!ErrorInfo.empty())
OS << " -: 0:Programs:" << ProgramCount << "\n";
const LineData &Line = I->second;
- for (uint32_t i = 0; !AllLines.empty(); ++i) {
- LineData::const_iterator BlocksIt = Line.find(i);
+ GCOVCoverage FileCoverage(Filename);
+ for (uint32_t LineIndex = 0; !AllLines.empty(); ++LineIndex) {
+ if (Options.BranchInfo) {
+ FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex);
+ if (FuncsIt != Line.Functions.end())
+ printFunctionSummary(OS, FuncsIt->second);
+ }
- // Add up the block counts to form line counts.
- if (BlocksIt != Line.end()) {
+ BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex);
+ if (BlocksIt == Line.Blocks.end()) {
+ // No basic blocks are on this line. Not an executable line of code.
+ OS << " -:";
+ std::pair<StringRef, StringRef> P = AllLines.split('\n');
+ OS << format("%5u:", LineIndex+1) << P.first << "\n";
+ AllLines = P.second;
+ } else {
const BlockVector &Blocks = BlocksIt->second;
+
+ // Add up the block counts to form line counts.
+ DenseMap<const GCOVFunction *, bool> LineExecs;
uint64_t LineCount = 0;
for (BlockVector::const_iterator I = Blocks.begin(), E = Blocks.end();
I != E; ++I) {
- LineCount += (*I)->getCount();
+ const GCOVBlock *Block = *I;
+ if (Options.AllBlocks) {
+ // Only take the highest block count for that line.
+ uint64_t BlockCount = Block->getCount();
+ LineCount = LineCount > BlockCount ? LineCount : BlockCount;
+ } else {
+ // Sum up all of the block counts.
+ LineCount += Block->getCount();
+ }
+
+ if (Options.FuncCoverage) {
+ // This is a slightly convoluted way to most accurately gather line
+ // statistics for functions. Basically what is happening is that we
+ // don't want to count a single line with multiple blocks more than
+ // once. However, we also don't simply want to give the total line
+ // count to every function that starts on the line. Thus, what is
+ // happening here are two things:
+ // 1) Ensure that the number of logical lines is only incremented
+ // once per function.
+ // 2) If there are multiple blocks on the same line, ensure that the
+ // number of lines executed is incremented as long as at least
+ // one of the blocks are executed.
+ const GCOVFunction *Function = &Block->getParent();
+ if (FuncCoverages.find(Function) == FuncCoverages.end()) {
+ std::pair<const GCOVFunction *, GCOVCoverage>
+ KeyValue(Function, GCOVCoverage(Function->getName()));
+ FuncCoverages.insert(KeyValue);
+ }
+ GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
+
+ if (LineExecs.find(Function) == LineExecs.end()) {
+ if (Block->getCount()) {
+ ++FuncCoverage.LinesExec;
+ LineExecs[Function] = true;
+ } else {
+ LineExecs[Function] = false;
+ }
+ ++FuncCoverage.LogicalLines;
+ } else if (!LineExecs[Function] && Block->getCount()) {
+ ++FuncCoverage.LinesExec;
+ LineExecs[Function] = true;
+ }
+ }
}
+
if (LineCount == 0)
OS << " #####:";
- else
+ else {
OS << format("%9" PRIu64 ":", LineCount);
- } else {
- OS << " -:";
+ ++FileCoverage.LinesExec;
+ }
+ ++FileCoverage.LogicalLines;
+
+ std::pair<StringRef, StringRef> P = AllLines.split('\n');
+ OS << format("%5u:", LineIndex+1) << P.first << "\n";
+ AllLines = P.second;
+
+ uint32_t BlockNo = 0;
+ uint32_t EdgeNo = 0;
+ for (BlockVector::const_iterator I = Blocks.begin(), E = Blocks.end();
+ I != E; ++I) {
+ const GCOVBlock *Block = *I;
+
+ // Only print block and branch information at the end of the block.
+ if (Block->getLastLine() != LineIndex+1)
+ continue;
+ if (Options.AllBlocks)
+ printBlockInfo(OS, *Block, LineIndex, BlockNo);
+ if (Options.BranchInfo) {
+ size_t NumEdges = Block->getNumDstEdges();
+ if (NumEdges > 1)
+ printBranchInfo(OS, *Block, FileCoverage, EdgeNo);
+ else if (Options.UncondBranch && NumEdges == 1)
+ printUncondBranchInfo(OS, EdgeNo, (*Block->dst_begin())->Count);
+ }
+ }
}
- std::pair<StringRef, StringRef> P = AllLines.split('\n');
- OS << format("%5u:", i+1) << P.first << "\n";
- AllLines = P.second;
}
+ FileCoverages.push_back(FileCoverage);
+ }
+
+ // FIXME: There is no way to detect calls given current instrumentation.
+ if (Options.FuncCoverage)
+ printFuncCoverage();
+ printFileCoverage();
+}
+
+/// printFunctionSummary - Print function and block summary.
+void FileInfo::printFunctionSummary(raw_fd_ostream &OS,
+ const FunctionVector &Funcs) const {
+ for (FunctionVector::const_iterator I = Funcs.begin(), E = Funcs.end();
+ I != E; ++I) {
+ const GCOVFunction *Func = *I;
+ uint64_t EntryCount = Func->getEntryCount();
+ uint32_t BlocksExec = 0;
+ for (GCOVFunction::BlockIterator I = Func->block_begin(),
+ E = Func->block_end(); I != E; ++I) {
+ const GCOVBlock *Block = *I;
+ if (Block->getNumDstEdges() && Block->getCount())
+ ++BlocksExec;
+ }
+
+ OS << "function " << Func->getName() << " called " << EntryCount
+ << " returned " << safeDiv(Func->getExitCount()*100, EntryCount)
+ << "% blocks executed "
+ << safeDiv(BlocksExec*100, Func->getNumBlocks()-1) << "%\n";
+ }
+}
+
+/// printBlockInfo - Output counts for each block.
+void FileInfo::printBlockInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
+ uint32_t LineIndex, uint32_t &BlockNo) const {
+ if (Block.getCount() == 0)
+ OS << " $$$$$:";
+ else
+ OS << format("%9" PRIu64 ":", Block.getCount());
+ OS << format("%5u-block %2u\n", LineIndex+1, BlockNo++);
+}
+
+/// printBranchInfo - Print conditional branch probabilities.
+void FileInfo::printBranchInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
+ GCOVCoverage &Coverage, uint32_t &EdgeNo) {
+ SmallVector<uint64_t, 16> BranchCounts;
+ uint64_t TotalCounts = 0;
+ for (GCOVBlock::EdgeIterator I = Block.dst_begin(), E = Block.dst_end();
+ I != E; ++I) {
+ const GCOVEdge *Edge = *I;
+ BranchCounts.push_back(Edge->Count);
+ TotalCounts += Edge->Count;
+ if (Block.getCount()) ++Coverage.BranchesExec;
+ if (Edge->Count) ++Coverage.BranchesTaken;
+ ++Coverage.Branches;
+
+ if (Options.FuncCoverage) {
+ const GCOVFunction *Function = &Block.getParent();
+ GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
+ if (Block.getCount()) ++FuncCoverage.BranchesExec;
+ if (Edge->Count) ++FuncCoverage.BranchesTaken;
+ ++FuncCoverage.Branches;
+ }
+ }
+
+ for (SmallVectorImpl<uint64_t>::const_iterator I = BranchCounts.begin(),
+ E = BranchCounts.end(); I != E; ++I) {
+ OS << format("branch %2u ", EdgeNo++)
+ << formatBranchInfo(Options, *I, TotalCounts) << "\n";
+ }
+}
+
+/// printUncondBranchInfo - Print unconditional branch probabilities.
+void FileInfo::printUncondBranchInfo(raw_fd_ostream &OS, uint32_t &EdgeNo,
+ uint64_t Count) const {
+ OS << format("unconditional %2u ", EdgeNo++)
+ << formatBranchInfo(Options, Count, Count) << "\n";
+}
+
+// printCoverage - Print generic coverage info used by both printFuncCoverage
+// and printFileCoverage.
+void FileInfo::printCoverage(const GCOVCoverage &Coverage) const {
+ outs() << format("Lines executed:%.2f%% of %u\n",
+ double(Coverage.LinesExec)*100/Coverage.LogicalLines,
+ Coverage.LogicalLines);
+ if (Options.BranchInfo) {
+ if (Coverage.Branches) {
+ outs() << format("Branches executed:%.2f%% of %u\n",
+ double(Coverage.BranchesExec)*100/Coverage.Branches,
+ Coverage.Branches);
+ outs() << format("Taken at least once:%.2f%% of %u\n",
+ double(Coverage.BranchesTaken)*100/Coverage.Branches,
+ Coverage.Branches);
+ } else {
+ outs() << "No branches\n";
+ }
+ outs() << "No calls\n"; // to be consistent with gcov
+ }
+}
+
+// printFuncCoverage - Print per-function coverage info.
+void FileInfo::printFuncCoverage() const {
+ for (MapVector<const GCOVFunction *, GCOVCoverage>::const_iterator I =
+ FuncCoverages.begin(), E = FuncCoverages.end(); I != E; ++I) {
+ const GCOVCoverage &Coverage = I->second;
+ outs() << "Function '" << Coverage.Name << "'\n";
+ printCoverage(Coverage);
+ outs() << "\n";
+ }
+}
+
+// printFileCoverage - Print per-file coverage info.
+void FileInfo::printFileCoverage() const {
+ for (SmallVectorImpl<GCOVCoverage>::const_iterator I =
+ FileCoverages.begin(), E = FileCoverages.end(); I != E; ++I) {
+ const GCOVCoverage &Coverage = *I;
+ outs() << "File '" << Coverage.Name << "'\n";
+ printCoverage(Coverage);
+ outs() << Coverage.Name << ":creating '" << Coverage.Name
+ << ".gcov'\n\n";
}
}