diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -31,6 +31,10 @@ // is textually included. #define COVMAP_V3 +namespace llvm { +extern cl::opt DebugInfoCorrelate; +} // namespace llvm + static llvm::cl::opt EmptyLineCommentCoverage( "emptyline-comment-coverage", llvm::cl::desc("Emit emptylines and comment lines as skipped regions (only " @@ -1821,6 +1825,22 @@ 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) { diff --git a/clang/test/CodeGen/coverage-profile-raw-version.c b/clang/test/CodeGen/coverage-profile-raw-version.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/coverage-profile-raw-version.c @@ -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; +} 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 --- /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 | 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; +} diff --git a/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp b/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/Linux/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 %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; +} diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h @@ -203,7 +203,8 @@ BinaryCoverageReader &operator=(const BinaryCoverageReader &) = delete; static Expected>> - create(MemoryBufferRef ObjectBuffer, StringRef Arch, + create(MemoryBufferRef ObjectBuffer, IndexedInstrProfReader &ProfileReader, + StringRef Arch, SmallVectorImpl> &ObjectFileBuffers, StringRef CompilationDir = "", SmallVectorImpl *BinaryIDs = nullptr); diff --git a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h --- a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h +++ b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h @@ -39,6 +39,8 @@ /// \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; @@ -52,6 +54,12 @@ /// 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; @@ -60,6 +68,7 @@ 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; } @@ -83,6 +92,7 @@ std::string Names; std::vector NamesVec; + std::string CovUnusedFuncNames; struct Probe { std::string FunctionName; @@ -205,6 +215,8 @@ void correlateProfileDataImpl( int MaxWarnings, InstrProfCorrelator::CorrelationData *Data = nullptr) override; + + Error correlateCovUnusedFuncNames(int MaxWarnings) override; }; } // end namespace llvm 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 @@ -361,7 +361,7 @@ SmallVector 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)); diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -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" @@ -1021,8 +1022,23 @@ return Sections; } +static Error getProfileNamesFromDebugInfo(StringRef FileName, + InstrProfSymtab &ProfileNames) { + std::unique_ptr 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> -loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, +loadBinaryFormat(std::unique_ptr Bin, + IndexedInstrProfReader &ProfileReader, StringRef Arch, StringRef CompilationDir = "", object::BuildIDRef *BinaryID = nullptr) { std::unique_ptr OF; @@ -1052,11 +1068,24 @@ // 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(coveragemap_error::malformed); + } + consumeError(std::move(E)); + } else { + std::vector NamesSectionRefs = *NamesSection; + if (NamesSectionRefs.size() != 1) + return make_error(coveragemap_error::malformed); + if (Error E = ProfileNames.create(NamesSectionRefs.back())) + return std::move(E); + } auto CoverageSection = lookupSections(*OF, getInstrProfSectionName(IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)); @@ -1071,15 +1100,6 @@ return CoverageMappingOrErr.takeError(); StringRef CoverageMapping = CoverageMappingOrErr.get(); - InstrProfSymtab ProfileNames; - std::vector NamesSectionRefs = *NamesSection; - if (NamesSectionRefs.size() != 1) - return make_error( - 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, @@ -1149,7 +1169,8 @@ Expected>> BinaryCoverageReader::create( - MemoryBufferRef ObjectBuffer, StringRef Arch, + MemoryBufferRef ObjectBuffer, IndexedInstrProfReader &ProfileReader, + StringRef Arch, SmallVectorImpl> &ObjectFileBuffers, StringRef CompilationDir, SmallVectorImpl *BinaryIDs) { std::vector> Readers; @@ -1195,8 +1216,8 @@ } return BinaryCoverageReader::create( - ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers, - CompilationDir, BinaryIDs); + ArchiveOrErr.get()->getMemoryBufferRef(), ProfileReader, Arch, + ObjectFileBuffers, CompilationDir, BinaryIDs); } } @@ -1209,8 +1230,8 @@ 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()) @@ -1230,8 +1251,9 @@ } 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())); diff --git a/llvm/lib/ProfileData/InstrProfCorrelator.cpp b/llvm/lib/ProfileData/InstrProfCorrelator.cpp --- a/llvm/lib/ProfileData/InstrProfCorrelator.cpp +++ b/llvm/lib/ProfileData/InstrProfCorrelator.cpp @@ -38,6 +38,8 @@ const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name"; const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash"; const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters"; +const char *InstrProfCorrelator::CovFunctionNameAttributeName = + "Cov Function Name"; llvm::Expected> InstrProfCorrelator::Context::get(std::unique_ptr Buffer, @@ -361,3 +363,76 @@ WithColor::warning() << format("Suppressed %d additional warnings\n", NumSuppressedWarnings); } + +template +Error DwarfInstrProfCorrelator::correlateCovUnusedFuncNames( + int MaxWarnings) { + bool UnlimitedWarnings = (MaxWarnings == 0); + // -N suppressed warnings means we can emit up to N (unsuppressed) warnings + int NumSuppressedWarnings = -MaxWarnings; + std::vector UnusedFuncNames; + auto IsDIEOfCovName = [](const DWARFDie &Die) { + const auto &ParentDie = Die.getParent(); + if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL()) + return false; + if (Die.getTag() != dwarf::DW_TAG_variable) + return false; + if (ParentDie.getParent().isValid()) + return false; + if (!Die.hasChildren()) + return false; + if (const char *Name = Die.getName(DINameKind::ShortName)) + return StringRef(Name).startswith(getCoverageUnusedNamesVarName()); + return false; + }; + auto MaybeAddCovFuncName = [&](DWARFDie Die) { + if (!IsDIEOfCovName(Die)) + return; + for (const DWARFDie &Child : Die.children()) { + if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation) + continue; + auto AnnotationFormName = Child.find(dwarf::DW_AT_name); + auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value); + if (!AnnotationFormName || !AnnotationFormValue) + continue; + auto AnnotationNameOrErr = AnnotationFormName->getAsCString(); + if (auto Err = AnnotationNameOrErr.takeError()) { + consumeError(std::move(Err)); + continue; + } + std::optional FunctionName; + StringRef AnnotationName = *AnnotationNameOrErr; + if (AnnotationName.compare( + InstrProfCorrelator::CovFunctionNameAttributeName) == 0) { + if (auto EC = + AnnotationFormValue->getAsCString().moveInto(FunctionName)) + consumeError(std::move(EC)); + } + if (!FunctionName) { + if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { + WithColor::warning() << format( + "Missing coverage function name value at DIE 0x%08" PRIx64, + Child.getOffset()); + } + return; + } + UnusedFuncNames.push_back(*FunctionName); + } + }; + for (auto &CU : DICtx->normal_units()) + for (const auto &Entry : CU->dies()) + MaybeAddCovFuncName(DWARFDie(CU.get(), &Entry)); + for (auto &CU : DICtx->dwo_units()) + for (const auto &Entry : CU->dies()) + MaybeAddCovFuncName(DWARFDie(CU.get(), &Entry)); + + if (!UnlimitedWarnings && NumSuppressedWarnings > 0) + WithColor::warning() << format("Suppressed %d additional warnings\n", + NumSuppressedWarnings); + if (!UnusedFuncNames.empty()) { + auto Result = collectPGOFuncNameStrings( + UnusedFuncNames, /*doCompression=*/false, this->CovUnusedFuncNames); + return Result; + } + return Error::success(); +} diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp --- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -766,6 +766,34 @@ if (isa(NC)) NC->dropAllReferences(); } + if (DebugInfoCorrelate && !ReferencedNames.empty()) { + MDNode *Node = *M->debug_compile_units_begin(); + DICompileUnit *CU = cast(Node); + DIBuilder DB(*M, true, CU); + LLVMContext &Ctx = M->getContext(); + SmallVector Annots; + for (auto *NameVar: ReferencedNames) { + Metadata *CovFunctionNameAnnotation[] = { + MDString::get(Ctx, InstrProfCorrelator::CovFunctionNameAttributeName), + MDString::get(Ctx, + std::string(getPGOFuncNameVarInitializer(NameVar))), + }; + Annots.push_back(MDNode::get(Ctx, CovFunctionNameAnnotation)); + } + auto Annotations = DB.getOrCreateArray(Annots); + auto *DICovName = DB.createGlobalVariableExpression( + CU, CoverageNamesVar->getName(), /*LinkageName=*/StringRef(), + CU->getFile(), + /*LineNo=*/0, DB.createUnspecifiedType("Coverage Type"), + /*IsLocalToUnit=*/true, /*IsDefined=*/true, /*Expr=*/nullptr, + /*Decl=*/nullptr, /*TemplateParams=*/nullptr, /*AlignInBits=*/0, + Annotations); + CoverageNamesVar->addDebugInfo(DICovName); + DB.finalize(); + ReferencedNames.clear(); + return; + } + CoverageNamesVar->eraseFromParent(); } diff --git a/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-byte-coverage.ll rename from llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll rename to llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-byte-coverage.ll diff --git a/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-clang-coverage.ll b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-clang-coverage.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-clang-coverage.ll @@ -0,0 +1,64 @@ +; Test if unused function names are correctly emitted as dwarf variable debug info under DW_TAG_compile_unit + +; RUN: opt < %s -passes=instrprof -debug-info-correlate -S > %t.ll +; RUN: FileCheck < %t.ll --implicit-check-not "{{__llvm_prf_data|__llvm_prf_names}}" %s +; RUN: %llc_dwarf -O0 -filetype=obj < %t.ll | llvm-dwarfdump - | FileCheck %s --check-prefix=CHECK-DWARF + +; REQUIRES: system-linux, object-emission + +@__profn_foo = private constant [3 x i8] c"foo" +@__profn_bar = private constant [3 x i8] c"bar" +@__profn_baz = private constant [3 x i8] c"baz" +@__llvm_coverage_names = internal constant [2 x ptr] [ptr @__profn_bar, ptr @__profn_baz] +; CHECK: @__llvm_coverage_names = internal constant [2 x ptr] [ptr @__profn_bar, ptr @__profn_baz], !dbg ![[DBG:[0-9]+]] +; CHECK: ![[DBG]] = !DIGlobalVariableExpression(var: ![[VAR:[0-9]+]], expr: !DIExpression()) +; CHECK: ![[VAR]] = {{.*}} !DIGlobalVariable(name: "__llvm_coverage_names" +; CHECK-SAME: scope: ![[SCOPE:[0-9]+]] +; CHECK-SAME: annotations: ![[ANNOTATIONS:[0-9]+]] +; CHECK: ![[SCOPE]] = {{.*}} !DICompileUnit( +; CHECK: ![[ANNOTATIONS]] = !{![[FUNC_NAME1:[0-9]+]], ![[FUNC_NAME2:[0-9]+]]} +; CHECK: ![[FUNC_NAME1]] = !{!"Cov Function Name", !"bar"} +; CHECK: ![[FUNC_NAME2]] = !{!"Cov Function Name", !"baz"} + +define void @_Z3foov() !dbg !12 { + call void @llvm.instrprof.increment(ptr @__profn_foo, i64 12345678, i32 2, i32 0) + ret void +} + +declare void @llvm.instrprof.increment(ptr, i64, i32, i32) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "debug-info-correlate.cpp", directory: "") +!2 = !{i32 7, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"branch-target-enforcement", i32 0} +!6 = !{i32 8, !"sign-return-address", i32 0} +!7 = !{i32 8, !"sign-return-address-all", i32 0} +!8 = !{i32 8, !"sign-return-address-with-bkey", i32 0} +!9 = !{i32 7, !"uwtable", i32 1} +!10 = !{i32 7, !"frame-pointer", i32 1} +!11 = !{!"clang version 14.0.0"} +!12 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !13, file: !13, line: 1, type: !14, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !16) +!13 = !DIFile(filename: "debug-info-correlate.cpp", directory: "") +!14 = !DISubroutineType(types: !15) +!15 = !{null} +!16 = !{} + +; CHECK-DWARF: DW_TAG_compile_unit +; CHECK-DWARF: DW_TAG_variable +; CHECK-DWARF: DW_AT_name ("__llvm_coverage_names") +; CHECK-DWARF: DW_AT_type ({{.*}} "Coverage Type") +; CHECK-DWARF: DW_TAG_LLVM_annotation +; CHECK-DWARF: DW_AT_name ("Cov Function Name") +; CHECK-DWARF: DW_AT_const_value ("bar") +; CHECK-DWARF: DW_TAG_LLVM_annotation +; CHECK-DWARF: DW_AT_name ("Cov Function Name") +; CHECK-DWARF: DW_AT_const_value ("baz") +; CHECK-DWARF: DW_TAG_unspecified_type +; CHECK-DWARF: DW_AT_name ("Coverage Type") +; CHECK-DWARF: NULL