#include "llvm/Support/GCOV.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryObject.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/system_error.h"
#include <algorithm>
using namespace llvm;
GCOVFile::~GCOVFile() {
DeleteContainerPointers(Functions);
}
bool GCOVFile::readGCNO(GCOVBuffer &Buffer) {
if (!Buffer.readGCNOFormat()) return false;
if (!Buffer.readGCOVVersion(Version)) return false;
if (!Buffer.readInt(Checksum)) return false;
while (true) {
if (!Buffer.readFunctionTag()) break;
GCOVFunction *GFun = new GCOVFunction(*this);
if (!GFun->readGCNO(Buffer, Version))
return false;
Functions.push_back(GFun);
}
GCNOInitialized = true;
return true;
}
bool GCOVFile::readGCDA(GCOVBuffer &Buffer) {
assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()");
if (!Buffer.readGCDAFormat()) return false;
GCOV::GCOVVersion GCDAVersion;
if (!Buffer.readGCOVVersion(GCDAVersion)) return false;
if (Version != GCDAVersion) {
errs() << "GCOV versions do not match.\n";
return false;
}
uint32_t GCDAChecksum;
if (!Buffer.readInt(GCDAChecksum)) return false;
if (Checksum != GCDAChecksum) {
errs() << "File checksums do not match: " << Checksum << " != "
<< GCDAChecksum << ".\n";
return false;
}
for (size_t i = 0, e = Functions.size(); i < e; ++i) {
if (!Buffer.readFunctionTag()) {
errs() << "Unexpected number of functions.\n";
return false;
}
if (!Functions[i]->readGCDA(Buffer, Version))
return false;
}
if (Buffer.readObjectTag()) {
uint32_t Length;
uint32_t Dummy;
if (!Buffer.readInt(Length)) return false;
if (!Buffer.readInt(Dummy)) return false; if (!Buffer.readInt(Dummy)) return false; if (!Buffer.readInt(RunCount)) return false;
Buffer.advanceCursor(Length-3);
}
while (Buffer.readProgramTag()) {
uint32_t Length;
if (!Buffer.readInt(Length)) return false;
Buffer.advanceCursor(Length);
++ProgramCount;
}
return true;
}
void GCOVFile::dump() const {
for (SmallVectorImpl<GCOVFunction *>::const_iterator I = Functions.begin(),
E = Functions.end(); I != E; ++I)
(*I)->dump();
}
void GCOVFile::collectLineCounts(FileInfo &FI) {
for (SmallVectorImpl<GCOVFunction *>::iterator I = Functions.begin(),
E = Functions.end(); I != E; ++I)
(*I)->collectLineCounts(FI);
FI.setRunCount(RunCount);
FI.setProgramCount(ProgramCount);
}
GCOVFunction::~GCOVFunction() {
DeleteContainerPointers(Blocks);
DeleteContainerPointers(Edges);
}
bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
uint32_t Dummy;
if (!Buff.readInt(Dummy)) return false; if (!Buff.readInt(Ident)) return false;
if (!Buff.readInt(Checksum)) return false;
if (Version != GCOV::V402) {
uint32_t CfgChecksum;
if (!Buff.readInt(CfgChecksum)) return false;
if (Parent.getChecksum() != CfgChecksum) {
errs() << "File checksums do not match: " << Parent.getChecksum()
<< " != " << CfgChecksum << " in (" << Name << ").\n";
return false;
}
}
if (!Buff.readString(Name)) return false;
if (!Buff.readString(Filename)) return false;
if (!Buff.readInt(LineNumber)) return false;
if (!Buff.readBlockTag()) {
errs() << "Block tag not found.\n";
return false;
}
uint32_t BlockCount;
if (!Buff.readInt(BlockCount)) return false;
for (uint32_t i = 0, e = BlockCount; i != e; ++i) {
if (!Buff.readInt(Dummy)) return false; Blocks.push_back(new GCOVBlock(*this, i));
}
while (Buff.readEdgeTag()) {
uint32_t EdgeCount;
if (!Buff.readInt(EdgeCount)) return false;
EdgeCount = (EdgeCount - 1) / 2;
uint32_t BlockNo;
if (!Buff.readInt(BlockNo)) return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
<< ").\n";
return false;
}
for (uint32_t i = 0, e = EdgeCount; i != e; ++i) {
uint32_t Dst;
if (!Buff.readInt(Dst)) return false;
GCOVEdge *Edge = new GCOVEdge(Blocks[BlockNo], Blocks[Dst]);
Edges.push_back(Edge);
Blocks[BlockNo]->addDstEdge(Edge);
Blocks[Dst]->addSrcEdge(Edge);
if (!Buff.readInt(Dummy)) return false; }
}
while (Buff.readLineTag()) {
uint32_t LineTableLength;
if (!Buff.readInt(LineTableLength)) return false;
uint32_t EndPos = Buff.getCursor() + LineTableLength*4;
uint32_t BlockNo;
if (!Buff.readInt(BlockNo)) return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
<< ").\n";
return false;
}
GCOVBlock *Block = Blocks[BlockNo];
if (!Buff.readInt(Dummy)) return false;
if (Buff.getCursor() != (EndPos - sizeof(uint32_t))) {
StringRef F;
if (!Buff.readString(F)) return false;
if (Filename != F) {
errs() << "Multiple sources for a single basic block: " << Filename
<< " != " << F << " (in " << Name << ").\n";
return false;
}
while (Buff.getCursor() < (EndPos - 2 * sizeof(uint32_t))) {
uint32_t Line;
if (!Buff.readInt(Line)) return false;
if (!Line) continue;
Block->addLine(Line);
}
if (!Buff.readInt(Dummy)) return false;
}
if (!Buff.readInt(Dummy)) return false;
}
return true;
}
bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
uint32_t Dummy;
if (!Buff.readInt(Dummy)) return false;
uint32_t GCDAIdent;
if (!Buff.readInt(GCDAIdent)) return false;
if (Ident != GCDAIdent) {
errs() << "Function identifiers do not match: " << Ident << " != "
<< GCDAIdent << " (in " << Name << ").\n";
return false;
}
uint32_t GCDAChecksum;
if (!Buff.readInt(GCDAChecksum)) return false;
if (Checksum != GCDAChecksum) {
errs() << "Function checksums do not match: " << Checksum << " != "
<< GCDAChecksum << " (in " << Name << ").\n";
return false;
}
uint32_t CfgChecksum;
if (Version != GCOV::V402) {
if (!Buff.readInt(CfgChecksum)) return false;
if (Parent.getChecksum() != CfgChecksum) {
errs() << "File checksums do not match: " << Parent.getChecksum()
<< " != " << CfgChecksum << " (in " << Name << ").\n";
return false;
}
}
StringRef GCDAName;
if (!Buff.readString(GCDAName)) return false;
if (Name != GCDAName) {
errs() << "Function names do not match: " << Name << " != " << GCDAName
<< ".\n";
return false;
}
if (!Buff.readArcTag()) {
errs() << "Arc tag not found (in " << Name << ").\n";
return false;
}
uint32_t Count;
if (!Buff.readInt(Count)) return false;
Count /= 2;
for (uint32_t BlockNo = 0; Count > 0; ++BlockNo) {
if (BlockNo >= Blocks.size()-1) {
errs() << "Unexpected number of edges (in " << Name << ").\n";
return false;
}
GCOVBlock &Block = *Blocks[BlockNo];
for (size_t EdgeNo = 0, End = Block.getNumDstEdges(); EdgeNo < End;
++EdgeNo) {
if (Count == 0) {
errs() << "Unexpected number of edges (in " << Name << ").\n";
return false;
}
uint64_t ArcCount;
if (!Buff.readInt64(ArcCount)) return false;
Block.addCount(EdgeNo, ArcCount);
--Count;
}
Block.sortDstEdges();
}
return true;
}
uint64_t GCOVFunction::getEntryCount() const {
return Blocks.front()->getCount();
}
uint64_t GCOVFunction::getExitCount() const {
return Blocks.back()->getCount();
}
void GCOVFunction::dump() const {
dbgs() << "===== " << Name << " @ " << Filename << ":" << LineNumber << "\n";
for (SmallVectorImpl<GCOVBlock *>::const_iterator I = Blocks.begin(),
E = Blocks.end(); I != E; ++I)
(*I)->dump();
}
void GCOVFunction::collectLineCounts(FileInfo &FI) {
if (LineNumber == 0)
return;
for (SmallVectorImpl<GCOVBlock *>::iterator I = Blocks.begin(),
E = Blocks.end(); I != E; ++I)
(*I)->collectLineCounts(FI);
FI.addFunctionLine(Filename, LineNumber, this);
}
GCOVBlock::~GCOVBlock() {
SrcEdges.clear();
DstEdges.clear();
Lines.clear();
}
void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) {
assert(DstEdgeNo < DstEdges.size()); DstEdges[DstEdgeNo]->Count = N;
Counter += N;
if (!DstEdges[DstEdgeNo]->Dst->getNumDstEdges())
DstEdges[DstEdgeNo]->Dst->Counter += N;
}
void GCOVBlock::sortDstEdges() {
if (!DstEdgesAreSorted) {
SortDstEdgesFunctor SortEdges;
std::stable_sort(DstEdges.begin(), DstEdges.end(), SortEdges);
}
}
void GCOVBlock::collectLineCounts(FileInfo &FI) {
for (SmallVectorImpl<uint32_t>::iterator I = Lines.begin(),
E = Lines.end(); I != E; ++I)
FI.addBlockLine(Parent.getFilename(), *I, this);
}
void GCOVBlock::dump() const {
dbgs() << "Block : " << Number << " Counter : " << Counter << "\n";
if (!SrcEdges.empty()) {
dbgs() << "\tSource Edges : ";
for (EdgeIterator I = SrcEdges.begin(), E = SrcEdges.end(); I != E; ++I) {
const GCOVEdge *Edge = *I;
dbgs() << Edge->Src->Number << " (" << Edge->Count << "), ";
}
dbgs() << "\n";
}
if (!DstEdges.empty()) {
dbgs() << "\tDestination Edges : ";
for (EdgeIterator I = DstEdges.begin(), E = DstEdges.end(); I != E; ++I) {
const GCOVEdge *Edge = *I;
dbgs() << Edge->Dst->Number << " (" << Edge->Count << "), ";
}
dbgs() << "\n";
}
if (!Lines.empty()) {
dbgs() << "\tLines : ";
for (SmallVectorImpl<uint32_t>::const_iterator I = Lines.begin(),
E = Lines.end(); I != E; ++I)
dbgs() << (*I) << ",";
dbgs() << "\n";
}
}
static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
if (!Numerator)
return 0;
return Numerator/Divisor;
}
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;
}
namespace {
class LineConsumer {
std::unique_ptr<MemoryBuffer> Buffer;
StringRef Remaining;
public:
LineConsumer(StringRef Filename) {
if (error_code EC = MemoryBuffer::getFileOrSTDIN(Filename, Buffer)) {
errs() << Filename << ": " << EC.message() << "\n";
Remaining = "";
} else
Remaining = Buffer->getBuffer();
}
bool empty() { return Remaining.empty(); }
void printNext(raw_ostream &OS, uint32_t LineNum) {
StringRef Line;
if (empty())
Line = "/*EOF*/";
else
std::tie(Line, Remaining) = Remaining.split("\n");
OS << format("%5u:", LineNum) << Line << "\n";
}
};
}
static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) {
if (!PreservePaths)
return sys::path::filename(Filename).str();
llvm::SmallString<256> Result("");
StringRef::iterator I, S, E;
for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) {
if (*I != '/')
continue;
if (I - S == 1 && *S == '.') {
} else if (I - S == 2 && *S == '.' && *(S + 1) == '.') {
Result.append("^#");
} else {
if (S < I)
Result.append(S, I);
Result.push_back('#');
}
S = I + 1;
}
if (S < I)
Result.append(S, I);
return Result.str();
}
std::string FileInfo::getCoveragePath(StringRef Filename,
StringRef MainFilename) {
if (Options.NoOutput)
return Filename;
std::string CoveragePath;
if (Options.LongFileNames && !Filename.equals(MainFilename))
CoveragePath =
mangleCoveragePath(MainFilename, Options.PreservePaths) + "##";
CoveragePath +=
mangleCoveragePath(Filename, Options.PreservePaths) + ".gcov";
return CoveragePath;
}
std::unique_ptr<raw_ostream>
FileInfo::openCoveragePath(StringRef CoveragePath) {
if (Options.NoOutput)
return std::unique_ptr<raw_null_ostream>(new raw_null_ostream());
std::string ErrorInfo;
auto OS = std::unique_ptr<raw_fd_ostream>(
new raw_fd_ostream(CoveragePath.str().c_str(), ErrorInfo));
if (!ErrorInfo.empty()) {
errs() << ErrorInfo << "\n";
return std::unique_ptr<raw_null_ostream>(new raw_null_ostream());
}
return std::move(OS);
}
void FileInfo::print(StringRef MainFilename, StringRef GCNOFile,
StringRef GCDAFile) {
for (StringMap<LineData>::const_iterator I = LineInfo.begin(),
E = LineInfo.end(); I != E; ++I) {
StringRef Filename = I->first();
auto AllLines = LineConsumer(Filename);
std::string CoveragePath = getCoveragePath(Filename, MainFilename);
std::unique_ptr<raw_ostream> S = openCoveragePath(CoveragePath);
raw_ostream &OS = *S;
OS << " -: 0:Source:" << Filename << "\n";
OS << " -: 0:Graph:" << GCNOFile << "\n";
OS << " -: 0:Data:" << GCDAFile << "\n";
OS << " -: 0:Runs:" << RunCount << "\n";
OS << " -: 0:Programs:" << ProgramCount << "\n";
const LineData &Line = I->second;
GCOVCoverage FileCoverage(Filename);
for (uint32_t LineIndex = 0;
LineIndex < Line.LastLine || !AllLines.empty(); ++LineIndex) {
if (Options.BranchInfo) {
FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex);
if (FuncsIt != Line.Functions.end())
printFunctionSummary(OS, FuncsIt->second);
}
BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex);
if (BlocksIt == Line.Blocks.end()) {
OS << " -:";
AllLines.printNext(OS, LineIndex + 1);
} else {
const BlockVector &Blocks = BlocksIt->second;
DenseMap<const GCOVFunction *, bool> LineExecs;
uint64_t LineCount = 0;
for (BlockVector::const_iterator I = Blocks.begin(), E = Blocks.end();
I != E; ++I) {
const GCOVBlock *Block = *I;
if (Options.AllBlocks) {
uint64_t BlockCount = Block->getCount();
LineCount = LineCount > BlockCount ? LineCount : BlockCount;
} else {
LineCount += Block->getCount();
}
if (Options.FuncCoverage) {
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 {
OS << format("%9" PRIu64 ":", LineCount);
++FileCoverage.LinesExec;
}
++FileCoverage.LogicalLines;
AllLines.printNext(OS, LineIndex + 1);
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;
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);
}
}
}
}
FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage));
}
if (Options.FuncCoverage)
printFuncCoverage();
printFileCoverage();
return;
}
void FileInfo::printFunctionSummary(raw_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";
}
}
void FileInfo::printBlockInfo(raw_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++);
}
void FileInfo::printBranchInfo(raw_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";
}
}
void FileInfo::printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
uint64_t Count) const {
OS << format("unconditional %2u ", EdgeNo++)
<< formatBranchInfo(Options, Count, Count) << "\n";
}
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"; }
}
void FileInfo::printFuncCoverage() const {
for (FuncCoverageMap::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";
}
}
void FileInfo::printFileCoverage() const {
for (FileCoverageList::const_iterator I = FileCoverages.begin(),
E = FileCoverages.end(); I != E; ++I) {
const std::string &Filename = I->first;
const GCOVCoverage &Coverage = I->second;
outs() << "File '" << Coverage.Name << "'\n";
printCoverage(Coverage);
if (!Options.NoOutput)
outs() << Coverage.Name << ":creating '" << Filename << "'\n";
outs() << "\n";
}
}