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