void dump() const;
void collectLineCounts(FileInfo &FI);
+ std::vector<std::string> filenames;
+ StringMap<unsigned> filenameToIdx;
+
private:
bool GCNOInitialized = false;
GCOV::GCOVVersion Version;
std::map<uint32_t, GCOVFunction *> IdentToFunction;
uint32_t RunCount = 0;
uint32_t ProgramCount = 0;
+
+ using iterator = pointee_iterator<
+ SmallVectorImpl<std::unique_ptr<GCOVFunction>>::const_iterator>;
+ iterator begin() const { return iterator(Functions.begin()); }
+ iterator end() const { return iterator(Functions.end()); }
};
struct GCOVArc {
using BlockIterator = pointee_iterator<
SmallVectorImpl<std::unique_ptr<GCOVBlock>>::const_iterator>;
- GCOVFunction(GCOVFile &P) {}
+ GCOVFunction(GCOVFile &file) : file(file) {}
StringRef getName() const { return Name; }
- StringRef getFilename() const { return Filename; }
+ StringRef getFilename() const;
size_t getNumBlocks() const { return Blocks.size(); }
uint64_t getEntryCount() const;
uint64_t getExitCount() const;
void dump() const;
void collectLineCounts(FileInfo &FI);
+ GCOVFile &file;
uint32_t ident = 0;
uint32_t linenoChecksum;
uint32_t cfgChecksum = 0;
uint32_t endColumn = 0;
uint8_t artificial = 0;
StringRef Name;
- StringRef Filename;
+ unsigned srcIdx;
SmallVector<std::unique_ptr<GCOVBlock>, 0> Blocks;
SmallVector<std::unique_ptr<GCOVArc>, 0> arcs, treeArcs;
};
SmallVector<uint32_t, 16> Lines;
};
+struct GCOVCoverage {
+ GCOVCoverage() = default;
+ GCOVCoverage(StringRef Name) : Name(Name) {}
+
+ StringRef Name;
+
+ uint32_t LogicalLines = 0;
+ uint32_t LinesExec = 0;
+
+ uint32_t Branches = 0;
+ uint32_t BranchesExec = 0;
+ uint32_t BranchesTaken = 0;
+};
+
+struct SourceInfo {
+ StringRef filename;
+ std::string name;
+ std::vector<GCOVFunction *> functions;
+ GCOVCoverage coverage;
+ SourceInfo(StringRef filename) : filename(filename) {}
+};
+
class FileInfo {
protected:
// It is unlikely--but possible--for multiple functions to be on the same
uint32_t LastLine = 0;
};
- struct GCOVCoverage {
- GCOVCoverage(StringRef Name) : Name(Name) {}
-
- StringRef Name;
-
- uint32_t LogicalLines = 0;
- uint32_t LinesExec = 0;
-
- uint32_t Branches = 0;
- uint32_t BranchesExec = 0;
- uint32_t BranchesTaken = 0;
- };
-
public:
+ friend class GCOVFile;
FileInfo(const GCOV::Options &Options) : Options(Options) {}
void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) {
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, GCOV::GCOVVersion Version);
+ StringRef GCDAFile, GCOVFile &file);
protected:
std::string getCoveragePath(StringRef Filename, StringRef MainFilename);
uint32_t RunCount = 0;
uint32_t ProgramCount = 0;
- using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>;
using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>;
- FileCoverageList FileCoverages;
FuncCoverageMap FuncCoverages;
+ std::vector<SourceInfo> sources;
};
} // end namespace llvm
if (Version >= GCOV::V407)
fn->cfgChecksum = buf.getWord();
buf.readString(fn->Name);
+ StringRef filename;
if (Version < GCOV::V800) {
- buf.readString(fn->Filename);
+ filename = buf.getString();
fn->startLine = buf.getWord();
} else {
fn->artificial = buf.getWord();
- fn->Filename = buf.getString();
+ filename = buf.getString();
fn->startLine = buf.getWord();
fn->startColumn = buf.getWord();
fn->endLine = buf.getWord();
if (Version >= GCOV::V900)
fn->endColumn = buf.getWord();
}
+ auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size());
+ if (r.second)
+ filenames.emplace_back(filename);
+ fn->srcIdx = r.first->second;
IdentToFunction[fn->ident] = fn;
} else if (tag == GCOV_TAG_BLOCKS && fn) {
if (Version < GCOV::V800) {
}
void GCOVFile::print(raw_ostream &OS) const {
- for (const auto &FPtr : Functions)
- FPtr->print(OS);
+ for (const GCOVFunction &f : *this)
+ f.print(OS);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
-void GCOVFile::collectLineCounts(FileInfo &FI) {
- for (const auto &FPtr : Functions)
- FPtr->collectLineCounts(FI);
- FI.setRunCount(RunCount);
- FI.setProgramCount(ProgramCount);
+void GCOVFile::collectLineCounts(FileInfo &fi) {
+ assert(fi.sources.empty());
+ for (StringRef filename : filenames)
+ fi.sources.emplace_back(filename);
+ for (GCOVFunction &f : *this) {
+ f.collectLineCounts(fi);
+ fi.sources[f.srcIdx].functions.push_back(&f);
+ }
+ fi.setRunCount(RunCount);
+ fi.setProgramCount(ProgramCount);
}
//===----------------------------------------------------------------------===//
// GCOVFunction implementation.
+StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; }
+
/// getEntryCount - Get the number of times the function was called by
/// retrieving the entry block's count.
uint64_t GCOVFunction::getEntryCount() const {
}
void GCOVFunction::print(raw_ostream &OS) const {
- OS << "===== " << Name << " (" << ident << ") @ " << Filename << ":"
+ OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":"
<< startLine << "\n";
for (const auto &Block : Blocks)
Block->print(OS);
for (const auto &Block : Blocks)
Block->collectLineCounts(FI);
- FI.addFunctionLine(Filename, startLine, this);
+ FI.addFunctionLine(getFilename(), startLine, this);
}
//===----------------------------------------------------------------------===//
/// print - Print source files with collected line count information.
void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
- StringRef GCNOFile, StringRef GCDAFile,
- GCOV::GCOVVersion Version) {
+ StringRef GCNOFile, StringRef GCDAFile, GCOVFile &file) {
SmallVector<StringRef, 4> Filenames;
for (const auto &LI : LineInfo)
Filenames.push_back(LI.first());
CovOS << " -: 0:Graph:" << GCNOFile << "\n";
CovOS << " -: 0:Data:" << GCDAFile << "\n";
CovOS << " -: 0:Runs:" << RunCount << "\n";
- if (Version < GCOV::V900)
+ if (file.getVersion() < GCOV::V900)
CovOS << " -: 0:Programs:" << ProgramCount << "\n";
const LineData &Line = LineInfo[Filename];
}
}
}
- FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage));
+ SourceInfo &source = sources[file.filenameToIdx.find(Filename)->second];
+ source.name = CoveragePath;
+ source.coverage = FileCoverage;
}
if (!Options.UseStdout) {
// printFileCoverage - Print per-file coverage info.
void FileInfo::printFileCoverage(raw_ostream &OS) const {
- for (const auto &FC : FileCoverages) {
- const std::string &Filename = FC.first;
- const GCOVCoverage &Coverage = FC.second;
+ for (const SourceInfo &source : sources) {
+ const GCOVCoverage &Coverage = source.coverage;
OS << "File '" << Coverage.Name << "'\n";
printCoverage(OS, Coverage);
if (!Options.NoOutput)
- OS << "Creating '" << Filename << "'\n";
+ OS << "Creating '" << source.name << "'\n";
OS << "\n";
}
}
OUT-FB-NEXT:Taken at least once:81.82% of 11
OUT-FB-NEXT:No calls
OUT-F-EMPTY:
- OUT:File './test.h'
- OUT-NEXT:Lines executed:100.00% of 1
- OUT-B-NEXT:No branches
- OUT-B-NEXT:No calls
-OUTFILE-NEXT:Creating 'test.h.gcov'
- OUT-EMPTY:
- OUT-NEXT:File 'test.cpp'
+ OUT:File 'test.cpp'
OUT-NEXT:Lines executed:81.40% of 43
OUT-B-NEXT:Branches executed:100.00% of 15
OUT-B-NEXT:Taken at least once:86.67% of 15
OUT-B-NEXT:No calls
OUTFILE-NEXT:Creating 'test.cpp.gcov'
+ OUT-EMPTY:
+ OUT-NEXT:File './test.h'
+ OUT-NEXT:Lines executed:100.00% of 1
+ OUT-B-NEXT:No branches
+ OUT-B-NEXT:No calls
+OUTFILE-NEXT:Creating 'test.h.gcov'
# Summarize unconditional branches too.
RUN: llvm-cov gcov test.c -a -b -u | FileCheck %s --check-prefixes=OUT,OUTFILE,OUT-B
RUN: llvm-cov gcov test.c -gcda=no_such_gcda_file | FileCheck %s --check-prefix=NO-GCDA
RUN: diff -aub test_no_gcda.cpp.gcov test.cpp.gcov
RUN: diff -aub test_no_gcda.h.gcov test.h.gcov
-NO-GCDA: File './test.h'
-NO-GCDA-NEXT: Lines executed:0.00% of 1
-NO-GCDA-NEXT: Creating 'test.h.gcov'
-NO-GCDA-EMPTY:
-NO-GCDA-NEXT: File 'test.cpp'
+NO-GCDA: File 'test.cpp'
NO-GCDA-NEXT: Lines executed:0.00% of 43
NO-GCDA-NEXT: Creating 'test.cpp.gcov'
+NO-GCDA-EMPTY:
+NO-GCDA-NEXT: File './test.h'
+NO-GCDA-NEXT: Lines executed:0.00% of 1
+NO-GCDA-NEXT: Creating 'test.h.gcov'
# Invalid gcno file.
RUN: llvm-cov gcov test.c -gcno=test_read_fail.gcno
FileInfo FI(Options);
GF.collectLineCounts(FI);
- FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF.getVersion());
+ FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF);
}
int gcovMain(int argc, const char *argv[]) {