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/DebugInfo/Symbolize/SymbolizableModule.h"
14 #include "llvm/Demangle/Demangle.h"
15 #include "llvm/IR/DebugInfoMetadata.h"
16 #include "llvm/MC/TargetRegistry.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/Format.h"
19 #include "llvm/Support/TargetSelect.h"
20 
21 #define DEBUG_TYPE "load-binary"
22 
23 using namespace llvm;
24 using namespace sampleprof;
25 
26 cl::opt<bool> ShowDisassemblyOnly("show-disassembly-only",
27                                   cl::desc("Print disassembled code."));
28 
29 cl::opt<bool> ShowSourceLocations("show-source-locations",
30                                   cl::desc("Print source locations."));
31 
32 static cl::opt<bool>
33     ShowCanonicalFnName("show-canonical-fname",
34                         cl::desc("Print canonical function name."));
35 
36 static cl::opt<bool> ShowPseudoProbe(
37     "show-pseudo-probe",
38     cl::desc("Print pseudo probe section and disassembled info."));
39 
40 static cl::opt<bool> UseDwarfCorrelation(
41     "use-dwarf-correlation",
42     cl::desc("Use dwarf for profile correlation even when binary contains "
43              "pseudo probe."));
44 
45 static cl::opt<std::string>
46     DWPPath("dwp", cl::init(""),
47             cl::desc("Path of .dwp file. When not specified, it will be "
48                      "<binary>.dwp in the same directory as the main binary."));
49 
50 static cl::list<std::string> DisassembleFunctions(
51     "disassemble-functions", cl::CommaSeparated,
52     cl::desc("List of functions to print disassembly for. Accept demangled "
53              "names only. Only work with show-disassembly-only"));
54 
55 extern cl::opt<bool> ShowDetailedWarning;
56 
57 namespace llvm {
58 namespace sampleprof {
59 
60 static const Target *getTarget(const ObjectFile *Obj) {
61   Triple TheTriple = Obj->makeTriple();
62   std::string Error;
63   std::string ArchName;
64   const Target *TheTarget =
65       TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
66   if (!TheTarget)
67     exitWithError(Error, Obj->getFileName());
68   return TheTarget;
69 }
70 
71 void BinarySizeContextTracker::addInstructionForContext(
72     const SampleContextFrameVector &Context, uint32_t InstrSize) {
73   ContextTrieNode *CurNode = &RootContext;
74   bool IsLeaf = true;
75   for (const auto &Callsite : reverse(Context)) {
76     StringRef CallerName = Callsite.FuncName;
77     LineLocation CallsiteLoc = IsLeaf ? LineLocation(0, 0) : Callsite.Location;
78     CurNode = CurNode->getOrCreateChildContext(CallsiteLoc, CallerName);
79     IsLeaf = false;
80   }
81 
82   CurNode->addFunctionSize(InstrSize);
83 }
84 
85 uint32_t
86 BinarySizeContextTracker::getFuncSizeForContext(const SampleContext &Context) {
87   ContextTrieNode *CurrNode = &RootContext;
88   ContextTrieNode *PrevNode = nullptr;
89   SampleContextFrames Frames = Context.getContextFrames();
90   int32_t I = Frames.size() - 1;
91   Optional<uint32_t> Size;
92 
93   // Start from top-level context-less function, traverse down the reverse
94   // context trie to find the best/longest match for given context, then
95   // retrieve the size.
96 
97   while (CurrNode && I >= 0) {
98     // Process from leaf function to callers (added to context).
99     const auto &ChildFrame = Frames[I--];
100     PrevNode = CurrNode;
101     CurrNode =
102         CurrNode->getChildContext(ChildFrame.Location, ChildFrame.FuncName);
103     if (CurrNode && CurrNode->getFunctionSize())
104       Size = CurrNode->getFunctionSize().getValue();
105   }
106 
107   // If we traversed all nodes along the path of the context and haven't
108   // found a size yet, pivot to look for size from sibling nodes, i.e size
109   // of inlinee under different context.
110   if (!Size) {
111     if (!CurrNode)
112       CurrNode = PrevNode;
113     while (!Size && CurrNode && !CurrNode->getAllChildContext().empty()) {
114       CurrNode = &CurrNode->getAllChildContext().begin()->second;
115       if (CurrNode->getFunctionSize())
116         Size = CurrNode->getFunctionSize().getValue();
117     }
118   }
119 
120   assert(Size && "We should at least find one context size.");
121   return Size.getValue();
122 }
123 
124 void BinarySizeContextTracker::trackInlineesOptimizedAway(
125     MCPseudoProbeDecoder &ProbeDecoder) {
126   ProbeFrameStack ProbeContext;
127   for (const auto &Child : ProbeDecoder.getDummyInlineRoot().getChildren())
128     trackInlineesOptimizedAway(ProbeDecoder, *Child.second.get(), ProbeContext);
129 }
130 
131 void BinarySizeContextTracker::trackInlineesOptimizedAway(
132     MCPseudoProbeDecoder &ProbeDecoder,
133     MCDecodedPseudoProbeInlineTree &ProbeNode, ProbeFrameStack &ProbeContext) {
134   StringRef FuncName =
135       ProbeDecoder.getFuncDescForGUID(ProbeNode.Guid)->FuncName;
136   ProbeContext.emplace_back(FuncName, 0);
137 
138   // This ProbeContext has a probe, so it has code before inlining and
139   // optimization. Make sure we mark its size as known.
140   if (!ProbeNode.getProbes().empty()) {
141     ContextTrieNode *SizeContext = &RootContext;
142     for (auto &ProbeFrame : reverse(ProbeContext)) {
143       StringRef CallerName = ProbeFrame.first;
144       LineLocation CallsiteLoc(ProbeFrame.second, 0);
145       SizeContext =
146           SizeContext->getOrCreateChildContext(CallsiteLoc, CallerName);
147     }
148     // Add 0 size to make known.
149     SizeContext->addFunctionSize(0);
150   }
151 
152   // DFS down the probe inline tree
153   for (const auto &ChildNode : ProbeNode.getChildren()) {
154     InlineSite Location = ChildNode.first;
155     ProbeContext.back().second = std::get<1>(Location);
156     trackInlineesOptimizedAway(ProbeDecoder, *ChildNode.second.get(),
157                                ProbeContext);
158   }
159 
160   ProbeContext.pop_back();
161 }
162 
163 void ProfiledBinary::warnNoFuncEntry() {
164   uint64_t NoFuncEntryNum = 0;
165   for (auto &F : BinaryFunctions) {
166     if (F.second.Ranges.empty())
167       continue;
168     bool hasFuncEntry = false;
169     for (auto &R : F.second.Ranges) {
170       if (FuncRange *FR = findFuncRangeForStartOffset(R.first)) {
171         if (FR->IsFuncEntry) {
172           hasFuncEntry = true;
173           break;
174         }
175       }
176     }
177 
178     if (!hasFuncEntry) {
179       NoFuncEntryNum++;
180       if (ShowDetailedWarning)
181         WithColor::warning()
182             << "Failed to determine function entry for " << F.first
183             << " due to inconsistent name from symbol table and dwarf info.\n";
184     }
185   }
186   emitWarningSummary(NoFuncEntryNum, BinaryFunctions.size(),
187                      "of functions failed to determine function entry due to "
188                      "inconsistent name from symbol table and dwarf info.");
189 }
190 
191 void ProfiledBinary::load() {
192   // Attempt to open the binary.
193   OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path);
194   Binary &ExeBinary = *OBinary.getBinary();
195 
196   auto *Obj = dyn_cast<ELFObjectFileBase>(&ExeBinary);
197   if (!Obj)
198     exitWithError("not a valid Elf image", Path);
199 
200   TheTriple = Obj->makeTriple();
201   // Current only support X86
202   if (!TheTriple.isX86())
203     exitWithError("unsupported target", TheTriple.getTriple());
204   LLVM_DEBUG(dbgs() << "Loading " << Path << "\n");
205 
206   // Find the preferred load address for text sections.
207   setPreferredTextSegmentAddresses(Obj);
208 
209   checkPseudoProbe(Obj);
210 
211   if (ShowDisassemblyOnly)
212     decodePseudoProbe(Obj);
213 
214   // Load debug info of subprograms from DWARF section.
215   // If path of debug info binary is specified, use the debug info from it,
216   // otherwise use the debug info from the executable binary.
217   if (!DebugBinaryPath.empty()) {
218     OwningBinary<Binary> DebugPath =
219         unwrapOrError(createBinary(DebugBinaryPath), DebugBinaryPath);
220     loadSymbolsFromDWARF(*cast<ObjectFile>(DebugPath.getBinary()));
221   } else {
222     loadSymbolsFromDWARF(*cast<ObjectFile>(&ExeBinary));
223   }
224 
225   // Disassemble the text sections.
226   disassemble(Obj);
227 
228   // Use function start and return address to infer prolog and epilog
229   ProEpilogTracker.inferPrologOffsets(StartOffset2FuncRangeMap);
230   ProEpilogTracker.inferEpilogOffsets(RetOffsets);
231 
232   warnNoFuncEntry();
233 
234   // TODO: decode other sections.
235 }
236 
237 bool ProfiledBinary::inlineContextEqual(uint64_t Address1, uint64_t Address2) {
238   uint64_t Offset1 = virtualAddrToOffset(Address1);
239   uint64_t Offset2 = virtualAddrToOffset(Address2);
240   const SampleContextFrameVector &Context1 = getFrameLocationStack(Offset1);
241   const SampleContextFrameVector &Context2 = getFrameLocationStack(Offset2);
242   if (Context1.size() != Context2.size())
243     return false;
244   if (Context1.empty())
245     return false;
246   // The leaf frame contains location within the leaf, and it
247   // needs to be remove that as it's not part of the calling context
248   return std::equal(Context1.begin(), Context1.begin() + Context1.size() - 1,
249                     Context2.begin(), Context2.begin() + Context2.size() - 1);
250 }
251 
252 SampleContextFrameVector
253 ProfiledBinary::getExpandedContext(const SmallVectorImpl<uint64_t> &Stack,
254                                    bool &WasLeafInlined) {
255   SampleContextFrameVector ContextVec;
256   if (Stack.empty())
257     return ContextVec;
258   // Process from frame root to leaf
259   for (auto Address : Stack) {
260     uint64_t Offset = virtualAddrToOffset(Address);
261     const SampleContextFrameVector &ExpandedContext =
262         getFrameLocationStack(Offset);
263     // An instruction without a valid debug line will be ignored by sample
264     // processing
265     if (ExpandedContext.empty())
266       return SampleContextFrameVector();
267     // Set WasLeafInlined to the size of inlined frame count for the last
268     // address which is leaf
269     WasLeafInlined = (ExpandedContext.size() > 1);
270     ContextVec.append(ExpandedContext);
271   }
272 
273   // Replace with decoded base discriminator
274   for (auto &Frame : ContextVec) {
275     Frame.Location.Discriminator = ProfileGeneratorBase::getBaseDiscriminator(
276         Frame.Location.Discriminator, UseFSDiscriminator);
277   }
278 
279   assert(ContextVec.size() && "Context length should be at least 1");
280 
281   // Compress the context string except for the leaf frame
282   auto LeafFrame = ContextVec.back();
283   LeafFrame.Location = LineLocation(0, 0);
284   ContextVec.pop_back();
285   CSProfileGenerator::compressRecursionContext(ContextVec);
286   CSProfileGenerator::trimContext(ContextVec);
287   ContextVec.push_back(LeafFrame);
288   return ContextVec;
289 }
290 
291 template <class ELFT>
292 void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj,
293                                                       StringRef FileName) {
294   const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
295   // FIXME: This should be the page size of the system running profiling.
296   // However such info isn't available at post-processing time, assuming
297   // 4K page now. Note that we don't use EXEC_PAGESIZE from <linux/param.h>
298   // because we may build the tools on non-linux.
299   uint32_t PageSize = 0x1000;
300   for (const typename ELFT::Phdr &Phdr : PhdrRange) {
301     if (Phdr.p_type == ELF::PT_LOAD) {
302       if (!FirstLoadableAddress)
303         FirstLoadableAddress = Phdr.p_vaddr & ~(PageSize - 1U);
304       if (Phdr.p_flags & ELF::PF_X) {
305         // Segments will always be loaded at a page boundary.
306         PreferredTextSegmentAddresses.push_back(Phdr.p_vaddr &
307                                                 ~(PageSize - 1U));
308         TextSegmentOffsets.push_back(Phdr.p_offset & ~(PageSize - 1U));
309       }
310     }
311   }
312 
313   if (PreferredTextSegmentAddresses.empty())
314     exitWithError("no executable segment found", FileName);
315 }
316 
317 void ProfiledBinary::setPreferredTextSegmentAddresses(
318     const ELFObjectFileBase *Obj) {
319   if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
320     setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
321   else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
322     setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
323   else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
324     setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
325   else if (const auto *ELFObj = cast<ELF64BEObjectFile>(Obj))
326     setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
327   else
328     llvm_unreachable("invalid ELF object format");
329 }
330 
331 void ProfiledBinary::checkPseudoProbe(const ELFObjectFileBase *Obj) {
332   if (UseDwarfCorrelation)
333     return;
334 
335   bool HasProbeDescSection = false;
336   bool HasPseudoProbeSection = false;
337 
338   StringRef FileName = Obj->getFileName();
339   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
340        SI != SE; ++SI) {
341     const SectionRef &Section = *SI;
342     StringRef SectionName = unwrapOrError(Section.getName(), FileName);
343     if (SectionName == ".pseudo_probe_desc") {
344       HasProbeDescSection = true;
345     } else if (SectionName == ".pseudo_probe") {
346       HasPseudoProbeSection = true;
347     }
348   }
349 
350   // set UsePseudoProbes flag, used for PerfReader
351   UsePseudoProbes = HasProbeDescSection && HasPseudoProbeSection;
352 }
353 
354 void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) {
355   if (!UsePseudoProbes)
356     return;
357 
358   std::unordered_set<uint64_t> ProfiledGuids;
359   if (!ShowDisassemblyOnly)
360     for (auto *F : ProfiledFunctions)
361       ProfiledGuids.insert(Function::getGUID(F->FuncName));
362 
363   StringRef FileName = Obj->getFileName();
364   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
365        SI != SE; ++SI) {
366     const SectionRef &Section = *SI;
367     StringRef SectionName = unwrapOrError(Section.getName(), FileName);
368 
369     if (SectionName == ".pseudo_probe_desc") {
370       StringRef Contents = unwrapOrError(Section.getContents(), FileName);
371       if (!ProbeDecoder.buildGUID2FuncDescMap(
372               reinterpret_cast<const uint8_t *>(Contents.data()),
373               Contents.size()))
374         exitWithError(
375             "Pseudo Probe decoder fail in .pseudo_probe_desc section");
376     } else if (SectionName == ".pseudo_probe") {
377       StringRef Contents = unwrapOrError(Section.getContents(), FileName);
378       if (!ProbeDecoder.buildAddress2ProbeMap(
379               reinterpret_cast<const uint8_t *>(Contents.data()),
380               Contents.size(), ProfiledGuids))
381         exitWithError("Pseudo Probe decoder fail in .pseudo_probe section");
382     }
383   }
384 
385   // Build TopLevelProbeFrameMap to track size for optimized inlinees when probe
386   // is available
387   if (TrackFuncContextSize) {
388     for (const auto &Child : ProbeDecoder.getDummyInlineRoot().getChildren()) {
389       auto *Frame = Child.second.get();
390       StringRef FuncName =
391           ProbeDecoder.getFuncDescForGUID(Frame->Guid)->FuncName;
392       TopLevelProbeFrameMap[FuncName] = Frame;
393     }
394   }
395 
396   if (ShowPseudoProbe)
397     ProbeDecoder.printGUID2FuncDescMap(outs());
398 }
399 
400 void ProfiledBinary::decodePseudoProbe() {
401   OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path);
402   Binary &ExeBinary = *OBinary.getBinary();
403   auto *Obj = dyn_cast<ELFObjectFileBase>(&ExeBinary);
404   decodePseudoProbe(Obj);
405 }
406 
407 void ProfiledBinary::setIsFuncEntry(uint64_t Offset, StringRef RangeSymName) {
408   // Note that the start offset of each ELF section can be a non-function
409   // symbol, we need to binary search for the start of a real function range.
410   auto *FuncRange = findFuncRangeForOffset(Offset);
411   // Skip external function symbol.
412   if (!FuncRange)
413     return;
414 
415   // Set IsFuncEntry to ture if there is only one range in the function or the
416   // RangeSymName from ELF is equal to its DWARF-based function name.
417   if (FuncRange->Func->Ranges.size() == 1 ||
418       (!FuncRange->IsFuncEntry && FuncRange->getFuncName() == RangeSymName))
419     FuncRange->IsFuncEntry = true;
420 }
421 
422 bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
423                                         SectionSymbolsTy &Symbols,
424                                         const SectionRef &Section) {
425   std::size_t SE = Symbols.size();
426   uint64_t SectionOffset = Section.getAddress() - getPreferredBaseAddress();
427   uint64_t SectSize = Section.getSize();
428   uint64_t StartOffset = Symbols[SI].Addr - getPreferredBaseAddress();
429   uint64_t NextStartOffset =
430       (SI + 1 < SE) ? Symbols[SI + 1].Addr - getPreferredBaseAddress()
431                     : SectionOffset + SectSize;
432   setIsFuncEntry(StartOffset,
433                  FunctionSamples::getCanonicalFnName(Symbols[SI].Name));
434 
435   StringRef SymbolName =
436       ShowCanonicalFnName
437           ? FunctionSamples::getCanonicalFnName(Symbols[SI].Name)
438           : Symbols[SI].Name;
439   bool ShowDisassembly =
440       ShowDisassemblyOnly && (DisassembleFunctionSet.empty() ||
441                               DisassembleFunctionSet.count(SymbolName));
442   if (ShowDisassembly)
443     outs() << '<' << SymbolName << ">:\n";
444 
445   auto WarnInvalidInsts = [](uint64_t Start, uint64_t End) {
446     WithColor::warning() << "Invalid instructions at "
447                          << format("%8" PRIx64, Start) << " - "
448                          << format("%8" PRIx64, End) << "\n";
449   };
450 
451   uint64_t Offset = StartOffset;
452   // Size of a consecutive invalid instruction range starting from Offset -1
453   // backwards.
454   uint64_t InvalidInstLength = 0;
455   while (Offset < NextStartOffset) {
456     MCInst Inst;
457     uint64_t Size;
458     // Disassemble an instruction.
459     bool Disassembled =
460         DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset),
461                                Offset + getPreferredBaseAddress(), nulls());
462     if (Size == 0)
463       Size = 1;
464 
465     if (ShowDisassembly) {
466       if (ShowPseudoProbe) {
467         ProbeDecoder.printProbeForAddress(outs(),
468                                           Offset + getPreferredBaseAddress());
469       }
470       outs() << format("%8" PRIx64 ":", Offset + getPreferredBaseAddress());
471       size_t Start = outs().tell();
472       if (Disassembled)
473         IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs());
474       else
475         outs() << "\t<unknown>";
476       if (ShowSourceLocations) {
477         unsigned Cur = outs().tell() - Start;
478         if (Cur < 40)
479           outs().indent(40 - Cur);
480         InstructionPointer IP(this, Offset);
481         outs() << getReversedLocWithContext(
482             symbolize(IP, ShowCanonicalFnName, ShowPseudoProbe));
483       }
484       outs() << "\n";
485     }
486 
487     if (Disassembled) {
488       const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode());
489 
490       // Record instruction size.
491       Offset2InstSizeMap[Offset] = Size;
492 
493       // Populate address maps.
494       CodeAddrOffsets.push_back(Offset);
495       if (MCDesc.isCall()) {
496         CallOffsets.insert(Offset);
497         UncondBranchOffsets.insert(Offset);
498       } else if (MCDesc.isReturn()) {
499         RetOffsets.insert(Offset);
500         UncondBranchOffsets.insert(Offset);
501       } else if (MCDesc.isBranch()) {
502         if (MCDesc.isUnconditionalBranch())
503           UncondBranchOffsets.insert(Offset);
504         BranchOffsets.insert(Offset);
505       }
506 
507       if (InvalidInstLength) {
508         WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1);
509         InvalidInstLength = 0;
510       }
511     } else {
512       InvalidInstLength += Size;
513     }
514 
515     Offset += Size;
516   }
517 
518   if (InvalidInstLength)
519     WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1);
520 
521   if (ShowDisassembly)
522     outs() << "\n";
523 
524   return true;
525 }
526 
527 void ProfiledBinary::setUpDisassembler(const ELFObjectFileBase *Obj) {
528   const Target *TheTarget = getTarget(Obj);
529   std::string TripleName = TheTriple.getTriple();
530   StringRef FileName = Obj->getFileName();
531 
532   MRI.reset(TheTarget->createMCRegInfo(TripleName));
533   if (!MRI)
534     exitWithError("no register info for target " + TripleName, FileName);
535 
536   MCTargetOptions MCOptions;
537   AsmInfo.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
538   if (!AsmInfo)
539     exitWithError("no assembly info for target " + TripleName, FileName);
540 
541   SubtargetFeatures Features = Obj->getFeatures();
542   STI.reset(
543       TheTarget->createMCSubtargetInfo(TripleName, "", Features.getString()));
544   if (!STI)
545     exitWithError("no subtarget info for target " + TripleName, FileName);
546 
547   MII.reset(TheTarget->createMCInstrInfo());
548   if (!MII)
549     exitWithError("no instruction info for target " + TripleName, FileName);
550 
551   MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get());
552   std::unique_ptr<MCObjectFileInfo> MOFI(
553       TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
554   Ctx.setObjectFileInfo(MOFI.get());
555   DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx));
556   if (!DisAsm)
557     exitWithError("no disassembler for target " + TripleName, FileName);
558 
559   MIA.reset(TheTarget->createMCInstrAnalysis(MII.get()));
560 
561   int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
562   IPrinter.reset(TheTarget->createMCInstPrinter(
563       Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI));
564   IPrinter->setPrintBranchImmAsAddress(true);
565 }
566 
567 void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
568   // Set up disassembler and related components.
569   setUpDisassembler(Obj);
570 
571   // Create a mapping from virtual address to symbol name. The symbols in text
572   // sections are the candidates to dissassemble.
573   std::map<SectionRef, SectionSymbolsTy> AllSymbols;
574   StringRef FileName = Obj->getFileName();
575   for (const SymbolRef &Symbol : Obj->symbols()) {
576     const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
577     const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
578     section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
579     if (SecI != Obj->section_end())
580       AllSymbols[*SecI].push_back(SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE));
581   }
582 
583   // Sort all the symbols. Use a stable sort to stabilize the output.
584   for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
585     stable_sort(SecSyms.second);
586 
587   DisassembleFunctionSet.insert(DisassembleFunctions.begin(),
588                                 DisassembleFunctions.end());
589   assert((DisassembleFunctionSet.empty() || ShowDisassemblyOnly) &&
590          "Functions to disassemble should be only specified together with "
591          "--show-disassembly-only");
592 
593   if (ShowDisassemblyOnly)
594     outs() << "\nDisassembly of " << FileName << ":\n";
595 
596   // Dissassemble a text section.
597   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
598        SI != SE; ++SI) {
599     const SectionRef &Section = *SI;
600     if (!Section.isText())
601       continue;
602 
603     uint64_t ImageLoadAddr = getPreferredBaseAddress();
604     uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr;
605     uint64_t SectSize = Section.getSize();
606     if (!SectSize)
607       continue;
608 
609     // Register the text section.
610     TextSections.insert({SectionOffset, SectSize});
611 
612     StringRef SectionName = unwrapOrError(Section.getName(), FileName);
613 
614     if (ShowDisassemblyOnly) {
615       outs() << "\nDisassembly of section " << SectionName;
616       outs() << " [" << format("0x%" PRIx64, Section.getAddress()) << ", "
617              << format("0x%" PRIx64, Section.getAddress() + SectSize)
618              << "]:\n\n";
619     }
620 
621     if (SectionName == ".plt")
622       continue;
623 
624     // Get the section data.
625     ArrayRef<uint8_t> Bytes =
626         arrayRefFromStringRef(unwrapOrError(Section.getContents(), FileName));
627 
628     // Get the list of all the symbols in this section.
629     SectionSymbolsTy &Symbols = AllSymbols[Section];
630 
631     // Disassemble symbol by symbol.
632     for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
633       if (!dissassembleSymbol(SI, Bytes, Symbols, Section))
634         exitWithError("disassembling error", FileName);
635     }
636   }
637 
638   // Dissassemble rodata section to check if FS discriminator symbol exists.
639   checkUseFSDiscriminator(Obj, AllSymbols);
640 }
641 
642 void ProfiledBinary::checkUseFSDiscriminator(
643     const ELFObjectFileBase *Obj,
644     std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
645   const char *FSDiscriminatorVar = "__llvm_fs_discriminator__";
646   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
647        SI != SE; ++SI) {
648     const SectionRef &Section = *SI;
649     if (!Section.isData() || Section.getSize() == 0)
650       continue;
651     SectionSymbolsTy &Symbols = AllSymbols[Section];
652 
653     for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
654       if (Symbols[SI].Name == FSDiscriminatorVar) {
655         UseFSDiscriminator = true;
656         return;
657       }
658     }
659   }
660 }
661 
662 void ProfiledBinary::loadSymbolsFromDWARFUnit(DWARFUnit &CompilationUnit) {
663   for (const auto &DieInfo : CompilationUnit.dies()) {
664     llvm::DWARFDie Die(&CompilationUnit, &DieInfo);
665 
666     if (!Die.isSubprogramDIE())
667       continue;
668     auto Name = Die.getName(llvm::DINameKind::LinkageName);
669     if (!Name)
670       Name = Die.getName(llvm::DINameKind::ShortName);
671     if (!Name)
672       continue;
673 
674     auto RangesOrError = Die.getAddressRanges();
675     if (!RangesOrError)
676       continue;
677     const DWARFAddressRangesVector &Ranges = RangesOrError.get();
678 
679     if (Ranges.empty())
680       continue;
681 
682     // Different DWARF symbols can have same function name, search or create
683     // BinaryFunction indexed by the name.
684     auto Ret = BinaryFunctions.emplace(Name, BinaryFunction());
685     auto &Func = Ret.first->second;
686     if (Ret.second)
687       Func.FuncName = Ret.first->first;
688 
689     for (const auto &Range : Ranges) {
690       uint64_t FuncStart = Range.LowPC;
691       uint64_t FuncSize = Range.HighPC - FuncStart;
692 
693       if (FuncSize == 0 || FuncStart < getPreferredBaseAddress())
694         continue;
695 
696       uint64_t StartOffset = FuncStart - getPreferredBaseAddress();
697       uint64_t EndOffset = Range.HighPC - getPreferredBaseAddress();
698 
699       // We may want to know all ranges for one function. Here group the
700       // ranges and store them into BinaryFunction.
701       Func.Ranges.emplace_back(StartOffset, EndOffset);
702 
703       auto R = StartOffset2FuncRangeMap.emplace(StartOffset, FuncRange());
704       if (R.second) {
705         FuncRange &FRange = R.first->second;
706         FRange.Func = &Func;
707         FRange.StartOffset = StartOffset;
708         FRange.EndOffset = EndOffset;
709       } else {
710         WithColor::warning()
711             << "Duplicated symbol start address at "
712             << format("%8" PRIx64, StartOffset + getPreferredBaseAddress())
713             << " " << R.first->second.getFuncName() << " and " << Name << "\n";
714       }
715     }
716   }
717 }
718 
719 void ProfiledBinary::loadSymbolsFromDWARF(ObjectFile &Obj) {
720   auto DebugContext = llvm::DWARFContext::create(
721       Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, DWPPath);
722   if (!DebugContext)
723     exitWithError("Error creating the debug info context", Path);
724 
725   for (const auto &CompilationUnit : DebugContext->compile_units())
726     loadSymbolsFromDWARFUnit(*CompilationUnit.get());
727 
728   // Handles DWO sections that can either be in .o, .dwo or .dwp files.
729   for (const auto &CompilationUnit : DebugContext->compile_units()) {
730     DWARFUnit *const DwarfUnit = CompilationUnit.get();
731     if (llvm::Optional<uint64_t> DWOId = DwarfUnit->getDWOId()) {
732       DWARFUnit *DWOCU = DwarfUnit->getNonSkeletonUnitDIE(false).getDwarfUnit();
733       if (!DWOCU->isDWOUnit()) {
734         std::string DWOName = dwarf::toString(
735             DwarfUnit->getUnitDIE().find(
736                 {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),
737             "");
738         WithColor::warning()
739             << "DWO debug information for " << DWOName
740             << " was not loaded. Please check the .o, .dwo or .dwp path.\n";
741         continue;
742       }
743       loadSymbolsFromDWARFUnit(*DWOCU);
744     }
745   }
746 
747   if (BinaryFunctions.empty())
748     WithColor::warning() << "Loading of DWARF info completed, but no binary "
749                             "functions have been retrieved.\n";
750 }
751 
752 void ProfiledBinary::populateSymbolListFromDWARF(
753     ProfileSymbolList &SymbolList) {
754   for (auto &I : StartOffset2FuncRangeMap)
755     SymbolList.add(I.second.getFuncName());
756 }
757 
758 void ProfiledBinary::setupSymbolizer() {
759   symbolize::LLVMSymbolizer::Options SymbolizerOpts;
760   SymbolizerOpts.PrintFunctions =
761       DILineInfoSpecifier::FunctionNameKind::LinkageName;
762   SymbolizerOpts.Demangle = false;
763   SymbolizerOpts.DefaultArch = TheTriple.getArchName().str();
764   SymbolizerOpts.UseSymbolTable = false;
765   SymbolizerOpts.RelativeAddresses = false;
766   SymbolizerOpts.DWPName = DWPPath;
767   Symbolizer = std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOpts);
768 }
769 
770 SampleContextFrameVector ProfiledBinary::symbolize(const InstructionPointer &IP,
771                                                    bool UseCanonicalFnName,
772                                                    bool UseProbeDiscriminator) {
773   assert(this == IP.Binary &&
774          "Binary should only symbolize its own instruction");
775   auto Addr = object::SectionedAddress{IP.Offset + getPreferredBaseAddress(),
776                                        object::SectionedAddress::UndefSection};
777   DIInliningInfo InlineStack = unwrapOrError(
778       Symbolizer->symbolizeInlinedCode(SymbolizerPath.str(), Addr),
779       SymbolizerPath);
780 
781   SampleContextFrameVector CallStack;
782   for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) {
783     const auto &CallerFrame = InlineStack.getFrame(I);
784     if (CallerFrame.FunctionName == "<invalid>")
785       break;
786 
787     StringRef FunctionName(CallerFrame.FunctionName);
788     if (UseCanonicalFnName)
789       FunctionName = FunctionSamples::getCanonicalFnName(FunctionName);
790 
791     uint32_t Discriminator = CallerFrame.Discriminator;
792     uint32_t LineOffset = (CallerFrame.Line - CallerFrame.StartLine) & 0xffff;
793     if (UseProbeDiscriminator) {
794       LineOffset =
795           PseudoProbeDwarfDiscriminator::extractProbeIndex(Discriminator);
796       Discriminator = 0;
797     }
798 
799     LineLocation Line(LineOffset, Discriminator);
800     auto It = NameStrings.insert(FunctionName.str());
801     CallStack.emplace_back(*It.first, Line);
802   }
803 
804   return CallStack;
805 }
806 
807 void ProfiledBinary::computeInlinedContextSizeForRange(uint64_t StartOffset,
808                                                        uint64_t EndOffset) {
809   uint64_t RangeBegin = offsetToVirtualAddr(StartOffset);
810   uint64_t RangeEnd = offsetToVirtualAddr(EndOffset);
811   InstructionPointer IP(this, RangeBegin, true);
812 
813   if (IP.Address != RangeBegin)
814     WithColor::warning() << "Invalid start instruction at "
815                          << format("%8" PRIx64, RangeBegin) << "\n";
816 
817   if (IP.Address >= RangeEnd)
818     return;
819 
820   do {
821     uint64_t Offset = virtualAddrToOffset(IP.Address);
822     const SampleContextFrameVector &SymbolizedCallStack =
823         getFrameLocationStack(Offset, UsePseudoProbes);
824     uint64_t Size = Offset2InstSizeMap[Offset];
825 
826     // Record instruction size for the corresponding context
827     FuncSizeTracker.addInstructionForContext(SymbolizedCallStack, Size);
828 
829   } while (IP.advance() && IP.Address < RangeEnd);
830 }
831 
832 void ProfiledBinary::computeInlinedContextSizeForFunc(
833     const BinaryFunction *Func) {
834   // Note that a function can be spilt into multiple ranges, so compute for all
835   // ranges of the function.
836   for (const auto &Range : Func->Ranges)
837     computeInlinedContextSizeForRange(Range.first, Range.second);
838 
839   // Track optimized-away inlinee for probed binary. A function inlined and then
840   // optimized away should still have their probes left over in places.
841   if (usePseudoProbes()) {
842     auto I = TopLevelProbeFrameMap.find(Func->FuncName);
843     if (I != TopLevelProbeFrameMap.end()) {
844       BinarySizeContextTracker::ProbeFrameStack ProbeContext;
845       FuncSizeTracker.trackInlineesOptimizedAway(ProbeDecoder, *I->second,
846                                                  ProbeContext);
847     }
848   }
849 }
850 
851 InstructionPointer::InstructionPointer(const ProfiledBinary *Binary,
852                                        uint64_t Address, bool RoundToNext)
853     : Binary(Binary), Address(Address) {
854   Index = Binary->getIndexForAddr(Address);
855   if (RoundToNext) {
856     // we might get address which is not the code
857     // it should round to the next valid address
858     if (Index >= Binary->getCodeOffsetsSize())
859       this->Address = UINT64_MAX;
860     else
861       this->Address = Binary->getAddressforIndex(Index);
862   }
863 }
864 
865 bool InstructionPointer::advance() {
866   Index++;
867   if (Index >= Binary->getCodeOffsetsSize()) {
868     Address = UINT64_MAX;
869     return false;
870   }
871   Address = Binary->getAddressforIndex(Index);
872   return true;
873 }
874 
875 bool InstructionPointer::backward() {
876   if (Index == 0) {
877     Address = 0;
878     return false;
879   }
880   Index--;
881   Address = Binary->getAddressforIndex(Index);
882   return true;
883 }
884 
885 void InstructionPointer::update(uint64_t Addr) {
886   Address = Addr;
887   Index = Binary->getIndexForAddr(Address);
888 }
889 
890 } // end namespace sampleprof
891 } // end namespace llvm
892