diff --git a/compiler-rt/test/profile/Linux/binary-id-lookup.c b/compiler-rt/test/profile/Linux/binary-id-lookup.c --- a/compiler-rt/test/profile/Linux/binary-id-lookup.c +++ b/compiler-rt/test/profile/Linux/binary-id-lookup.c @@ -8,12 +8,20 @@ // RUN: cp %t/libfoo.so %t/.build-id/12/345678.debug // RUN: cp %t.main %t/.build-id/ab/cd1234.debug // RUN: llvm-profdata merge -o %t.profdata %t.profdir/default_*.profraw + // RUN: llvm-cov show -instr-profile %t.profdata -debug-file-directory %t | FileCheck %s // RUN: llvm-cov show -instr-profile %t.profdata %t/libfoo.so -sources %t/foo.c -object %t.main | FileCheck %s --check-prefix=FOO-ONLY // RUN: llvm-cov show -instr-profile %t.profdata -debug-file-directory %t -sources %t/foo.c | FileCheck %s --check-prefix=FOO-ONLY // RUN: llvm-cov show -instr-profile %t.profdata -debug-file-directory %t %t/libfoo.so -sources %t/foo.c | FileCheck %s --check-prefix=FOO-ONLY + +// RUN: rm %t/.build-id/ab/cd1234.debug +// RUN: llvm-cov show -instr-profile %t.profdata -debug-file-directory %t %t.main | FileCheck %s +// RUN: llvm-cov show -instr-profile %t.profdata -debug-file-directory %t | FileCheck %s --check-prefix=FOO-ONLY +// RUN: not llvm-cov show -instr-profile %t.profdata -debug-file-directory %t --check-binary-ids 2>&1 | FileCheck %s --check-prefix=MISSING-BINARY-ID -DFILENAME=%t.profdata + // RUN: echo "bad" > %t/.build-id/ab/cd1234.debug // RUN: llvm-cov show -instr-profile %t.profdata -debug-file-directory %t %t.main | FileCheck %s + // RUN: not llvm-cov show -instr-profile %t.profdata -debug-file-directory %t/empty 2>&1 | FileCheck %s --check-prefix=NODATA // CHECK: 1| 1|void foo(void) {} @@ -21,6 +29,7 @@ // CHECK: 3| 1|int main() { // FOO-ONLY: 1| 1|void foo(void) {} +// MISSING-BINARY-ID: error: Failed to load coverage: '[[FILENAME]]': Missing binary ID: abcd1234 // NODATA: error: Failed to load coverage: '': No coverage data found //--- foo.c diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -360,6 +360,11 @@ Provides local directories to search for objects corresponding to binary IDs in the profile (as with debuginfod). Defaults to system build ID directories. +.. option:: -check-binary-id + +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. + .. program:: llvm-cov report .. _llvm-cov-report: @@ -441,6 +446,11 @@ Provides a directory to search for objects corresponding to binary IDs in the profile. +.. option:: -check-binary-id + +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. + .. program:: llvm-cov export .. _llvm-cov-export: @@ -527,3 +537,8 @@ Provides a directory to search for objects corresponding to binary IDs in the profile. + +.. option:: -check-binary-id + +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. diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -622,7 +622,8 @@ load(ArrayRef ObjectFilenames, StringRef ProfileFilename, vfs::FileSystem &FS, ArrayRef Arches = std::nullopt, StringRef CompilationDir = "", - const object::BuildIDFetcher *BIDFetcher = nullptr); + const object::BuildIDFetcher *BIDFetcher = nullptr, + bool CheckBinaryIDs = false); /// The number of functions that couldn't have their profiles mapped. /// diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -15,7 +15,9 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/BuildID.h" #include "llvm/ProfileData/Coverage/CoverageMappingReader.h" @@ -382,11 +384,10 @@ return Error::success(); } -Expected> -CoverageMapping::load(ArrayRef ObjectFilenames, - StringRef ProfileFilename, vfs::FileSystem &FS, - ArrayRef Arches, StringRef CompilationDir, - const object::BuildIDFetcher *BIDFetcher) { +Expected> CoverageMapping::load( + ArrayRef ObjectFilenames, StringRef 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)); @@ -430,13 +431,19 @@ for (object::BuildIDRef BinaryID : BinaryIDsToFetch) { std::optional PathOpt = BIDFetcher->fetch(BinaryID); - if (!PathOpt) - continue; - std::string Path = std::move(*PathOpt); - StringRef Arch = Arches.size() == 1 ? Arches.front() : StringRef(); - if (Error E = loadFromFile(Path, Arch, CompilationDir, *ProfileReader, - *Coverage, DataFound)) - return std::move(E); + 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)) + return std::move(E); + } else if (CheckBinaryIDs) { + return createFileError( + ProfileFilename, + createStringError(errc::no_such_file_or_directory, + "Missing binary ID: " + + llvm::toHex(BinaryID, /*LowerCase=*/true))); + } } } diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -185,6 +185,8 @@ std::unique_ptr NameAllowlist; std::unique_ptr BIDFetcher; + + bool CheckBinaryIDs; }; } @@ -440,9 +442,9 @@ warning("profile data may be out of date - object is newer", ObjectFilename); auto FS = vfs::getRealFileSystem(); - auto CoverageOrErr = - CoverageMapping::load(ObjectFilenames, PGOFilename, *FS, CoverageArches, - ViewOpts.CompilationDirectory, BIDFetcher.get()); + auto CoverageOrErr = CoverageMapping::load( + ObjectFilenames, PGOFilename, *FS, CoverageArches, + ViewOpts.CompilationDirectory, BIDFetcher.get(), CheckBinaryIDs); if (Error E = CoverageOrErr.takeError()) { error("Failed to load coverage: " + toString(std::move(E))); return nullptr; @@ -761,6 +763,10 @@ "compilation-dir", cl::init(""), cl::desc("Directory used as a base for relative coverage mapping paths")); + cl::opt CheckBinaryIDs( + "check-binary-ids", cl::desc("Fail if an object couldn't be found for a " + "binary ID in the profile")); + auto commandLineParser = [&, this](int argc, const char **argv) -> int { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); ViewOpts.Debug = DebugDump; @@ -770,6 +776,7 @@ } else { BIDFetcher = std::make_unique(DebugFileDirectory); } + this->CheckBinaryIDs = CheckBinaryIDs; if (!CovFilename.empty()) ObjectFilenames.emplace_back(CovFilename);