165d7fd02SEllis Hoag //===-- InstrProfCorrelator.cpp -------------------------------------------===//
265d7fd02SEllis Hoag //
365d7fd02SEllis Hoag // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
465d7fd02SEllis Hoag // See https://llvm.org/LICENSE.txt for license information.
565d7fd02SEllis Hoag // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
665d7fd02SEllis Hoag //
765d7fd02SEllis Hoag //===----------------------------------------------------------------------===//
865d7fd02SEllis Hoag
965d7fd02SEllis Hoag #include "llvm/ProfileData/InstrProfCorrelator.h"
10fc97efa4Sserge-sans-paille #include "llvm/DebugInfo/DIContext.h"
11fc97efa4Sserge-sans-paille #include "llvm/DebugInfo/DWARF/DWARFContext.h"
12fc97efa4Sserge-sans-paille #include "llvm/DebugInfo/DWARF/DWARFDie.h"
13290e4823Sserge-sans-paille #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
14fc97efa4Sserge-sans-paille #include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
15fc97efa4Sserge-sans-paille #include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h"
16fc97efa4Sserge-sans-paille #include "llvm/DebugInfo/DWARF/DWARFUnit.h"
1765d7fd02SEllis Hoag #include "llvm/Object/MachO.h"
1865d7fd02SEllis Hoag #include "llvm/Support/Debug.h"
1965d7fd02SEllis Hoag
2065d7fd02SEllis Hoag #define DEBUG_TYPE "correlator"
2165d7fd02SEllis Hoag
2265d7fd02SEllis Hoag using namespace llvm;
2365d7fd02SEllis Hoag
2465d7fd02SEllis Hoag /// Get the __llvm_prf_cnts section.
getCountersSection(const object::ObjectFile & Obj)2565d7fd02SEllis Hoag Expected<object::SectionRef> getCountersSection(const object::ObjectFile &Obj) {
2665d7fd02SEllis Hoag for (auto &Section : Obj.sections())
2765d7fd02SEllis Hoag if (auto SectionName = Section.getName())
2865d7fd02SEllis Hoag if (SectionName.get() == INSTR_PROF_CNTS_SECT_NAME)
2965d7fd02SEllis Hoag return Section;
3065d7fd02SEllis Hoag return make_error<InstrProfError>(
316d523911SEllis Hoag instrprof_error::unable_to_correlate_profile,
326d523911SEllis Hoag "could not find counter section (" INSTR_PROF_CNTS_SECT_NAME ")");
3365d7fd02SEllis Hoag }
3465d7fd02SEllis Hoag
3565d7fd02SEllis Hoag const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name";
3665d7fd02SEllis Hoag const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash";
3765d7fd02SEllis Hoag const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters";
3865d7fd02SEllis Hoag
3965d7fd02SEllis Hoag llvm::Expected<std::unique_ptr<InstrProfCorrelator::Context>>
get(std::unique_ptr<MemoryBuffer> Buffer,const object::ObjectFile & Obj)4065d7fd02SEllis Hoag InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer,
4165d7fd02SEllis Hoag const object::ObjectFile &Obj) {
4265d7fd02SEllis Hoag auto CountersSection = getCountersSection(Obj);
4365d7fd02SEllis Hoag if (auto Err = CountersSection.takeError())
4465d7fd02SEllis Hoag return std::move(Err);
4565d7fd02SEllis Hoag auto C = std::make_unique<Context>();
4665d7fd02SEllis Hoag C->Buffer = std::move(Buffer);
4765d7fd02SEllis Hoag C->CountersSectionStart = CountersSection->getAddress();
4865d7fd02SEllis Hoag C->CountersSectionEnd = C->CountersSectionStart + CountersSection->getSize();
4965d7fd02SEllis Hoag C->ShouldSwapBytes = Obj.isLittleEndian() != sys::IsLittleEndianHost;
5065d7fd02SEllis Hoag return Expected<std::unique_ptr<Context>>(std::move(C));
5165d7fd02SEllis Hoag }
5265d7fd02SEllis Hoag
5365d7fd02SEllis Hoag llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
get(StringRef DebugInfoFilename)5465d7fd02SEllis Hoag InstrProfCorrelator::get(StringRef DebugInfoFilename) {
5565d7fd02SEllis Hoag auto DsymObjectsOrErr =
5665d7fd02SEllis Hoag object::MachOObjectFile::findDsymObjectMembers(DebugInfoFilename);
5765d7fd02SEllis Hoag if (auto Err = DsymObjectsOrErr.takeError())
5865d7fd02SEllis Hoag return std::move(Err);
5965d7fd02SEllis Hoag if (!DsymObjectsOrErr->empty()) {
6065d7fd02SEllis Hoag // TODO: Enable profile correlation when there are multiple objects in a
6165d7fd02SEllis Hoag // dSYM bundle.
6265d7fd02SEllis Hoag if (DsymObjectsOrErr->size() > 1)
636d523911SEllis Hoag return make_error<InstrProfError>(
646d523911SEllis Hoag instrprof_error::unable_to_correlate_profile,
656d523911SEllis Hoag "using multiple objects is not yet supported");
6665d7fd02SEllis Hoag DebugInfoFilename = *DsymObjectsOrErr->begin();
6765d7fd02SEllis Hoag }
6865d7fd02SEllis Hoag auto BufferOrErr =
6965d7fd02SEllis Hoag errorOrToExpected(MemoryBuffer::getFile(DebugInfoFilename));
7065d7fd02SEllis Hoag if (auto Err = BufferOrErr.takeError())
7165d7fd02SEllis Hoag return std::move(Err);
7265d7fd02SEllis Hoag
7365d7fd02SEllis Hoag return get(std::move(*BufferOrErr));
7465d7fd02SEllis Hoag }
7565d7fd02SEllis Hoag
7665d7fd02SEllis Hoag llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
get(std::unique_ptr<MemoryBuffer> Buffer)7765d7fd02SEllis Hoag InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer) {
7865d7fd02SEllis Hoag auto BinOrErr = object::createBinary(*Buffer);
7965d7fd02SEllis Hoag if (auto Err = BinOrErr.takeError())
8065d7fd02SEllis Hoag return std::move(Err);
8165d7fd02SEllis Hoag
8265d7fd02SEllis Hoag if (auto *Obj = dyn_cast<object::ObjectFile>(BinOrErr->get())) {
8365d7fd02SEllis Hoag auto CtxOrErr = Context::get(std::move(Buffer), *Obj);
8465d7fd02SEllis Hoag if (auto Err = CtxOrErr.takeError())
8565d7fd02SEllis Hoag return std::move(Err);
8665d7fd02SEllis Hoag auto T = Obj->makeTriple();
8765d7fd02SEllis Hoag if (T.isArch64Bit())
8865d7fd02SEllis Hoag return InstrProfCorrelatorImpl<uint64_t>::get(std::move(*CtxOrErr), *Obj);
8965d7fd02SEllis Hoag if (T.isArch32Bit())
9065d7fd02SEllis Hoag return InstrProfCorrelatorImpl<uint32_t>::get(std::move(*CtxOrErr), *Obj);
9165d7fd02SEllis Hoag }
9265d7fd02SEllis Hoag return make_error<InstrProfError>(
936d523911SEllis Hoag instrprof_error::unable_to_correlate_profile, "not an object file");
9465d7fd02SEllis Hoag }
9565d7fd02SEllis Hoag
getDataSize() const96c9baa560SEllis Hoag Optional<size_t> InstrProfCorrelator::getDataSize() const {
97c9baa560SEllis Hoag if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint32_t>>(this)) {
98c9baa560SEllis Hoag return C->getDataSize();
99c9baa560SEllis Hoag } else if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint64_t>>(this)) {
100c9baa560SEllis Hoag return C->getDataSize();
101c9baa560SEllis Hoag }
102c9baa560SEllis Hoag return {};
103c9baa560SEllis Hoag }
104c9baa560SEllis Hoag
10565d7fd02SEllis Hoag namespace llvm {
10665d7fd02SEllis Hoag
10765d7fd02SEllis Hoag template <>
InstrProfCorrelatorImpl(std::unique_ptr<InstrProfCorrelator::Context> Ctx)10865d7fd02SEllis Hoag InstrProfCorrelatorImpl<uint32_t>::InstrProfCorrelatorImpl(
10965d7fd02SEllis Hoag std::unique_ptr<InstrProfCorrelator::Context> Ctx)
11065d7fd02SEllis Hoag : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit,
11165d7fd02SEllis Hoag std::move(Ctx)) {}
11265d7fd02SEllis Hoag template <>
InstrProfCorrelatorImpl(std::unique_ptr<InstrProfCorrelator::Context> Ctx)11365d7fd02SEllis Hoag InstrProfCorrelatorImpl<uint64_t>::InstrProfCorrelatorImpl(
11465d7fd02SEllis Hoag std::unique_ptr<InstrProfCorrelator::Context> Ctx)
11565d7fd02SEllis Hoag : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit,
11665d7fd02SEllis Hoag std::move(Ctx)) {}
11765d7fd02SEllis Hoag template <>
classof(const InstrProfCorrelator * C)11865d7fd02SEllis Hoag bool InstrProfCorrelatorImpl<uint32_t>::classof(const InstrProfCorrelator *C) {
11965d7fd02SEllis Hoag return C->getKind() == InstrProfCorrelatorKind::CK_32Bit;
12065d7fd02SEllis Hoag }
12165d7fd02SEllis Hoag template <>
classof(const InstrProfCorrelator * C)12265d7fd02SEllis Hoag bool InstrProfCorrelatorImpl<uint64_t>::classof(const InstrProfCorrelator *C) {
12365d7fd02SEllis Hoag return C->getKind() == InstrProfCorrelatorKind::CK_64Bit;
12465d7fd02SEllis Hoag }
12565d7fd02SEllis Hoag
12665d7fd02SEllis Hoag } // end namespace llvm
12765d7fd02SEllis Hoag
12865d7fd02SEllis Hoag template <class IntPtrT>
12965d7fd02SEllis Hoag llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>>
get(std::unique_ptr<InstrProfCorrelator::Context> Ctx,const object::ObjectFile & Obj)13065d7fd02SEllis Hoag InstrProfCorrelatorImpl<IntPtrT>::get(
13165d7fd02SEllis Hoag std::unique_ptr<InstrProfCorrelator::Context> Ctx,
13265d7fd02SEllis Hoag const object::ObjectFile &Obj) {
13365d7fd02SEllis Hoag if (Obj.isELF() || Obj.isMachO()) {
13465d7fd02SEllis Hoag auto DICtx = DWARFContext::create(Obj);
13565d7fd02SEllis Hoag return std::make_unique<DwarfInstrProfCorrelator<IntPtrT>>(std::move(DICtx),
13665d7fd02SEllis Hoag std::move(Ctx));
13765d7fd02SEllis Hoag }
1386d523911SEllis Hoag return make_error<InstrProfError>(
1396d523911SEllis Hoag instrprof_error::unable_to_correlate_profile,
1406d523911SEllis Hoag "unsupported debug info format (only DWARF is supported)");
14165d7fd02SEllis Hoag }
14265d7fd02SEllis Hoag
14365d7fd02SEllis Hoag template <class IntPtrT>
correlateProfileData()14465d7fd02SEllis Hoag Error InstrProfCorrelatorImpl<IntPtrT>::correlateProfileData() {
145f1705952SEllis Hoag assert(Data.empty() && Names.empty() && NamesVec.empty());
14665d7fd02SEllis Hoag correlateProfileDataImpl();
1476d523911SEllis Hoag if (Data.empty() || NamesVec.empty())
1486d523911SEllis Hoag return make_error<InstrProfError>(
1496d523911SEllis Hoag instrprof_error::unable_to_correlate_profile,
1506d523911SEllis Hoag "could not find any profile metadata in debug info");
15165d7fd02SEllis Hoag auto Result =
152f1705952SEllis Hoag collectPGOFuncNameStrings(NamesVec, /*doCompression=*/false, Names);
15318ffb5dcSEllis Hoag CounterOffsets.clear();
154f1705952SEllis Hoag NamesVec.clear();
15565d7fd02SEllis Hoag return Result;
15665d7fd02SEllis Hoag }
15765d7fd02SEllis Hoag
15865d7fd02SEllis Hoag template <class IntPtrT>
addProbe(StringRef FunctionName,uint64_t CFGHash,IntPtrT CounterOffset,IntPtrT FunctionPtr,uint32_t NumCounters)15965d7fd02SEllis Hoag void InstrProfCorrelatorImpl<IntPtrT>::addProbe(StringRef FunctionName,
16065d7fd02SEllis Hoag uint64_t CFGHash,
16165d7fd02SEllis Hoag IntPtrT CounterOffset,
16265d7fd02SEllis Hoag IntPtrT FunctionPtr,
16365d7fd02SEllis Hoag uint32_t NumCounters) {
16418ffb5dcSEllis Hoag // Check if a probe was already added for this counter offset.
16518ffb5dcSEllis Hoag if (!CounterOffsets.insert(CounterOffset).second)
16618ffb5dcSEllis Hoag return;
16765d7fd02SEllis Hoag Data.push_back({
16865d7fd02SEllis Hoag maybeSwap<uint64_t>(IndexedInstrProf::ComputeHash(FunctionName)),
16965d7fd02SEllis Hoag maybeSwap<uint64_t>(CFGHash),
17065d7fd02SEllis Hoag // In this mode, CounterPtr actually stores the section relative address
17165d7fd02SEllis Hoag // of the counter.
17265d7fd02SEllis Hoag maybeSwap<IntPtrT>(CounterOffset),
17365d7fd02SEllis Hoag maybeSwap<IntPtrT>(FunctionPtr),
17465d7fd02SEllis Hoag // TODO: Value profiling is not yet supported.
17565d7fd02SEllis Hoag /*ValuesPtr=*/maybeSwap<IntPtrT>(0),
17665d7fd02SEllis Hoag maybeSwap<uint32_t>(NumCounters),
17765d7fd02SEllis Hoag /*NumValueSites=*/{maybeSwap<uint16_t>(0), maybeSwap<uint16_t>(0)},
17865d7fd02SEllis Hoag });
179f1705952SEllis Hoag NamesVec.push_back(FunctionName.str());
18065d7fd02SEllis Hoag }
18165d7fd02SEllis Hoag
18265d7fd02SEllis Hoag template <class IntPtrT>
18365d7fd02SEllis Hoag llvm::Optional<uint64_t>
getLocation(const DWARFDie & Die) const18465d7fd02SEllis Hoag DwarfInstrProfCorrelator<IntPtrT>::getLocation(const DWARFDie &Die) const {
18565d7fd02SEllis Hoag auto Locations = Die.getLocations(dwarf::DW_AT_location);
18665d7fd02SEllis Hoag if (!Locations) {
18765d7fd02SEllis Hoag consumeError(Locations.takeError());
18865d7fd02SEllis Hoag return {};
18965d7fd02SEllis Hoag }
19065d7fd02SEllis Hoag auto &DU = *Die.getDwarfUnit();
19165d7fd02SEllis Hoag auto AddressSize = DU.getAddressByteSize();
1929c2891a8SEllis Hoag for (auto &Location : *Locations) {
19365d7fd02SEllis Hoag DataExtractor Data(Location.Expr, DICtx->isLittleEndian(), AddressSize);
19465d7fd02SEllis Hoag DWARFExpression Expr(Data, AddressSize);
1959c2891a8SEllis Hoag for (auto &Op : Expr) {
1969c2891a8SEllis Hoag if (Op.getCode() == dwarf::DW_OP_addr) {
19765d7fd02SEllis Hoag return Op.getRawOperand(0);
1989c2891a8SEllis Hoag } else if (Op.getCode() == dwarf::DW_OP_addrx) {
1999c2891a8SEllis Hoag uint64_t Index = Op.getRawOperand(0);
2009c2891a8SEllis Hoag if (auto SA = DU.getAddrOffsetSectionItem(Index))
2019c2891a8SEllis Hoag return SA->Address;
2029c2891a8SEllis Hoag }
2039c2891a8SEllis Hoag }
20465d7fd02SEllis Hoag }
20565d7fd02SEllis Hoag return {};
20665d7fd02SEllis Hoag }
20765d7fd02SEllis Hoag
20865d7fd02SEllis Hoag template <class IntPtrT>
isDIEOfProbe(const DWARFDie & Die)20965d7fd02SEllis Hoag bool DwarfInstrProfCorrelator<IntPtrT>::isDIEOfProbe(const DWARFDie &Die) {
21065d7fd02SEllis Hoag const auto &ParentDie = Die.getParent();
21165d7fd02SEllis Hoag if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL())
21265d7fd02SEllis Hoag return false;
21365d7fd02SEllis Hoag if (Die.getTag() != dwarf::DW_TAG_variable)
21465d7fd02SEllis Hoag return false;
21565d7fd02SEllis Hoag if (!ParentDie.isSubprogramDIE())
21665d7fd02SEllis Hoag return false;
21765d7fd02SEllis Hoag if (!Die.hasChildren())
21865d7fd02SEllis Hoag return false;
21965d7fd02SEllis Hoag if (const char *Name = Die.getName(DINameKind::ShortName))
22065d7fd02SEllis Hoag return StringRef(Name).startswith(getInstrProfCountersVarPrefix());
22165d7fd02SEllis Hoag return false;
22265d7fd02SEllis Hoag }
22365d7fd02SEllis Hoag
22465d7fd02SEllis Hoag template <class IntPtrT>
correlateProfileDataImpl()22565d7fd02SEllis Hoag void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl() {
22665d7fd02SEllis Hoag auto maybeAddProbe = [&](DWARFDie Die) {
22765d7fd02SEllis Hoag if (!isDIEOfProbe(Die))
22865d7fd02SEllis Hoag return;
22965d7fd02SEllis Hoag Optional<const char *> FunctionName;
23065d7fd02SEllis Hoag Optional<uint64_t> CFGHash;
23165d7fd02SEllis Hoag Optional<uint64_t> CounterPtr = getLocation(Die);
23265d7fd02SEllis Hoag auto FunctionPtr =
23365d7fd02SEllis Hoag dwarf::toAddress(Die.getParent().find(dwarf::DW_AT_low_pc));
23465d7fd02SEllis Hoag Optional<uint64_t> NumCounters;
23565d7fd02SEllis Hoag for (const DWARFDie &Child : Die.children()) {
23665d7fd02SEllis Hoag if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation)
23765d7fd02SEllis Hoag continue;
23865d7fd02SEllis Hoag auto AnnotationFormName = Child.find(dwarf::DW_AT_name);
23965d7fd02SEllis Hoag auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value);
24065d7fd02SEllis Hoag if (!AnnotationFormName || !AnnotationFormValue)
24165d7fd02SEllis Hoag continue;
24265d7fd02SEllis Hoag auto AnnotationNameOrErr = AnnotationFormName->getAsCString();
24365d7fd02SEllis Hoag if (auto Err = AnnotationNameOrErr.takeError()) {
24465d7fd02SEllis Hoag consumeError(std::move(Err));
24565d7fd02SEllis Hoag continue;
24665d7fd02SEllis Hoag }
24765d7fd02SEllis Hoag StringRef AnnotationName = *AnnotationNameOrErr;
24865d7fd02SEllis Hoag if (AnnotationName.compare(
24965d7fd02SEllis Hoag InstrProfCorrelator::FunctionNameAttributeName) == 0) {
25065d7fd02SEllis Hoag if (auto EC =
25165d7fd02SEllis Hoag AnnotationFormValue->getAsCString().moveInto(FunctionName))
25265d7fd02SEllis Hoag consumeError(std::move(EC));
25365d7fd02SEllis Hoag } else if (AnnotationName.compare(
25465d7fd02SEllis Hoag InstrProfCorrelator::CFGHashAttributeName) == 0) {
25565d7fd02SEllis Hoag CFGHash = AnnotationFormValue->getAsUnsignedConstant();
25665d7fd02SEllis Hoag } else if (AnnotationName.compare(
25765d7fd02SEllis Hoag InstrProfCorrelator::NumCountersAttributeName) == 0) {
25865d7fd02SEllis Hoag NumCounters = AnnotationFormValue->getAsUnsignedConstant();
25965d7fd02SEllis Hoag }
26065d7fd02SEllis Hoag }
26165d7fd02SEllis Hoag if (!FunctionName || !CFGHash || !CounterPtr || !NumCounters) {
26265d7fd02SEllis Hoag LLVM_DEBUG(dbgs() << "Incomplete DIE for probe\n\tFunctionName: "
26365d7fd02SEllis Hoag << FunctionName << "\n\tCFGHash: " << CFGHash
26465d7fd02SEllis Hoag << "\n\tCounterPtr: " << CounterPtr
26565d7fd02SEllis Hoag << "\n\tNumCounters: " << NumCounters);
26665d7fd02SEllis Hoag LLVM_DEBUG(Die.dump(dbgs()));
26765d7fd02SEllis Hoag return;
26865d7fd02SEllis Hoag }
26965d7fd02SEllis Hoag uint64_t CountersStart = this->Ctx->CountersSectionStart;
27065d7fd02SEllis Hoag uint64_t CountersEnd = this->Ctx->CountersSectionEnd;
27165d7fd02SEllis Hoag if (*CounterPtr < CountersStart || *CounterPtr >= CountersEnd) {
27265d7fd02SEllis Hoag LLVM_DEBUG(
27365d7fd02SEllis Hoag dbgs() << "CounterPtr out of range for probe\n\tFunction Name: "
27465d7fd02SEllis Hoag << FunctionName << "\n\tExpected: [0x"
27565d7fd02SEllis Hoag << Twine::utohexstr(CountersStart) << ", 0x"
27665d7fd02SEllis Hoag << Twine::utohexstr(CountersEnd) << ")\n\tActual: 0x"
27765d7fd02SEllis Hoag << Twine::utohexstr(*CounterPtr));
27865d7fd02SEllis Hoag LLVM_DEBUG(Die.dump(dbgs()));
27965d7fd02SEllis Hoag return;
28065d7fd02SEllis Hoag }
28165d7fd02SEllis Hoag if (!FunctionPtr) {
28265d7fd02SEllis Hoag LLVM_DEBUG(dbgs() << "Could not find address of " << *FunctionName
28365d7fd02SEllis Hoag << "\n");
28465d7fd02SEllis Hoag LLVM_DEBUG(Die.dump(dbgs()));
28565d7fd02SEllis Hoag }
28665d7fd02SEllis Hoag this->addProbe(*FunctionName, *CFGHash, *CounterPtr - CountersStart,
287*129b531cSKazu Hirata FunctionPtr.value_or(0), *NumCounters);
28865d7fd02SEllis Hoag };
28965d7fd02SEllis Hoag for (auto &CU : DICtx->normal_units())
29065d7fd02SEllis Hoag for (const auto &Entry : CU->dies())
29165d7fd02SEllis Hoag maybeAddProbe(DWARFDie(CU.get(), &Entry));
29265d7fd02SEllis Hoag for (auto &CU : DICtx->dwo_units())
29365d7fd02SEllis Hoag for (const auto &Entry : CU->dies())
29465d7fd02SEllis Hoag maybeAddProbe(DWARFDie(CU.get(), &Entry));
29565d7fd02SEllis Hoag }
296