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