1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[gcov] Fix .gcda decoding and support GCC 8, 9 and 10

GCDAProfiling.c unnecessarily writes function names to .gcda files.
GCC 4.2 gcc/libgcov.c (now renamed to libgcc/libgcov*) did not write function
names. gcov-7 (compatible) crashes on .gcda produced by libclang_rt.profile
rL176173 realized the problem and introduced a mode to remove function
names.

llvm-cov code apparently takes GCDAProfiling.c output format as truth
and tries to decode function names.  Additionally, llvm-cov tries to
decode tags in certain order which does not match libgcov emitted .gcda
files.

This patch fixes the .gcda decoder and makes it work with GCC 8 and 9
(10 is compatible with 9). Note, line statistics are broken and not
fixed by this patch.

Add test/tools/llvm-cov/gcov-{4.7,8,9}.c to test compatibility.
This commit is contained in:
Fangrui Song 2020-05-09 10:36:34 -07:00
parent 366aeb3a0b
commit d7b402943b
17 changed files with 334 additions and 340 deletions

View File

@ -28,6 +28,7 @@
#include <cstddef>
#include <cstdint>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
@ -40,7 +41,7 @@ class FileInfo;
namespace GCOV {
enum GCOVVersion { V402, V404, V704 };
enum GCOVVersion { V402, V407, V800, V900 };
/// A struct for passing gcov options between functions.
struct Options {
@ -92,23 +93,29 @@ public:
/// readGCOVVersion - Read GCOV version.
bool readGCOVVersion(GCOV::GCOVVersion &Version) {
StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (VersionStr == "*204") {
Cursor += 4;
StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4);
Cursor += 4;
int Major =
Str[3] >= 'A' ? (Str[3] - 'A') * 10 + Str[2] - '0' : Str[3] - '0';
int Minor = Str[1] - '0';
if (Major >= 9) {
// PR gcov-profile/84846, r269678
Version = GCOV::V900;
return true;
} else if (Major >= 8) {
// PR gcov-profile/48463
Version = GCOV::V800;
return true;
} else if (Major > 4 || (Major == 4 && Minor >= 7)) {
// r173147
Version = GCOV::V407;
return true;
} else {
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";
Cursor -= 4;
errs() << "unexpected version: " << Str << "\n";
return false;
}
@ -160,42 +167,6 @@ public:
return true;
}
/// 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') {
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;
return true;
}
/// 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') {
return false;
}
Cursor += 4;
return true;
}
bool readInt(uint32_t &Val) {
if (Buffer->getBuffer().size() < Cursor + 4) {
errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n";
@ -234,6 +205,7 @@ public:
uint64_t getCursor() const { return Cursor; }
void advanceCursor(uint32_t n) { Cursor += n * 4; }
void setCursor(uint64_t c) { Cursor = c; }
private:
MemoryBuffer *Buffer;
@ -248,6 +220,7 @@ public:
bool readGCNO(GCOVBuffer &Buffer);
bool readGCDA(GCOVBuffer &Buffer);
GCOV::GCOVVersion getVersion() const { return Version; }
uint32_t getChecksum() const { return Checksum; }
void print(raw_ostream &OS) const;
void dump() const;
@ -257,17 +230,20 @@ private:
bool GCNOInitialized = false;
GCOV::GCOVVersion Version;
uint32_t Checksum = 0;
StringRef cwd;
SmallVector<std::unique_ptr<GCOVFunction>, 16> Functions;
std::map<uint32_t, GCOVFunction *> IdentToFunction;
uint32_t RunCount = 0;
uint32_t ProgramCount = 0;
};
/// GCOVEdge - Collects edge information.
struct GCOVEdge {
GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D) {}
struct GCOVArc {
GCOVArc(GCOVBlock &src, GCOVBlock &dst, bool fallthrough)
: src(src), dst(dst), fallthrough(fallthrough) {}
GCOVBlock &Src;
GCOVBlock &Dst;
GCOVBlock &src;
GCOVBlock &dst;
bool fallthrough;
uint64_t Count = 0;
uint64_t CyclesCount = 0;
};
@ -278,10 +254,9 @@ public:
using BlockIterator = pointee_iterator<
SmallVectorImpl<std::unique_ptr<GCOVBlock>>::const_iterator>;
GCOVFunction(GCOVFile &P) : Parent(P) {}
GCOVFunction(GCOVFile &P) {}
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(); }
@ -298,15 +273,18 @@ public:
void dump() const;
void collectLineCounts(FileInfo &FI);
private:
GCOVFile &Parent;
uint32_t Ident = 0;
uint32_t Checksum;
uint32_t LineNumber = 0;
uint32_t ident = 0;
uint32_t linenoChecksum;
uint32_t cfgChecksum = 0;
uint32_t startLine = 0;
uint32_t startColumn = 0;
uint32_t endLine = 0;
uint32_t endColumn = 0;
uint8_t artificial = 0;
StringRef Name;
StringRef Filename;
SmallVector<std::unique_ptr<GCOVBlock>, 16> Blocks;
SmallVector<std::unique_ptr<GCOVEdge>, 16> Edges;
SmallVector<std::unique_ptr<GCOVBlock>, 0> Blocks;
SmallVector<std::unique_ptr<GCOVArc>, 0> arcs, treeArcs;
};
/// GCOVBlock - Collects block information.
@ -319,47 +297,31 @@ class GCOVBlock {
};
public:
using EdgeIterator = SmallVectorImpl<GCOVEdge *>::const_iterator;
using EdgeIterator = SmallVectorImpl<GCOVArc *>::const_iterator;
using BlockVector = SmallVector<const GCOVBlock *, 4>;
using BlockVectorLists = SmallVector<BlockVector, 4>;
using Edges = SmallVector<GCOVEdge *, 4>;
using Edges = SmallVector<GCOVArc *, 4>;
GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N) {}
~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
SrcEdges.push_back(Edge);
}
void addSrcEdge(GCOVArc *Edge) { pred.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);
}
void addDstEdge(GCOVArc *Edge) { succ.push_back(Edge); }
size_t getNumSrcEdges() const { return SrcEdges.size(); }
size_t getNumDstEdges() const { return DstEdges.size(); }
void sortDstEdges();
size_t getNumSrcEdges() const { return pred.size(); }
size_t getNumDstEdges() const { return succ.size(); }
EdgeIterator src_begin() const { return SrcEdges.begin(); }
EdgeIterator src_end() const { return SrcEdges.end(); }
iterator_range<EdgeIterator> srcs() const {
return make_range(src_begin(), src_end());
return make_range(pred.begin(), pred.end());
}
EdgeIterator dst_begin() const { return DstEdges.begin(); }
EdgeIterator dst_end() const { return DstEdges.end(); }
iterator_range<EdgeIterator> dsts() const {
return make_range(dst_begin(), dst_end());
return make_range(succ.begin(), succ.end());
}
void print(raw_ostream &OS) const;
@ -376,13 +338,12 @@ public:
static void getCyclesCount(const BlockVector &Blocks, uint64_t &Count);
static uint64_t getLineCount(const BlockVector &Blocks);
private:
public:
GCOVFunction &Parent;
uint32_t Number;
uint64_t Counter = 0;
bool DstEdgesAreSorted = true;
SmallVector<GCOVEdge *, 16> SrcEdges;
SmallVector<GCOVEdge *, 16> DstEdges;
SmallVector<GCOVArc *, 2> pred;
SmallVector<GCOVArc *, 2> succ;
SmallVector<uint32_t, 16> Lines;
};
@ -438,7 +399,7 @@ public:
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);
StringRef GCDAFile, GCOV::GCOVVersion Version);
protected:
std::string getCoveragePath(StringRef Filename, StringRef MainFilename);

