summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/Support/GCOV.h25
-rw-r--r--lib/IR/GCOV.cpp96
-rw-r--r--test/tools/llvm-cov/Inputs/test_-b_-f.output65
-rw-r--r--test/tools/llvm-cov/Inputs/test_-f.output38
-rw-r--r--test/tools/llvm-cov/llvm-cov.test4
-rw-r--r--tools/llvm-cov/llvm-cov.cpp6
6 files changed, 211 insertions, 23 deletions
diff --git a/include/llvm/Support/GCOV.h b/include/llvm/Support/GCOV.h
index eb536f50dc..4e7920b8e9 100644
--- a/include/llvm/Support/GCOV.h
+++ b/include/llvm/Support/GCOV.h
@@ -16,6 +16,7 @@
#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/Support/MemoryBuffer.h"
@@ -36,12 +37,14 @@ namespace GCOV {
/// GCOVOptions - A struct for passing gcov options between functions.
struct GCOVOptions {
- GCOVOptions(bool A, bool B, bool C, bool U) :
- AllBlocks(A), BranchInfo(B), BranchCount(C), UncondBranch(U) {}
+ GCOVOptions(bool A, bool B, bool C, bool F, bool U) :
+ AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), UncondBranch(U)
+ {}
bool AllBlocks;
bool BranchInfo;
bool BranchCount;
+ bool FuncCoverage;
bool UncondBranch;
};
@@ -300,6 +303,7 @@ public:
GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N), Counter(0),
DstEdgesAreSorted(true), SrcEdges(), DstEdges(), Lines() {}
~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);
@@ -352,10 +356,12 @@ class FileInfo {
};
struct GCOVCoverage {
- GCOVCoverage() :
- LogicalLines(0), LinesExec(0), Branches(0), BranchesExec(0),
+ GCOVCoverage(StringRef Name) :
+ Name(Name), LogicalLines(0), LinesExec(0), Branches(0), BranchesExec(0),
BranchesTaken(0) {}
+ StringRef Name;
+
uint32_t LogicalLines;
uint32_t LinesExec;
@@ -376,22 +382,27 @@ public:
}
void setRunCount(uint32_t Runs) { RunCount = Runs; }
void setProgramCount(uint32_t Programs) { ProgramCount = Programs; }
- void print(StringRef GCNOFile, StringRef GCDAFile) const;
+ void print(StringRef GCNOFile, StringRef GCDAFile);
private:
void printFunctionSummary(raw_fd_ostream &OS,
const FunctionVector &Funcs) const;
void printBlockInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
uint32_t LineIndex, uint32_t &BlockNo) const;
void printBranchInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
- GCOVCoverage &Coverage, uint32_t &EdgeNo) const;
+ GCOVCoverage &Coverage, uint32_t &EdgeNo);
void printUncondBranchInfo(raw_fd_ostream &OS, uint32_t &EdgeNo,
uint64_t Count) const;
- void printFileCoverage(StringRef Filename, GCOVCoverage &Coverage) const;
+
+ void printCoverage(const GCOVCoverage &Coverage) const;
+ void printFuncCoverage() const;
+ void printFileCoverage() const;
const GCOVOptions &Options;
StringMap<LineData> LineInfo;
uint32_t RunCount;
uint32_t ProgramCount;
+ SmallVector<GCOVCoverage, 4> FileCoverages;
+ MapVector<const GCOVFunction *, GCOVCoverage> FuncCoverages;
};
}
diff --git a/lib/IR/GCOV.cpp b/lib/IR/GCOV.cpp
index b33eee6692..140d259c2a 100644
--- a/lib/IR/GCOV.cpp
+++ b/lib/IR/GCOV.cpp
@@ -430,7 +430,7 @@ static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) {
}
/// 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();
@@ -454,7 +454,7 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) const {
OS << " -: 0:Programs:" << ProgramCount << "\n";
const LineData &Line = I->second;
- GCOVCoverage Coverage;
+ GCOVCoverage FileCoverage(Filename);
for (uint32_t LineIndex = 0; !AllLines.empty(); ++LineIndex) {
if (Options.BranchInfo) {
FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex);
@@ -473,6 +473,7 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) const {
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) {
@@ -485,15 +486,49 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) const {
// 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 {
OS << format("%9" PRIu64 ":", LineCount);
- ++Coverage.LinesExec;
+ ++FileCoverage.LinesExec;
}
- ++Coverage.LogicalLines;
+ ++FileCoverage.LogicalLines;
std::pair<StringRef, StringRef> P = AllLines.split('\n');
OS << format("%5u:", LineIndex+1) << P.first << "\n";
@@ -513,17 +548,20 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) const {
if (Options.BranchInfo) {
size_t NumEdges = Block->getNumDstEdges();
if (NumEdges > 1)
- printBranchInfo(OS, *Block, Coverage, EdgeNo);
+ printBranchInfo(OS, *Block, FileCoverage, EdgeNo);
else if (Options.UncondBranch && NumEdges == 1)
printUncondBranchInfo(OS, EdgeNo, (*Block->dst_begin())->Count);
}
}
}
}
-
- // FIXME: There is no way to detect calls given current instrumentation.
- printFileCoverage(Filename, Coverage);
+ 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.
@@ -560,7 +598,7 @@ void FileInfo::printBlockInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
/// printBranchInfo - Print conditional branch probabilities.
void FileInfo::printBranchInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
- GCOVCoverage &Coverage, uint32_t &EdgeNo) const {
+ 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();
@@ -571,6 +609,14 @@ void FileInfo::printBranchInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
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(),
@@ -587,10 +633,9 @@ void FileInfo::printUncondBranchInfo(raw_fd_ostream &OS, uint32_t &EdgeNo,
<< formatBranchInfo(Options, Count, Count) << "\n";
}
-/// printFileCoverage - Print per-file coverage info.
-void FileInfo::printFileCoverage(StringRef Filename,
- GCOVCoverage &Coverage) const {
- outs() << "File '" << Filename << "'\n";
+// printCoverage - Print generic coverage info used by both printFuncCoverage
+// and printFileCoverage.
+void FileInfo::printCoverage(const GCOVCoverage &Coverage) const {
outs() << format("Lines executed:%.2lf%% of %u\n",
double(Coverage.LinesExec)*100/Coverage.LogicalLines,
Coverage.LogicalLines);
@@ -607,6 +652,27 @@ void FileInfo::printFileCoverage(StringRef Filename,
}
outs() << "No calls\n"; // to be consistent with gcov
}
- outs() << Filename << ":creating '" << Filename << ".gcov'\n";
- outs() << "\n";
+}
+
+// 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";
+ }
}
diff --git a/test/tools/llvm-cov/Inputs/test_-b_-f.output b/test/tools/llvm-cov/Inputs/test_-b_-f.output
new file mode 100644
index 0000000000..c3ccd05b17
--- /dev/null
+++ b/test/tools/llvm-cov/Inputs/test_-b_-f.output
@@ -0,0 +1,65 @@
+Function '_ZN1A1BEv'
+Lines executed:100.00% of 1
+No branches
+No calls
+
+Function '_Z7uselessv'
+Lines executed:0.00% of 1
+No branches
+No calls
+
+Function '_Z12more_uselessv'
+Lines executed:0.00% of 1
+No branches
+No calls
+
+Function '_Z3foov'
+Lines executed:100.00% of 2
+No branches
+No calls
+
+Function '_Z3barv'
+Lines executed:0.00% of 2
+No branches
+No calls
+
+Function '_Z6assignii'
+Lines executed:100.00% of 3
+No branches
+No calls
+
+Function '_Z15initialize_gridv'
+Lines executed:100.00% of 4
+Branches executed:100.00% of 4
+Taken at least once:100.00% of 4
+No calls
+
+Function 'main'
+Lines executed:91.67% of 24
+Branches executed:100.00% of 11
+Taken at least once:81.82% of 11
+No calls
+
+Function '_ZN1AC1Ev'
+Lines executed:100.00% of 1
+No branches
+No calls
+
+Function '_ZN1AC2Ev'
+No executable lines
+No branches
+No calls
+
+File 'test.cpp'
+Lines executed:84.21% of 38
+Branches executed:100.00% of 15
+Taken at least once:86.67% of 15
+No calls
+test.cpp:creating 'test.cpp.gcov'
+
+File './test.h'
+Lines executed:100.00% of 1
+No branches
+No calls
+./test.h:creating 'test.h.gcov'
+
diff --git a/test/tools/llvm-cov/Inputs/test_-f.output b/test/tools/llvm-cov/Inputs/test_-f.output
new file mode 100644
index 0000000000..9e98d88880
--- /dev/null
+++ b/test/tools/llvm-cov/Inputs/test_-f.output
@@ -0,0 +1,38 @@
+Function '_ZN1A1BEv'
+Lines executed:100.00% of 1
+
+Function '_Z7uselessv'
+Lines executed:0.00% of 1
+
+Function '_Z12more_uselessv'
+Lines executed:0.00% of 1
+
+Function '_Z3foov'
+Lines executed:100.00% of 2
+
+Function '_Z3barv'
+Lines executed:0.00% of 2
+
+Function '_Z6assignii'
+Lines executed:100.00% of 3
+
+Function '_Z15initialize_gridv'
+Lines executed:100.00% of 4
+
+Function 'main'
+Lines executed:91.67% of 24
+
+Function '_ZN1AC1Ev'
+Lines executed:100.00% of 1
+
+Function '_ZN1AC2Ev'
+Lines executed:100.00% of 1
+
+File 'test.cpp'
+Lines executed:84.21% of 38
+test.cpp:creating 'test.cpp.gcov'
+
+File './test.h'
+Lines executed:100.00% of 1
+./test.h:creating './test.h.gcov'
+
diff --git a/test/tools/llvm-cov/llvm-cov.test b/test/tools/llvm-cov/llvm-cov.test
index da39307168..4e4fb52ffa 100644
--- a/test/tools/llvm-cov/llvm-cov.test
+++ b/test/tools/llvm-cov/llvm-cov.test
@@ -6,6 +6,7 @@ RUN: cd %t
RUN: cp %p/Inputs/test* .
RUN: llvm-cov -gcno=test.gcno -gcda=test.gcda | diff test_no_options.output -
+RUN: llvm-cov -gcno=test.gcno -gcda=test.gcda -f | diff test_-f.output -
RUN: diff -aub test_no_options.cpp.gcov test.cpp.gcov
RUN: diff -aub test_no_options.h.gcov test.h.gcov
@@ -14,6 +15,9 @@ RUN: diff -aub test_-a.cpp.gcov test.cpp.gcov
RUN: diff -aub test_-a.h.gcov test.h.gcov
RUN: llvm-cov -gcno=test.gcno -gcda=test.gcda -a -b | diff test_-b.output -
+# This is expected to fail because gcov doesn't actually output real branch or
+# call statistics on a per function basis.
+RUN: llvm-cov -gcno=test.gcno -gcda=test.gcda -a -b -f | not diff test_-b_-f.output -
RUN: diff -aub test_-a_-b.cpp.gcov test.cpp.gcov
RUN: diff -aub test_-a_-b.h.gcov test.h.gcov
diff --git a/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp
index 5372317005..31f6ad0058 100644
--- a/tools/llvm-cov/llvm-cov.cpp
+++ b/tools/llvm-cov/llvm-cov.cpp
@@ -41,6 +41,9 @@ BranchCount("c", cl::init(false), cl::desc("display branch counts instead of \
probabilities (requires -b)"));
static cl::opt<bool>
+FuncCoverage("f", cl::init(false), cl::desc("output function coverage"));
+
+static cl::opt<bool>
UncondBranch("u", cl::init(false), cl::desc("display unconditional branch info \
(requires -b)"));
@@ -84,7 +87,8 @@ int main(int argc, char **argv) {
if (DumpGCOV)
GF.dump();
- GCOVOptions Options(AllBlocks, BranchInfo, BranchCount, UncondBranch);
+ GCOVOptions Options(AllBlocks, BranchInfo, BranchCount, FuncCoverage,
+ UncondBranch);
FileInfo FI(Options);
GF.collectLineCounts(FI);
FI.print(InputGCNO, InputGCDA);