1 //===-- ProfiledBinary.cpp - Binary decoder ---------------------*- C++ -*-===// 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 "ProfiledBinary.h" 10 #include "ErrorHandling.h" 11 #include "ProfileGenerator.h" 12 #include "llvm/ADT/Triple.h" 13 #include "llvm/Demangle/Demangle.h" 14 #include "llvm/IR/DebugInfoMetadata.h" 15 #include "llvm/Support/CommandLine.h" 16 #include "llvm/Support/Format.h" 17 #include "llvm/Support/TargetRegistry.h" 18 #include "llvm/Support/TargetSelect.h" 19 20 #define DEBUG_TYPE "load-binary" 21 22 using namespace llvm; 23 using namespace sampleprof; 24 25 cl::opt<bool> ShowDisassemblyOnly("show-disassembly-only", cl::ReallyHidden, 26 cl::init(false), cl::ZeroOrMore, 27 cl::desc("Print disassembled code.")); 28 29 cl::opt<bool> ShowSourceLocations("show-source-locations", cl::ReallyHidden, 30 cl::init(false), cl::ZeroOrMore, 31 cl::desc("Print source locations.")); 32 33 cl::opt<bool> ShowCanonicalFnName("show-canonical-fname", cl::ReallyHidden, 34 cl::init(false), cl::ZeroOrMore, 35 cl::desc("Print canonical function name.")); 36 37 cl::opt<bool> ShowPseudoProbe( 38 "show-pseudo-probe", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore, 39 cl::desc("Print pseudo probe section and disassembled info.")); 40 41 namespace llvm { 42 namespace sampleprof { 43 44 static const Target *getTarget(const ObjectFile *Obj) { 45 Triple TheTriple = Obj->makeTriple(); 46 std::string Error; 47 std::string ArchName; 48 const Target *TheTarget = 49 TargetRegistry::lookupTarget(ArchName, TheTriple, Error); 50 if (!TheTarget) 51 exitWithError(Error, Obj->getFileName()); 52 return TheTarget; 53 } 54 55 void ProfiledBinary::load() { 56 // Attempt to open the binary. 57 OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path); 58 Binary &Binary = *OBinary.getBinary(); 59 60 auto *Obj = dyn_cast<ELFObjectFileBase>(&Binary); 61 if (!Obj) 62 exitWithError("not a valid Elf image", Path); 63 64 TheTriple = Obj->makeTriple(); 65 // Current only support X86 66 if (!TheTriple.isX86()) 67 exitWithError("unsupported target", TheTriple.getTriple()); 68 LLVM_DEBUG(dbgs() << "Loading " << Path << "\n"); 69 70 // Find the preferred load address for text sections. 71 setPreferredTextSegmentAddresses(Obj); 72 73 // Decode pseudo probe related section 74 decodePseudoProbe(Obj); 75 76 // Disassemble the text sections. 77 disassemble(Obj); 78 79 // Use function start and return address to infer prolog and epilog 80 ProEpilogTracker.inferPrologOffsets(FuncStartAddrMap); 81 ProEpilogTracker.inferEpilogOffsets(RetAddrs); 82 83 // TODO: decode other sections. 84 } 85 86 bool ProfiledBinary::inlineContextEqual(uint64_t Address1, 87 uint64_t Address2) const { 88 uint64_t Offset1 = virtualAddrToOffset(Address1); 89 uint64_t Offset2 = virtualAddrToOffset(Address2); 90 const FrameLocationStack &Context1 = getFrameLocationStack(Offset1); 91 const FrameLocationStack &Context2 = getFrameLocationStack(Offset2); 92 if (Context1.size() != Context2.size()) 93 return false; 94 if (Context1.empty()) 95 return false; 96 // The leaf frame contains location within the leaf, and it 97 // needs to be remove that as it's not part of the calling context 98 return std::equal(Context1.begin(), Context1.begin() + Context1.size() - 1, 99 Context2.begin(), Context2.begin() + Context2.size() - 1); 100 } 101 102 std::string 103 ProfiledBinary::getExpandedContextStr(const SmallVectorImpl<uint64_t> &Stack, 104 bool &WasLeafInlined) const { 105 std::string ContextStr; 106 SmallVector<std::string, 16> ContextVec; 107 // Process from frame root to leaf 108 for (auto Address : Stack) { 109 uint64_t Offset = virtualAddrToOffset(Address); 110 const FrameLocationStack &ExpandedContext = getFrameLocationStack(Offset); 111 // An instruction without a valid debug line will be ignored by sample 112 // processing 113 if (ExpandedContext.empty()) 114 return std::string(); 115 // Set WasLeafInlined to the size of inlined frame count for the last 116 // address which is leaf 117 WasLeafInlined = (ExpandedContext.size() > 1); 118 for (const auto &Loc : ExpandedContext) { 119 ContextVec.push_back(getCallSite(Loc)); 120 } 121 } 122 123 assert(ContextVec.size() && "Context length should be at least 1"); 124 // Compress the context string except for the leaf frame 125 std::string LeafFrame = ContextVec.back(); 126 ContextVec.pop_back(); 127 CSProfileGenerator::compressRecursionContext<std::string>(ContextVec); 128 129 std::ostringstream OContextStr; 130 for (uint32_t I = 0; I < (uint32_t)ContextVec.size(); I++) { 131 if (OContextStr.str().size()) { 132 OContextStr << " @ "; 133 } 134 OContextStr << ContextVec[I]; 135 } 136 // Only keep the function name for the leaf frame 137 if (OContextStr.str().size()) 138 OContextStr << " @ "; 139 OContextStr << StringRef(LeafFrame).split(":").first.str(); 140 return OContextStr.str(); 141 } 142 143 template <class ELFT> 144 void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName) { 145 const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName); 146 for (const typename ELFT::Phdr &Phdr : PhdrRange) { 147 if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_flags & ELF::PF_X)) { 148 // Segments will always be loaded at a page boundary. 149 PreferredTextSegmentAddresses.push_back(Phdr.p_vaddr & ~(Phdr.p_align - 1U)); 150 TextSegmentOffsets.push_back(Phdr.p_offset & ~(Phdr.p_align - 1U)); 151 } 152 } 153 154 if (PreferredTextSegmentAddresses.empty()) 155 exitWithError("no executable segment found", FileName); 156 } 157 158 void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFObjectFileBase *Obj) { 159 if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) 160 setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); 161 else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) 162 setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); 163 else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) 164 setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); 165 else if (const auto *ELFObj = cast<ELF64BEObjectFile>(Obj)) 166 setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); 167 else 168 llvm_unreachable("invalid ELF object format"); 169 } 170 171 void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) { 172 StringRef FileName = Obj->getFileName(); 173 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end(); 174 SI != SE; ++SI) { 175 const SectionRef &Section = *SI; 176 StringRef SectionName = unwrapOrError(Section.getName(), FileName); 177 178 if (SectionName == ".pseudo_probe_desc") { 179 StringRef Contents = unwrapOrError(Section.getContents(), FileName); 180 if (!ProbeDecoder.buildGUID2FuncDescMap( 181 reinterpret_cast<const uint8_t *>(Contents.data()), 182 Contents.size())) 183 exitWithError("Pseudo Probe decoder fail in .pseudo_probe_desc section"); 184 } else if (SectionName == ".pseudo_probe") { 185 StringRef Contents = unwrapOrError(Section.getContents(), FileName); 186 if (!ProbeDecoder.buildAddress2ProbeMap( 187 reinterpret_cast<const uint8_t *>(Contents.data()), 188 Contents.size())) 189 exitWithError("Pseudo Probe decoder fail in .pseudo_probe section"); 190 // set UsePseudoProbes flag, used for PerfReader 191 UsePseudoProbes = true; 192 } 193 } 194 195 if (ShowPseudoProbe) 196 ProbeDecoder.printGUID2FuncDescMap(outs()); 197 } 198 199 bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes, 200 SectionSymbolsTy &Symbols, 201 const SectionRef &Section) { 202 std::size_t SE = Symbols.size(); 203 uint64_t SectionOffset = Section.getAddress() - getPreferredBaseAddress(); 204 uint64_t SectSize = Section.getSize(); 205 uint64_t StartOffset = Symbols[SI].Addr - getPreferredBaseAddress(); 206 uint64_t EndOffset = (SI + 1 < SE) 207 ? Symbols[SI + 1].Addr - getPreferredBaseAddress() 208 : SectionOffset + SectSize; 209 if (StartOffset >= EndOffset) 210 return true; 211 212 StringRef SymbolName = 213 ShowCanonicalFnName 214 ? FunctionSamples::getCanonicalFnName(Symbols[SI].Name) 215 : Symbols[SI].Name; 216 if (ShowDisassemblyOnly) 217 outs() << '<' << SymbolName << ">:\n"; 218 219 auto WarnInvalidInsts = [](uint64_t Start, uint64_t End) { 220 WithColor::warning() << "Invalid instructions at " 221 << format("%8" PRIx64, Start) << " - " 222 << format("%8" PRIx64, End) << "\n"; 223 }; 224 225 uint64_t Offset = StartOffset; 226 // Size of a consecutive invalid instruction range starting from Offset -1 227 // backwards. 228 uint64_t InvalidInstLength = 0; 229 while (Offset < EndOffset) { 230 MCInst Inst; 231 uint64_t Size; 232 // Disassemble an instruction. 233 bool Disassembled = 234 DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset), 235 Offset + getPreferredBaseAddress(), nulls()); 236 if (Size == 0) 237 Size = 1; 238 239 if (ShowDisassemblyOnly) { 240 if (ShowPseudoProbe) { 241 ProbeDecoder.printProbeForAddress(outs(), 242 Offset + getPreferredBaseAddress()); 243 } 244 outs() << format("%8" PRIx64 ":", Offset + getPreferredBaseAddress()); 245 size_t Start = outs().tell(); 246 if (Disassembled) 247 IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs()); 248 else 249 outs() << "\t<unknown>"; 250 if (ShowSourceLocations) { 251 unsigned Cur = outs().tell() - Start; 252 if (Cur < 40) 253 outs().indent(40 - Cur); 254 InstructionPointer IP(this, Offset); 255 outs() << getReversedLocWithContext(symbolize(IP, ShowCanonicalFnName)); 256 } 257 outs() << "\n"; 258 } 259 260 if (Disassembled) { 261 const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode()); 262 // Populate a vector of the symbolized callsite at this location 263 // We don't need symbolized info for probe-based profile, just use an 264 // empty stack as an entry to indicate a valid binary offset 265 FrameLocationStack SymbolizedCallStack; 266 if (!UsePseudoProbes) { 267 InstructionPointer IP(this, Offset); 268 SymbolizedCallStack = symbolize(IP, true); 269 } 270 Offset2LocStackMap[Offset] = SymbolizedCallStack; 271 // Populate address maps. 272 CodeAddrs.push_back(Offset); 273 if (MCDesc.isCall()) 274 CallAddrs.insert(Offset); 275 else if (MCDesc.isReturn()) 276 RetAddrs.insert(Offset); 277 278 if (InvalidInstLength) { 279 WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1); 280 InvalidInstLength = 0; 281 } 282 } else { 283 InvalidInstLength += Size; 284 } 285 286 Offset += Size; 287 } 288 289 if (InvalidInstLength) 290 WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1); 291 292 if (ShowDisassemblyOnly) 293 outs() << "\n"; 294 295 FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str(); 296 return true; 297 } 298 299 void ProfiledBinary::setUpDisassembler(const ELFObjectFileBase *Obj) { 300 const Target *TheTarget = getTarget(Obj); 301 std::string TripleName = TheTriple.getTriple(); 302 StringRef FileName = Obj->getFileName(); 303 304 MRI.reset(TheTarget->createMCRegInfo(TripleName)); 305 if (!MRI) 306 exitWithError("no register info for target " + TripleName, FileName); 307 308 MCTargetOptions MCOptions; 309 AsmInfo.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); 310 if (!AsmInfo) 311 exitWithError("no assembly info for target " + TripleName, FileName); 312 313 SubtargetFeatures Features = Obj->getFeatures(); 314 STI.reset( 315 TheTarget->createMCSubtargetInfo(TripleName, "", Features.getString())); 316 if (!STI) 317 exitWithError("no subtarget info for target " + TripleName, FileName); 318 319 MII.reset(TheTarget->createMCInstrInfo()); 320 if (!MII) 321 exitWithError("no instruction info for target " + TripleName, FileName); 322 323 MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get()); 324 std::unique_ptr<MCObjectFileInfo> MOFI( 325 TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); 326 Ctx.setObjectFileInfo(MOFI.get()); 327 DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx)); 328 if (!DisAsm) 329 exitWithError("no disassembler for target " + TripleName, FileName); 330 331 MIA.reset(TheTarget->createMCInstrAnalysis(MII.get())); 332 333 int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); 334 IPrinter.reset(TheTarget->createMCInstPrinter( 335 Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI)); 336 IPrinter->setPrintBranchImmAsAddress(true); 337 } 338 339 void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) { 340 // Set up disassembler and related components. 341 setUpDisassembler(Obj); 342 343 // Create a mapping from virtual address to symbol name. The symbols in text 344 // sections are the candidates to dissassemble. 345 std::map<SectionRef, SectionSymbolsTy> AllSymbols; 346 StringRef FileName = Obj->getFileName(); 347 for (const SymbolRef &Symbol : Obj->symbols()) { 348 const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName); 349 const StringRef Name = unwrapOrError(Symbol.getName(), FileName); 350 section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName); 351 if (SecI != Obj->section_end()) 352 AllSymbols[*SecI].push_back(SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE)); 353 } 354 355 // Sort all the symbols. Use a stable sort to stabilize the output. 356 for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols) 357 stable_sort(SecSyms.second); 358 359 if (ShowDisassemblyOnly) 360 outs() << "\nDisassembly of " << FileName << ":\n"; 361 362 // Dissassemble a text section. 363 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end(); 364 SI != SE; ++SI) { 365 const SectionRef &Section = *SI; 366 if (!Section.isText()) 367 continue; 368 369 uint64_t ImageLoadAddr = getPreferredBaseAddress(); 370 uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr; 371 uint64_t SectSize = Section.getSize(); 372 if (!SectSize) 373 continue; 374 375 // Register the text section. 376 TextSections.insert({SectionOffset, SectSize}); 377 378 if (ShowDisassemblyOnly) { 379 StringRef SectionName = unwrapOrError(Section.getName(), FileName); 380 outs() << "\nDisassembly of section " << SectionName; 381 outs() << " [" << format("0x%" PRIx64, Section.getAddress()) << ", " 382 << format("0x%" PRIx64, Section.getAddress() + SectSize) 383 << "]:\n\n"; 384 } 385 386 // Get the section data. 387 ArrayRef<uint8_t> Bytes = 388 arrayRefFromStringRef(unwrapOrError(Section.getContents(), FileName)); 389 390 // Get the list of all the symbols in this section. 391 SectionSymbolsTy &Symbols = AllSymbols[Section]; 392 393 // Disassemble symbol by symbol. 394 for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) { 395 if (!dissassembleSymbol(SI, Bytes, Symbols, Section)) 396 exitWithError("disassembling error", FileName); 397 } 398 } 399 } 400 401 void ProfiledBinary::setupSymbolizer() { 402 symbolize::LLVMSymbolizer::Options SymbolizerOpts; 403 SymbolizerOpts.PrintFunctions = 404 DILineInfoSpecifier::FunctionNameKind::LinkageName; 405 SymbolizerOpts.Demangle = false; 406 SymbolizerOpts.DefaultArch = TheTriple.getArchName().str(); 407 SymbolizerOpts.UseSymbolTable = false; 408 SymbolizerOpts.RelativeAddresses = false; 409 Symbolizer = std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOpts); 410 } 411 412 FrameLocationStack ProfiledBinary::symbolize(const InstructionPointer &IP, 413 bool UseCanonicalFnName) { 414 assert(this == IP.Binary && 415 "Binary should only symbolize its own instruction"); 416 auto Addr = object::SectionedAddress{IP.Offset + getPreferredBaseAddress(), 417 object::SectionedAddress::UndefSection}; 418 DIInliningInfo InlineStack = 419 unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName()); 420 421 FrameLocationStack CallStack; 422 423 for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) { 424 const auto &CallerFrame = InlineStack.getFrame(I); 425 if (CallerFrame.FunctionName == "<invalid>") 426 break; 427 StringRef FunctionName(CallerFrame.FunctionName); 428 if (UseCanonicalFnName) 429 FunctionName = FunctionSamples::getCanonicalFnName(FunctionName); 430 LineLocation Line(CallerFrame.Line - CallerFrame.StartLine, 431 DILocation::getBaseDiscriminatorFromDiscriminator( 432 CallerFrame.Discriminator, 433 /* IsFSDiscriminator */ false)); 434 FrameLocation Callsite(FunctionName.str(), Line); 435 CallStack.push_back(Callsite); 436 } 437 438 return CallStack; 439 } 440 441 InstructionPointer::InstructionPointer(ProfiledBinary *Binary, uint64_t Address, 442 bool RoundToNext) 443 : Binary(Binary), Address(Address) { 444 Index = Binary->getIndexForAddr(Address); 445 if (RoundToNext) { 446 // we might get address which is not the code 447 // it should round to the next valid address 448 this->Address = Binary->getAddressforIndex(Index); 449 } 450 } 451 452 void InstructionPointer::advance() { 453 Index++; 454 Address = Binary->getAddressforIndex(Index); 455 } 456 457 void InstructionPointer::backward() { 458 Index--; 459 Address = Binary->getAddressforIndex(Index); 460 } 461 462 void InstructionPointer::update(uint64_t Addr) { 463 Address = Addr; 464 Index = Binary->getIndexForAddr(Address); 465 } 466 467 } // end namespace sampleprof 468 } // end namespace llvm 469