Skip to content

[llvm-cov] Add --debug-info flag to llvm-cov to lookup debug info file. #66798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the binary already has debug info, we could use for correlation instead of passing the redundant --debug-info=%t, right? Of course this wouldn't work for MachO or if the binary is stripped of debug info. Is it possible (or at least easy to only require --debug-info if the provided binary doesn't already have it?


// Test debug info correlate with clang coverage (online merging).

Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<object::BuildID> *FoundBinaryIDs = nullptr);
Expand Down Expand Up @@ -623,8 +624,8 @@ class CoverageMapping {
/// Ignores non-instrumented object files unless all are not instrumented.
static Expected<std::unique_ptr<CoverageMapping>>
load(ArrayRef<StringRef> ObjectFilenames, StringRef ProfileFilename,
vfs::FileSystem &FS, ArrayRef<StringRef> Arches = std::nullopt,
StringRef CompilationDir = "",
vfs::FileSystem &FS, StringRef DebugInfoFilename = "",
ArrayRef<StringRef> Arches = std::nullopt, StringRef CompilationDir = "",
const object::BuildIDFetcher *BIDFetcher = nullptr,
bool CheckBinaryIDs = false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,9 @@ class BinaryCoverageReader : public CoverageMappingReader {
BinaryCoverageReader &operator=(const BinaryCoverageReader &) = delete;

static Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>>
create(MemoryBufferRef ObjectBuffer,
StringRef Arch,
create(MemoryBufferRef ObjectBuffer, StringRef Arch,
SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
InstrProfSymtab& IndexedProfSymTab,
StringRef DebugInfoFilename, InstrProfSymtab &ProfSymTab,
StringRef CompilationDir = "",
SmallVectorImpl<object::BuildIDRef> *BinaryIDs = nullptr);

Expand Down
18 changes: 11 additions & 7 deletions llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<object::BuildID> *FoundBinaryIDs) {
StringRef DebugInfoFilename, IndexedInstrProfReader &ProfileReader,
CoverageMapping &Coverage, bool &DataFound,
SmallVectorImpl<object::BuildID> *FoundBinaryIDs) {
auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(
Filename, /*IsText=*/false, /*RequiresNullTerminator=*/false);
if (std::error_code EC = CovMappingBufOrErr.getError())
Expand All @@ -362,7 +363,7 @@ Error CoverageMapping::loadFromFile(

SmallVector<object::BuildIDRef> 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));
Expand All @@ -388,7 +389,8 @@ Error CoverageMapping::loadFromFile(

Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
ArrayRef<StringRef> ObjectFilenames, StringRef ProfileFilename,
vfs::FileSystem &FS, ArrayRef<StringRef> Arches, StringRef CompilationDir,
vfs::FileSystem &FS, StringRef DebugInfoFilename,
ArrayRef<StringRef> Arches, StringRef CompilationDir,
const object::BuildIDFetcher *BIDFetcher, bool CheckBinaryIDs) {
auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename, FS);
if (Error E = ProfileReaderOrErr.takeError())
Expand All @@ -409,7 +411,8 @@ Expected<std::unique_ptr<CoverageMapping>> 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);
}

Expand All @@ -436,8 +439,9 @@ Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
if (PathOpt) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BIDFetcher->fetch(BinaryID); should return the unstripped file with debug info using either local filesystem or debuginfod, see

std::optional<std::string> BuildIDFetcher::fetch(BuildIDRef BuildID) const {

DebuginfodFetcher::fetch(ArrayRef<uint8_t> BuildID) const {

If PathOpt contains value and DebugInfoFilename is an empty string, we should be using *PathOpt as the debug info filename.

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(
Expand Down
29 changes: 15 additions & 14 deletions llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1014,18 +1014,18 @@ static Expected<std::vector<SectionRef>> lookupSections(ObjectFile &OF,
Expected<StringRef> 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())
return make_error<CoverageMapError>(coveragemap_error::no_data_found);
return Sections;
}

static Error getProfileNamesFromDebugInfo(StringRef FileName,
static Error getProfileNamesFromDebugInfo(StringRef DebugInfoFilename,
InstrProfSymtab &ProfileNames) {
std::unique_ptr<InstrProfCorrelator> 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;
Expand All @@ -1038,7 +1038,8 @@ static Error getProfileNamesFromDebugInfo(StringRef FileName,

static Expected<std::unique_ptr<BinaryCoverageReader>>
loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
InstrProfSymtab &ProfSymTab, StringRef CompilationDir = "",
StringRef DebugInfoFilename, InstrProfSymtab &ProfSymTab,
StringRef CompilationDir = "",
object::BuildIDRef *BinaryID = nullptr) {
std::unique_ptr<ObjectFile> OF;
if (auto *Universal = dyn_cast<MachOUniversalBinary>(Bin.get())) {
Expand Down Expand Up @@ -1077,9 +1078,9 @@ loadBinaryFormat(std::unique_ptr<Binary> 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<CoverageMapError>(coveragemap_error::malformed);
}
consumeError(std::move(E));
Expand Down Expand Up @@ -1176,8 +1177,8 @@ Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>>
BinaryCoverageReader::create(
MemoryBufferRef ObjectBuffer, StringRef Arch,
SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
InstrProfSymtab &ProfSymTab, StringRef CompilationDir,
SmallVectorImpl<object::BuildIDRef> *BinaryIDs) {
StringRef DebugInfoFilename, InstrProfSymtab &ProfSymTab,
StringRef CompilationDir, SmallVectorImpl<object::BuildIDRef> *BinaryIDs) {
std::vector<std::unique_ptr<BinaryCoverageReader>> Readers;

if (ObjectBuffer.getBuffer().size() > sizeof(TestingFormatMagic)) {
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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())
Expand All @@ -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()));
Expand Down
11 changes: 9 additions & 2 deletions llvm/tools/llvm-cov/CodeCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,9 @@ std::unique_ptr<CoverageMapping> 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;
Expand Down Expand Up @@ -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<std::string> DebugInfoFilename(
"debug-info", cl::init(""),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"debug-info", cl::init(""),
"debug-info",

NIT: The default value is already the empty string

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;
Expand Down Expand Up @@ -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;
};
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-cov/CoverageViewOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down