1 //===-- InstrProfCorrelator.cpp -------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/ProfileData/InstrProfCorrelator.h" 10 #include "llvm/Object/MachO.h" 11 #include "llvm/Support/Debug.h" 12 #include "llvm/Support/FileSystem.h" 13 #include "llvm/Support/Path.h" 14 15 #define DEBUG_TYPE "correlator" 16 17 using namespace llvm; 18 19 /// Get the __llvm_prf_cnts section. 20 Expected<object::SectionRef> getCountersSection(const object::ObjectFile &Obj) { 21 for (auto &Section : Obj.sections()) 22 if (auto SectionName = Section.getName()) 23 if (SectionName.get() == INSTR_PROF_CNTS_SECT_NAME) 24 return Section; 25 return make_error<InstrProfError>( 26 instrprof_error::unable_to_correlate_profile); 27 } 28 29 const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name"; 30 const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash"; 31 const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters"; 32 33 llvm::Expected<std::unique_ptr<InstrProfCorrelator::Context>> 34 InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer, 35 const object::ObjectFile &Obj) { 36 auto CountersSection = getCountersSection(Obj); 37 if (auto Err = CountersSection.takeError()) 38 return std::move(Err); 39 auto C = std::make_unique<Context>(); 40 C->Buffer = std::move(Buffer); 41 C->CountersSectionStart = CountersSection->getAddress(); 42 C->CountersSectionEnd = C->CountersSectionStart + CountersSection->getSize(); 43 C->ShouldSwapBytes = Obj.isLittleEndian() != sys::IsLittleEndianHost; 44 return Expected<std::unique_ptr<Context>>(std::move(C)); 45 } 46 47 llvm::Expected<std::unique_ptr<InstrProfCorrelator>> 48 InstrProfCorrelator::get(StringRef DebugInfoFilename) { 49 auto DsymObjectsOrErr = 50 object::MachOObjectFile::findDsymObjectMembers(DebugInfoFilename); 51 if (auto Err = DsymObjectsOrErr.takeError()) 52 return std::move(Err); 53 if (!DsymObjectsOrErr->empty()) { 54 // TODO: Enable profile correlation when there are multiple objects in a 55 // dSYM bundle. 56 if (DsymObjectsOrErr->size() > 1) 57 return createStringError( 58 std::error_code(), 59 "Profile correlation using multiple objects is not yet supported"); 60 DebugInfoFilename = *DsymObjectsOrErr->begin(); 61 } 62 auto BufferOrErr = 63 errorOrToExpected(MemoryBuffer::getFile(DebugInfoFilename)); 64 if (auto Err = BufferOrErr.takeError()) 65 return std::move(Err); 66 67 return get(std::move(*BufferOrErr)); 68 } 69 70 llvm::Expected<std::unique_ptr<InstrProfCorrelator>> 71 InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer) { 72 auto BinOrErr = object::createBinary(*Buffer); 73 if (auto Err = BinOrErr.takeError()) 74 return std::move(Err); 75 76 if (auto *Obj = dyn_cast<object::ObjectFile>(BinOrErr->get())) { 77 auto CtxOrErr = Context::get(std::move(Buffer), *Obj); 78 if (auto Err = CtxOrErr.takeError()) 79 return std::move(Err); 80 auto T = Obj->makeTriple(); 81 if (T.isArch64Bit()) 82 return InstrProfCorrelatorImpl<uint64_t>::get(std::move(*CtxOrErr), *Obj); 83 if (T.isArch32Bit()) 84 return InstrProfCorrelatorImpl<uint32_t>::get(std::move(*CtxOrErr), *Obj); 85 } 86 return make_error<InstrProfError>( 87 instrprof_error::unable_to_correlate_profile); 88 } 89 90 template <> 91 InstrProfCorrelatorImpl<uint32_t>::InstrProfCorrelatorImpl( 92 std::unique_ptr<InstrProfCorrelator::Context> Ctx) 93 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit, 94 std::move(Ctx)) {} 95 template <> 96 InstrProfCorrelatorImpl<uint64_t>::InstrProfCorrelatorImpl( 97 std::unique_ptr<InstrProfCorrelator::Context> Ctx) 98 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit, 99 std::move(Ctx)) {} 100 template <> 101 bool InstrProfCorrelatorImpl<uint32_t>::classof(const InstrProfCorrelator *C) { 102 return C->getKind() == InstrProfCorrelatorKind::CK_32Bit; 103 } 104 template <> 105 bool InstrProfCorrelatorImpl<uint64_t>::classof(const InstrProfCorrelator *C) { 106 return C->getKind() == InstrProfCorrelatorKind::CK_64Bit; 107 } 108 109 template <class IntPtrT> 110 llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>> 111 InstrProfCorrelatorImpl<IntPtrT>::get( 112 std::unique_ptr<InstrProfCorrelator::Context> Ctx, 113 const object::ObjectFile &Obj) { 114 if (Obj.isELF() || Obj.isMachO()) { 115 auto DICtx = DWARFContext::create(Obj); 116 return std::make_unique<DwarfInstrProfCorrelator<IntPtrT>>(std::move(DICtx), 117 std::move(Ctx)); 118 } 119 return make_error<InstrProfError>(instrprof_error::unsupported_debug_format); 120 } 121 122 template <class IntPtrT> 123 Error InstrProfCorrelatorImpl<IntPtrT>::correlateProfileData() { 124 assert(Data.empty() && CompressedNames.empty() && Names.empty()); 125 correlateProfileDataImpl(); 126 auto Result = 127 collectPGOFuncNameStrings(Names, /*doCompression=*/true, CompressedNames); 128 Names.clear(); 129 return Result; 130 } 131 132 template <class IntPtrT> 133 void InstrProfCorrelatorImpl<IntPtrT>::addProbe(StringRef FunctionName, 134 uint64_t CFGHash, 135 IntPtrT CounterOffset, 136 IntPtrT FunctionPtr, 137 uint32_t NumCounters) { 138 Data.push_back({ 139 maybeSwap<uint64_t>(IndexedInstrProf::ComputeHash(FunctionName)), 140 maybeSwap<uint64_t>(CFGHash), 141 // In this mode, CounterPtr actually stores the section relative address 142 // of the counter. 143 maybeSwap<IntPtrT>(CounterOffset), 144 maybeSwap<IntPtrT>(FunctionPtr), 145 // TODO: Value profiling is not yet supported. 146 /*ValuesPtr=*/maybeSwap<IntPtrT>(0), 147 maybeSwap<uint32_t>(NumCounters), 148 /*NumValueSites=*/{maybeSwap<uint16_t>(0), maybeSwap<uint16_t>(0)}, 149 }); 150 Names.push_back(FunctionName.str()); 151 } 152 153 template <class IntPtrT> 154 llvm::Optional<uint64_t> 155 DwarfInstrProfCorrelator<IntPtrT>::getLocation(const DWARFDie &Die) const { 156 auto Locations = Die.getLocations(dwarf::DW_AT_location); 157 if (!Locations) { 158 consumeError(Locations.takeError()); 159 return {}; 160 } 161 auto &DU = *Die.getDwarfUnit(); 162 for (auto &Location : *Locations) { 163 auto AddressSize = DU.getAddressByteSize(); 164 DataExtractor Data(Location.Expr, DICtx->isLittleEndian(), AddressSize); 165 DWARFExpression Expr(Data, AddressSize); 166 for (auto &Op : Expr) 167 if (Op.getCode() == dwarf::DW_OP_addr) 168 return Op.getRawOperand(0); 169 } 170 return {}; 171 } 172 173 template <class IntPtrT> 174 bool DwarfInstrProfCorrelator<IntPtrT>::isDIEOfProbe(const DWARFDie &Die) { 175 const auto &ParentDie = Die.getParent(); 176 if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL()) 177 return false; 178 if (Die.getTag() != dwarf::DW_TAG_variable) 179 return false; 180 if (!ParentDie.isSubprogramDIE()) 181 return false; 182 if (!Die.hasChildren()) 183 return false; 184 if (const char *Name = Die.getName(DINameKind::ShortName)) 185 return StringRef(Name).startswith(getInstrProfCountersVarPrefix()); 186 return false; 187 } 188 189 template <class IntPtrT> 190 void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl() { 191 auto maybeAddProbe = [&](DWARFDie Die) { 192 if (!isDIEOfProbe(Die)) 193 return; 194 Optional<const char *> FunctionName; 195 Optional<uint64_t> CFGHash; 196 Optional<uint64_t> CounterPtr = getLocation(Die); 197 auto FunctionPtr = 198 dwarf::toAddress(Die.getParent().find(dwarf::DW_AT_low_pc)); 199 Optional<uint64_t> NumCounters; 200 for (const DWARFDie &Child : Die.children()) { 201 if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation) 202 continue; 203 auto AnnotationFormName = Child.find(dwarf::DW_AT_name); 204 auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value); 205 if (!AnnotationFormName || !AnnotationFormValue) 206 continue; 207 auto AnnotationNameOrErr = AnnotationFormName->getAsCString(); 208 if (auto Err = AnnotationNameOrErr.takeError()) { 209 consumeError(std::move(Err)); 210 continue; 211 } 212 StringRef AnnotationName = *AnnotationNameOrErr; 213 if (AnnotationName.compare( 214 InstrProfCorrelator::FunctionNameAttributeName) == 0) { 215 if (auto EC = 216 AnnotationFormValue->getAsCString().moveInto(FunctionName)) 217 consumeError(std::move(EC)); 218 } else if (AnnotationName.compare( 219 InstrProfCorrelator::CFGHashAttributeName) == 0) { 220 CFGHash = AnnotationFormValue->getAsUnsignedConstant(); 221 } else if (AnnotationName.compare( 222 InstrProfCorrelator::NumCountersAttributeName) == 0) { 223 NumCounters = AnnotationFormValue->getAsUnsignedConstant(); 224 } 225 } 226 if (!FunctionName || !CFGHash || !CounterPtr || !NumCounters) { 227 LLVM_DEBUG(dbgs() << "Incomplete DIE for probe\n\tFunctionName: " 228 << FunctionName << "\n\tCFGHash: " << CFGHash 229 << "\n\tCounterPtr: " << CounterPtr 230 << "\n\tNumCounters: " << NumCounters); 231 LLVM_DEBUG(Die.dump(dbgs())); 232 return; 233 } 234 uint64_t CountersStart = this->Ctx->CountersSectionStart; 235 uint64_t CountersEnd = this->Ctx->CountersSectionEnd; 236 if (*CounterPtr < CountersStart || *CounterPtr >= CountersEnd) { 237 LLVM_DEBUG( 238 dbgs() << "CounterPtr out of range for probe\n\tFunction Name: " 239 << FunctionName << "\n\tExpected: [0x" 240 << Twine::utohexstr(CountersStart) << ", 0x" 241 << Twine::utohexstr(CountersEnd) << ")\n\tActual: 0x" 242 << Twine::utohexstr(*CounterPtr)); 243 LLVM_DEBUG(Die.dump(dbgs())); 244 return; 245 } 246 if (!FunctionPtr) { 247 LLVM_DEBUG(dbgs() << "Could not find address of " << *FunctionName 248 << "\n"); 249 LLVM_DEBUG(Die.dump(dbgs())); 250 } 251 this->addProbe(*FunctionName, *CFGHash, *CounterPtr - CountersStart, 252 FunctionPtr.getValueOr(0), *NumCounters); 253 }; 254 for (auto &CU : DICtx->normal_units()) 255 for (const auto &Entry : CU->dies()) 256 maybeAddProbe(DWARFDie(CU.get(), &Entry)); 257 for (auto &CU : DICtx->dwo_units()) 258 for (const auto &Entry : CU->dies()) 259 maybeAddProbe(DWARFDie(CU.get(), &Entry)); 260 } 261