diff --git a/compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp b/compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp new file mode 100644 index 0000000000000..3849a7f57aa43 --- /dev/null +++ b/compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp @@ -0,0 +1,78 @@ +// Test debug info correlate with clang coverage. + +// Test the case when there is no __llvm_prf_names in the binary. +// RUN: %clang_profgen -o %t.normal -fcoverage-mapping %S/../Inputs/instrprof-debug-info-correlate-main.cpp %S/../Inputs/instrprof-debug-info-correlate-foo.cpp +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal +// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw + +// RUN: %clang_profgen -o %t -g -mllvm --debug-info-correlate -fcoverage-mapping %S/../Inputs/instrprof-debug-info-correlate-main.cpp %S/../Inputs/instrprof-debug-info-correlate-foo.cpp +// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t +// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t.dSYM %t.proflite + +// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata) + +// RUN: llvm-cov report --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NONAME + +// Test debug info correlate with clang coverage (online merging). + +// RUN: env LLVM_PROFILE_FILE=%t-1.profraw %run %t.normal +// RUN: env LLVM_PROFILE_FILE=%t-2.profraw %run %t.normal +// RUN: llvm-profdata merge -o %t.normal.profdata %t-1.profraw %t-2.profraw + +// RUN: rm -rf %t.profdir && mkdir %t.profdir +// RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.proflite %run %t +// RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.proflite %run %t +// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t.dSYM %t.profdir + +// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata) + +// RUN: llvm-cov report --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NONAME +// RUN: llvm-cov report --instr-profile=%t.normal.profdata %t | FileCheck %s -check-prefix=NONAME + +// NONAME: Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover Branches Missed Branches Cover +// NONAME-NEXT: -- +// NONAME-NEXT: instrprof-debug-info-correlate-bar.h 3 1 66.67% 1 0 100.00% 5 1 80.00% 2 1 50.00% +// NONAME-NEXT: instrprof-debug-info-correlate-foo.cpp 5 2 60.00% 2 1 50.00% 6 2 66.67% 2 1 50.00% +// NONAME-NEXT: instrprof-debug-info-correlate-main.cpp 4 0 100.00% 1 0 100.00% 5 0 100.00% 2 0 100.00% +// NONAME-NEXT: -- +// NONAME-NEXT: TOTAL 12 3 75.00% 4 1 75.00% 16 3 81.25% 6 2 66.67% + +// Test the case when there is __llvm_prf_names in the binary (those are names of uninstrumented functions). +// RUN: %clang_profgen -o %t.normal -fcoverage-mapping -mllvm -enable-name-compression=false %s +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal +// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw + +// RUN: %clang_profgen -o %t -g -mllvm --debug-info-correlate -fcoverage-mapping -mllvm -enable-name-compression=false %s +// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t +// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t.dSYM %t.proflite + +// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata) + +// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t --debug-info=%t.dSYM | FileCheck %s -check-prefix=NAME + +// Test debug info correlate with clang coverage (online merging). + +// RUN: env LLVM_PROFILE_FILE=%t-1.profraw %run %t.normal +// RUN: env LLVM_PROFILE_FILE=%t-2.profraw %run %t.normal +// RUN: llvm-profdata merge -o %t.normal.profdata %t-1.profraw %t-2.profraw + +// RUN: rm -rf %t.profdir && mkdir %t.profdir +// RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.proflite %run %t +// RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.proflite %run %t +// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t.dSYM %t.profdir + +// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata) + +// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t --debug-info=%t.dSYM | FileCheck %s -check-prefix=NAME +// NAME: _Z9used_funcv +// NAME: main +// NAME: _ZN1A11unused_funcEv + +struct A { + void unused_func() {} +}; +void used_func() {} +int main() { + used_func(); + return 0; +} diff --git a/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp b/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp index 06901de4a2429..d44e258ce3a98 100644 --- a/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp +++ b/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp @@ -48,7 +48,7 @@ // RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata) -// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NAME +// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t --debug-info=%t | FileCheck %s -check-prefix=NAME // Test debug info correlate with clang coverage (online merging). @@ -63,7 +63,7 @@ // RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata) -// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NAME +// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t --debug-info=%t | FileCheck %s -check-prefix=NAME // NAME: _Z9used_funcv // NAME: main // NAME: _ZN1A11unused_funcEv diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h index b407fe277c543..9a12e4e6f4641 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -594,6 +594,7 @@ class CoverageMapping { // Load coverage records from file. static Error loadFromFile(StringRef Filename, StringRef Arch, StringRef CompilationDir, + StringRef DebugInfoFilename, IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage, bool &DataFound, SmallVectorImpl *FoundBinaryIDs = nullptr); @@ -623,8 +624,8 @@ class CoverageMapping { /// Ignores non-instrumented object files unless all are not instrumented. static Expected> load(ArrayRef ObjectFilenames, StringRef ProfileFilename, - vfs::FileSystem &FS, ArrayRef Arches = std::nullopt, - StringRef CompilationDir = "", + vfs::FileSystem &FS, StringRef DebugInfoFilename = "", + ArrayRef Arches = std::nullopt, StringRef CompilationDir = "", const object::BuildIDFetcher *BIDFetcher = nullptr, bool CheckBinaryIDs = false); diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h index 41935ae6bb398..82395595eb8f8 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h @@ -203,10 +203,9 @@ class BinaryCoverageReader : public CoverageMappingReader { BinaryCoverageReader &operator=(const BinaryCoverageReader &) = delete; static Expected>> - create(MemoryBufferRef ObjectBuffer, - StringRef Arch, + create(MemoryBufferRef ObjectBuffer, StringRef Arch, SmallVectorImpl> &ObjectFileBuffers, - InstrProfSymtab& IndexedProfSymTab, + StringRef DebugInfoFilename, InstrProfSymtab &ProfSymTab, StringRef CompilationDir = "", SmallVectorImpl *BinaryIDs = nullptr); diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index a99785ee0df54..94bcaa98c9500 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -349,8 +349,9 @@ static Error handleMaybeNoDataFoundError(Error E) { Error CoverageMapping::loadFromFile( StringRef Filename, StringRef Arch, StringRef CompilationDir, - IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage, - bool &DataFound, SmallVectorImpl *FoundBinaryIDs) { + StringRef DebugInfoFilename, IndexedInstrProfReader &ProfileReader, + CoverageMapping &Coverage, bool &DataFound, + SmallVectorImpl *FoundBinaryIDs) { auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN( Filename, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (std::error_code EC = CovMappingBufOrErr.getError()) @@ -362,7 +363,7 @@ Error CoverageMapping::loadFromFile( SmallVector BinaryIDs; auto CoverageReadersOrErr = BinaryCoverageReader::create( - CovMappingBufRef, Arch, Buffers, ProfSymTab, + CovMappingBufRef, Arch, Buffers, DebugInfoFilename, ProfSymTab, CompilationDir, FoundBinaryIDs ? &BinaryIDs : nullptr); if (Error E = CoverageReadersOrErr.takeError()) { E = handleMaybeNoDataFoundError(std::move(E)); @@ -388,7 +389,8 @@ Error CoverageMapping::loadFromFile( Expected> CoverageMapping::load( ArrayRef ObjectFilenames, StringRef ProfileFilename, - vfs::FileSystem &FS, ArrayRef Arches, StringRef CompilationDir, + vfs::FileSystem &FS, StringRef DebugInfoFilename, + ArrayRef Arches, StringRef CompilationDir, const object::BuildIDFetcher *BIDFetcher, bool CheckBinaryIDs) { auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename, FS); if (Error E = ProfileReaderOrErr.takeError()) @@ -409,7 +411,8 @@ Expected> CoverageMapping::load( for (const auto &File : llvm::enumerate(ObjectFilenames)) { if (Error E = loadFromFile(File.value(), GetArch(File.index()), CompilationDir, - *ProfileReader, *Coverage, DataFound, &FoundBinaryIDs)) + DebugInfoFilename, *ProfileReader, *Coverage, + DataFound, &FoundBinaryIDs)) return std::move(E); } @@ -436,8 +439,9 @@ 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, DebugInfoFilename, CompilationDir, + *ProfileReader, *Coverage, DataFound)) return std::move(E); } else if (CheckBinaryIDs) { return createFileError( diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp index b17caaf998073..962202238ba39 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -1014,7 +1014,7 @@ static Expected> lookupSections(ObjectFile &OF, Expected NameOrErr = Section.getName(); if (!NameOrErr) return NameOrErr.takeError(); - if (stripSuffix(*NameOrErr) == Name) + if (stripSuffix(*NameOrErr) == Name && Section.getSize() != 0) Sections.push_back(Section); } if (Sections.empty()) @@ -1022,10 +1022,10 @@ static Expected> lookupSections(ObjectFile &OF, return Sections; } -static Error getProfileNamesFromDebugInfo(StringRef FileName, +static Error getProfileNamesFromDebugInfo(StringRef DebugInfoFilename, InstrProfSymtab &ProfileNames) { std::unique_ptr Correlator; - if (auto E = InstrProfCorrelator::get(FileName).moveInto(Correlator)) + if (auto E = InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator)) return E; if (auto E = Correlator->correlateCovUnusedFuncNames(0)) return E; @@ -1038,7 +1038,8 @@ static Error getProfileNamesFromDebugInfo(StringRef FileName, static Expected> loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, - InstrProfSymtab &ProfSymTab, StringRef CompilationDir = "", + StringRef DebugInfoFilename, InstrProfSymtab &ProfSymTab, + StringRef CompilationDir = "", object::BuildIDRef *BinaryID = nullptr) { std::unique_ptr OF; if (auto *Universal = dyn_cast(Bin.get())) { @@ -1077,9 +1078,9 @@ loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, lookupSections(*OF, getInstrProfSectionName(IPSK_name, ObjFormat, /*AddSegmentInfo=*/false)); if (auto E = NamesSection.takeError()) { - if (OF->hasDebugInfo()) { + if (!DebugInfoFilename.empty()) { if (auto E = - getProfileNamesFromDebugInfo(OF->getFileName(), ProfileNames)) + getProfileNamesFromDebugInfo(DebugInfoFilename, ProfileNames)) return make_error(coveragemap_error::malformed); } consumeError(std::move(E)); @@ -1176,8 +1177,8 @@ Expected>> BinaryCoverageReader::create( MemoryBufferRef ObjectBuffer, StringRef Arch, SmallVectorImpl> &ObjectFileBuffers, - InstrProfSymtab &ProfSymTab, StringRef CompilationDir, - SmallVectorImpl *BinaryIDs) { + StringRef DebugInfoFilename, InstrProfSymtab &ProfSymTab, + StringRef CompilationDir, SmallVectorImpl *BinaryIDs) { std::vector> Readers; if (ObjectBuffer.getBuffer().size() > sizeof(TestingFormatMagic)) { @@ -1221,8 +1222,8 @@ BinaryCoverageReader::create( } return BinaryCoverageReader::create( - ArchiveOrErr.get()->getMemoryBufferRef(), Arch, - ObjectFileBuffers, ProfSymTab, CompilationDir, BinaryIDs); + ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers, + DebugInfoFilename, ProfSymTab, CompilationDir, BinaryIDs); } } @@ -1235,8 +1236,8 @@ BinaryCoverageReader::create( return ChildBufOrErr.takeError(); auto ChildReadersOrErr = BinaryCoverageReader::create( - ChildBufOrErr.get(), Arch, ObjectFileBuffers, ProfSymTab, - CompilationDir, BinaryIDs); + ChildBufOrErr.get(), Arch, ObjectFileBuffers, DebugInfoFilename, + ProfSymTab, CompilationDir, BinaryIDs); if (!ChildReadersOrErr) return ChildReadersOrErr.takeError(); for (auto &Reader : ChildReadersOrErr.get()) @@ -1257,8 +1258,8 @@ BinaryCoverageReader::create( object::BuildIDRef BinaryID; auto ReaderOrErr = - loadBinaryFormat(std::move(Bin), Arch, ProfSymTab, CompilationDir, - BinaryIDs ? &BinaryID : nullptr); + loadBinaryFormat(std::move(Bin), Arch, DebugInfoFilename, ProfSymTab, + CompilationDir, BinaryIDs ? &BinaryID : nullptr); if (!ReaderOrErr) return ReaderOrErr.takeError(); Readers.push_back(std::move(ReaderOrErr.get())); diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index b5d763d8643cd..9a8bbfa291a67 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -444,8 +444,9 @@ std::unique_ptr CodeCoverageTool::load() { ObjectFilename); auto FS = vfs::getRealFileSystem(); auto CoverageOrErr = CoverageMapping::load( - ObjectFilenames, PGOFilename, *FS, CoverageArches, - ViewOpts.CompilationDirectory, BIDFetcher.get(), CheckBinaryIDs); + ObjectFilenames, PGOFilename, *FS, ViewOpts.DebugInfoFilename, + CoverageArches, ViewOpts.CompilationDirectory, BIDFetcher.get(), + CheckBinaryIDs); if (Error E = CoverageOrErr.takeError()) { error("failed to load coverage: " + toString(std::move(E))); return nullptr; @@ -774,6 +775,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { "check-binary-ids", cl::desc("Fail if an object couldn't be found for a " "binary ID in the profile")); + cl::opt DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc("Read and extract profile metadata from debug info and show " + "the functions it found.")); + auto commandLineParser = [&, this](int argc, const char **argv) -> int { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); ViewOpts.Debug = DebugDump; @@ -929,6 +935,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { ViewOpts.ExportSummaryOnly = SummaryOnly; ViewOpts.NumThreads = NumThreads; ViewOpts.CompilationDirectory = CompilationDirectory; + ViewOpts.DebugInfoFilename = DebugInfoFilename; return 0; }; diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h index eb852859e9cbe..f0e37848a4550 100644 --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -54,6 +54,7 @@ struct CoverageViewOptions { std::string CompilationDirectory; float HighCovWatermark; float LowCovWatermark; + std::string DebugInfoFilename; /// Change the output's stream color if the colors are enabled. ColoredRawOstream colored_ostream(raw_ostream &OS,