diff --git a/llvm/include/llvm/ProfileData/GCOV.h b/llvm/include/llvm/ProfileData/GCOV.h index d5dc88e3aed1d5..294782c2c94d20 100644 --- a/llvm/include/llvm/ProfileData/GCOV.h +++ b/llvm/include/llvm/ProfileData/GCOV.h @@ -192,6 +192,9 @@ class GCOVFile { void dump() const; void collectLineCounts(FileInfo &FI); + std::vector filenames; + StringMap filenameToIdx; + private: bool GCNOInitialized = false; GCOV::GCOVVersion Version; @@ -201,6 +204,11 @@ class GCOVFile { std::map IdentToFunction; uint32_t RunCount = 0; uint32_t ProgramCount = 0; + + using iterator = pointee_iterator< + SmallVectorImpl>::const_iterator>; + iterator begin() const { return iterator(Functions.begin()); } + iterator end() const { return iterator(Functions.end()); } }; struct GCOVArc { @@ -220,10 +228,10 @@ class GCOVFunction { using BlockIterator = pointee_iterator< SmallVectorImpl>::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; @@ -238,6 +246,7 @@ class GCOVFunction { void dump() const; void collectLineCounts(FileInfo &FI); + GCOVFile &file; uint32_t ident = 0; uint32_t linenoChecksum; uint32_t cfgChecksum = 0; @@ -247,7 +256,7 @@ class GCOVFunction { uint32_t endColumn = 0; uint8_t artificial = 0; StringRef Name; - StringRef Filename; + unsigned srcIdx; SmallVector, 0> Blocks; SmallVector, 0> arcs, treeArcs; }; @@ -312,6 +321,28 @@ class GCOVBlock { SmallVector 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 functions; + GCOVCoverage coverage; + SourceInfo(StringRef filename) : filename(filename) {} +}; + class FileInfo { protected: // It is unlikely--but possible--for multiple functions to be on the same @@ -332,20 +363,8 @@ class FileInfo { 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) { @@ -364,7 +383,7 @@ class FileInfo { 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); @@ -386,11 +405,10 @@ class FileInfo { uint32_t RunCount = 0; uint32_t ProgramCount = 0; - using FileCoverageList = SmallVector, 4>; using FuncCoverageMap = MapVector; - FileCoverageList FileCoverages; FuncCoverageMap FuncCoverages; + std::vector sources; }; } // end namespace llvm diff --git a/llvm/lib/ProfileData/GCOV.cpp b/llvm/lib/ProfileData/GCOV.cpp index 2e0d32074d3c0d..a9dd53341b0105 100644 --- a/llvm/lib/ProfileData/GCOV.cpp +++ b/llvm/lib/ProfileData/GCOV.cpp @@ -68,18 +68,23 @@ bool GCOVFile::readGCNO(GCOVBuffer &buf) { 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) { @@ -224,8 +229,8 @@ bool GCOVFile::readGCDA(GCOVBuffer &buf) { } 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) @@ -235,16 +240,23 @@ LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } /// 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 { @@ -258,7 +270,7 @@ uint64_t GCOVFunction::getExitCount() 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); @@ -279,7 +291,7 @@ void GCOVFunction::collectLineCounts(FileInfo &FI) { for (const auto &Block : Blocks) Block->collectLineCounts(FI); - FI.addFunctionLine(Filename, startLine, this); + FI.addFunctionLine(getFilename(), startLine, this); } //===----------------------------------------------------------------------===// @@ -596,8 +608,7 @@ FileInfo::openCoveragePath(StringRef CoveragePath) { /// 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 Filenames; for (const auto &LI : LineInfo) Filenames.push_back(LI.first()); @@ -619,7 +630,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, 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]; @@ -707,7 +718,9 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, } } } - 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) { @@ -816,13 +829,12 @@ void FileInfo::printFuncCoverage(raw_ostream &OS) const { // 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"; } } diff --git a/llvm/test/tools/llvm-cov/llvm-cov.test b/llvm/test/tools/llvm-cov/llvm-cov.test index 79e51b453c2c89..4e9ef083c611a7 100644 --- a/llvm/test/tools/llvm-cov/llvm-cov.test +++ b/llvm/test/tools/llvm-cov/llvm-cov.test @@ -131,18 +131,18 @@ RUN: FileCheck %s --check-prefixes=H,H-A,H-B --match-full-lines --strict-whitesp 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 @@ -158,13 +158,13 @@ H-C: unconditional 0 taken 1 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 diff --git a/llvm/tools/llvm-cov/gcov.cpp b/llvm/tools/llvm-cov/gcov.cpp index bd3b6bef9e7cf8..3ebf6a654bd3aa 100644 --- a/llvm/tools/llvm-cov/gcov.cpp +++ b/llvm/tools/llvm-cov/gcov.cpp @@ -77,7 +77,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, 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[]) {