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 "could not find counter section (" INSTR_PROF_CNTS_SECT_NAME ")"); 28 } 29 30 const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name"; 31 const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash"; 32 const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters"; 33 34 llvm::Expected<std::unique_ptr<InstrProfCorrelator::Context>> 35 InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer, 36 const object::ObjectFile &Obj) { 37 auto CountersSection = getCountersSection(Obj); 38 if (auto Err = CountersSection.takeError()) 39 return std::move(Err); 40 auto C = std::make_unique<Context>(); 41 C->Buffer = std::move(Buffer); 42 C->CountersSectionStart = CountersSection->getAddress(); 43 C->CountersSectionEnd = C->CountersSectionStart + CountersSection->getSize(); 44 C->ShouldSwapBytes = Obj.isLittleEndian() != sys::IsLittleEndianHost; 45 return Expected<std::unique_ptr<Context>>(std::move(C)); 46 } 47 48 llvm::Expected<std::unique_ptr<InstrProfCorrelator>> 49 InstrProfCorrelator::get(StringRef DebugInfoFilename) { 50 auto DsymObjectsOrErr = 51 object::MachOObjectFile::findDsymObjectMembers(DebugInfoFilename); 52 if (auto Err = DsymObjectsOrErr.takeError()) 53 return std::move(Err); 54 if (!DsymObjectsOrErr->empty()) { 55 // TODO: Enable profile correlation when there are multiple objects in a 56 // dSYM bundle. 57 if (DsymObjectsOrErr->size() > 1) 58 return make_error<InstrProfError>( 59 instrprof_error::unable_to_correlate_profile, 60 "using multiple objects is not yet supported"); 61 DebugInfoFilename = *DsymObjectsOrErr->begin(); 62 } 63 auto BufferOrErr = 64 errorOrToExpected(MemoryBuffer::getFile(DebugInfoFilename)); 65 if (auto Err = BufferOrErr.takeError()) 66 return std::move(Err); 67 68 return get(std::move(*BufferOrErr)); 69 } 70 71 llvm::Expected<std::unique_ptr<InstrProfCorrelator>> 72 InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer) { 73 auto BinOrErr = object::createBinary(*Buffer); 74 if (auto Err = BinOrErr.takeError()) 75 return std::move(Err); 76 77 if (auto *Obj = dyn_cast<object::ObjectFile>(BinOrErr->get())) { 78 auto CtxOrErr = Context::get(std::move(Buffer), *Obj); 79 if (auto Err = CtxOrErr.takeError()) 80 return std::move(Err); 81 auto T = Obj->makeTriple(); 82 if (T.isArch64Bit()) 83 return InstrProfCorrelatorImpl<uint64_t>::get(std::move(*CtxOrErr), *Obj); 84 if (T.isArch32Bit()) 85 return InstrProfCorrelatorImpl<uint32_t>::get(std::move(*CtxOrErr), *Obj); 86 } 87 return make_error<InstrProfError>( 88 instrprof_error::unable_to_correlate_profile, "not an object file"); 89 } 90 91 namespace llvm { 92 93 template <> 94 InstrProfCorrelatorImpl<uint32_t>::InstrProfCorrelatorImpl( 95 std::unique_ptr<InstrProfCorrelator::Context> Ctx) 96 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit, 97 std::move(Ctx)) {} 98 template <> 99 InstrProfCorrelatorImpl<uint64_t>::InstrProfCorrelatorImpl( 100 std::unique_ptr<InstrProfCorrelator::Context> Ctx) 101 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit, 102 std::move(Ctx)) {} 103 template <> 104 bool InstrProfCorrelatorImpl<uint32_t>::classof(const InstrProfCorrelator *C) { 105 return C->getKind() == InstrProfCorrelatorKind::CK_32Bit; 106 } 107 template <> 108 bool InstrProfCorrelatorImpl<uint64_t>::classof(const InstrProfCorrelator *C) { 109 return C->getKind() == InstrProfCorrelatorKind::CK_64Bit; 110 } 111 112 } // end namespace llvm 113 114 template <class IntPtrT> 115 llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>> 116 InstrProfCorrelatorImpl<IntPtrT>::get( 117 std::unique_ptr<InstrProfCorrelator::Context> Ctx, 118 const object::ObjectFile &Obj) { 119 if (Obj.isELF() || Obj.isMachO()) { 120 auto DICtx = DWARFContext::create(Obj); 121 return std::make_unique<DwarfInstrProfCorrelator<IntPtrT>>(std::move(DICtx), 122 std::move(Ctx)); 123 } 124 return make_error<InstrProfError>( 125 instrprof_error::unable_to_correlate_profile, 126 "unsupported debug info format (only DWARF is supported)"); 127 } 128 129 template <class IntPtrT> 130 Error InstrProfCorrelatorImpl<IntPtrT>::correlateProfileData() { 131 assert(Data.empty() && Names.empty() && NamesVec.empty()); 132 correlateProfileDataImpl(); 133 if (Data.empty() || NamesVec.empty()) 134 return make_error<InstrProfError>( 135 instrprof_error::unable_to_correlate_profile, 136 "could not find any profile metadata in debug info"); 137 auto Result = 138 collectPGOFuncNameStrings(NamesVec, /*doCompression=*/false, Names); 139 CounterOffsets.clear(); 140 NamesVec.clear(); 141 return Result; 142 } 143 144 template <class IntPtrT> 145 void InstrProfCorrelatorImpl<IntPtrT>::addProbe(StringRef FunctionName, 146 uint64_t CFGHash, 147 IntPtrT CounterOffset, 148 IntPtrT FunctionPtr, 149 uint32_t NumCounters) { 150 // Check if a probe was already added for this counter offset. 151 if (!CounterOffsets.insert(CounterOffset).second) 152 return; 153 Data.push_back({ 154 maybeSwap<uint64_t>(IndexedInstrProf::ComputeHash(FunctionName)), 155 maybeSwap<uint64_t>(CFGHash), 156 // In this mode, CounterPtr actually stores the section relative address 157 // of the counter. 158 maybeSwap<IntPtrT>(CounterOffset), 159 maybeSwap<IntPtrT>(FunctionPtr), 160 // TODO: Value profiling is not yet supported. 161 /*ValuesPtr=*/maybeSwap<IntPtrT>(0), 162 maybeSwap<uint32_t>(NumCounters), 163 /*NumValueSites=*/{maybeSwap<uint16_t>(0), maybeSwap<uint16_t>(0)}, 164 }); 165 NamesVec.push_back(FunctionName.str()); 166 } 167 168 template <class IntPtrT> 169 llvm::Optional<uint64_t> 170 DwarfInstrProfCorrelator<IntPtrT>::getLocation(const DWARFDie &Die) const { 171 auto Locations = Die.getLocations(dwarf::DW_AT_location); 172 if (!Locations) { 173 consumeError(Locations.takeError()); 174 return {}; 175 } 176 auto &DU = *Die.getDwarfUnit(); 177 auto AddressSize = DU.getAddressByteSize(); 178 for (auto &Location : *Locations) { 179 DataExtractor Data(Location.Expr, DICtx->isLittleEndian(), AddressSize); 180 DWARFExpression Expr(Data, AddressSize); 181 for (auto &Op : Expr) { 182 if (Op.getCode() == dwarf::DW_OP_addr) { 183 return Op.getRawOperand(0); 184 } else if (Op.getCode() == dwarf::DW_OP_addrx) { 185 uint64_t Index = Op.getRawOperand(0); 186 if (auto SA = DU.getAddrOffsetSectionItem(Index)) 187 return SA->Address; 188 } 189 } 190 } 191 return {}; 192 } 193 194 template <class IntPtrT> 195 bool DwarfInstrProfCorrelator<IntPtrT>::isDIEOfProbe(const DWARFDie &Die) { 196 const auto &ParentDie = Die.getParent(); 197 if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL()) 198 return false; 199 if (Die.getTag() != dwarf::DW_TAG_variable) 200 return false; 201 if (!ParentDie.isSubprogramDIE()) 202 return false; 203 if (!Die.hasChildren()) 204 return false; 205 if (const char *Name = Die.getName(DINameKind::ShortName)) 206 return StringRef(Name).startswith(getInstrProfCountersVarPrefix()); 207 return false; 208 } 209 210 template <class IntPtrT> 211 void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl() { 212 auto maybeAddProbe = [&](DWARFDie Die) { 213 if (!isDIEOfProbe(Die)) 214 return; 215 Optional<const char *> FunctionName; 216 Optional<uint64_t> CFGHash; 217 Optional<uint64_t> CounterPtr = getLocation(Die); 218 auto FunctionPtr = 219 dwarf::toAddress(Die.getParent().find(dwarf::DW_AT_low_pc)); 220 Optional<uint64_t> NumCounters; 221 for (const DWARFDie &Child : Die.children()) { 222 if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation) 223 continue; 224 auto AnnotationFormName = Child.find(dwarf::DW_AT_name); 225 auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value); 226 if (!AnnotationFormName || !AnnotationFormValue) 227 continue; 228 auto AnnotationNameOrErr = AnnotationFormName->getAsCString(); 229 if (auto Err = AnnotationNameOrErr.takeError()) { 230 consumeError(std::move(Err)); 231 continue; 232 } 233 StringRef AnnotationName = *AnnotationNameOrErr; 234 if (AnnotationName.compare( 235 InstrProfCorrelator::FunctionNameAttributeName) == 0) { 236 if (auto EC = 237 AnnotationFormValue->getAsCString().moveInto(FunctionName)) 238 consumeError(std::move(EC)); 239 } else if (AnnotationName.compare( 240 InstrProfCorrelator::CFGHashAttributeName) == 0) { 241 CFGHash = AnnotationFormValue->getAsUnsignedConstant(); 242 } else if (AnnotationName.compare( 243 InstrProfCorrelator::NumCountersAttributeName) == 0) { 244 NumCounters = AnnotationFormValue->getAsUnsignedConstant(); 245 } 246 } 247 if (!FunctionName || !CFGHash || !CounterPtr || !NumCounters) { 248 LLVM_DEBUG(dbgs() << "Incomplete DIE for probe\n\tFunctionName: " 249 << FunctionName << "\n\tCFGHash: " << CFGHash 250 << "\n\tCounterPtr: " << CounterPtr 251 << "\n\tNumCounters: " << NumCounters); 252 LLVM_DEBUG(Die.dump(dbgs())); 253 return; 254 } 255 uint64_t CountersStart = this->Ctx->CountersSectionStart; 256 uint64_t CountersEnd = this->Ctx->CountersSectionEnd; 257 if (*CounterPtr < CountersStart || *CounterPtr >= CountersEnd) { 258 LLVM_DEBUG( 259 dbgs() << "CounterPtr out of range for probe\n\tFunction Name: " 260 << FunctionName << "\n\tExpected: [0x" 261 << Twine::utohexstr(CountersStart) << ", 0x" 262 << Twine::utohexstr(CountersEnd) << ")\n\tActual: 0x" 263 << Twine::utohexstr(*CounterPtr)); 264 LLVM_DEBUG(Die.dump(dbgs())); 265 return; 266 } 267 if (!FunctionPtr) { 268 LLVM_DEBUG(dbgs() << "Could not find address of " << *FunctionName 269 << "\n"); 270 LLVM_DEBUG(Die.dump(dbgs())); 271 } 272 this->addProbe(*FunctionName, *CFGHash, *CounterPtr - CountersStart, 273 FunctionPtr.getValueOr(0), *NumCounters); 274 }; 275 for (auto &CU : DICtx->normal_units()) 276 for (const auto &Entry : CU->dies()) 277 maybeAddProbe(DWARFDie(CU.get(), &Entry)); 278 for (auto &CU : DICtx->dwo_units()) 279 for (const auto &Entry : CU->dies()) 280 maybeAddProbe(DWARFDie(CU.get(), &Entry)); 281 } 282