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