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