| //===-- InstrProfCorrelator.cpp -------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ProfileData/InstrProfCorrelator.h" | |
| #include "llvm/DebugInfo/DIContext.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFDie.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFUnit.h" | |
| #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" | |
| #include "llvm/Object/MachO.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/WithColor.h" | |
| #include <optional> | |
| #define DEBUG_TYPE "correlator" | |
| using namespace llvm; | |
| /// Get profile section. | |
| static Expected<object::SectionRef> | |
| getInstrProfSection(const object::ObjectFile &Obj, InstrProfSectKind IPSK) { | |
| // On COFF, the getInstrProfSectionName returns the section names may followed | |
| // by "$M". The linker removes the dollar and everything after it in the final | |
| // binary. Do the same to match. | |
| Triple::ObjectFormatType ObjFormat = Obj.getTripleObjectFormat(); | |
| auto StripSuffix = [ObjFormat](StringRef N) { | |
| return ObjFormat == Triple::COFF ? N.split('$').first : N; | |
| }; | |
| std::string ExpectedSectionName = | |
| getInstrProfSectionName(IPSK, ObjFormat, | |
| /*AddSegmentInfo=*/false); | |
| ExpectedSectionName = StripSuffix(ExpectedSectionName); | |
| for (auto &Section : Obj.sections()) { | |
| if (auto SectionName = Section.getName()) | |
| if (*SectionName == ExpectedSectionName) | |
| return Section; | |
| } | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "could not find section (" + Twine(ExpectedSectionName) + ")"); | |
| } | |
| const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name"; | |
| const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash"; | |
| const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters"; | |
| const char *InstrProfCorrelator::NumBitmapBitsAttributeName = "Num BitmapBits"; | |
| llvm::Expected<std::unique_ptr<InstrProfCorrelator::Context>> | |
| InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer, | |
| object::ObjectFile &Obj, | |
| ProfCorrelatorKind FileKind) { | |
| auto C = std::make_unique<Context>(); | |
| auto CountersSection = getInstrProfSection(Obj, IPSK_cnts); | |
| if (auto Err = CountersSection.takeError()) | |
| return std::move(Err); | |
| Triple::ObjectFormatType ObjFormat = Obj.getTripleObjectFormat(); | |
| if (FileKind == ProfCorrelatorKind::Binary) { | |
| auto DataSection = getInstrProfSection(Obj, IPSK_covdata); | |
| if (auto Err = DataSection.takeError()) | |
| return std::move(Err); | |
| auto DataOrErr = DataSection->getContents(); | |
| if (!DataOrErr) | |
| return DataOrErr.takeError(); | |
| auto NameSection = getInstrProfSection(Obj, IPSK_covname); | |
| if (auto Err = NameSection.takeError()) | |
| return std::move(Err); | |
| auto NameOrErr = NameSection->getContents(); | |
| if (!NameOrErr) | |
| return NameOrErr.takeError(); | |
| C->DataStart = DataOrErr->data(); | |
| C->DataEnd = DataOrErr->data() + DataOrErr->size(); | |
| C->NameStart = NameOrErr->data(); | |
| C->NameSize = NameOrErr->size(); | |
| if (ObjFormat == Triple::MachO) { | |
| std::string FullSectionName = | |
| getInstrProfSectionName(IPSK_covdata, ObjFormat); | |
| SmallVector<StringRef, 3> SegmentAndSection; | |
| StringRef(FullSectionName).split(SegmentAndSection, ',', 2); | |
| auto *MachO = static_cast<object::MachOObjectFile *>(&Obj); | |
| Error Err = Error::success(); | |
| for (const object::MachOChainedFixupEntry &Entry : | |
| MachO->fixupTable(Err)) { | |
| if (Entry.isRebase() && Entry.segmentName() == SegmentAndSection[0] && | |
| Entry.sectionName() == SegmentAndSection[1]) { | |
| C->MachOFixups[Entry.address() - DataSection->getAddress()] = | |
| Entry.pointerValue(); | |
| } | |
| } | |
| if (Err) | |
| return std::move(Err); | |
| } | |
| } | |
| C->Buffer = std::move(Buffer); | |
| C->CountersSectionStart = CountersSection->getAddress(); | |
| C->CountersSectionEnd = C->CountersSectionStart + CountersSection->getSize(); | |
| auto BitmapSection = getInstrProfSection(Obj, IPSK_bitmap); | |
| if (auto E = BitmapSection.takeError()) { | |
| // It is not an error if NumBitmapBytes of each function is zero. | |
| consumeError(std::move(E)); | |
| C->BitmapSectionStart = 0; | |
| C->BitmapSectionEnd = 0; | |
| } else { | |
| C->BitmapSectionStart = BitmapSection->getAddress(); | |
| C->BitmapSectionEnd = C->BitmapSectionStart + BitmapSection->getSize(); | |
| } | |
| // In COFF object file, there's a null byte at the beginning of both the | |
| // counter and bitmap sections which doesn't exist in raw profile. | |
| if (ObjFormat == Triple::COFF) { | |
| ++C->CountersSectionStart; | |
| if (C->BitmapSectionStart) | |
| ++C->BitmapSectionStart; | |
| } | |
| C->ShouldSwapBytes = Obj.isLittleEndian() != sys::IsLittleEndianHost; | |
| return Expected<std::unique_ptr<Context>>(std::move(C)); | |
| } | |
| llvm::Expected<std::unique_ptr<InstrProfCorrelator>> | |
| InstrProfCorrelator::get(StringRef Filename, ProfCorrelatorKind FileKind, | |
| const object::BuildIDFetcher *BIDFetcher, | |
| const ArrayRef<object::BuildID> BIs) { | |
| // Might be overwritten from BuildIDFetcher. | |
| std::string EffectiveFilename = Filename.str(); | |
| if (BIDFetcher) { | |
| if (BIs.empty()) | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "unsupported profile binary correlation when there is no build ID " | |
| "in a profile"); | |
| if (BIs.size() > 1) | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "unsupported profile binary correlation when there are multiple " | |
| "build IDs in a profile"); | |
| Expected<std::string> Path = BIDFetcher->fetch(BIs.front()); | |
| if (!Path) { | |
| // Propagate as InstrProf specific error type. | |
| consumeError(Path.takeError()); | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "Missing build ID: " + llvm::toHex(BIs.front(), | |
| /*LowerCase=*/true)); | |
| } | |
| EffectiveFilename = *Path; | |
| } | |
| if (FileKind == ProfCorrelatorKind::DebugInfo) { | |
| auto DsymObjectsOrErr = | |
| object::MachOObjectFile::findDsymObjectMembers(EffectiveFilename); | |
| if (auto Err = DsymObjectsOrErr.takeError()) | |
| return std::move(Err); | |
| if (!DsymObjectsOrErr->empty()) { | |
| // TODO: Enable profile correlation when there are multiple objects in a | |
| // dSYM bundle. | |
| if (DsymObjectsOrErr->size() > 1) | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "using multiple objects is not yet supported"); | |
| EffectiveFilename = *DsymObjectsOrErr->begin(); | |
| } | |
| auto BufferOrErr = | |
| errorOrToExpected(MemoryBuffer::getFile(EffectiveFilename)); | |
| if (auto Err = BufferOrErr.takeError()) | |
| return std::move(Err); | |
| return get(std::move(*BufferOrErr), FileKind); | |
| } | |
| auto BufferOrErr = | |
| errorOrToExpected(MemoryBuffer::getFile(EffectiveFilename)); | |
| if (auto Err = BufferOrErr.takeError()) | |
| return std::move(Err); | |
| return get(std::move(*BufferOrErr), FileKind); | |
| } | |
| llvm::Expected<std::unique_ptr<InstrProfCorrelator>> | |
| InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer, | |
| ProfCorrelatorKind FileKind) { | |
| auto BinOrErr = object::createBinary(*Buffer); | |
| if (auto Err = BinOrErr.takeError()) | |
| return std::move(Err); | |
| if (auto *Obj = dyn_cast<object::ObjectFile>(BinOrErr->get())) { | |
| auto CtxOrErr = Context::get(std::move(Buffer), *Obj, FileKind); | |
| if (auto Err = CtxOrErr.takeError()) | |
| return std::move(Err); | |
| auto T = Obj->makeTriple(); | |
| if (T.isArch64Bit()) | |
| return InstrProfCorrelatorImpl<uint64_t>::get(std::move(*CtxOrErr), *Obj, | |
| FileKind); | |
| if (T.isArch32Bit()) | |
| return InstrProfCorrelatorImpl<uint32_t>::get(std::move(*CtxOrErr), *Obj, | |
| FileKind); | |
| } | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, "not an object file"); | |
| } | |
| std::optional<size_t> InstrProfCorrelator::getDataSize() const { | |
| if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint32_t>>(this)) | |
| return C->getDataSize(); | |
| if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint64_t>>(this)) | |
| return C->getDataSize(); | |
| return {}; | |
| } | |
| namespace llvm { | |
| template <> | |
| InstrProfCorrelatorImpl<uint32_t>::InstrProfCorrelatorImpl( | |
| std::unique_ptr<InstrProfCorrelator::Context> Ctx) | |
| : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit, | |
| std::move(Ctx)) {} | |
| template <> | |
| InstrProfCorrelatorImpl<uint64_t>::InstrProfCorrelatorImpl( | |
| std::unique_ptr<InstrProfCorrelator::Context> Ctx) | |
| : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit, | |
| std::move(Ctx)) {} | |
| template <> | |
| bool InstrProfCorrelatorImpl<uint32_t>::classof(const InstrProfCorrelator *C) { | |
| return C->getKind() == InstrProfCorrelatorKind::CK_32Bit; | |
| } | |
| template <> | |
| bool InstrProfCorrelatorImpl<uint64_t>::classof(const InstrProfCorrelator *C) { | |
| return C->getKind() == InstrProfCorrelatorKind::CK_64Bit; | |
| } | |
| } // end namespace llvm | |
| template <class IntPtrT> | |
| llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>> | |
| InstrProfCorrelatorImpl<IntPtrT>::get( | |
| std::unique_ptr<InstrProfCorrelator::Context> Ctx, | |
| const object::ObjectFile &Obj, ProfCorrelatorKind FileKind) { | |
| if (FileKind == ProfCorrelatorKind::DebugInfo) { | |
| if (Obj.isELF() || Obj.isMachO()) { | |
| auto DICtx = DWARFContext::create(Obj); | |
| return std::make_unique<DwarfInstrProfCorrelator<IntPtrT>>( | |
| std::move(DICtx), std::move(Ctx)); | |
| } | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "unsupported debug info format (only DWARF is supported)"); | |
| } | |
| if (Obj.isELF() || Obj.isCOFF() || Obj.isMachO()) | |
| return std::make_unique<BinaryInstrProfCorrelator<IntPtrT>>(std::move(Ctx)); | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "unsupported binary format (only ELF, COFF, and Mach-O are supported)"); | |
| } | |
| template <class IntPtrT> | |
| Error InstrProfCorrelatorImpl<IntPtrT>::correlateProfileData(int MaxWarnings) { | |
| assert(Data.empty() && Names.empty() && NamesVec.empty()); | |
| correlateProfileDataImpl(MaxWarnings); | |
| if (this->Data.empty()) | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "could not find any profile data metadata in correlated file"); | |
| Error Result = correlateProfileNameImpl(); | |
| this->CounterOffsets.clear(); | |
| this->BitmapOffsets.clear(); | |
| this->NamesVec.clear(); | |
| return Result; | |
| } | |
| template <> struct yaml::MappingTraits<InstrProfCorrelator::CorrelationData> { | |
| static void mapping(yaml::IO &io, | |
| InstrProfCorrelator::CorrelationData &Data) { | |
| io.mapRequired("Probes", Data.Probes); | |
| } | |
| }; | |
| template <> struct yaml::MappingTraits<InstrProfCorrelator::Probe> { | |
| static void mapping(yaml::IO &io, InstrProfCorrelator::Probe &P) { | |
| io.mapRequired("Function Name", P.FunctionName); | |
| io.mapOptional("Linkage Name", P.LinkageName); | |
| io.mapRequired("CFG Hash", P.CFGHash); | |
| io.mapRequired("Counter Offset", P.CounterOffset); | |
| io.mapRequired("Num Counters", P.NumCounters); | |
| io.mapRequired("Bitmap Offset", P.BitmapOffset); | |
| io.mapRequired("Num BitmapBytes", P.NumBitmapBytes); | |
| io.mapOptional("File", P.FilePath); | |
| io.mapOptional("Line", P.LineNumber); | |
| } | |
| }; | |
| template <> struct yaml::SequenceElementTraits<InstrProfCorrelator::Probe> { | |
| static const bool flow = false; | |
| }; | |
| template <class IntPtrT> | |
| Error InstrProfCorrelatorImpl<IntPtrT>::dumpYaml(int MaxWarnings, | |
| raw_ostream &OS) { | |
| InstrProfCorrelator::CorrelationData Data; | |
| correlateProfileDataImpl(MaxWarnings, &Data); | |
| if (Data.Probes.empty()) | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "could not find any profile data metadata in debug info"); | |
| yaml::Output YamlOS(OS); | |
| YamlOS << Data; | |
| return Error::success(); | |
| } | |
| template <class IntPtrT> | |
| void InstrProfCorrelatorImpl<IntPtrT>::addDataProbe( | |
| uint64_t NameRef, uint64_t CFGHash, IntPtrT CounterOffset, | |
| IntPtrT BitmapOffset, IntPtrT FunctionPtr, uint32_t NumCounters, | |
| uint32_t NumBitmapBytes) { | |
| // Check if a probe was already added for this counter offset. | |
| if (NumCounters && !CounterOffsets.insert(CounterOffset).second) | |
| return; | |
| // Check if a probe was already added for this bitmap offset. | |
| if (NumBitmapBytes && !BitmapOffsets.insert(BitmapOffset).second) | |
| return; | |
| Data.push_back({ | |
| maybeSwap<uint64_t>(NameRef), | |
| maybeSwap<uint64_t>(CFGHash), | |
| // In this mode, CounterPtr actually stores the section relative address | |
| // of the counter. | |
| maybeSwap<IntPtrT>(CounterOffset), | |
| /*UniformCounterPtr=*/maybeSwap<IntPtrT>(0), | |
| maybeSwap<IntPtrT>(BitmapOffset), | |
| maybeSwap<IntPtrT>(FunctionPtr), | |
| // TODO: Value profiling is not yet supported. | |
| /*ValuesPtr=*/maybeSwap<IntPtrT>(0), | |
| maybeSwap<uint32_t>(NumCounters), | |
| /*NumValueSites=*/{maybeSwap<uint16_t>(0), maybeSwap<uint16_t>(0)}, | |
| /*OffloadDeviceWaveSize=*/maybeSwap<uint16_t>(0), | |
| maybeSwap<uint32_t>(NumBitmapBytes), | |
| }); | |
| } | |
| template <class IntPtrT> | |
| std::optional<uint64_t> | |
| DwarfInstrProfCorrelator<IntPtrT>::getLocation(const DWARFDie &Die) const { | |
| auto Locations = Die.getLocations(dwarf::DW_AT_location); | |
| if (!Locations) { | |
| consumeError(Locations.takeError()); | |
| return {}; | |
| } | |
| auto &DU = *Die.getDwarfUnit(); | |
| auto AddressSize = DU.getAddressByteSize(); | |
| for (auto &Location : *Locations) { | |
| DataExtractor Data(Location.Expr, DICtx->isLittleEndian()); | |
| DWARFExpression Expr(Data, AddressSize); | |
| for (auto &Op : Expr) { | |
| if (Op.getCode() == dwarf::DW_OP_addr) | |
| return Op.getRawOperand(0); | |
| if (Op.getCode() == dwarf::DW_OP_addrx) { | |
| uint64_t Index = Op.getRawOperand(0); | |
| if (auto SA = DU.getAddrOffsetSectionItem(Index)) | |
| return SA->Address; | |
| } | |
| } | |
| } | |
| return {}; | |
| } | |
| template <class IntPtrT> | |
| bool DwarfInstrProfCorrelator<IntPtrT>::isDIEOfProbe(const DWARFDie &Die, | |
| StringRef Prefix) { | |
| 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.isSubprogramDIE()) | |
| return false; | |
| if (!Die.hasChildren()) | |
| return false; | |
| if (const char *Name = Die.getName(DINameKind::ShortName)) | |
| return StringRef(Name).starts_with(Prefix); | |
| return false; | |
| } | |
| template <class IntPtrT> | |
| std::optional<std::pair<InstrProfCorrelator::Probe, IntPtrT>> | |
| DwarfInstrProfCorrelator<IntPtrT>::addCountersToDataProbe( | |
| const DWARFDie &Die, const bool UnlimitedWarnings, | |
| int &NumSuppressedWarnings) { | |
| std::optional<const char *> FunctionName; | |
| std::optional<uint64_t> CFGHash; | |
| std::optional<uint64_t> CounterPtr = getLocation(Die); | |
| auto FnDie = Die.getParent(); | |
| auto FunctionPtr = dwarf::toAddress(FnDie.find(dwarf::DW_AT_low_pc)); | |
| std::optional<uint64_t> NumCounters; | |
| 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; | |
| } | |
| StringRef AnnotationName = *AnnotationNameOrErr; | |
| if (AnnotationName == InstrProfCorrelator::FunctionNameAttributeName) { | |
| if (auto EC = AnnotationFormValue->getAsCString().moveInto(FunctionName)) | |
| consumeError(std::move(EC)); | |
| } else if (AnnotationName == InstrProfCorrelator::CFGHashAttributeName) { | |
| CFGHash = AnnotationFormValue->getAsUnsignedConstant(); | |
| } else if (AnnotationName == | |
| InstrProfCorrelator::NumCountersAttributeName) { | |
| NumCounters = AnnotationFormValue->getAsUnsignedConstant(); | |
| } | |
| } | |
| // If there is no function and no counter, assume it was dead-stripped | |
| if (!FunctionPtr && !CounterPtr) | |
| return std::nullopt; | |
| if (!FunctionName || !CFGHash || !CounterPtr || !NumCounters) { | |
| if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { | |
| WithColor::warning() << "Incomplete DIE for function " << FunctionName | |
| << ": CFGHash=" << CFGHash | |
| << " CounterPtr=" << CounterPtr | |
| << " NumCounters=" << NumCounters << "\n"; | |
| LLVM_DEBUG(Die.dump(dbgs())); | |
| } | |
| return std::nullopt; | |
| } | |
| uint64_t CountersStart = this->Ctx->CountersSectionStart; | |
| uint64_t CountersEnd = this->Ctx->CountersSectionEnd; | |
| if (*CounterPtr < CountersStart || *CounterPtr >= CountersEnd) { | |
| if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { | |
| WithColor::warning() << format( | |
| "CounterPtr out of range for function %s: Actual=0x%x " | |
| "Expected=[0x%x, 0x%x)\n", | |
| *FunctionName, *CounterPtr, CountersStart, CountersEnd); | |
| LLVM_DEBUG(Die.dump(dbgs())); | |
| } | |
| return std::nullopt; | |
| } | |
| if (!FunctionPtr && (UnlimitedWarnings || ++NumSuppressedWarnings < 1)) { | |
| WithColor::warning() << format("Could not find address of function %s\n", | |
| *FunctionName); | |
| LLVM_DEBUG(Die.dump(dbgs())); | |
| } | |
| // In debug info correlation mode, the CounterPtr is an absolute address | |
| // of the counter, but it's expected to be relative later when iterating | |
| // Data. | |
| IntPtrT CounterOffset = *CounterPtr - CountersStart; | |
| InstrProfCorrelator::Probe P = {}; | |
| P.FunctionName = *FunctionName; | |
| if (const char *Name = FnDie.getName(DINameKind::LinkageName)) | |
| P.LinkageName = Name; | |
| P.CFGHash = *CFGHash; | |
| P.CounterOffset = CounterOffset; | |
| P.NumCounters = *NumCounters; | |
| auto FilePath = FnDie.getDeclFile( | |
| DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath); | |
| if (!FilePath.empty()) | |
| P.FilePath = FilePath; | |
| if (auto LineNumber = FnDie.getDeclLine()) | |
| P.LineNumber = LineNumber; | |
| return std::optional<std::pair<InstrProfCorrelator::Probe, IntPtrT>>( | |
| {P, FunctionPtr.value_or(0)}); | |
| } | |
| template <class IntPtrT> | |
| std::optional<std::pair<InstrProfCorrelator::Probe, IntPtrT>> | |
| DwarfInstrProfCorrelator<IntPtrT>::addBitmapToDataProbe( | |
| const DWARFDie &Die, const bool UnlimitedWarnings, | |
| int &NumSuppressedWarnings) { | |
| std::optional<const char *> FunctionName; | |
| std::optional<uint64_t> BitmapPtr = getLocation(Die); | |
| uint64_t NumBitmapBytes; | |
| 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; | |
| } | |
| StringRef AnnotationName = *AnnotationNameOrErr; | |
| if (AnnotationName == InstrProfCorrelator::FunctionNameAttributeName) { | |
| if (auto EC = AnnotationFormValue->getAsCString().moveInto(FunctionName)) | |
| consumeError(std::move(EC)); | |
| } else if (AnnotationName == | |
| InstrProfCorrelator::NumBitmapBitsAttributeName) { | |
| std::optional<uint64_t> NumBitmapBits = | |
| AnnotationFormValue->getAsUnsignedConstant(); | |
| NumBitmapBytes = alignTo(*NumBitmapBits, CHAR_BIT) / CHAR_BIT; | |
| } | |
| } | |
| if (!FunctionName || !BitmapPtr || !NumBitmapBytes) { | |
| if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { | |
| WithColor::warning() << "Incomplete DIE for function " << FunctionName | |
| << " BitmapPtr=" << BitmapPtr | |
| << " NumBitmapBytes=" << NumBitmapBytes << "\n"; | |
| LLVM_DEBUG(Die.dump(dbgs())); | |
| } | |
| return std::nullopt; | |
| } | |
| uint64_t BitmapStart = this->Ctx->BitmapSectionStart; | |
| uint64_t BitmapEnd = this->Ctx->BitmapSectionEnd; | |
| if (!BitmapStart && !BitmapEnd && NumBitmapBytes) { | |
| auto E = make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "could not find profile bitmap section in correlated file"); | |
| return std::nullopt; | |
| } | |
| if (*BitmapPtr < BitmapStart || (*BitmapPtr >= BitmapEnd && NumBitmapBytes)) { | |
| if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { | |
| WithColor::warning() << format( | |
| "BitmapPtr out of range for function %s: Actual=0x%x " | |
| "Expected=[0x%x, 0x%x)\n", | |
| *FunctionName, *BitmapPtr, BitmapStart, BitmapEnd); | |
| LLVM_DEBUG(Die.dump(dbgs())); | |
| } | |
| return std::nullopt; | |
| } | |
| // In debug info correlation mode, the BitmapPtr is an absolute address of | |
| // the bitmap, but it's expected to be relative later when iterating Data. | |
| IntPtrT BitmapOffset = *BitmapPtr - BitmapStart; | |
| InstrProfCorrelator::Probe P = {}; | |
| P.FunctionName = *FunctionName; | |
| P.BitmapOffset = BitmapOffset; | |
| P.NumBitmapBytes = NumBitmapBytes; | |
| return std::optional<std::pair<InstrProfCorrelator::Probe, IntPtrT>>({P, 0}); | |
| } | |
| template <class IntPtrT> | |
| void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl( | |
| int MaxWarnings, InstrProfCorrelator::CorrelationData *Data) { | |
| // Map from FunctionName string to (Probe, FunctionPtr) pair. | |
| // We use it to collect data from all functions Counter and Bitmap DIEs. | |
| MapVector<std::string, std::pair<InstrProfCorrelator::Probe, IntPtrT>, | |
| StringMap<unsigned>> | |
| Probes; | |
| bool UnlimitedWarnings = (MaxWarnings == 0); | |
| // -N suppressed warnings means we can emit up to N (unsuppressed) warnings | |
| int NumSuppressedWarnings = -MaxWarnings; | |
| auto MaybeAddProbe = [&](DWARFDie Die) { | |
| std::optional<std::pair<InstrProfCorrelator::Probe, IntPtrT>> ProbeData; | |
| if (isDIEOfProbe(Die, getInstrProfCountersVarPrefix())) | |
| ProbeData = | |
| addCountersToDataProbe(Die, UnlimitedWarnings, NumSuppressedWarnings); | |
| else if (isDIEOfProbe(Die, getInstrProfBitmapVarPrefix())) | |
| ProbeData = | |
| addBitmapToDataProbe(Die, UnlimitedWarnings, NumSuppressedWarnings); | |
| if (!ProbeData) | |
| return; | |
| auto [Probe, FunctionPtr] = *ProbeData; | |
| auto [It, Inserted] = | |
| Probes.try_emplace(Probe.FunctionName, Probe, FunctionPtr); | |
| if (!Inserted) { | |
| auto &P = It->second.first; | |
| if (isDIEOfProbe(Die, getInstrProfCountersVarPrefix())) { | |
| P.LinkageName = Probe.LinkageName; | |
| P.CFGHash = Probe.CFGHash; | |
| P.CounterOffset = Probe.CounterOffset; | |
| P.NumCounters = Probe.NumCounters; | |
| P.FilePath = Probe.FilePath; | |
| P.LineNumber = Probe.LineNumber; | |
| } else { | |
| P.BitmapOffset = Probe.BitmapOffset; | |
| P.NumBitmapBytes = Probe.NumBitmapBytes; | |
| } | |
| } | |
| }; | |
| for (auto &CU : DICtx->normal_units()) | |
| for (const auto &Entry : CU->dies()) | |
| MaybeAddProbe(DWARFDie(CU.get(), &Entry)); | |
| for (auto &CU : DICtx->dwo_units()) | |
| for (const auto &Entry : CU->dies()) | |
| MaybeAddProbe(DWARFDie(CU.get(), &Entry)); | |
| for (const auto &[FunctionName, ProbeData] : Probes) { | |
| const auto &[Probe, FunctionPtr] = ProbeData; | |
| if (Data) | |
| Data->Probes.push_back(Probe); | |
| else { | |
| this->NamesVec.push_back(FunctionName); | |
| uint64_t NameRef = IndexedInstrProf::ComputeHash(FunctionName); | |
| this->addDataProbe(NameRef, Probe.CFGHash, Probe.CounterOffset, | |
| Probe.BitmapOffset, FunctionPtr, Probe.NumCounters, | |
| Probe.NumBitmapBytes); | |
| } | |
| } | |
| if (!UnlimitedWarnings && NumSuppressedWarnings > 0) | |
| WithColor::warning() << format("Suppressed %d additional warnings\n", | |
| NumSuppressedWarnings); | |
| } | |
| template <class IntPtrT> | |
| Error DwarfInstrProfCorrelator<IntPtrT>::correlateProfileNameImpl() { | |
| if (this->NamesVec.empty()) { | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "could not find any profile name metadata in debug info"); | |
| } | |
| auto Result = | |
| collectGlobalObjectNameStrings(this->NamesVec, | |
| /*doCompression=*/false, this->Names); | |
| return Result; | |
| } | |
| template <class IntPtrT> | |
| void BinaryInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl( | |
| int MaxWarnings, InstrProfCorrelator::CorrelationData *CorrelateData) { | |
| using RawProfData = RawInstrProf::ProfileData<IntPtrT>; | |
| bool UnlimitedWarnings = (MaxWarnings == 0); | |
| // -N suppressed warnings means we can emit up to N (unsuppressed) warnings | |
| int NumSuppressedWarnings = -MaxWarnings; | |
| const RawProfData *DataStart = (const RawProfData *)this->Ctx->DataStart; | |
| const RawProfData *DataEnd = (const RawProfData *)this->Ctx->DataEnd; | |
| // We need to use < here because the last data record may have no padding. | |
| for (const RawProfData *I = DataStart; I < DataEnd; ++I) { | |
| uint64_t CounterPtr = this->template maybeSwap<IntPtrT>(I->CounterPtr); | |
| uint64_t CountersStart = this->Ctx->CountersSectionStart; | |
| uint64_t CountersEnd = this->Ctx->CountersSectionEnd; | |
| uint64_t BitmapPtr = this->template maybeSwap<IntPtrT>(I->BitmapPtr); | |
| uint64_t BitmapStart = this->Ctx->BitmapSectionStart; | |
| uint64_t BitmapEnd = this->Ctx->BitmapSectionEnd; | |
| if (!BitmapStart && !BitmapEnd && I->NumBitmapBytes) { | |
| auto E = make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "could not find profile bitmap section in correlated file"); | |
| return; | |
| } | |
| if (!this->Ctx->MachOFixups.empty()) { | |
| auto GetPtrByOffset = [&](uint64_t Offset, uint64_t &Ptr) { | |
| auto It = this->Ctx->MachOFixups.find(Offset); | |
| if (It != this->Ctx->MachOFixups.end()) { | |
| Ptr = It->second; | |
| } else if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { | |
| WithColor::warning() << format( | |
| "Mach-O fixup not found for covdata offset 0x%llx\n", Offset); | |
| } | |
| }; | |
| uint64_t CounterOffset = (uint64_t)&I->CounterPtr - (uint64_t)DataStart; | |
| uint64_t BitmapOffset = (uint64_t)&I->BitmapPtr - (uint64_t)DataStart; | |
| GetPtrByOffset(CounterOffset, CounterPtr); | |
| GetPtrByOffset(BitmapOffset, BitmapPtr); | |
| } | |
| if (CounterPtr < CountersStart || CounterPtr >= CountersEnd) { | |
| if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { | |
| WithColor::warning() | |
| << format("CounterPtr out of range for function: Actual=0x%x " | |
| "Expected=[0x%x, 0x%x) at data offset=0x%x\n", | |
| CounterPtr, CountersStart, CountersEnd, | |
| (I - DataStart) * sizeof(RawProfData)); | |
| } | |
| } | |
| if (I->NumBitmapBytes && | |
| (BitmapPtr < BitmapStart || BitmapPtr >= BitmapEnd)) { | |
| if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) { | |
| WithColor::warning() | |
| << format("BitmapPtr out of range for function: Actual=0x%x " | |
| "Expected=[0x%x, 0x%x) at data offset=0x%x\n", | |
| BitmapPtr, BitmapStart, BitmapEnd, | |
| (I - DataStart) * sizeof(RawProfData)); | |
| } | |
| } | |
| // In binary correlation mode, CounterPtr and BitmapPtr are absolute | |
| // addresses, but they're expected to be relative later when iterating Data. | |
| IntPtrT CounterOffset = CounterPtr - CountersStart; | |
| IntPtrT BitmapOffset = BitmapPtr - BitmapStart; | |
| this->addDataProbe(I->NameRef, I->FuncHash, CounterOffset, BitmapOffset, | |
| I->FunctionPointer, I->NumCounters, I->NumBitmapBytes); | |
| } | |
| } | |
| template <class IntPtrT> | |
| Error BinaryInstrProfCorrelator<IntPtrT>::correlateProfileNameImpl() { | |
| if (this->Ctx->NameSize == 0) { | |
| return make_error<InstrProfError>( | |
| instrprof_error::unable_to_correlate_profile, | |
| "could not find any profile data metadata in object file"); | |
| } | |
| this->Names.append(this->Ctx->NameStart, this->Ctx->NameSize); | |
| return Error::success(); | |
| } |