Skip to content

Commit

Permalink
[Coverage] Allow Clang coverage to be used with debug info correlation.
Browse files Browse the repository at this point in the history
Debug info correlation is an option in InstrProfiling pass, which is used by
both IR instrumentation and front-end instrumentation. So, Clang coverage can
also benefits the binary size saving from it.

Reviewed By: ellis

Differential Revision: https://reviews.llvm.org/D157913
  • Loading branch information
ZequanWu committed Sep 15, 2023
1 parent 79e96b2 commit 32db121
Show file tree
Hide file tree
Showing 12 changed files with 409 additions and 22 deletions.
20 changes: 20 additions & 0 deletions clang/lib/CodeGen/CoverageMappingGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
// is textually included.
#define COVMAP_V3

namespace llvm {
extern cl::opt<bool> DebugInfoCorrelate;
} // namespace llvm

static llvm::cl::opt<bool> EmptyLineCommentCoverage(
"emptyline-comment-coverage",
llvm::cl::desc("Emit emptylines and comment lines as skipped regions (only "
Expand Down Expand Up @@ -1821,6 +1825,22 @@ void CoverageMappingModuleGen::emit() {
llvm::GlobalValue::InternalLinkage, NamesArrVal,
llvm::getCoverageUnusedNamesVarName());
}
const StringRef VarName(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
llvm::Type *IntTy64 = llvm::Type::getInt64Ty(Ctx);
uint64_t ProfileVersion = INSTR_PROF_RAW_VERSION;
if (llvm::DebugInfoCorrelate)
ProfileVersion |= VARIANT_MASK_DBG_CORRELATE;
auto *VersionVariable = new llvm::GlobalVariable(
CGM.getModule(), llvm::Type::getInt64Ty(Ctx), true,
llvm::GlobalValue::WeakAnyLinkage,
llvm::Constant::getIntegerValue(IntTy64, llvm::APInt(64, ProfileVersion)),
VarName);
VersionVariable->setVisibility(llvm::GlobalValue::HiddenVisibility);
llvm::Triple TT(CGM.getModule().getTargetTriple());
if (TT.supportsCOMDAT()) {
VersionVariable->setLinkage(llvm::GlobalValue::ExternalLinkage);
VersionVariable->setComdat(CGM.getModule().getOrInsertComdat(VarName));
}
}

unsigned CoverageMappingModuleGen::getFileID(FileEntryRef File) {
Expand Down
9 changes: 9 additions & 0 deletions clang/test/CodeGen/coverage-profile-raw-version.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %clang_cc1 -debug-info-kind=standalone -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -debug-info-kind=standalone -mllvm -debug-info-correlate -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -o - %s | FileCheck %s --check-prefix=DEBUG_INFO

// CHECK: @__llvm_profile_raw_version = hidden constant i64 8, comdat
// DEBUG_INFO: @__llvm_profile_raw_version = hidden constant i64 576460752303423496, comdat

int main() {
return 0;
}
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 | 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 | 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;
}
78 changes: 78 additions & 0 deletions compiler-rt/test/profile/Linux/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 %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 %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 %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 | 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 %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 | 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 @@ -203,7 +203,8 @@ 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, IndexedInstrProfReader &ProfileReader,
StringRef Arch,
SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
StringRef CompilationDir = "",
SmallVectorImpl<object::BuildIDRef> *BinaryIDs = nullptr);
Expand Down
12 changes: 12 additions & 0 deletions llvm/include/llvm/ProfileData/InstrProfCorrelator.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class InstrProfCorrelator {
/// \param MaxWarnings the maximum number of warnings to emit (0 = no limit)
virtual Error correlateProfileData(int MaxWarnings) = 0;

virtual Error correlateCovUnusedFuncNames(int MaxWarnings) = 0;

/// Process debug info and dump the correlation data.
/// \param MaxWarnings the maximum number of warnings to emit (0 = no limit)
virtual Error dumpYaml(int MaxWarnings, raw_ostream &OS) = 0;
Expand All @@ -52,6 +54,12 @@ class InstrProfCorrelator {
/// Return the number of bytes in the names string.
size_t getNamesSize() const { return Names.size(); }

const char *getCovUnusedFuncNamesPointer() const {
return CovUnusedFuncNames.c_str();
}

size_t getCovUnusedFuncNamesSize() const { return CovUnusedFuncNames.size(); }

/// Return the size of the counters section in bytes.
uint64_t getCountersSectionSize() const {
return Ctx->CountersSectionEnd - Ctx->CountersSectionStart;
Expand All @@ -60,6 +68,7 @@ class InstrProfCorrelator {
static const char *FunctionNameAttributeName;
static const char *CFGHashAttributeName;
static const char *NumCountersAttributeName;
static const char *CovFunctionNameAttributeName;

enum InstrProfCorrelatorKind { CK_32Bit, CK_64Bit };
InstrProfCorrelatorKind getKind() const { return Kind; }
Expand All @@ -83,6 +92,7 @@ class InstrProfCorrelator {

std::string Names;
std::vector<std::string> NamesVec;
std::string CovUnusedFuncNames;

struct Probe {
std::string FunctionName;
Expand Down Expand Up @@ -205,6 +215,8 @@ class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
void correlateProfileDataImpl(
int MaxWarnings,
InstrProfCorrelator::CorrelationData *Data = nullptr) override;

Error correlateCovUnusedFuncNames(int MaxWarnings) override;
};

} // end namespace llvm
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ Error CoverageMapping::loadFromFile(

SmallVector<object::BuildIDRef> BinaryIDs;
auto CoverageReadersOrErr = BinaryCoverageReader::create(
CovMappingBufRef, Arch, Buffers, CompilationDir,
CovMappingBufRef, ProfileReader, Arch, Buffers, CompilationDir,
FoundBinaryIDs ? &BinaryIDs : nullptr);
if (Error E = CoverageReadersOrErr.takeError()) {
E = handleMaybeNoDataFoundError(std::move(E));
Expand Down
62 changes: 42 additions & 20 deletions llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -1021,8 +1022,23 @@ static Expected<std::vector<SectionRef>> lookupSections(ObjectFile &OF,
return Sections;
}

static Error getProfileNamesFromDebugInfo(StringRef FileName,
InstrProfSymtab &ProfileNames) {
std::unique_ptr<InstrProfCorrelator> Correlator;
if (auto E = InstrProfCorrelator::get(FileName).moveInto(Correlator))
return E;
if (auto E = Correlator->correlateCovUnusedFuncNames(0))
return E;
if (auto E = ProfileNames.create(
StringRef(Correlator->getCovUnusedFuncNamesPointer(),
Correlator->getCovUnusedFuncNamesSize())))
return E;
return Error::success();
}

static Expected<std::unique_ptr<BinaryCoverageReader>>
loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
loadBinaryFormat(std::unique_ptr<Binary> Bin,
IndexedInstrProfReader &ProfileReader, StringRef Arch,
StringRef CompilationDir = "",
object::BuildIDRef *BinaryID = nullptr) {
std::unique_ptr<ObjectFile> OF;
Expand Down Expand Up @@ -1052,11 +1068,24 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,

// Look for the sections that we are interested in.
auto ObjFormat = OF->getTripleObjectFormat();
InstrProfSymtab ProfileNames = ProfileReader.getSymtab();
auto NamesSection =
lookupSections(*OF, getInstrProfSectionName(IPSK_name, ObjFormat,
/*AddSegmentInfo=*/false));
if (auto E = NamesSection.takeError())
return std::move(E);
/*AddSegmentInfo=*/false));
if (auto E = NamesSection.takeError()) {
if (OF->hasDebugInfo()) {
if (auto E =
getProfileNamesFromDebugInfo(OF->getFileName(), ProfileNames))
return make_error<CoverageMapError>(coveragemap_error::malformed);
}
consumeError(std::move(E));
} else {
std::vector<SectionRef> NamesSectionRefs = *NamesSection;
if (NamesSectionRefs.size() != 1)
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (Error E = ProfileNames.create(NamesSectionRefs.back()))
return std::move(E);
}
auto CoverageSection =
lookupSections(*OF, getInstrProfSectionName(IPSK_covmap, ObjFormat,
/*AddSegmentInfo=*/false));
Expand All @@ -1071,15 +1100,6 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
return CoverageMappingOrErr.takeError();
StringRef CoverageMapping = CoverageMappingOrErr.get();

InstrProfSymtab ProfileNames;
std::vector<SectionRef> NamesSectionRefs = *NamesSection;
if (NamesSectionRefs.size() != 1)
return make_error<CoverageMapError>(
coveragemap_error::malformed,
"the size of coverage mapping section is not one");
if (Error E = ProfileNames.create(NamesSectionRefs.back()))
return std::move(E);

// Look for the coverage records section (Version4 only).
auto CoverageRecordsSections =
lookupSections(*OF, getInstrProfSectionName(IPSK_covfun, ObjFormat,
Expand Down Expand Up @@ -1149,7 +1169,8 @@ static bool isArchSpecifierInvalidOrMissing(Binary *Bin, StringRef Arch) {

Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>>
BinaryCoverageReader::create(
MemoryBufferRef ObjectBuffer, StringRef Arch,
MemoryBufferRef ObjectBuffer, IndexedInstrProfReader &ProfileReader,
StringRef Arch,
SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
StringRef CompilationDir, SmallVectorImpl<object::BuildIDRef> *BinaryIDs) {
std::vector<std::unique_ptr<BinaryCoverageReader>> Readers;
Expand Down Expand Up @@ -1195,8 +1216,8 @@ BinaryCoverageReader::create(
}

return BinaryCoverageReader::create(
ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers,
CompilationDir, BinaryIDs);
ArchiveOrErr.get()->getMemoryBufferRef(), ProfileReader, Arch,
ObjectFileBuffers, CompilationDir, BinaryIDs);
}
}

Expand All @@ -1209,8 +1230,8 @@ BinaryCoverageReader::create(
return ChildBufOrErr.takeError();

auto ChildReadersOrErr = BinaryCoverageReader::create(
ChildBufOrErr.get(), Arch, ObjectFileBuffers, CompilationDir,
BinaryIDs);
ChildBufOrErr.get(), ProfileReader, Arch, ObjectFileBuffers,
CompilationDir, BinaryIDs);
if (!ChildReadersOrErr)
return ChildReadersOrErr.takeError();
for (auto &Reader : ChildReadersOrErr.get())
Expand All @@ -1230,8 +1251,9 @@ BinaryCoverageReader::create(
}

object::BuildIDRef BinaryID;
auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch, CompilationDir,
BinaryIDs ? &BinaryID : nullptr);
auto ReaderOrErr =
loadBinaryFormat(std::move(Bin), ProfileReader, Arch, CompilationDir,
BinaryIDs ? &BinaryID : nullptr);
if (!ReaderOrErr)
return ReaderOrErr.takeError();
Readers.push_back(std::move(ReaderOrErr.get()));
Expand Down

0 comments on commit 32db121

Please sign in to comment.