View File

@ -25,24 +25,41 @@
using namespace llvm;
enum : uint32_t {
GCOV_ARC_ON_TREE = 1 << 0,
GCOV_ARC_FALLTHROUGH = 1 << 2,
GCOV_TAG_FUNCTION = 0x01000000,
GCOV_TAG_COUNTER_ARCS = 0x01a10000,
// GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9.
GCOV_TAG_OBJECT_SUMMARY = 0xa1000000,
GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000,
};
//===----------------------------------------------------------------------===//
// GCOVFile implementation.
/// readGCNO - Read GCNO buffer.
bool GCOVFile::readGCNO(GCOVBuffer &Buffer) {
if (!Buffer.readGCNOFormat())
bool GCOVFile::readGCNO(GCOVBuffer &buf) {
if (!buf.readGCNOFormat())
return false;
if (!Buffer.readGCOVVersion(Version))
if (!buf.readGCOVVersion(Version))
return false;
if (!Buffer.readInt(Checksum))
if (!buf.readInt(Checksum))
return false;
if (Version >= GCOV::V900 && !buf.readString(cwd))
return false;
uint32_t hasUnexecutedBlocks;
if (Version >= GCOV::V800 && !buf.readInt(hasUnexecutedBlocks))
return false;
while (true) {
if (!Buffer.readFunctionTag())
if (!buf.readFunctionTag())
break;
auto GFun = std::make_unique<GCOVFunction>(*this);
if (!GFun->readGCNO(Buffer, Version))
if (!GFun->readGCNO(buf, Version))
return false;
IdentToFunction[GFun->ident] = GFun.get();
Functions.push_back(std::move(GFun));
}
@ -52,12 +69,12 @@ bool GCOVFile::readGCNO(GCOVBuffer &Buffer) {
/// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be
/// called after readGCNO().
bool GCOVFile::readGCDA(GCOVBuffer &Buffer) {
bool GCOVFile::readGCDA(GCOVBuffer &buf) {
assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()");
if (!Buffer.readGCDAFormat())
if (!buf.readGCDAFormat())
return false;
GCOV::GCOVVersion GCDAVersion;
if (!Buffer.readGCOVVersion(GCDAVersion))
if (!buf.readGCOVVersion(GCDAVersion))
return false;
if (Version != GCDAVersion) {
errs() << "GCOV versions do not match.\n";
@ -65,40 +82,69 @@ bool GCOVFile::readGCDA(GCOVBuffer &Buffer) {
}
uint32_t GCDAChecksum;
if (!Buffer.readInt(GCDAChecksum))
if (!buf.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";
uint32_t dummy, tag, length;
uint32_t ident;
GCOVFunction *fn = nullptr;
while (buf.readInt(tag) && tag) {
if (!buf.readInt(length))
return false;
uint32_t cursor = buf.getCursor();
if (tag == GCOV_TAG_OBJECT_SUMMARY) {
buf.readInt(dummy);
buf.readInt(dummy);
buf.readInt(RunCount);
} else if (tag == GCOV_TAG_PROGRAM_SUMMARY) {
++ProgramCount;
} else if (tag == GCOV_TAG_FUNCTION) {
if (length == 0) // Placeholder
continue;
// length>3 is to be compatible with some clang --coverage generated
// tests. As of GCC 10, GCOV_TAG_FUNCTION_LENGTH has never been larger
// than 3.
if (length < 3 || !buf.readInt(ident))
return false;
auto It = IdentToFunction.find(ident);
uint32_t linenoChecksum, cfgChecksum;
buf.readInt(linenoChecksum);
buf.readInt(cfgChecksum);
if (Version < GCOV::V407)
cfgChecksum = 0;
if (It != IdentToFunction.end()) {
fn = It->second;
if (linenoChecksum != fn->linenoChecksum ||
cfgChecksum != fn->cfgChecksum) {
errs() << fn->Name
<< format(": checksum mismatch, (%u, %u) != (%u, %u)\n",
linenoChecksum, cfgChecksum, fn->linenoChecksum,
fn->cfgChecksum);
return false;
}
}
} else if (tag == GCOV_TAG_COUNTER_ARCS && fn) {
if (length != 2 * fn->arcs.size()) {
errs() << fn->Name
<< format(
": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n",
length, unsigned(2 * fn->arcs.size()));
return false;
}
for (std::unique_ptr<GCOVArc> &arc : fn->arcs) {
if (!buf.readInt64(arc->Count))
return false;
// FIXME Fix counters
arc->src.Counter += arc->Count;
if (arc->dst.succ.empty())
arc->dst.Counter += arc->Count;
}
}
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; // checksum
if (!Buffer.readInt(Dummy))
return false; // num
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;
buf.setCursor(cursor + 4 * length);
}
return true;
@ -128,82 +174,98 @@ void GCOVFile::collectLineCounts(FileInfo &FI) {
/// readGCNO - Read a function from the GCNO buffer. Return false if an error
/// occurs.
bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
bool GCOVFunction::readGCNO(GCOVBuffer &buf, GCOV::GCOVVersion Version) {
uint32_t Dummy;
if (!Buff.readInt(Dummy))
if (!buf.readInt(Dummy))
return false; // Function header length
if (!Buff.readInt(Ident))
if (!buf.readInt(ident))
return false;
if (!Buff.readInt(Checksum))
if (!buf.readInt(linenoChecksum))
return false;
if (Version != GCOV::V402) {
uint32_t CfgChecksum;
if (!Buff.readInt(CfgChecksum))
if (Version >= GCOV::V407 && !buf.readInt(cfgChecksum))
return false;
if (!buf.readString(Name))
return false;
if (Version < GCOV::V800) {
if (!buf.readString(Filename))
return false;
if (Parent.getChecksum() != CfgChecksum) {
errs() << "File checksums do not match: " << Parent.getChecksum()
<< " != " << CfgChecksum << " in (" << Name << ").\n";
if (!buf.readInt(startLine))
return false;
} else {
if (!buf.readInt(Dummy))
return false;
artificial = Dummy;
if (!buf.readString(Filename))
return false;
if (!buf.readInt(startLine))
return false;
if (!buf.readInt(startColumn))
return false;
if (!buf.readInt(endLine))
return false;
if (Version >= GCOV::V900 && !buf.readInt(endColumn))
return false;
}
}
if (!Buff.readString(Name))
return false;
if (!Buff.readString(Filename))
return false;
if (!Buff.readInt(LineNumber))
return false;
// read blocks.
if (!Buff.readBlockTag()) {
if (!buf.readBlockTag()) {
errs() << "Block tag not found.\n";
return false;
}
if (Version >= GCOV::V800 && !buf.readInt(Dummy))
return false;
uint32_t BlockCount;
if (!Buff.readInt(BlockCount))
if (!buf.readInt(BlockCount))
return false;
for (uint32_t i = 0, e = BlockCount; i != e; ++i) {
if (!Buff.readInt(Dummy))
if (Version < GCOV::V800 && !buf.readInt(Dummy))
return false; // Block flags;
Blocks.push_back(std::make_unique<GCOVBlock>(*this, i));
}
// read edges.
while (Buff.readEdgeTag()) {
while (buf.readEdgeTag()) {
uint32_t EdgeCount;
if (!Buff.readInt(EdgeCount))
if (!buf.readInt(EdgeCount))
return false;
EdgeCount = (EdgeCount - 1) / 2;
uint32_t BlockNo;
if (!Buff.readInt(BlockNo))
if (!buf.readInt(BlockNo))
return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
<< ").\n";
return false;
}
GCOVBlock *src = Blocks[BlockNo].get();
for (uint32_t i = 0, e = EdgeCount; i != e; ++i) {
uint32_t Dst;
if (!Buff.readInt(Dst))
uint32_t dstNo, flags;
if (!buf.readInt(dstNo))
return false;
Edges.push_back(std::make_unique<GCOVEdge>(*Blocks[BlockNo], *Blocks[Dst]));
GCOVEdge *Edge = Edges.back().get();
Blocks[BlockNo]->addDstEdge(Edge);
Blocks[Dst]->addSrcEdge(Edge);
if (!Buff.readInt(Dummy))
return false; // Edge flag
if (!buf.readInt(flags))
return false;
GCOVBlock *dst = Blocks[dstNo].get();
auto arc =
std::make_unique<GCOVArc>(*src, *dst, flags & GCOV_ARC_FALLTHROUGH);
src->addDstEdge(arc.get());
dst->addSrcEdge(arc.get());
if (flags & GCOV_ARC_ON_TREE)
treeArcs.push_back(std::move(arc));
else
arcs.push_back(std::move(arc));
}
}
// read line table.
while (Buff.readLineTag()) {
while (buf.readLineTag()) {
uint32_t LineTableLength;
// Read the length of this line table.
if (!Buff.readInt(LineTableLength))
if (!buf.readInt(LineTableLength))
return false;
uint32_t EndPos = Buff.getCursor() + LineTableLength * 4;
uint32_t EndPos = buf.getCursor() + LineTableLength * 4;
uint32_t BlockNo;
// Read the block number this table is associated with.
if (!Buff.readInt(BlockNo))
if (!buf.readInt(BlockNo))
return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
@ -213,24 +275,24 @@ bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
GCOVBlock &Block = *Blocks[BlockNo];
// Read the word that pads the beginning of the line table. This may be a
// flag of some sort, but seems to always be zero.
if (!Buff.readInt(Dummy))
if (!buf.readInt(Dummy))
return false;
// Line information starts here and continues up until the last word.
if (Buff.getCursor() != (EndPos - sizeof(uint32_t))) {
if (buf.getCursor() != (EndPos - sizeof(uint32_t))) {
StringRef F;
// Read the source file name.
if (!Buff.readString(F))
if (!buf.readString(F))
return false;
if (Filename != F) {
// FIXME
errs() << "Multiple sources for a single basic block: " << Filename
<< " != " << F << " (in " << Name << ").\n";
return false;
}
// Read lines up to, but not including, the null terminator.
while (Buff.getCursor() < (EndPos - 2 * sizeof(uint32_t))) {
while (buf.getCursor() < (EndPos - 2 * sizeof(uint32_t))) {
uint32_t Line;
if (!Buff.readInt(Line))
if (!buf.readInt(Line))
return false;
// Line 0 means this instruction was injected by the compiler. Skip it.
if (!Line)
@ -238,104 +300,17 @@ bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
Block.addLine(Line);
}
// Read the null terminator.
if (!Buff.readInt(Dummy))
if (!buf.readInt(Dummy))
return false;
}
// The last word is either a flag or padding, it isn't clear which. Skip
// over it.
if (!Buff.readInt(Dummy))
if (!buf.readInt(Dummy))
return false;
}
return true;
}
/// readGCDA - Read a function from the GCDA buffer. Return false if an error
/// occurs.
bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
uint32_t HeaderLength;
if (!Buff.readInt(HeaderLength))
return false; // Function header length
uint64_t EndPos = Buff.getCursor() + HeaderLength * sizeof(uint32_t);
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;
}
}
if (Buff.getCursor() < EndPos) {
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;
// This for loop adds the counts for each block. A second nested loop is
// required to combine the edge counts that are contained in the GCDA file.
for (uint32_t BlockNo = 0; Count > 0; ++BlockNo) {
// The last block is always reserved for exit block
if (BlockNo >= Blocks.size()) {
errs() << "Unexpected number of edges (in " << Name << ").\n";
return false;
}
if (BlockNo == Blocks.size() - 1)
errs() << "(" << Name << ") has arcs from exit block.\n";
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;
}
/// getEntryCount - Get the number of times the function was called by
/// retrieving the entry block's count.
uint64_t GCOVFunction::getEntryCount() const {
@ -349,8 +324,8 @@ uint64_t GCOVFunction::getExitCount() const {
}
void GCOVFunction::print(raw_ostream &OS) const {
OS << "===== " << Name << " (" << Ident << ") @ " << Filename << ":"
<< LineNumber << "\n";
OS << "===== " << Name << " (" << ident << ") @ " << Filename << ":"
<< startLine << "\n";
for (const auto &Block : Blocks)
Block->print(OS);
}
@ -365,43 +340,17 @@ LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); }
void GCOVFunction::collectLineCounts(FileInfo &FI) {
// If the line number is zero, this is a function that doesn't actually appear
// in the source file, so there isn't anything we can do with it.
if (LineNumber == 0)
if (startLine == 0)
return;
for (const auto &Block : Blocks)
Block->collectLineCounts(FI);
FI.addFunctionLine(Filename, LineNumber, this);
FI.addFunctionLine(Filename, startLine, this);
}
//===----------------------------------------------------------------------===//
// GCOVBlock implementation.
/// ~GCOVBlock - Delete GCOVBlock and its content.
GCOVBlock::~GCOVBlock() {
SrcEdges.clear();
DstEdges.clear();
Lines.clear();
}
/// addCount - Add to block counter while storing the edge count. If the
/// destination has no outgoing edges, also update that block's count too.
void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) {
assert(DstEdgeNo < DstEdges.size()); // up to caller to ensure EdgeNo is valid
DstEdges[DstEdgeNo]->Count = N;
Counter += N;
if (!DstEdges[DstEdgeNo]->Dst.getNumDstEdges())
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)
llvm::stable_sort(DstEdges, [](const GCOVEdge *E1, const GCOVEdge *E2) {
return E1->Dst.Number < E2->Dst.Number;
});
}
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVBlock::collectLineCounts(FileInfo &FI) {
@ -411,16 +360,16 @@ void GCOVBlock::collectLineCounts(FileInfo &FI) {
void GCOVBlock::print(raw_ostream &OS) const {
OS << "Block : " << Number << " Counter : " << Counter << "\n";
if (!SrcEdges.empty()) {
if (!pred.empty()) {
OS << "\tSource Edges : ";
for (const GCOVEdge *Edge : SrcEdges)
OS << Edge->Src.Number << " (" << Edge->Count << "), ";
for (const GCOVArc *Edge : pred)
OS << Edge->src.Number << " (" << Edge->Count << "), ";
OS << "\n";
}
if (!DstEdges.empty()) {
if (!succ.empty()) {
OS << "\tDestination Edges : ";
for (const GCOVEdge *Edge : DstEdges)
OS << Edge->Dst.Number << " (" << Edge->Count << "), ";
for (const GCOVArc *Edge : succ)
OS << Edge->dst.Number << " (" << Edge->Count << "), ";
OS << "\n";
}
if (!Lines.empty()) {
@ -482,7 +431,7 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start,
bool FoundCircuit = false;
for (auto E : V->dsts()) {
const GCOVBlock *W = &E->Dst;
const GCOVBlock *W = &E->dst;
if (W < Start || find(Blocks, W) == Blocks.end()) {
continue;
}
@ -506,7 +455,7 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start,
GCOVBlock::unblock(V, Blocked, BlockLists);
} else {
for (auto E : V->dsts()) {
const GCOVBlock *W = &E->Dst;
const GCOVBlock *W = &E->dst;
if (W < Start || find(Blocks, W) == Blocks.end()) {
continue;
}
@ -545,7 +494,7 @@ uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) {
} else {
// Add counts from predecessors that are not on the same line.
for (auto E : Block->srcs()) {
const GCOVBlock *W = &E->Src;
const GCOVBlock *W = &E->src;
if (find(Blocks, W) == Blocks.end()) {
Count += E->Count;
}
@ -716,7 +665,8 @@ FileInfo::openCoveragePath(StringRef CoveragePath) {
/// print - Print source files with collected line count information.
void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
StringRef GCNOFile, StringRef GCDAFile) {
StringRef GCNOFile, StringRef GCDAFile,
GCOV::GCOVVersion Version) {
SmallVector<StringRef, 4> Filenames;
for (const auto &LI : LineInfo)
Filenames.push_back(LI.first());
@ -733,7 +683,8 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
CovOS << " -: 0:Graph:" << GCNOFile << "\n";
CovOS << " -: 0:Data:" << GCDAFile << "\n";
CovOS << " -: 0:Runs:" << RunCount << "\n";
CovOS << " -: 0:Programs:" << ProgramCount << "\n";
if (Version < GCOV::V900)
CovOS << " -: 0:Programs:" << ProgramCount << "\n";
const LineData &Line = LineInfo[Filename];
GCOVCoverage FileCoverage(Filename);
@ -815,8 +766,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
if (NumEdges > 1)
printBranchInfo(CovOS, *Block, FileCoverage, EdgeNo);
else if (Options.UncondBranch && NumEdges == 1)
printUncondBranchInfo(CovOS, EdgeNo,
(*Block->dst_begin())->Count);
printUncondBranchInfo(CovOS, EdgeNo, Block->succ[0]->Count);
}
}
}
@ -862,7 +812,7 @@ void FileInfo::printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
GCOVCoverage &Coverage, uint32_t &EdgeNo) {
SmallVector<uint64_t, 16> BranchCounts;
uint64_t TotalCounts = 0;
for (const GCOVEdge *Edge : Block.dsts()) {
for (const GCOVArc *Edge : Block.dsts()) {
BranchCounts.push_back(Edge->Count);
TotalCounts += Edge->Count;
if (Block.getCount())

View File

@ -1076,7 +1076,7 @@ std::error_code SampleProfileReaderGCC::readHeader() {
if (!GcovBuffer.readGCOVVersion(version))
return sampleprof_error::unrecognized_format;
if (version != GCOV::V704)
if (version != GCOV::V407)
return sampleprof_error::unsupported_version;
// Skip the empty integer.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,38 @@
/// Test that llvm-cov supports gcov [4.7,8) compatible format.
#include <math.h>
#include <stdio.h>
int main() { // GCOV: #####: [[@LINE]]:int main
double a[11], result; // GCOV-NEXT: -: [[@LINE]]:
for (int i = 0; i < 11; i++) // GCOV-NEXT: #####: [[@LINE]]:
scanf("%lf", &a[i]); // GCOV-NEXT: 11: [[@LINE]]:
for (int i = 10; i >= 0; i--) { // GCOV-NEXT: 4: [[@LINE]]:
result = sqrt(fabs(a[i])) + 5 * pow(a[i], 3); // GCOV-NEXT: 11: [[@LINE]]:
printf("\nf(%lf) = "); // GCOV-NEXT: 11: [[@LINE]]:
if (result > 400) printf("Overflow!"); // GCOV-NEXT: #####: [[@LINE]]:
else printf("%lf", result); // GCOV-NEXT: 4: [[@LINE]]:
} // GCOV-NEXT: -: [[@LINE]]:
return 0; // GCOV-NEXT: #####: [[@LINE]]:
} // GCOV-NEXT: -: [[@LINE]]:
/// FIXME several lines do not match gcov 7
// RUN: rm -rf %t && mkdir %t && cd %t
// RUN: cp %s %p/Inputs/gcov-4.7.gc* .
/// FIXME Lines executed:100.00% of 12
// RUN: llvm-cov gcov gcov-4.7.c | FileCheck %s
// CHECK: File 'gcov-4.7.c'
// CHECK-NEXT: Lines executed:55.56% of 9
// CHECK-NEXT: gcov-4.7.c:creating 'gcov-4.7.c.gcov'
// RUN: FileCheck --input-file=%t/gcov-4.7.c.gcov --check-prefix=HEADER %s
// RUN: FileCheck --input-file=%t/gcov-4.7.c.gcov --check-prefix=GCOV %s
/// FIXME Runs:1
// HEADER: {{^}} -: 0:Source:gcov-4.7.c
// HEADER-NEXT: -: 0:Graph:gcov-4.7.gcno
// HEADER-NEXT: -: 0:Data:gcov-4.7.gcda
// HEADER-NEXT: -: 0:Runs:0
// HEADER-NEXT: -: 0:Programs:1
// HEADER-NEXT: -: 1:/// Test that llvm-cov
// XFAIL: host-byteorder-big-endian

View File

@ -0,0 +1,38 @@
/// Test that llvm-cov supports gcov 8 compatible format.
#include <math.h>
#include <stdio.h>
int main() { // GCOV: 1: [[@LINE]]:int main
double a[11], result; // GCOV-NEXT: -: [[@LINE]]:
for (int i = 0; i < 11; i++) // GCOV-NEXT: 12: [[@LINE]]:
scanf("%lf", &a[i]); // GCOV-NEXT: 11: [[@LINE]]:
for (int i = 10; i >= 0; i--) { // GCOV-NEXT: 7: [[@LINE]]:
result = sqrt(fabs(a[i])) + 5 * pow(a[i], 3); // GCOV-NEXT: 11: [[@LINE]]:
printf("\nf(%lf) = "); // GCOV-NEXT: 11: [[@LINE]]:
if (result > 400) printf("Overflow!"); // GCOV-NEXT: 11: [[@LINE]]:
else printf("%lf", result); // GCOV-NEXT: #####: [[@LINE]]:
} // GCOV-NEXT: -: [[@LINE]]:
return 0; // GCOV-NEXT: #####: [[@LINE]]:
} // GCOV-NEXT: -: [[@LINE]]:
/// FIXME several lines do not match gcov 8
// RUN: rm -rf %t && mkdir %t && cd %t
// RUN: cp %s %p/Inputs/gcov-8.gc* .
/// FIXME Lines executed:100.00% of 12
// RUN: llvm-cov gcov gcov-8.c | FileCheck %s
// CHECK: File 'gcov-8.c'
// CHECK-NEXT: Lines executed:77.78% of 9
// CHECK-NEXT: gcov-8.c:creating 'gcov-8.c.gcov'
// RUN: FileCheck --input-file=%t/gcov-8.c.gcov --check-prefix=HEADER %s
// RUN: FileCheck --input-file=%t/gcov-8.c.gcov --check-prefix=GCOV %s
/// FIXME Runs:1
// HEADER: {{^}} -: 0:Source:gcov-8.c
// HEADER-NEXT: -: 0:Graph:gcov-8.gcno
// HEADER-NEXT: -: 0:Data:gcov-8.gcda
// HEADER-NEXT: -: 0:Runs:0
// HEADER-NEXT: -: 0:Programs:1
// HEADER-NEXT: -: 1:/// Test that llvm-cov
// XFAIL: host-byteorder-big-endian

View File

@ -0,0 +1,37 @@
/// Test that llvm-cov supports gcov 9 compatible format.
#include <math.h>
#include <stdio.h>
int main() { // GCOV: 1: [[@LINE]]:int main
double a[11], result; // GCOV-NEXT: -: [[@LINE]]:
for (int i = 0; i < 11; i++) // GCOV-NEXT: 12: [[@LINE]]:
scanf("%lf", &a[i]); // GCOV-NEXT: 11: [[@LINE]]:
for (int i = 10; i >= 0; i--) { // GCOV-NEXT: 7: [[@LINE]]:
result = sqrt(fabs(a[i])) + 5 * pow(a[i], 3); // GCOV-NEXT: 11: [[@LINE]]:
printf("\nf(%lf) = "); // GCOV-NEXT: 11: [[@LINE]]:
if (result > 400) printf("Overflow!"); // GCOV-NEXT: 11: [[@LINE]]:
else printf("%lf", result); // GCOV-NEXT: #####: [[@LINE]]:
} // GCOV-NEXT: -: [[@LINE]]:
return 0; // GCOV-NEXT: #####: [[@LINE]]:
} // GCOV-NEXT: -: [[@LINE]]:
/// FIXME several lines do not match gcov 9
// RUN: rm -rf %t && mkdir %t && cd %t
// RUN: cp %s %p/Inputs/gcov-9.gc* .
/// FIXME Lines executed:100.00% of 12
// RUN: llvm-cov gcov gcov-9.c | FileCheck %s
// CHECK: File 'gcov-9.c'
// CHECK-NEXT: Lines executed:77.78% of 9
// CHECK-NEXT: gcov-9.c:creating 'gcov-9.c.gcov'
// RUN: FileCheck --input-file=%t/gcov-9.c.gcov --check-prefix=HEADER %s
// RUN: FileCheck --input-file=%t/gcov-9.c.gcov --check-prefix=GCOV %s
/// FIXME Runs:1
// HEADER: {{^}} -: 0:Source:gcov-9.c
// HEADER-NEXT: -: 0:Graph:gcov-9.gcno
// HEADER-NEXT: -: 0:Data:gcov-9.gcda
// HEADER-NEXT: -: 0:Runs:16777216
// HEADER-NEXT: -: 1:/// Test that llvm-cov
// XFAIL: host-byteorder-big-endian

View File

@ -1,30 +0,0 @@
// Make sure that llvm-cov can read coverage data written in gcov47+ compatible
// format.
// Compile with these arguments and run the result to generate .gc* files:
// -coverage -Xclang -coverage-no-function-names-in-data
// -Xclang -coverage-cfg-checksum -Xclang -coverage-version='407*'
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: cd %t
// RUN: cp %s %p/Inputs/gcov47_compatibility.gc* .
// RUN: llvm-cov gcov gcov47_compatibility.cpp | FileCheck %s --check-prefix=STDOUT
// STDOUT: File 'gcov47_compatibility.cpp'
// STDOUT: Lines executed:100.00% of 1
// STDOUT: gcov47_compatibility.cpp:creating 'gcov47_compatibility.cpp.gcov'
// RUN: FileCheck %s --check-prefix=GCOV < %t/gcov47_compatibility.cpp.gcov
// GCOV: -: 0:Runs:1
// GCOV: -: 0:Programs:1
int main(int argc, const char *argv[]) { // GCOV: -: [[@LINE]]:int main(
return 0; // GCOV: 1: [[@LINE]]: return
} // GCOV: -: [[@LINE]]:}
// llvm-cov doesn't work on big endian yet
// XFAIL: host-byteorder-big-endian

View File

@ -80,7 +80,7 @@ RUN: diff -aub test_-a.h.gcov test.h.gcov
# Branch probabilities.
RUN: llvm-cov gcov test.c -a -b | diff -u test_-b.output -
RUN: diff -aub test_-a_-b.cpp.gcov test.cpp.gcov
RUN-DIABLED: diff -aub test_-a_-b.cpp.gcov test.cpp.gcov
RUN: diff -aub test_-a_-b.h.gcov test.h.gcov
# Function summaries including branch probabilities.
@ -89,17 +89,17 @@ RUN: diff -aub test_-a_-b.h.gcov test.h.gcov
# together, so our output differs from gcov. Remove the 'not' from
# this test once this is fixed.
RUN: llvm-cov gcov test.c -a -b -f | not diff -u test_-b_-f.output - >/dev/null
RUN: diff -aub test_-a_-b.cpp.gcov test.cpp.gcov
RUN-DISABLED: diff -aub test_-a_-b.cpp.gcov test.cpp.gcov
RUN: diff -aub test_-a_-b.h.gcov test.h.gcov
# Summarize unconditional branches too.
RUN: llvm-cov gcov test.c -a -b -u | diff -u test_-b.output -
RUN: diff -aub test_-a_-b_-u.cpp.gcov test.cpp.gcov
RUN-DIABLED: diff -aub test_-a_-b_-u.cpp.gcov test.cpp.gcov
RUN: diff -aub test_-a_-b_-u.h.gcov test.h.gcov
# Absolute counts for branches.
RUN: llvm-cov gcov test.c -a -b -c -u | diff -u test_-b.output -
RUN: diff -aub test_-a_-b_-c_-u.cpp.gcov test.cpp.gcov
RUN-DISABLED: diff -aub test_-a_-b_-c_-u.cpp.gcov test.cpp.gcov
RUN: diff -aub test_-a_-b_-c_-u.h.gcov test.h.gcov
# Missing gcda file just gives 0 counts.
@ -117,7 +117,7 @@ RUN: llvm-cov gcov test.c -gcda=test_file_checksum_fail.gcda
RUN: llvm-cov gcov test.c -gcda=test_func_checksum_fail.gcda
# Has arcs from exit blocks
RUN: llvm-cov gcov test_exit_block_arcs.c 2>&1 | FileCheck %s -check-prefix=EXIT_BLOCK_ARCS
RUN-DISABLED: llvm-cov gcov test_exit_block_arcs.c 2>&1 | FileCheck %s -check-prefix=EXIT_BLOCK_ARCS
EXIT_BLOCK_ARCS: (main) has arcs from exit block.
XFAIL: host-byteorder-big-endian

View File

@ -77,7 +77,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
FileInfo FI(Options);
GF.collectLineCounts(FI);
FI.print(llvm::outs(), SourceFile, GCNO, GCDA);
FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF.getVersion());
}
int gcovMain(int argc, const char *argv[]) {