diff options
-rw-r--r-- | include/llvm/Support/GCOV.h | 25 | ||||
-rw-r--r-- | lib/IR/GCOV.cpp | 96 | ||||
-rw-r--r-- | test/tools/llvm-cov/Inputs/test_-b_-f.output | 65 | ||||
-rw-r--r-- | test/tools/llvm-cov/Inputs/test_-f.output | 38 | ||||
-rw-r--r-- | test/tools/llvm-cov/llvm-cov.test | 4 | ||||
-rw-r--r-- | tools/llvm-cov/llvm-cov.cpp | 6 |
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); |