diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst index 968f3c452f558..f4db60cf06fa7 100644 --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -380,6 +380,11 @@ OPTIONS Fail if an object file cannot be found for a binary ID present in the profile, neither on the command line nor via binary ID lookup. +.. option:: -empty-profile + + Display the baseline coverage of the binaries with all zero execution counts. + Mutually exclusive with -instr-profile. + .. program:: llvm-cov report .. _llvm-cov-report: @@ -470,6 +475,11 @@ OPTIONS Fail if an object file cannot be found for a binary ID present in the profile, neither on the command line nor via binary ID lookup. +.. option:: -empty-profile + + Display the baseline coverage of the binaries with all zero execution counts. + Mutually exclusive with -instr-profile. + .. program:: llvm-cov export .. _llvm-cov-export: @@ -562,6 +572,11 @@ OPTIONS Fail if an object file cannot be found for a binary ID present in the profile, neither on the command line nor via binary ID lookup. +.. option:: -empty-profile + + Export the baseline coverage of the binaries with all zero execution counts. + Mutually exclusive with -instr-profile. + CONVERT-FOR-TESTING COMMAND --------------------------- diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h index e62ce5e3d8fa6..d1230b0ba7c58 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -991,18 +991,23 @@ class CoverageMapping { // Load coverage records from readers. static Error loadFromReaders( ArrayRef> CoverageReaders, - IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage); + std::optional> + &ProfileReader, + CoverageMapping &Coverage); // Load coverage records from file. static Error loadFromFile(StringRef Filename, StringRef Arch, StringRef CompilationDir, - IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage, - bool &DataFound, + std::optional> + &ProfileReader, + CoverageMapping &Coverage, bool &DataFound, SmallVectorImpl *FoundBinaryIDs = nullptr); /// Add a function record corresponding to \p Record. - Error loadFunctionRecord(const CoverageMappingRecord &Record, - IndexedInstrProfReader &ProfileReader); + Error loadFunctionRecord( + const CoverageMappingRecord &Record, + const std::optional> + &ProfileReader); /// Look up the indices for function records which are at least partially /// defined in the specified file. This is guaranteed to return a superset of @@ -1018,15 +1023,16 @@ class CoverageMapping { /// Load the coverage mapping using the given readers. LLVM_ABI static Expected> load(ArrayRef> CoverageReaders, - IndexedInstrProfReader &ProfileReader); + std::optional> + &ProfileReader); /// Load the coverage mapping from the given object files and profile. If /// \p Arches is non-empty, it must specify an architecture for each object. /// Ignores non-instrumented object files unless all are not instrumented. LLVM_ABI static Expected> - load(ArrayRef ObjectFilenames, StringRef ProfileFilename, - vfs::FileSystem &FS, ArrayRef Arches = {}, - StringRef CompilationDir = "", + load(ArrayRef ObjectFilenames, + std::optional ProfileFilename, vfs::FileSystem &FS, + ArrayRef Arches = {}, StringRef CompilationDir = "", const object::BuildIDFetcher *BIDFetcher = nullptr, bool CheckBinaryIDs = false); diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index dd74eb054a34c..429ec5c19f1f8 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -823,7 +823,8 @@ class MCDCDecisionRecorder { Error CoverageMapping::loadFunctionRecord( const CoverageMappingRecord &Record, - IndexedInstrProfReader &ProfileReader) { + const std::optional> + &ProfileReader) { StringRef OrigFuncName = Record.FunctionName; if (OrigFuncName.empty()) return make_error(coveragemap_error::malformed, @@ -837,35 +838,44 @@ Error CoverageMapping::loadFunctionRecord( CounterMappingContext Ctx(Record.Expressions); std::vector Counts; - if (Error E = ProfileReader.getFunctionCounts(Record.FunctionName, - Record.FunctionHash, Counts)) { - instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E))); - if (IPE == instrprof_error::hash_mismatch) { - FuncHashMismatches.emplace_back(std::string(Record.FunctionName), - Record.FunctionHash); - return Error::success(); + if (ProfileReader) { + if (Error E = ProfileReader.value().get().getFunctionCounts( + Record.FunctionName, Record.FunctionHash, Counts)) { + instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E))); + if (IPE == instrprof_error::hash_mismatch) { + FuncHashMismatches.emplace_back(std::string(Record.FunctionName), + Record.FunctionHash); + return Error::success(); + } + if (IPE != instrprof_error::unknown_function) + return make_error(IPE); + Counts.assign(getMaxCounterID(Ctx, Record) + 1, 0); } - if (IPE != instrprof_error::unknown_function) - return make_error(IPE); + } else { Counts.assign(getMaxCounterID(Ctx, Record) + 1, 0); } Ctx.setCounts(Counts); bool IsVersion11 = - ProfileReader.getVersion() < IndexedInstrProf::ProfVersion::Version12; + ProfileReader && ProfileReader.value().get().getVersion() < + IndexedInstrProf::ProfVersion::Version12; BitVector Bitmap; - if (Error E = ProfileReader.getFunctionBitmap(Record.FunctionName, - Record.FunctionHash, Bitmap)) { - instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E))); - if (IPE == instrprof_error::hash_mismatch) { - FuncHashMismatches.emplace_back(std::string(Record.FunctionName), - Record.FunctionHash); - return Error::success(); + if (ProfileReader) { + if (Error E = ProfileReader.value().get().getFunctionBitmap( + Record.FunctionName, Record.FunctionHash, Bitmap)) { + instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E))); + if (IPE == instrprof_error::hash_mismatch) { + FuncHashMismatches.emplace_back(std::string(Record.FunctionName), + Record.FunctionHash); + return Error::success(); + } + if (IPE != instrprof_error::unknown_function) + return make_error(IPE); + Bitmap = BitVector(getMaxBitmapSize(Record, IsVersion11)); } - if (IPE != instrprof_error::unknown_function) - return make_error(IPE); - Bitmap = BitVector(getMaxBitmapSize(Record, IsVersion11)); + } else { + Bitmap = BitVector(getMaxBitmapSize(Record, false)); } Ctx.setBitmap(std::move(Bitmap)); @@ -959,10 +969,14 @@ Error CoverageMapping::loadFunctionRecord( // of CoverageMappingReader instances. Error CoverageMapping::loadFromReaders( ArrayRef> CoverageReaders, - IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage) { - assert(!Coverage.SingleByteCoverage || - *Coverage.SingleByteCoverage == ProfileReader.hasSingleByteCoverage()); - Coverage.SingleByteCoverage = ProfileReader.hasSingleByteCoverage(); + std::optional> + &ProfileReader, + CoverageMapping &Coverage) { + assert(!Coverage.SingleByteCoverage || !ProfileReader || + *Coverage.SingleByteCoverage == + ProfileReader.value().get().hasSingleByteCoverage()); + Coverage.SingleByteCoverage = + !ProfileReader || ProfileReader.value().get().hasSingleByteCoverage(); for (const auto &CoverageReader : CoverageReaders) { for (auto RecordOrErr : *CoverageReader) { if (Error E = RecordOrErr.takeError()) @@ -977,7 +991,8 @@ Error CoverageMapping::loadFromReaders( Expected> CoverageMapping::load( ArrayRef> CoverageReaders, - IndexedInstrProfReader &ProfileReader) { + std::optional> + &ProfileReader) { auto Coverage = std::unique_ptr(new CoverageMapping()); if (Error E = loadFromReaders(CoverageReaders, ProfileReader, *Coverage)) return std::move(E); @@ -986,18 +1001,19 @@ Expected> CoverageMapping::load( // If E is a no_data_found error, returns success. Otherwise returns E. static Error handleMaybeNoDataFoundError(Error E) { - return handleErrors( - std::move(E), [](const CoverageMapError &CME) { - if (CME.get() == coveragemap_error::no_data_found) - return static_cast(Error::success()); - return make_error(CME.get(), CME.getMessage()); - }); + return handleErrors(std::move(E), [](const CoverageMapError &CME) { + if (CME.get() == coveragemap_error::no_data_found) + return static_cast(Error::success()); + return make_error(CME.get(), CME.getMessage()); + }); } Error CoverageMapping::loadFromFile( StringRef Filename, StringRef Arch, StringRef CompilationDir, - IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage, - bool &DataFound, SmallVectorImpl *FoundBinaryIDs) { + std::optional> + &ProfileReader, + CoverageMapping &Coverage, bool &DataFound, + SmallVectorImpl *FoundBinaryIDs) { auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN( Filename, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (std::error_code EC = CovMappingBufOrErr.getError()) @@ -1033,13 +1049,23 @@ Error CoverageMapping::loadFromFile( } Expected> CoverageMapping::load( - ArrayRef ObjectFilenames, StringRef ProfileFilename, - vfs::FileSystem &FS, ArrayRef Arches, StringRef CompilationDir, + ArrayRef ObjectFilenames, + std::optional ProfileFilename, vfs::FileSystem &FS, + ArrayRef Arches, StringRef CompilationDir, const object::BuildIDFetcher *BIDFetcher, bool CheckBinaryIDs) { - auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename, FS); - if (Error E = ProfileReaderOrErr.takeError()) - return createFileError(ProfileFilename, std::move(E)); - auto ProfileReader = std::move(ProfileReaderOrErr.get()); + std::unique_ptr ProfileReader; + if (ProfileFilename) { + auto ProfileReaderOrErr = + IndexedInstrProfReader::create(ProfileFilename.value(), FS); + if (Error E = ProfileReaderOrErr.takeError()) + return createFileError(ProfileFilename.value(), std::move(E)); + ProfileReader = std::move(ProfileReaderOrErr.get()); + } + auto ProfileReaderRef = + ProfileReader + ? std::optional>( + *ProfileReader) + : std::nullopt; auto Coverage = std::unique_ptr(new CoverageMapping()); bool DataFound = false; @@ -1053,16 +1079,17 @@ Expected> CoverageMapping::load( SmallVector FoundBinaryIDs; for (const auto &File : llvm::enumerate(ObjectFilenames)) { - if (Error E = - loadFromFile(File.value(), GetArch(File.index()), CompilationDir, - *ProfileReader, *Coverage, DataFound, &FoundBinaryIDs)) + if (Error E = loadFromFile(File.value(), GetArch(File.index()), + CompilationDir, ProfileReaderRef, *Coverage, + DataFound, &FoundBinaryIDs)) return std::move(E); } if (BIDFetcher) { std::vector ProfileBinaryIDs; - if (Error E = ProfileReader->readBinaryIds(ProfileBinaryIDs)) - return createFileError(ProfileFilename, std::move(E)); + if (ProfileReader) + if (Error E = ProfileReader->readBinaryIds(ProfileBinaryIDs)) + return createFileError(ProfileFilename.value(), std::move(E)); SmallVector BinaryIDsToFetch; if (!ProfileBinaryIDs.empty()) { @@ -1082,12 +1109,12 @@ Expected> CoverageMapping::load( if (PathOpt) { std::string Path = std::move(*PathOpt); StringRef Arch = Arches.size() == 1 ? Arches.front() : StringRef(); - if (Error E = loadFromFile(Path, Arch, CompilationDir, *ProfileReader, - *Coverage, DataFound)) + if (Error E = loadFromFile(Path, Arch, CompilationDir, ProfileReaderRef, + *Coverage, DataFound)) return std::move(E); } else if (CheckBinaryIDs) { return createFileError( - ProfileFilename, + ProfileFilename.value(), createStringError(errc::no_such_file_or_directory, "Missing binary ID: " + llvm::toHex(BinaryID, /*LowerCase=*/true))); diff --git a/llvm/test/tools/llvm-cov/showLineExecutionCounts-lcov-baseline.test b/llvm/test/tools/llvm-cov/showLineExecutionCounts-lcov-baseline.test new file mode 100644 index 0000000000000..bce886bdf510b --- /dev/null +++ b/llvm/test/tools/llvm-cov/showLineExecutionCounts-lcov-baseline.test @@ -0,0 +1,37 @@ +// FULL: SF:{{.*}}showLineExecutionCounts.cpp +// FULL: FN:6,main +// FULL: FNDA:0,main +// FULL: FNF:1 +// FULL: FNH:0 +int main() { // FULL: DA:[[@LINE]],0 + int x = 0; // FULL: DA:[[@LINE]],0 + // FULL: DA:[[@LINE]],0 + if (x) { // FULL: DA:[[@LINE]],0 + x = 0; // FULL: DA:[[@LINE]],0 + } else { // FULL: DA:[[@LINE]],0 + x = 1; // FULL: DA:[[@LINE]],0 + } // FULL: DA:[[@LINE]],0 + // FULL: DA:[[@LINE]],0 + for (int i = 0; i < 100; ++i) { // FULL: DA:[[@LINE]],0 + x = 1; // FULL: DA:[[@LINE]],0 + } // FULL: DA:[[@LINE]],0 + // FULL: DA:[[@LINE]],0 + x = x < 10 ? x + 1 : x - 1; // FULL: DA:[[@LINE]],0 + x = x > 10 ? // FULL: DA:[[@LINE]],0 + x - 1: // FULL: DA:[[@LINE]],0 + x + 1; // FULL: DA:[[@LINE]],0 + // FULL: DA:[[@LINE]],0 + return 0; // FULL: DA:[[@LINE]],0 +} // FULL: DA:[[@LINE]],0 +// FULL: LF:20 +// FULL: LH:0 +// FULL: end_of_record +// RUN: llvm-cov export -format=lcov %S/Inputs/lineExecutionCounts.covmapping -empty-profile %s | FileCheck -check-prefixes=FULL %s + +// RUN: llvm-cov export -format=lcov -summary-only %S/Inputs/lineExecutionCounts.covmapping -empty-profile %s | FileCheck -check-prefixes=SUMMARYONLY %s +// SUMMARYONLY: SF:{{.*}}showLineExecutionCounts.cpp +// SUMMARYONLY: FNF:1 +// SUMMARYONLY: FNH:0 +// SUMMARYONLY: LF:20 +// SUMMARYONLY: LH:0 +// SUMMARYONLY: end_of_record diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index 1f2484cd4dda9..6c66858c4de8c 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -153,7 +153,7 @@ class CodeCoverageTool { bool HadSourceFiles = false; /// The path to the indexed profile. - std::string PGOFilename; + std::optional PGOFilename; /// A list of input source files. std::vector SourceFiles; @@ -455,10 +455,12 @@ static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { } std::unique_ptr CodeCoverageTool::load() { - for (StringRef ObjectFilename : ObjectFilenames) - if (modifiedTimeGT(ObjectFilename, PGOFilename)) - warning("profile data may be out of date - object is newer", - ObjectFilename); + if (PGOFilename) { + for (StringRef ObjectFilename : ObjectFilenames) + if (modifiedTimeGT(ObjectFilename, PGOFilename.value())) + warning("profile data may be out of date - object is newer", + ObjectFilename); + } auto FS = vfs::getRealFileSystem(); auto CoverageOrErr = CoverageMapping::load( ObjectFilenames, PGOFilename, *FS, CoverageArches, @@ -668,11 +670,16 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { "dump-collected-paths", cl::Optional, cl::Hidden, cl::desc("Show the collected paths to source files")); - cl::opt PGOFilename( - "instr-profile", cl::Required, cl::location(this->PGOFilename), + cl::opt PGOFilename( + "instr-profile", cl::Optional, cl::desc( "File with the profile data obtained after an instrumented run")); + cl::opt EmptyProfile( + "empty-profile", cl::Optional, + cl::desc("Use a synthetic profile with no data to generate " + "baseline coverage")); + cl::list Arches( "arch", cl::desc("architectures of the coverage mapping binaries")); @@ -805,6 +812,15 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { } this->CheckBinaryIDs = CheckBinaryIDs; + if (!PGOFilename.empty() == EmptyProfile) { + error( + "exactly one of -instr-profile and -empty-profile must be specified"); + return 1; + } + if (!PGOFilename.empty()) { + this->PGOFilename = std::make_optional(PGOFilename.getValue()); + } + if (!CovFilename.empty()) ObjectFilenames.emplace_back(CovFilename); for (const std::string &Filename : CovFilenames) @@ -1116,20 +1132,22 @@ int CodeCoverageTool::doShow(int argc, const char **argv, } } - sys::fs::file_status Status; - if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { - error("could not read profile data!" + EC.message(), PGOFilename); - return 1; - } + if (PGOFilename) { + sys::fs::file_status Status; + if (std::error_code EC = sys::fs::status(PGOFilename.value(), Status)) { + error("could not read profile data!" + EC.message(), PGOFilename.value()); + return 1; + } - if (ShowCreatedTime) { - auto ModifiedTime = Status.getLastModificationTime(); - std::string ModifiedTimeStr = to_string(ModifiedTime); - size_t found = ModifiedTimeStr.rfind(':'); - ViewOpts.CreatedTimeStr = - (found != std::string::npos) - ? "Created: " + ModifiedTimeStr.substr(0, found) - : "Created: " + ModifiedTimeStr; + if (ShowCreatedTime) { + auto ModifiedTime = Status.getLastModificationTime(); + std::string ModifiedTimeStr = to_string(ModifiedTime); + size_t found = ModifiedTimeStr.rfind(':'); + ViewOpts.CreatedTimeStr = + (found != std::string::npos) + ? "Created: " + ModifiedTimeStr.substr(0, found) + : "Created: " + ModifiedTimeStr; + } } auto Coverage = load(); @@ -1238,10 +1256,12 @@ int CodeCoverageTool::doReport(int argc, const char **argv, return 1; } - sys::fs::file_status Status; - if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { - error("could not read profile data!" + EC.message(), PGOFilename); - return 1; + if (PGOFilename) { + sys::fs::file_status Status; + if (std::error_code EC = sys::fs::status(PGOFilename.value(), Status)) { + error("could not read profile data!" + EC.message(), PGOFilename.value()); + return 1; + } } auto Coverage = load(); @@ -1303,10 +1323,12 @@ int CodeCoverageTool::doExport(int argc, const char **argv, return 1; } - sys::fs::file_status Status; - if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { - error("could not read profile data!" + EC.message(), PGOFilename); - return 1; + if (PGOFilename) { + sys::fs::file_status Status; + if (std::error_code EC = sys::fs::status(PGOFilename.value(), Status)) { + error("could not read profile data!" + EC.message(), PGOFilename.value()); + return 1; + } } auto Coverage = load(); diff --git a/llvm/unittests/ProfileData/CoverageMappingTest.cpp b/llvm/unittests/ProfileData/CoverageMappingTest.cpp index 46f881ecddb5f..ec81e5f274efa 100644 --- a/llvm/unittests/ProfileData/CoverageMappingTest.cpp +++ b/llvm/unittests/ProfileData/CoverageMappingTest.cpp @@ -277,7 +277,9 @@ struct CoverageMappingTest : ::testing::TestWithParam> { CoverageReaders.push_back( std::make_unique(Funcs)); } - return CoverageMapping::load(CoverageReaders, *ProfileReader); + auto ProfileReaderRef = std::make_optional( + std::reference_wrapper(*ProfileReader)); + return CoverageMapping::load(CoverageReaders, ProfileReaderRef); } Error loadCoverageMapping(bool EmitFilenames = true) {