Files
Stefan Gränitz d0487fec8e Reland [llvm] Errorize DebuginfodFetcher for inspection at call-sites (#194872)
Failure to fetch debuginfod is rarely an error, but there are case where
we want to distinguish error reasons down the line, for example in order
to test connection timeouts.
2026-07-09 11:18:27 +02:00

706 lines
28 KiB
C++

//===-- 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 == InstrProfCorrelator::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 == DEBUG_INFO) {
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);
}
if (FileKind == BINARY) {
auto BufferOrErr =
errorOrToExpected(MemoryBuffer::getFile(EffectiveFilename));
if (auto Err = BufferOrErr.takeError())
return std::move(Err);
return get(std::move(*BufferOrErr), FileKind);
}
return make_error<InstrProfError>(
instrprof_error::unable_to_correlate_profile,
"unsupported correlation kind (only DWARF debug info and Binary format "
"(ELF/COFF) are supported)");
}
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 == DEBUG_INFO) {
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();
}