[compiler-rt] e3200da - [gcov] Support .gcno/.gcda in gcov 8, 9 or 10 compatible formats
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 7 11:27:56 PDT 2020
Author: Fangrui Song
Date: 2020-06-07T11:27:49-07:00
New Revision: e3200dab606cf038338d05ba545cb54f734ef2e9
URL: https://github.com/llvm/llvm-project/commit/e3200dab606cf038338d05ba545cb54f734ef2e9
DIFF: https://github.com/llvm/llvm-project/commit/e3200dab606cf038338d05ba545cb54f734ef2e9.diff
LOG: [gcov] Support .gcno/.gcda in gcov 8, 9 or 10 compatible formats
Added:
Modified:
compiler-rt/lib/profile/GCDAProfiling.c
compiler-rt/test/profile/gcov-basic.c
llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
Removed:
################################################################################
diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
index cea7eb8ec954..d15540d7c18a 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -545,10 +545,15 @@ void llvm_gcda_summary_info() {
}
val = read_32bit_value(); /* length */
- read_32bit_value();
- if (gcov_version < 90)
+ uint32_t prev_runs;
+ if (gcov_version < 90) {
read_32bit_value();
- uint32_t prev_runs = read_32bit_value();
+ read_32bit_value();
+ prev_runs = read_32bit_value();
+ } else {
+ prev_runs = read_32bit_value();
+ read_32bit_value();
+ }
for (uint32_t i = gcov_version < 90 ? 3 : 2; i < val; ++i)
read_32bit_value();
/* Add previous run count to new counter, if not already counted before. */
diff --git a/compiler-rt/test/profile/gcov-basic.c b/compiler-rt/test/profile/gcov-basic.c
index f29dfd49b0a7..e00cebf4b781 100644
--- a/compiler-rt/test/profile/gcov-basic.c
+++ b/compiler-rt/test/profile/gcov-basic.c
@@ -2,19 +2,33 @@
/// gcov 3.4 redesigned the format and changed the extension from .da to .gcda
// RUN: %clang --coverage -Xclang -coverage-version='304*' %s -o %t
-// RUN: rm -f gcov-basic.gcda && %run %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
// RUN: llvm-cov gcov -t gcov-basic.gcno | FileCheck %s
/// r173147: split checksum into cfg checksum and line checksum.
// RUN: %clang --coverage -Xclang -coverage-version='407*' %s -o %t
-// RUN: rm -f gcov-basic.gcda && %run %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
// RUN: llvm-cov gcov -t gcov-basic.gcno | FileCheck %s
/// r189778: the exit block moved from the last to the second.
// RUN: %clang --coverage -Xclang -coverage-version='408*' %s -o %t
-// RUN: rm -f gcov-basic.gcda && %run %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
// RUN: llvm-cov gcov -t gcov-basic.gcno
-int main() { // CHECK: 1: [[@LINE]]:int main
- return 0; // CHECK-NEXT: 1: [[@LINE]]:
+/// PR gcov-profile/48463
+// RUN: %clang --coverage -Xclang -coverage-version='800*' %s -o %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
+// RUN: llvm-cov gcov -t gcov-basic.gcno
+
+/// PR gcov-profile/84846, r269678
+// RUN: %clang --coverage -Xclang -coverage-version='900*' %s -o %t
+// RUN: rm -f gcov-basic.gcda && %run %t && %run %t a
+// RUN: llvm-cov gcov -t gcov-basic.gcno
+
+// CHECK: Runs:2
+
+int main(int argc, char *argv[]) { // CHECK: 2: [[@LINE]]:int main
+ if (argc > 1) // CHECK-NEXT: 2: [[@LINE]]:
+ puts("hello"); // CHECK-NEXT: 1: [[@LINE]]:
+ return 0; // CHECK-NEXT: 2: [[@LINE]]:
}
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index ccd9d26222a9..8c4ba2bb4edf 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -62,6 +62,12 @@ static cl::opt<std::string> DefaultGCOVVersion("default-gcov-version",
cl::init("408*"), cl::Hidden,
cl::ValueRequired);
+// Returns the number of words which will be used to represent this string.
+static unsigned wordsOfString(StringRef s) {
+ // Length + NUL-terminated string + 0~3 padding NULs.
+ return (s.size() / 4) + 2;
+}
+
GCOVOptions GCOVOptions::getDefault() {
GCOVOptions Options;
Options.EmitNotes = true;
@@ -87,6 +93,18 @@ class GCOVProfiler {
runOnModule(Module &M,
std::function<const TargetLibraryInfo &(Function &F)> GetTLI);
+ void write(uint32_t i) {
+ char Bytes[4];
+ endian::write32(Bytes, i, Endian);
+ os->write(Bytes, 4);
+ }
+ void writeString(StringRef s) {
+ write(wordsOfString(s) - 1);
+ os->write(s.data(), s.size());
+ os->write_zeros(4 - s.size() % 4);
+ }
+ void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); }
+
private:
// Create the .gcno files for the Module based on DebugInfo.
void emitProfileNotes();
@@ -120,6 +138,8 @@ class GCOVProfiler {
std::string mangleName(const DICompileUnit *CU, GCovFileType FileType);
GCOVOptions Options;
+ support::endianness Endian;
+ raw_ostream *os;
// Checksum, produced by hash of EdgeDestinations
SmallVector<uint32_t, 4> FileChecksums;
@@ -196,37 +216,13 @@ static SmallString<128> getFilename(const DISubprogram *SP) {
namespace {
class GCOVRecord {
protected:
- support::endianness Endian;
-
- GCOVRecord(support::endianness Endian) : Endian(Endian) {}
-
- void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); }
-
- void write(uint32_t i) {
- char Bytes[4];
- endian::write32(Bytes, i, Endian);
- os->write(Bytes, 4);
- }
-
- // Returns the length measured in 4-byte blocks that will be used to
- // represent this string in a GCOV file
- static unsigned lengthOfGCOVString(StringRef s) {
- // A GCOV string is a length, followed by a NUL, then between 0 and 3 NULs
- // padding out to the next 4-byte word. The length is measured in 4-byte
- // words including padding, not bytes of actual string.
- return (s.size() / 4) + 1;
- }
-
- void writeGCOVString(StringRef s) {
- uint32_t Len = lengthOfGCOVString(s);
- write(Len);
- writeBytes(s.data(), s.size());
+ GCOVProfiler *P;
- // Write 1 to 4 bytes of NUL padding.
- writeBytes("\0\0\0\0", 4 - (s.size() % 4));
- }
+ GCOVRecord(GCOVProfiler *P) : P(P) {}
- raw_ostream *os;
+ void write(uint32_t i) { P->write(i); }
+ void writeString(StringRef s) { P->writeString(s); }
+ void writeBytes(const char *Bytes, int Size) { P->writeBytes(Bytes, Size); }
};
class GCOVFunction;
@@ -243,23 +239,20 @@ namespace {
}
uint32_t length() const {
- // Here 2 = 1 for string length + 1 for '0' id#.
- return lengthOfGCOVString(Filename) + 2 + Lines.size();
+ return 1 + wordsOfString(Filename) + Lines.size();
}
void writeOut() {
write(0);
- writeGCOVString(Filename);
+ writeString(Filename);
for (int i = 0, e = Lines.size(); i != e; ++i)
write(Lines[i]);
}
- GCOVLines(StringRef F, raw_ostream *os, support::endianness Endian)
- : GCOVRecord(Endian), Filename(std::string(F)) {
- this->os = os;
- }
+ GCOVLines(GCOVProfiler *P, StringRef F)
+ : GCOVRecord(P), Filename(std::string(F)) {}
- private:
+ private:
std::string Filename;
SmallVector<uint32_t, 32> Lines;
};
@@ -271,8 +264,7 @@ namespace {
class GCOVBlock : public GCOVRecord {
public:
GCOVLines &getFile(StringRef Filename) {
- return LinesByFile.try_emplace(Filename, Filename, os, Endian)
- .first->second;
+ return LinesByFile.try_emplace(Filename, P, Filename).first->second;
}
void addEdge(GCOVBlock &Successor) {
@@ -312,10 +304,8 @@ namespace {
private:
friend class GCOVFunction;
- GCOVBlock(uint32_t Number, raw_ostream *os, support::endianness Endian)
- : GCOVRecord(Endian), Number(Number) {
- this->os = os;
- }
+ GCOVBlock(GCOVProfiler *P, uint32_t Number)
+ : GCOVRecord(P), Number(Number) {}
uint32_t Number;
StringMap<GCOVLines> LinesByFile;
@@ -327,21 +317,18 @@ namespace {
// object users can construct, the blocks and lines will be rooted here.
class GCOVFunction : public GCOVRecord {
public:
- GCOVFunction(const DISubprogram *SP, Function *F, raw_ostream *os,
- support::endianness Endian, uint32_t Ident,
- bool UseCfgChecksum, bool ExitBlockBeforeBody)
- : GCOVRecord(Endian), SP(SP), Ident(Ident),
- UseCfgChecksum(UseCfgChecksum), ReturnBlock(1, os, Endian) {
- this->os = os;
-
+ GCOVFunction(GCOVProfiler *P, Function *F, const DISubprogram *SP,
+ unsigned EndLine, uint32_t Ident, int Version)
+ : GCOVRecord(P), SP(SP), EndLine(EndLine), Ident(Ident),
+ Version(Version), ReturnBlock(P, 1) {
LLVM_DEBUG(dbgs() << "Function: " << getFunctionName(SP) << "\n");
-
+ bool ExitBlockBeforeBody = Version >= 48;
uint32_t i = 0;
for (auto &BB : *F) {
// Skip index 1 if it's assigned to the ReturnBlock.
if (i == 1 && ExitBlockBeforeBody)
++i;
- Blocks.insert(std::make_pair(&BB, GCOVBlock(i++, os, Endian)));
+ Blocks.insert(std::make_pair(&BB, GCOVBlock(P, i++)));
}
if (!ExitBlockBeforeBody)
ReturnBlock.Number = i;
@@ -380,26 +367,45 @@ namespace {
void writeOut(uint32_t CfgChecksum) {
write(GCOV_TAG_FUNCTION);
SmallString<128> Filename = getFilename(SP);
- uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) +
- 1 + lengthOfGCOVString(Filename) + 1;
- if (UseCfgChecksum)
- ++BlockLen;
+ uint32_t BlockLen =
+ 2 + (Version >= 47) + wordsOfString(getFunctionName(SP));
+ if (Version < 80)
+ BlockLen += wordsOfString(Filename) + 1;
+ else
+ BlockLen += 1 + wordsOfString(Filename) + 3 + (Version >= 90);
+
write(BlockLen);
write(Ident);
write(FuncChecksum);
- if (UseCfgChecksum)
+ if (Version >= 47)
write(CfgChecksum);
- writeGCOVString(getFunctionName(SP));
- writeGCOVString(Filename);
- write(SP->getLine());
+ writeString(getFunctionName(SP));
+ if (Version < 80) {
+ writeString(Filename);
+ write(SP->getLine());
+ } else {
+ write(SP->isArtificial()); // artificial
+ writeString(Filename);
+ write(SP->getLine()); // start_line
+ write(0); // start_column
+ // EndLine is the last line with !dbg. It is not the } line as in GCC,
+ // but good enough.
+ write(EndLine);
+ if (Version >= 90)
+ write(0); // end_column
+ }
// Emit count of blocks.
write(GCOV_TAG_BLOCKS);
- write(Blocks.size() + 1);
- for (int i = 0, e = Blocks.size() + 1; i != e; ++i) {
- write(0); // No flags on our blocks.
+ if (Version < 80) {
+ write(Blocks.size() + 1);
+ for (int i = Blocks.size() + 1; i; --i)
+ write(0);
+ } else {
+ write(1);
+ write(Blocks.size() + 1);
}
- LLVM_DEBUG(dbgs() << Blocks.size() << " blocks.\n");
+ LLVM_DEBUG(dbgs() << (Blocks.size() + 1) << " blocks\n");
// Emit edges between blocks.
Function *F = Blocks.begin()->first->getParent();
@@ -425,9 +431,10 @@ namespace {
private:
const DISubprogram *SP;
+ unsigned EndLine;
uint32_t Ident;
uint32_t FuncChecksum;
- bool UseCfgChecksum;
+ int Version;
DenseMap<BasicBlock *, GCOVBlock> Blocks;
GCOVBlock ReturnBlock;
};
@@ -572,9 +579,10 @@ PreservedAnalyses GCOVProfilerPass::run(Module &M,
return PreservedAnalyses::none();
}
-static bool functionHasLines(Function &F) {
+static bool functionHasLines(const Function &F, unsigned &EndLine) {
// Check whether this function actually has any source lines. Not only
// do these waste space, they also can crash gcov.
+ EndLine = 0;
for (auto &BB : F) {
for (auto &I : BB) {
// Debug intrinsic locations correspond to the location of the
@@ -587,6 +595,7 @@ static bool functionHasLines(Function &F) {
// Artificial lines such as calls to the global constructors.
if (Loc.getLine() == 0) continue;
+ EndLine = std::max(EndLine, Loc.getLine());
return true;
}
@@ -689,6 +698,15 @@ void GCOVProfiler::emitProfileNotes() {
NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu");
if (!CU_Nodes) return;
+ int Version;
+ {
+ uint8_t c3 = Options.Version[0];
+ uint8_t c2 = Options.Version[1];
+ uint8_t c1 = Options.Version[2];
+ Version = c3 >= 'A' ? (c3 - 'A') * 100 + (c2 - '0') * 10 + c1 - '0'
+ : (c3 - '0') * 10 + c1 - '0';
+ }
+
for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) {
// Each compile unit gets its own .gcno file. This means that whether we run
// this pass over the original .o's as they're produced, or run it after
@@ -711,14 +729,14 @@ void GCOVProfiler::emitProfileNotes() {
std::string EdgeDestinations;
- auto Endian = M->getDataLayout().isLittleEndian()
- ? support::endianness::little
- : support::endianness::big;
+ Endian = M->getDataLayout().isLittleEndian() ? support::endianness::little
+ : support::endianness::big;
unsigned FunctionIdent = 0;
for (auto &F : M->functions()) {
DISubprogram *SP = F.getSubprogram();
+ unsigned EndLine;
if (!SP) continue;
- if (!functionHasLines(F) || !isFunctionInstrumented(F))
+ if (!functionHasLines(F, EndLine) || !isFunctionInstrumented(F))
continue;
// TODO: Functions using scope-based EH are currently not supported.
if (isUsingScopeBasedEH(F)) continue;
@@ -731,11 +749,8 @@ void GCOVProfiler::emitProfileNotes() {
++It;
EntryBlock.splitBasicBlock(It);
- bool UseCfgChecksum = strncmp(Options.Version, "407", 3) >= 0;
- bool ExitBlockBeforeBody = strncmp(Options.Version, "408", 3) >= 0;
- Funcs.push_back(
- std::make_unique<GCOVFunction>(SP, &F, &out, Endian, FunctionIdent++,
- UseCfgChecksum, ExitBlockBeforeBody));
+ Funcs.push_back(std::make_unique<GCOVFunction>(this, &F, SP, EndLine,
+ FunctionIdent++, Version));
GCOVFunction &Func = *Funcs.back();
// Add the function line number to the lines of the entry block
@@ -785,7 +800,9 @@ void GCOVProfiler::emitProfileNotes() {
}
char Tmp[4];
- FileChecksums.push_back(hash_value(EdgeDestinations));
+ os = &out;
+ auto Stamp = static_cast<uint32_t>(hash_value(EdgeDestinations));
+ FileChecksums.push_back(Stamp);
if (Endian == support::endianness::big) {
out.write("gcno", 4);
out.write(Options.Version, 4);
@@ -794,13 +811,17 @@ void GCOVProfiler::emitProfileNotes() {
std::reverse_copy(Options.Version, Options.Version + 4, Tmp);
out.write(Tmp, 4);
}
- endian::write32(Tmp, FileChecksums.back(), Endian);
- out.write(Tmp, 4);
+ write(Stamp);
+ if (Version >= 90)
+ writeString(""); // unuseful current_working_directory
+ if (Version >= 80)
+ write(0); // unuseful has_unexecuted_blocks
for (auto &Func : Funcs)
- Func->writeOut(FileChecksums.back());
+ Func->writeOut(Stamp);
- out.write("\0\0\0\0\0\0\0\0", 8); // EOF
+ write(0);
+ write(0);
out.close();
}
}
@@ -814,8 +835,9 @@ bool GCOVProfiler::emitProfileArcs() {
SmallVector<std::pair<GlobalVariable *, MDNode *>, 8> CountersBySP;
for (auto &F : M->functions()) {
DISubprogram *SP = F.getSubprogram();
+ unsigned EndLine;
if (!SP) continue;
- if (!functionHasLines(F) || !isFunctionInstrumented(F))
+ if (!functionHasLines(F, EndLine) || !isFunctionInstrumented(F))
continue;
// TODO: Functions using scope-based EH are currently not supported.
if (isUsingScopeBasedEH(F)) continue;
More information about the llvm-commits
mailing list