//
//===----------------------------------------------------------------------===//
-#include "llvm/Support/Debug.h"
#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;
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
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)
+ return;
+
for (SmallVectorImpl<GCOVBlock *>::iterator I = Blocks.begin(),
E = Blocks.end(); I != E; ++I)
(*I)->collectLineCounts(FI);
return OS;
}
+/// Convert a path to a gcov filename. If PreservePaths is true, this
+/// translates "/" to "#", ".." to "^", and drops ".", to match gcov.
+static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) {
+ if (!PreservePaths)
+ return (sys::path::filename(Filename) + ".gcov").str();
+
+ // This behaviour is defined by gcov in terms of text replacements, so it's
+ // not likely to do anything useful on filesystems with different textual
+ // conventions.
+ 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 == '.') {
+ // ".", the current directory, is skipped.
+ } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') {
+ // "..", the parent directory, is replaced with "^".
+ Result.append("^#");
+ } else {
+ if (S < I)
+ // Leave other components intact,
+ Result.append(S, I);
+ // And separate with "#".
+ Result.push_back('#');
+ }
+ S = I + 1;
+ }
+
+ if (S < I)
+ Result.append(S, I);
+ Result.append(".gcov");
+ return Result.str();
+}
+
/// 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();
- OwningPtr<MemoryBuffer> Buff;
+ std::unique_ptr<MemoryBuffer> Buff;
if (error_code ec = MemoryBuffer::getFileOrSTDIN(Filename, Buff)) {
errs() << Filename << ": " << ec.message() << "\n";
return;
}
StringRef AllLines = Buff->getBuffer();
- std::string CovFilename = Filename.str() + ".gcov";
+ std::string CoveragePath = mangleCoveragePath(Filename,
+ Options.PreservePaths);
std::string ErrorInfo;
- raw_fd_ostream OS(CovFilename.c_str(), ErrorInfo);
+ raw_fd_ostream OS(CoveragePath.c_str(), ErrorInfo, sys::fs::F_Text);
if (!ErrorInfo.empty())
errs() << ErrorInfo << "\n";
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);
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) {
// 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";
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(std::make_pair(CoveragePath, FileCoverage));
}
+
+ // FIXME: There is no way to detect calls given current instrumentation.
+ if (Options.FuncCoverage)
+ printFuncCoverage();
+ printFileCoverage();
}
/// printFunctionSummary - Print function and block summary.
/// 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();
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(),
<< formatBranchInfo(Options, Count, Count) << "\n";
}
-/// printFileCoverage - Print per-file coverage info.
-void FileInfo::printFileCoverage(StringRef Filename,
- GCOVCoverage &Coverage) const {
- outs() << "File '" << Filename << "'\n";
- outs() << format("Lines executed:%.2lf%% of %u\n",
+// printCoverage - Print generic coverage info used by both printFuncCoverage
+// and printFileCoverage.
+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:%.2lf%% of %u\n",
+ outs() << format("Branches executed:%.2f%% of %u\n",
double(Coverage.BranchesExec)*100/Coverage.Branches,
Coverage.Branches);
- outs() << format("Taken at least once:%.2lf%% of %u\n",
+ outs() << format("Taken at least once:%.2f%% of %u\n",
double(Coverage.BranchesTaken)*100/Coverage.Branches,
Coverage.Branches);
} else {
}
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 (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";
+ }
+}
+
+// printFileCoverage - Print per-file coverage info.
+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);
+ outs() << Coverage.Name << ":creating '" << Filename << "'\n\n";
+ }
}