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 "MissingFrameInferrer.h"
12#include "Options.h"
13#include "ProfileGenerator.h"
14#include "llvm/DebugInfo/PDB/IPDBSession.h"
15#include "llvm/DebugInfo/PDB/PDB.h"
16#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
17#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
18#include "llvm/Demangle/Demangle.h"
19#include "llvm/IR/DebugInfoMetadata.h"
20#include "llvm/MC/TargetRegistry.h"
21#include "llvm/Object/COFF.h"
22#include "llvm/Support/CommandLine.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/Format.h"
25#include "llvm/Support/TargetSelect.h"
26#include "llvm/TargetParser/Triple.h"
27#include <optional>
28
29#define DEBUG_TYPE "load-binary"
30
31namespace llvm {
32
33using namespace object;
34
35cl::opt<bool> ShowDisassemblyOnly("show-disassembly-only",
36 cl::desc("Print disassembled code."),
37 cl::cat(ProfGenCategory));
38
39cl::opt<bool> ShowSourceLocations("show-source-locations",
40 cl::desc("Print source locations."),
41 cl::cat(ProfGenCategory));
42
43cl::opt<bool> LoadFunctionFromSymbol(
44 "load-function-from-symbol", cl::init(Val: true),
45 cl::desc("Gather additional binary function info from symbols (e.g. "
46 "symtab) in case dwarf info is incomplete."),
47 cl::cat(ProfGenCategory));
48
49static cl::opt<bool>
50 ShowCanonicalFnName("show-canonical-fname",
51 cl::desc("Print canonical function name."),
52 cl::cat(ProfGenCategory));
53
54static cl::opt<bool> ShowPseudoProbe(
55 "show-pseudo-probe",
56 cl::desc("Print pseudo probe section and disassembled info."),
57 cl::cat(ProfGenCategory));
58
59static cl::opt<bool> UseDwarfCorrelation(
60 "use-dwarf-correlation",
61 cl::desc("Use dwarf for profile correlation even when binary contains "
62 "pseudo probe."),
63 cl::cat(ProfGenCategory));
64
65static cl::opt<std::string>
66 DWPPath("dwp", cl::init(Val: ""),
67 cl::desc("Path of .dwp file. When not specified, it will be "
68 "<binary>.dwp in the same directory as the main binary."),
69 cl::cat(ProfGenCategory));
70
71static cl::list<std::string> DisassembleFunctions(
72 "disassemble-functions", cl::CommaSeparated,
73 cl::desc("List of functions to print disassembly for. Accept demangled "
74 "names only. Only work with show-disassembly-only"),
75 cl::cat(ProfGenCategory));
76
77static cl::opt<bool>
78 KernelBinary("kernel",
79 cl::desc("Generate the profile for Linux kernel binary."),
80 cl::cat(ProfGenCategory));
81
82namespace sampleprof {
83
84static const Target *getTarget(const ObjectFile *Obj) {
85 Triple TheTriple = Obj->makeTriple();
86 std::string Error;
87 std::string ArchName;
88 const Target *TheTarget =
89 TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
90 if (!TheTarget)
91 exitWithError(Message: Error, Whence: Obj->getFileName());
92 return TheTarget;
93}
94
95void BinarySizeContextTracker::addInstructionForContext(
96 const SampleContextFrameVector &Context, uint32_t InstrSize) {
97 ContextTrieNode *CurNode = &RootContext;
98 bool IsLeaf = true;
99 for (const auto &Callsite : reverse(C: Context)) {
100 FunctionId CallerName = Callsite.Func;
101 LineLocation CallsiteLoc = IsLeaf ? LineLocation(0, 0) : Callsite.Location;
102 CurNode = CurNode->getOrCreateChildContext(CallSite: CallsiteLoc, ChildName: CallerName);
103 IsLeaf = false;
104 }
105
106 CurNode->addFunctionSize(FSize: InstrSize);
107}
108
109uint32_t
110BinarySizeContextTracker::getFuncSizeForContext(const ContextTrieNode *Node) {
111 ContextTrieNode *CurrNode = &RootContext;
112 ContextTrieNode *PrevNode = nullptr;
113
114 std::optional<uint32_t> Size;
115
116 // Start from top-level context-less function, traverse down the reverse
117 // context trie to find the best/longest match for given context, then
118 // retrieve the size.
119 LineLocation CallSiteLoc(0, 0);
120 while (CurrNode && Node->getParentContext() != nullptr) {
121 PrevNode = CurrNode;
122 CurrNode = CurrNode->getChildContext(CallSite: CallSiteLoc, ChildName: Node->getFuncName());
123 if (CurrNode && CurrNode->getFunctionSize())
124 Size = *CurrNode->getFunctionSize();
125 CallSiteLoc = Node->getCallSiteLoc();
126 Node = Node->getParentContext();
127 }
128
129 // If we traversed all nodes along the path of the context and haven't
130 // found a size yet, pivot to look for size from sibling nodes, i.e size
131 // of inlinee under different context.
132 if (!Size) {
133 if (!CurrNode)
134 CurrNode = PrevNode;
135 while (!Size && CurrNode && !CurrNode->getAllChildContext().empty()) {
136 CurrNode = &CurrNode->getAllChildContext().begin()->second;
137 if (CurrNode->getFunctionSize())
138 Size = *CurrNode->getFunctionSize();
139 }
140 }
141
142 assert(Size && "We should at least find one context size.");
143 return *Size;
144}
145
146void BinarySizeContextTracker::trackInlineesOptimizedAway(
147 MCPseudoProbeDecoder &ProbeDecoder) {
148 ProbeFrameStack ProbeContext;
149 for (const auto &Child : ProbeDecoder.getDummyInlineRoot().getChildren())
150 trackInlineesOptimizedAway(ProbeDecoder, ProbeNode: Child, Context&: ProbeContext);
151}
152
153void BinarySizeContextTracker::trackInlineesOptimizedAway(
154 MCPseudoProbeDecoder &ProbeDecoder,
155 const MCDecodedPseudoProbeInlineTree &ProbeNode,
156 ProbeFrameStack &ProbeContext) {
157 StringRef FuncName =
158 ProbeDecoder.getFuncDescForGUID(GUID: ProbeNode.Guid)->FuncName;
159 ProbeContext.emplace_back(Args&: FuncName, Args: 0);
160
161 // This ProbeContext has a probe, so it has code before inlining and
162 // optimization. Make sure we mark its size as known.
163 if (!ProbeNode.getProbes().empty()) {
164 ContextTrieNode *SizeContext = &RootContext;
165 for (auto &ProbeFrame : reverse(C&: ProbeContext)) {
166 StringRef CallerName = ProbeFrame.first;
167 LineLocation CallsiteLoc(ProbeFrame.second, 0);
168 SizeContext =
169 SizeContext->getOrCreateChildContext(CallSite: CallsiteLoc,
170 ChildName: FunctionId(CallerName));
171 }
172 // Add 0 size to make known.
173 SizeContext->addFunctionSize(FSize: 0);
174 }
175
176 // DFS down the probe inline tree
177 for (const auto &ChildNode : ProbeNode.getChildren()) {
178 InlineSite Location = ChildNode.getInlineSite();
179 ProbeContext.back().second = std::get<1>(t&: Location);
180 trackInlineesOptimizedAway(ProbeDecoder, ProbeNode: ChildNode, ProbeContext);
181 }
182
183 ProbeContext.pop_back();
184}
185
186ProfiledBinary::ProfiledBinary(const StringRef ExeBinPath,
187 const StringRef DebugBinPath)
188 : Path(ExeBinPath), DebugBinaryPath(DebugBinPath),
189 SymbolizerOpts(getSymbolizerOpts()), ProEpilogTracker(this),
190 Symbolizer(std::make_unique<symbolize::LLVMSymbolizer>(args&: SymbolizerOpts)),
191 TrackFuncContextSize(EnableCSPreInliner && UseContextCostForPreInliner) {
192 // Point to executable binary if debug info binary is not specified.
193 SymbolizerPath = DebugBinPath.empty() ? ExeBinPath : DebugBinPath;
194 if (InferMissingFrames)
195 MissingContextInferrer = std::make_unique<MissingFrameInferrer>(args: this);
196 load();
197}
198
199ProfiledBinary::~ProfiledBinary() = default;
200
201void ProfiledBinary::warnNoFuncEntry() {
202 uint64_t NoFuncEntryNum = 0;
203 for (auto &F : BinaryFunctions) {
204 if (F.second.Ranges.empty())
205 continue;
206 bool hasFuncEntry = false;
207 for (auto &R : F.second.Ranges) {
208 if (FuncRange *FR = findFuncRangeForStartAddr(Address: R.first)) {
209 if (FR->IsFuncEntry) {
210 hasFuncEntry = true;
211 break;
212 }
213 }
214 }
215
216 if (!hasFuncEntry) {
217 NoFuncEntryNum++;
218 if (ShowDetailedWarning)
219 WithColor::warning()
220 << "Failed to determine function entry for " << F.first
221 << " due to inconsistent name from symbol table and dwarf info.\n";
222 }
223 }
224 emitWarningSummary(Num: NoFuncEntryNum, Total: BinaryFunctions.size(),
225 Msg: "of functions failed to determine function entry due to "
226 "inconsistent name from symbol table and dwarf info.");
227}
228
229void ProfiledBinary::load() {
230 // Attempt to open the binary.
231 OwningBinary<Binary> OBinary = unwrapOrError(EO: createBinary(Path), Args&: Path);
232 Binary &ExeBinary = *OBinary.getBinary();
233
234 IsCOFF = isa<COFFObjectFile>(Val: &ExeBinary);
235 if (!isa<ELFObjectFileBase>(Val: &ExeBinary) && !IsCOFF)
236 exitWithError(Message: "not a valid ELF/COFF image", Whence: Path);
237
238 auto *Obj = cast<ObjectFile>(Val: &ExeBinary);
239 TheTriple = Obj->makeTriple();
240
241 LLVM_DEBUG(dbgs() << "Loading " << Path << "\n");
242
243 // Mark the binary as a kernel image;
244 IsKernel = KernelBinary;
245
246 // Find the preferred load address for text sections.
247 setPreferredTextSegmentAddresses(Obj);
248
249 // Load debug info of subprograms from DWARF section.
250 // If path of debug info binary is specified, use the debug info from it,
251 // otherwise use the debug info from the executable binary.
252 OwningBinary<Binary> DebugBinary;
253 ObjectFile *PseudoProbeObj = nullptr;
254 if (!DebugBinaryPath.empty()) {
255 DebugBinary = unwrapOrError(EO: createBinary(Path: DebugBinaryPath), Args&: DebugBinaryPath);
256 ObjectFile *DebugObj = cast<ObjectFile>(Val: DebugBinary.getBinary());
257 loadSymbolsFromDWARF(Obj&: *DebugObj);
258 if (checkPseudoProbe(Obj: DebugObj, ObjPath: DebugBinaryPath))
259 PseudoProbeObj = DebugObj;
260 } else {
261 loadSymbolsFromDWARF(Obj&: *Obj);
262 }
263
264 // Prefer loading pseudo probe from binary.
265 if (checkPseudoProbe(Obj, ObjPath: Path))
266 PseudoProbeObj = Obj;
267
268 DisassembleFunctionSet.insert_range(R&: DisassembleFunctions);
269
270 if (usePseudoProbes())
271 populateSymbolAddressList(O: Obj);
272
273 if (ShowDisassemblyOnly && PseudoProbeObj)
274 decodePseudoProbe(Obj: PseudoProbeObj);
275
276 if (LoadFunctionFromSymbol && usePseudoProbes())
277 loadSymbolsFromSymtab(O: Obj);
278
279 // Disassemble the text sections.
280 disassemble(O: Obj);
281
282 // Use function start and return address to infer prolog and epilog
283 ProEpilogTracker.inferPrologAddresses(FuncStartAddressMap&: StartAddrToFuncRangeMap);
284 ProEpilogTracker.inferEpilogAddresses(RetAddrs&: RetAddressSet);
285
286 warnNoFuncEntry();
287
288 // TODO: decode other sections.
289}
290
291bool ProfiledBinary::inlineContextEqual(uint64_t Address1, uint64_t Address2) {
292 const SampleContextFrameVector &Context1 =
293 getCachedFrameLocationStack(Address: Address1);
294 const SampleContextFrameVector &Context2 =
295 getCachedFrameLocationStack(Address: Address2);
296 if (Context1.size() != Context2.size())
297 return false;
298 if (Context1.empty())
299 return false;
300 // The leaf frame contains location within the leaf, and it
301 // needs to be remove that as it's not part of the calling context
302 return std::equal(first1: Context1.begin(), last1: Context1.begin() + Context1.size() - 1,
303 first2: Context2.begin(), last2: Context2.begin() + Context2.size() - 1);
304}
305
306SampleContextFrameVector
307ProfiledBinary::getExpandedContext(const SmallVectorImpl<uint64_t> &Stack,
308 bool &WasLeafInlined) {
309 SampleContextFrameVector ContextVec;
310 if (Stack.empty())
311 return ContextVec;
312 // Process from frame root to leaf
313 for (auto Address : Stack) {
314 const SampleContextFrameVector &ExpandedContext =
315 getCachedFrameLocationStack(Address);
316 // An instruction without a valid debug line will be ignored by sample
317 // processing
318 if (ExpandedContext.empty())
319 return SampleContextFrameVector();
320 // Set WasLeafInlined to the size of inlined frame count for the last
321 // address which is leaf
322 WasLeafInlined = (ExpandedContext.size() > 1);
323 ContextVec.append(RHS: ExpandedContext);
324 }
325
326 // Replace with decoded base discriminator
327 for (auto &Frame : ContextVec) {
328 Frame.Location.Discriminator = ProfileGeneratorBase::getBaseDiscriminator(
329 Discriminator: Frame.Location.Discriminator, UseFSD: UseFSDiscriminator);
330 }
331
332 assert(ContextVec.size() && "Context length should be at least 1");
333
334 // Compress the context string except for the leaf frame
335 auto LeafFrame = ContextVec.back();
336 LeafFrame.Location = LineLocation(0, 0);
337 ContextVec.pop_back();
338 CSProfileGenerator::compressRecursionContext(Context&: ContextVec);
339 CSProfileGenerator::trimContext(S&: ContextVec);
340 ContextVec.push_back(Elt: LeafFrame);
341 return ContextVec;
342}
343
344template <class ELFT>
345void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj,
346 StringRef FileName) {
347 const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
348 // FIXME: This should be the page size of the system running profiling.
349 // However such info isn't available at post-processing time, assuming
350 // 4K page now. Note that we don't use EXEC_PAGESIZE from <linux/param.h>
351 // because we may build the tools on non-linux.
352 uint64_t PageSize = 0x1000;
353 for (const typename ELFT::Phdr &Phdr : PhdrRange) {
354 if (Phdr.p_type == ELF::PT_LOAD) {
355 if (!FirstLoadableAddress)
356 FirstLoadableAddress = Phdr.p_vaddr & ~(PageSize - 1U);
357 if (Phdr.p_flags & ELF::PF_X) {
358 // Segments will always be loaded at a page boundary.
359 PreferredTextSegmentAddresses.push_back(Phdr.p_vaddr &
360 ~(PageSize - 1U));
361 TextSegmentOffsets.push_back(Phdr.p_offset & ~(PageSize - 1U));
362 } else {
363 PhdrInfo Info;
364 Info.FileOffset = Phdr.p_offset;
365 Info.FileSz = Phdr.p_filesz;
366 Info.VirtualAddr = Phdr.p_vaddr;
367 NonTextPhdrInfo.push_back(Elt: Info);
368 }
369 }
370 }
371
372 if (PreferredTextSegmentAddresses.empty())
373 exitWithError(Message: "no executable segment found", Whence: FileName);
374}
375
376uint64_t ProfiledBinary::CanonicalizeNonTextAddress(uint64_t Address) {
377 uint64_t FileOffset = 0;
378 auto MMapIter = NonTextMMapEvents.lower_bound(x: Address);
379 if (MMapIter == NonTextMMapEvents.end())
380 return Address; // No non-text mmap event found, return the address as is.
381
382 const auto &MMapEvent = MMapIter->second;
383
384 // If the address is within the non-text mmap event, calculate its file
385 // offset in the binary.
386 if (MMapEvent.Address <= Address &&
387 Address < MMapEvent.Address + MMapEvent.Size)
388 FileOffset = Address - MMapEvent.Address + MMapEvent.Offset;
389
390 // If the address is not within the non-text mmap event, return the address
391 // as is.
392 if (FileOffset == 0)
393 return Address;
394
395 for (const auto &PhdrInfo : NonTextPhdrInfo) {
396 // Find the program section that contains the file offset and map the
397 // file offset to the virtual address.
398 if (PhdrInfo.FileOffset <= FileOffset &&
399 FileOffset < PhdrInfo.FileOffset + PhdrInfo.FileSz)
400 return PhdrInfo.VirtualAddr + (FileOffset - PhdrInfo.FileOffset);
401 }
402
403 return Address;
404}
405
406void ProfiledBinary::setPreferredTextSegmentAddresses(const COFFObjectFile *Obj,
407 StringRef FileName) {
408 uint64_t ImageBase = Obj->getImageBase();
409 if (!ImageBase)
410 exitWithError(Message: "Not a COFF image", Whence: FileName);
411
412 PreferredTextSegmentAddresses.push_back(x: ImageBase);
413 FirstLoadableAddress = ImageBase;
414
415 for (SectionRef Section : Obj->sections()) {
416 const coff_section *Sec = Obj->getCOFFSection(Section);
417 if (Sec->Characteristics & COFF::IMAGE_SCN_CNT_CODE)
418 TextSegmentOffsets.push_back(x: Sec->VirtualAddress);
419 }
420}
421
422void ProfiledBinary::setPreferredTextSegmentAddresses(const ObjectFile *Obj) {
423 if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Val: Obj))
424 setPreferredTextSegmentAddresses(Obj: ELFObj->getELFFile(), FileName: Obj->getFileName());
425 else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Val: Obj))
426 setPreferredTextSegmentAddresses(Obj: ELFObj->getELFFile(), FileName: Obj->getFileName());
427 else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Val: Obj))
428 setPreferredTextSegmentAddresses(Obj: ELFObj->getELFFile(), FileName: Obj->getFileName());
429 else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Val: Obj))
430 setPreferredTextSegmentAddresses(Obj: ELFObj->getELFFile(), FileName: Obj->getFileName());
431 else if (const auto *COFFObj = dyn_cast<COFFObjectFile>(Val: Obj))
432 setPreferredTextSegmentAddresses(Obj: COFFObj, FileName: Obj->getFileName());
433 else
434 llvm_unreachable("invalid object format");
435}
436
437bool ProfiledBinary::checkPseudoProbe(const ObjectFile *Obj,
438 StringRef ObjPath) {
439 if (UseDwarfCorrelation)
440 return false;
441
442 bool HasProbeDescSection = false;
443 bool HasPseudoProbeSection = false;
444
445 StringRef FileName = Obj->getFileName();
446 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
447 SI != SE; ++SI) {
448 const SectionRef &Section = *SI;
449 StringRef SectionName = unwrapOrError(EO: Section.getName(), Args&: FileName);
450 if (SectionName == ".pseudo_probe_desc") {
451 HasProbeDescSection = true;
452 } else if (SectionName == ".pseudo_probe") {
453 HasPseudoProbeSection = true;
454 }
455 }
456
457 if (HasProbeDescSection && HasPseudoProbeSection) {
458 PseudoProbeBinPath = ObjPath;
459 return true;
460 }
461
462 return false;
463}
464
465void ProfiledBinary::decodePseudoProbe(const ObjectFile *Obj) {
466 if (!usePseudoProbes())
467 return;
468
469 LLVM_DEBUG(dbgs() << "Decoding pseudo probe in " << Obj->getFileName()
470 << "\n");
471
472 MCPseudoProbeDecoder::Uint64Set GuidFilter;
473 MCPseudoProbeDecoder::Uint64Map FuncStartAddresses;
474 if (ShowDisassemblyOnly) {
475 if (DisassembleFunctionSet.empty()) {
476 FuncStartAddresses = SymbolStartAddrs;
477 } else {
478 for (auto &F : DisassembleFunctionSet) {
479 auto GUID = Function::getGUIDAssumingExternalLinkage(GlobalName: F.first());
480 if (auto StartAddr = SymbolStartAddrs.lookup(Val: GUID)) {
481 FuncStartAddresses[GUID] = StartAddr;
482 FuncRange &Range = StartAddrToFuncRangeMap[StartAddr];
483 GuidFilter.insert(
484 V: Function::getGUIDAssumingExternalLinkage(GlobalName: Range.getFuncName()));
485 }
486 }
487 }
488 } else {
489 for (auto *F : ProfiledFunctions) {
490 GuidFilter.insert(V: Function::getGUIDAssumingExternalLinkage(GlobalName: F->FuncName));
491 // DWARF name might be broken when a DWARF32 .debug_str.dwo section
492 // execeeds 4GB. We expect symbol table to contain the correct function
493 // names which matches the pseudo probe. Adding back all the GUIDs if
494 // possible.
495 auto AltGUIDs = AlternativeFunctionGUIDs.equal_range(x: F);
496 for (const auto &[_, Func] : make_range(p: AltGUIDs))
497 GuidFilter.insert(V: Func);
498 for (auto &Range : F->Ranges) {
499 auto GUIDs = StartAddrToSymMap.equal_range(x: Range.first);
500 for (const auto &[StartAddr, Func] : make_range(p: GUIDs))
501 FuncStartAddresses[Func] = StartAddr;
502 }
503 }
504 }
505
506 StringRef FileName = Obj->getFileName();
507 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
508 SI != SE; ++SI) {
509 const SectionRef &Section = *SI;
510 StringRef SectionName = unwrapOrError(EO: Section.getName(), Args&: FileName);
511
512 if (SectionName == ".pseudo_probe_desc") {
513 StringRef Contents = unwrapOrError(EO: Section.getContents(), Args&: FileName);
514 if (!ProbeDecoder.buildGUID2FuncDescMap(
515 Start: reinterpret_cast<const uint8_t *>(Contents.data()),
516 Size: Contents.size()))
517 exitWithError(
518 Message: "Pseudo Probe decoder fail in .pseudo_probe_desc section");
519 } else if (SectionName == ".pseudo_probe") {
520 StringRef Contents = unwrapOrError(EO: Section.getContents(), Args&: FileName);
521 if (!ProbeDecoder.buildAddress2ProbeMap(
522 Start: reinterpret_cast<const uint8_t *>(Contents.data()),
523 Size: Contents.size(), GuildFilter: GuidFilter, FuncStartAddrs: FuncStartAddresses))
524 exitWithError(Message: "Pseudo Probe decoder fail in .pseudo_probe section");
525 }
526 }
527
528 // Build TopLevelProbeFrameMap to track size for optimized inlinees when probe
529 // is available
530 if (TrackFuncContextSize) {
531 for (auto &Child : ProbeDecoder.getDummyInlineRoot().getChildren()) {
532 auto *Frame = &Child;
533 StringRef FuncName =
534 ProbeDecoder.getFuncDescForGUID(GUID: Frame->Guid)->FuncName;
535 TopLevelProbeFrameMap[FuncName] = Frame;
536 }
537 }
538
539 if (ShowPseudoProbe)
540 ProbeDecoder.printGUID2FuncDescMap(OS&: outs());
541}
542
543void ProfiledBinary::decodePseudoProbe() {
544 OwningBinary<Binary> OBinary =
545 unwrapOrError(EO: createBinary(Path: PseudoProbeBinPath), Args&: PseudoProbeBinPath);
546 auto *Obj = cast<ObjectFile>(Val: OBinary.getBinary());
547 decodePseudoProbe(Obj);
548}
549
550void ProfiledBinary::setIsFuncEntry(FuncRange *FuncRange,
551 StringRef RangeSymName) {
552 // Skip external function symbol.
553 if (!FuncRange)
554 return;
555
556 // Set IsFuncEntry to ture if there is only one range in the function or the
557 // RangeSymName from ELF is equal to its DWARF-based function name.
558 if (FuncRange->Func->Ranges.size() == 1 ||
559 (!FuncRange->IsFuncEntry &&
560 (FuncRange->getFuncName() == RangeSymName ||
561 FuncRange->Func->NameStatus != DwarfNameStatus::Matched)))
562 FuncRange->IsFuncEntry = true;
563}
564
565bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
566 SectionSymbolsTy &Symbols,
567 const SectionRef &Section) {
568 std::size_t SE = Symbols.size();
569 uint64_t SectionAddress = Section.getAddress();
570 uint64_t SectSize = Section.getSize();
571 uint64_t StartAddress = Symbols[SI].Addr;
572 uint64_t NextStartAddress =
573 (SI + 1 < SE) ? Symbols[SI + 1].Addr : SectionAddress + SectSize;
574 FuncRange *FRange = findFuncRange(Address: StartAddress);
575 setIsFuncEntry(FuncRange: FRange, RangeSymName: FunctionSamples::getCanonicalFnName(FnName: Symbols[SI].Name));
576 StringRef SymbolName =
577 ShowCanonicalFnName
578 ? FunctionSamples::getCanonicalFnName(FnName: Symbols[SI].Name)
579 : Symbols[SI].Name;
580 bool ShowDisassembly =
581 ShowDisassemblyOnly && (DisassembleFunctionSet.empty() ||
582 DisassembleFunctionSet.count(Key: SymbolName));
583 if (ShowDisassembly)
584 outs() << '<' << SymbolName << ">:\n";
585
586 uint64_t Address = StartAddress;
587 // Size of a consecutive invalid instruction range starting from Address -1
588 // backwards.
589 uint64_t InvalidInstLength = 0;
590 while (Address < NextStartAddress) {
591 MCInst Inst;
592 uint64_t Size;
593 // Disassemble an instruction.
594 bool Disassembled = DisAsm->getInstruction(
595 Instr&: Inst, Size, Bytes: Bytes.slice(N: Address - SectionAddress), Address, CStream&: nulls());
596 if (Size == 0)
597 Size = 1;
598
599 if (ShowDisassembly) {
600 if (ShowPseudoProbe) {
601 ProbeDecoder.printProbeForAddress(OS&: outs(), Address);
602 }
603 outs() << format(Fmt: "%8" PRIx64 ":", Vals: Address);
604 size_t Start = outs().tell();
605 if (Disassembled)
606 IPrinter->printInst(MI: &Inst, Address: Address + Size, Annot: "", STI: *STI, OS&: outs());
607 else
608 outs() << "\t<unknown>";
609 if (ShowSourceLocations) {
610 unsigned Cur = outs().tell() - Start;
611 if (Cur < 40)
612 outs().indent(NumSpaces: 40 - Cur);
613 InstructionPointer IP(this, Address);
614 outs() << getReversedLocWithContext(
615 Context: symbolize(IP, UseCanonicalFnName: ShowCanonicalFnName, UseProbeDiscriminator: ShowPseudoProbe));
616 }
617 outs() << "\n";
618 }
619
620 if (Disassembled) {
621 const MCInstrDesc &MCDesc = MII->get(Opcode: Inst.getOpcode());
622
623 // Record instruction size.
624 AddressToInstSizeMap[Address] = Size;
625
626 // Populate address maps.
627 CodeAddressVec.push_back(x: Address);
628 if (MCDesc.isCall()) {
629 CallAddressSet.insert(x: Address);
630 UncondBranchAddrSet.insert(x: Address);
631 } else if (MCDesc.isReturn()) {
632 RetAddressSet.insert(x: Address);
633 UncondBranchAddrSet.insert(x: Address);
634 } else if (MCDesc.isBranch()) {
635 if (MCDesc.isUnconditionalBranch())
636 UncondBranchAddrSet.insert(x: Address);
637 BranchAddressSet.insert(x: Address);
638 }
639
640 // Record potential call targets for tail frame inference later-on.
641 if (InferMissingFrames && FRange) {
642 uint64_t Target = 0;
643 [[maybe_unused]] bool Err =
644 MIA->evaluateBranch(Inst, Addr: Address, Size, Target);
645 if (MCDesc.isCall()) {
646 // Indirect call targets are unknown at this point. Recording the
647 // unknown target (zero) for further LBR-based refinement.
648 MissingContextInferrer->CallEdges[Address].insert(x: Target);
649 } else if (MCDesc.isUnconditionalBranch()) {
650 assert(Err &&
651 "target should be known for unconditional direct branch");
652 // Any inter-function unconditional jump is considered tail call at
653 // this point. This is not 100% accurate and could further be
654 // optimized based on some source annotation.
655 FuncRange *ToFRange = findFuncRange(Address: Target);
656 if (ToFRange && ToFRange->Func != FRange->Func)
657 MissingContextInferrer->TailCallEdges[Address].insert(x: Target);
658 LLVM_DEBUG({
659 dbgs() << "Direct Tail call: " << format("%8" PRIx64 ":", Address);
660 IPrinter->printInst(&Inst, Address + Size, "", *STI.get(), dbgs());
661 dbgs() << "\n";
662 });
663 } else if (MCDesc.isIndirectBranch() && MCDesc.isBarrier()) {
664 // This is an indirect branch but not necessarily an indirect tail
665 // call. The isBarrier check is to filter out conditional branch.
666 // Similar with indirect call targets, recording the unknown target
667 // (zero) for further LBR-based refinement.
668 MissingContextInferrer->TailCallEdges[Address].insert(x: Target);
669 LLVM_DEBUG({
670 dbgs() << "Indirect Tail call: "
671 << format("%8" PRIx64 ":", Address);
672 IPrinter->printInst(&Inst, Address + Size, "", *STI.get(), dbgs());
673 dbgs() << "\n";
674 });
675 }
676 }
677
678 if (InvalidInstLength) {
679 AddrsWithInvalidInstruction.insert(
680 V: {Address - InvalidInstLength, Address - 1});
681 InvalidInstLength = 0;
682 }
683 } else {
684 InvalidInstLength += Size;
685 }
686
687 Address += Size;
688 }
689
690 if (InvalidInstLength)
691 AddrsWithInvalidInstruction.insert(
692 V: {Address - InvalidInstLength, Address - 1});
693
694 if (ShowDisassembly)
695 outs() << "\n";
696
697 return true;
698}
699
700void ProfiledBinary::setUpDisassembler(const ObjectFile *Obj) {
701 const Target *TheTarget = getTarget(Obj);
702 StringRef FileName = Obj->getFileName();
703
704 MRI.reset(p: TheTarget->createMCRegInfo(TT: TheTriple));
705 if (!MRI)
706 exitWithError(Message: "no register info for target " + TheTriple.str(), Whence: FileName);
707
708 MCTargetOptions MCOptions;
709 AsmInfo.reset(p: TheTarget->createMCAsmInfo(MRI: *MRI, TheTriple, Options: MCOptions));
710 if (!AsmInfo)
711 exitWithError(Message: "no assembly info for target " + TheTriple.str(), Whence: FileName);
712
713 Expected<SubtargetFeatures> Features = Obj->getFeatures();
714 if (!Features)
715 exitWithError(E: Features.takeError(), Whence: FileName);
716 STI.reset(
717 p: TheTarget->createMCSubtargetInfo(TheTriple, CPU: "", Features: Features->getString()));
718 if (!STI)
719 exitWithError(Message: "no subtarget info for target " + TheTriple.str(), Whence: FileName);
720
721 MII.reset(p: TheTarget->createMCInstrInfo());
722 if (!MII)
723 exitWithError(Message: "no instruction info for target " + TheTriple.str(),
724 Whence: FileName);
725
726 MCContext Ctx(TheTriple, AsmInfo.get(), MRI.get(), STI.get());
727 std::unique_ptr<MCObjectFileInfo> MOFI(
728 TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
729 Ctx.setObjectFileInfo(MOFI.get());
730 DisAsm.reset(p: TheTarget->createMCDisassembler(STI: *STI, Ctx));
731 if (!DisAsm)
732 exitWithError(Message: "no disassembler for target " + TheTriple.str(), Whence: FileName);
733
734 MIA.reset(p: TheTarget->createMCInstrAnalysis(Info: MII.get()));
735
736 int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
737 IPrinter.reset(p: TheTarget->createMCInstPrinter(T: TheTriple, SyntaxVariant: AsmPrinterVariant,
738 MAI: *AsmInfo, MII: *MII, MRI: *MRI));
739 IPrinter->setPrintBranchImmAsAddress(true);
740}
741
742void ProfiledBinary::disassemble(const ObjectFile *Obj) {
743 // Set up disassembler and related components.
744 setUpDisassembler(Obj);
745
746 // Create a mapping from virtual address to symbol name. The symbols in text
747 // sections are the candidates to dissassemble.
748 std::map<SectionRef, SectionSymbolsTy> AllSymbols;
749 StringRef FileName = Obj->getFileName();
750 for (const SymbolRef &Symbol : Obj->symbols()) {
751 const uint64_t Addr = unwrapOrError(EO: Symbol.getAddress(), Args&: FileName);
752 const StringRef Name = unwrapOrError(EO: Symbol.getName(), Args&: FileName);
753 section_iterator SecI = unwrapOrError(EO: Symbol.getSection(), Args&: FileName);
754 if (SecI != Obj->section_end())
755 AllSymbols[*SecI].push_back(x: SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE));
756 }
757
758 // Sort all the symbols. Use a stable sort to stabilize the output.
759 for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
760 stable_sort(Range&: SecSyms.second);
761
762 assert((DisassembleFunctionSet.empty() || ShowDisassemblyOnly) &&
763 "Functions to disassemble should be only specified together with "
764 "--show-disassembly-only");
765
766 if (ShowDisassemblyOnly)
767 outs() << "\nDisassembly of " << FileName << ":\n";
768
769 // Dissassemble a text section.
770 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
771 SI != SE; ++SI) {
772 const SectionRef &Section = *SI;
773 if (!Section.isText())
774 continue;
775
776 uint64_t ImageLoadAddr = getPreferredBaseAddress();
777 uint64_t SectionAddress = Section.getAddress() - ImageLoadAddr;
778 uint64_t SectSize = Section.getSize();
779 if (!SectSize)
780 continue;
781
782 // Register the text section.
783 TextSections.insert(x: {SectionAddress, SectSize});
784
785 StringRef SectionName = unwrapOrError(EO: Section.getName(), Args&: FileName);
786
787 if (ShowDisassemblyOnly) {
788 outs() << "\nDisassembly of section " << SectionName;
789 outs() << " [" << format(Fmt: "0x%" PRIx64, Vals: Section.getAddress()) << ", "
790 << format(Fmt: "0x%" PRIx64, Vals: Section.getAddress() + SectSize)
791 << "]:\n\n";
792 }
793
794 if (isa<ELFObjectFileBase>(Val: Obj) && SectionName == ".plt")
795 continue;
796
797 // Get the section data.
798 ArrayRef<uint8_t> Bytes =
799 arrayRefFromStringRef(Input: unwrapOrError(EO: Section.getContents(), Args&: FileName));
800
801 // Get the list of all the symbols in this section.
802 SectionSymbolsTy &Symbols = AllSymbols[Section];
803
804 // Disassemble symbol by symbol.
805 for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
806 if (!dissassembleSymbol(SI, Bytes, Symbols, Section))
807 exitWithError(Message: "disassembling error", Whence: FileName);
808 }
809 }
810
811 if (!AddrsWithInvalidInstruction.empty()) {
812 if (ShowDetailedWarning) {
813 for (auto &Addr : AddrsWithInvalidInstruction) {
814 WithColor::warning()
815 << "Invalid instructions at " << format(Fmt: "%8" PRIx64, Vals: Addr.first)
816 << " - " << format(Fmt: "%8" PRIx64, Vals: Addr.second) << "\n";
817 }
818 }
819 WithColor::warning() << "Found " << AddrsWithInvalidInstruction.size()
820 << " invalid instructions\n";
821 AddrsWithInvalidInstruction.clear();
822 }
823
824 // Dissassemble rodata section to check if FS discriminator symbol exists.
825 checkUseFSDiscriminator(Obj, AllSymbols);
826}
827
828void ProfiledBinary::checkUseFSDiscriminator(
829 const ObjectFile *Obj, std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
830 const char *FSDiscriminatorVar = "__llvm_fs_discriminator__";
831 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
832 SI != SE; ++SI) {
833 const SectionRef &Section = *SI;
834 if (!Section.isData() || Section.getSize() == 0)
835 continue;
836 SectionSymbolsTy &Symbols = AllSymbols[Section];
837
838 for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
839 if (Symbols[SI].Name == FSDiscriminatorVar) {
840 UseFSDiscriminator = true;
841 return;
842 }
843 }
844 }
845}
846
847void ProfiledBinary::populateSymbolAddressList(const ObjectFile *Obj) {
848 // Create a mapping from virtual address to symbol GUID and the other way
849 // around.
850 StringRef FileName = Obj->getFileName();
851 for (const SymbolRef &Symbol : Obj->symbols()) {
852 const uint64_t Addr = unwrapOrError(EO: Symbol.getAddress(), Args&: FileName);
853 const StringRef Name = unwrapOrError(EO: Symbol.getName(), Args&: FileName);
854 uint64_t GUID = Function::getGUIDAssumingExternalLinkage(GlobalName: Name);
855 SymbolStartAddrs[GUID] = Addr;
856 StartAddrToSymMap.emplace(args: Addr, args&: GUID);
857 }
858}
859
860void ProfiledBinary::loadSymbolsFromSymtab(const ObjectFile *Obj) {
861 // Load binary functions from symbol table when Debug info is incomplete.
862 // Strip the internal suffixes which are not reflected in the DWARF info.
863 const SmallVector<StringRef, 10> Suffixes(
864 {// Internal suffixes from CoroSplit pass
865 ".cleanup", ".destroy", ".resume",
866 // Internal suffixes from Bolt
867 ".cold", ".warm",
868 // Compiler/LTO internal
869 ".llvm.", ".part.", ".isra.", ".constprop.", ".lto_priv."});
870 StringRef FileName = Obj->getFileName();
871
872 // COFF symtab does not have size field. Try to load size from PDB instead.
873 std::unique_ptr<pdb::IPDBSession> PDBSession;
874 if (auto *COFFObj = dyn_cast<COFFObjectFile>(Val: Obj)) {
875 if (auto E = pdb::loadDataForEXE(Type: pdb::PDB_ReaderType::Native, Path: FileName,
876 Session&: PDBSession)) {
877 StringRef PdbPath;
878 const codeview::DebugInfo *PdbInfo;
879 if (auto Err = COFFObj->getDebugPDBInfo(Info&: PdbInfo, PDBFileName&: PdbPath))
880 consumeError(Err: std::move(Err));
881
882 auto Style = PdbPath.starts_with(Prefix: "/") ? sys::path::Style::posix
883 : sys::path::Style::windows;
884 WithColor::warning() << "Cannot load PDB file "
885 << sys::path::filename(path: PdbPath, style: Style) << " for "
886 << FileName << ": " << E << "\n";
887 consumeError(Err: std::move(E));
888 } else {
889 PDBSession->setLoadAddress(FirstLoadableAddress);
890 }
891 }
892
893 for (const SymbolRef &Symbol : Obj->symbols()) {
894 const SymbolRef::Type Type = unwrapOrError(EO: Symbol.getType(), Args&: FileName);
895 const uint64_t StartAddr = unwrapOrError(EO: Symbol.getAddress(), Args&: FileName);
896 const StringRef Name = unwrapOrError(EO: Symbol.getName(), Args&: FileName);
897 uint64_t Size = 0;
898 if (isa<ELFObjectFileBase>(Val: Obj)) {
899 ELFSymbolRef ElfSymbol(Symbol);
900 Size = ElfSymbol.getSize();
901 } else if (PDBSession) {
902 if (std::unique_ptr<pdb::PDBSymbol> Sym = PDBSession->findSymbolByAddress(
903 Address: StartAddr, Type: pdb::PDB_SymType::Function)) {
904 auto FuncSym = cast<pdb::PDBSymbolFunc>(Val: std::move(Sym));
905 if (StartAddr == FuncSym->getVirtualAddress())
906 Size = FuncSym->getLength();
907 }
908 }
909
910 if (Size == 0 || Type != SymbolRef::ST_Function)
911 continue;
912
913 const uint64_t EndAddr = StartAddr + Size;
914 const StringRef SymName =
915 FunctionSamples::getCanonicalFnName(FnName: Name, Suffixes);
916 assert(StartAddr < EndAddr && StartAddr >= getPreferredBaseAddress() &&
917 "Function range is invalid.");
918
919 auto Range = findFuncRange(Address: StartAddr);
920 if (!Range) {
921 assert(findFuncRange(EndAddr - 1) == nullptr &&
922 "Function range overlaps with existing functions.");
923 // Function from symbol table not found previously in DWARF, store ranges.
924 auto Ret = BinaryFunctions.emplace(args: SymName, args: BinaryFunction());
925 auto &Func = Ret.first->second;
926 if (Ret.second) {
927 Func.FuncName = Ret.first->first;
928 HashBinaryFunctions[Function::getGUIDAssumingExternalLinkage(GlobalName: SymName)] =
929 &Func;
930 }
931
932 Func.NameStatus = DwarfNameStatus::Missing;
933 Func.Ranges.emplace_back(args: StartAddr, args: EndAddr);
934
935 auto R = StartAddrToFuncRangeMap.emplace(args: StartAddr, args: FuncRange());
936 FuncRange &FRange = R.first->second;
937
938 FRange.Func = &Func;
939 FRange.StartAddress = StartAddr;
940 FRange.EndAddress = EndAddr;
941
942 } else if (SymName != Range->getFuncName()) {
943 // Function range already found from DWARF or symtab, but the symbol name
944 // from symbol table is inconsistent with the existing name associated
945 // with the range. Log this discrepancy and the alternative function GUID.
946 if (ShowDetailedWarning)
947 WithColor::warning()
948 << "Conflicting name for symbol " << Name << " with range ("
949 << format(Fmt: "%8" PRIx64, Vals: StartAddr) << ", "
950 << format(Fmt: "%8" PRIx64, Vals: EndAddr) << ")"
951 << ", but the existing symbol " << Range->getFuncName()
952 << " indicates an overlapping range ("
953 << format(Fmt: "%8" PRIx64, Vals: Range->StartAddress) << ", "
954 << format(Fmt: "%8" PRIx64, Vals: Range->EndAddress) << ")\n";
955
956 assert(StartAddr == Range->StartAddress && EndAddr == Range->EndAddress &&
957 "Mismatched function range");
958
959 Range->Func->NameStatus = DwarfNameStatus::Mismatch;
960 AlternativeFunctionGUIDs.emplace(
961 args&: Range->Func, args: Function::getGUIDAssumingExternalLinkage(GlobalName: SymName));
962
963 } else if (StartAddr != Range->StartAddress &&
964 EndAddr != Range->EndAddress) {
965 // Function already found in DWARF or symtab, but the address range from
966 // symbol table conflicts/overlaps with the existing one.
967 WithColor::warning() << "Conflicting range for symbol " << Name
968 << " with range (" << format(Fmt: "%8" PRIx64, Vals: StartAddr)
969 << ", " << format(Fmt: "%8" PRIx64, Vals: EndAddr) << ")"
970 << ", but the existing symbol "
971 << Range->getFuncName()
972 << " indicates another range ("
973 << format(Fmt: "%8" PRIx64, Vals: Range->StartAddress) << ", "
974 << format(Fmt: "%8" PRIx64, Vals: Range->EndAddress) << ")\n";
975 }
976 }
977}
978
979void ProfiledBinary::loadSymbolsFromDWARFUnit(DWARFUnit &CompilationUnit) {
980 for (const auto &DieInfo : CompilationUnit.dies()) {
981 llvm::DWARFDie Die(&CompilationUnit, &DieInfo);
982
983 if (!Die.isSubprogramDIE())
984 continue;
985 auto Name = Die.getName(Kind: llvm::DINameKind::LinkageName);
986 if (!Name)
987 Name = Die.getName(Kind: llvm::DINameKind::ShortName);
988 if (!Name)
989 continue;
990
991 auto RangesOrError = Die.getAddressRanges();
992 if (!RangesOrError)
993 continue;
994 const DWARFAddressRangesVector &Ranges = RangesOrError.get();
995
996 if (Ranges.empty())
997 continue;
998
999 // Different DWARF symbols can have same function name, search or create
1000 // BinaryFunction indexed by the name.
1001 auto Ret = BinaryFunctions.emplace(args&: Name, args: BinaryFunction());
1002 auto &Func = Ret.first->second;
1003 if (Ret.second)
1004 Func.FuncName = Ret.first->first;
1005
1006 for (const auto &Range : Ranges) {
1007 uint64_t StartAddress = Range.LowPC;
1008 uint64_t EndAddress = Range.HighPC;
1009
1010 if (EndAddress <= StartAddress ||
1011 StartAddress < getPreferredBaseAddress())
1012 continue;
1013
1014 // We may want to know all ranges for one function. Here group the
1015 // ranges and store them into BinaryFunction.
1016 Func.Ranges.emplace_back(args&: StartAddress, args&: EndAddress);
1017
1018 auto R = StartAddrToFuncRangeMap.emplace(args&: StartAddress, args: FuncRange());
1019 if (R.second) {
1020 FuncRange &FRange = R.first->second;
1021 FRange.Func = &Func;
1022 FRange.StartAddress = StartAddress;
1023 FRange.EndAddress = EndAddress;
1024 } else {
1025 AddrsWithMultipleSymbols.insert(V: StartAddress);
1026 if (ShowDetailedWarning)
1027 WithColor::warning()
1028 << "Duplicated symbol start address at "
1029 << format(Fmt: "%8" PRIx64, Vals: StartAddress) << " "
1030 << R.first->second.getFuncName() << " and " << Name << "\n";
1031 }
1032 }
1033 }
1034}
1035
1036void ProfiledBinary::loadSymbolsFromDWARF(ObjectFile &Obj) {
1037 auto DebugContext = llvm::DWARFContext::create(
1038 Obj, RelocAction: DWARFContext::ProcessDebugRelocations::Process, L: nullptr, DWPName: DWPPath);
1039 if (!DebugContext)
1040 exitWithError(Message: "Error creating the debug info context", Whence: Path);
1041
1042 for (const auto &CompilationUnit : DebugContext->compile_units())
1043 loadSymbolsFromDWARFUnit(CompilationUnit&: *CompilationUnit);
1044
1045 // Handles DWO sections that can either be in .o, .dwo or .dwp files.
1046 uint32_t NumOfDWOMissing = 0;
1047 for (const auto &CompilationUnit : DebugContext->compile_units()) {
1048 DWARFUnit *const DwarfUnit = CompilationUnit.get();
1049 if (DwarfUnit->getDWOId()) {
1050 DWARFUnit *DWOCU = DwarfUnit->getNonSkeletonUnitDIE(ExtractUnitDIEOnly: false).getDwarfUnit();
1051 if (!DWOCU->isDWOUnit()) {
1052 NumOfDWOMissing++;
1053 if (ShowDetailedWarning) {
1054 std::string DWOName = dwarf::toString(
1055 V: DwarfUnit->getUnitDIE().find(
1056 Attrs: {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),
1057 Default: "");
1058 WithColor::warning() << "DWO debug information for " << DWOName
1059 << " was not loaded.\n";
1060 }
1061 continue;
1062 }
1063 loadSymbolsFromDWARFUnit(CompilationUnit&: *DWOCU);
1064 }
1065 }
1066
1067 if (NumOfDWOMissing)
1068 WithColor::warning()
1069 << " DWO debug information was not loaded for " << NumOfDWOMissing
1070 << " modules. Please check the .o, .dwo or .dwp path.\n";
1071 if (BinaryFunctions.empty())
1072 WithColor::warning() << "Loading of DWARF info completed, but no binary "
1073 "functions have been retrieved.\n";
1074 // Populate the hash binary function map for MD5 function name lookup. This
1075 // is done after BinaryFunctions are finalized.
1076 for (auto &BinaryFunction : BinaryFunctions) {
1077 HashBinaryFunctions[MD5Hash(Str: StringRef(BinaryFunction.first))] =
1078 &BinaryFunction.second;
1079 }
1080
1081 if (!AddrsWithMultipleSymbols.empty()) {
1082 WithColor::warning() << "Found " << AddrsWithMultipleSymbols.size()
1083 << " start addresses with multiple symbols\n";
1084 AddrsWithMultipleSymbols.clear();
1085 }
1086}
1087
1088void ProfiledBinary::populateSymbolListFromDWARF(
1089 ProfileSymbolList &SymbolList) {
1090 for (auto &I : StartAddrToFuncRangeMap)
1091 SymbolList.add(Name: I.second.getFuncName());
1092}
1093
1094symbolize::LLVMSymbolizer::Options ProfiledBinary::getSymbolizerOpts() const {
1095 symbolize::LLVMSymbolizer::Options SymbolizerOpts;
1096 SymbolizerOpts.PrintFunctions =
1097 DILineInfoSpecifier::FunctionNameKind::LinkageName;
1098 SymbolizerOpts.Demangle = false;
1099 SymbolizerOpts.DefaultArch = TheTriple.getArchName().str();
1100 SymbolizerOpts.UseSymbolTable = false;
1101 SymbolizerOpts.RelativeAddresses = false;
1102 SymbolizerOpts.DWPName = DWPPath;
1103 return SymbolizerOpts;
1104}
1105
1106SampleContextFrameVector ProfiledBinary::symbolize(const InstructionPointer &IP,
1107 bool UseCanonicalFnName,
1108 bool UseProbeDiscriminator) {
1109 assert(this == IP.Binary &&
1110 "Binary should only symbolize its own instruction");
1111 DIInliningInfo InlineStack =
1112 unwrapOrError(EO: Symbolizer->symbolizeInlinedCode(
1113 ModuleName: SymbolizerPath.str(), ModuleOffset: getSectionedAddress(Address: IP.Address)),
1114 Args&: SymbolizerPath);
1115
1116 SampleContextFrameVector CallStack;
1117 for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) {
1118 const auto &CallerFrame = InlineStack.getFrame(Index: I);
1119 if (CallerFrame.FunctionName.empty() ||
1120 (CallerFrame.FunctionName == "<invalid>"))
1121 break;
1122
1123 StringRef FunctionName(CallerFrame.FunctionName);
1124 if (UseCanonicalFnName)
1125 FunctionName = FunctionSamples::getCanonicalFnName(FnName: FunctionName);
1126
1127 uint32_t Discriminator = CallerFrame.Discriminator;
1128 uint32_t LineOffset = (CallerFrame.Line - CallerFrame.StartLine) & 0xffff;
1129 if (UseProbeDiscriminator) {
1130 LineOffset =
1131 PseudoProbeDwarfDiscriminator::extractProbeIndex(Value: Discriminator);
1132 Discriminator = 0;
1133 }
1134
1135 LineLocation Line(LineOffset, Discriminator);
1136 auto It = NameStrings.insert(x: FunctionName.str());
1137 CallStack.emplace_back(Args: FunctionId(StringRef(*It.first)), Args&: Line);
1138 }
1139
1140 return CallStack;
1141}
1142
1143StringRef ProfiledBinary::symbolizeDataAddress(uint64_t Address) {
1144 DIGlobal DataDIGlobal =
1145 unwrapOrError(EO: Symbolizer->symbolizeData(ModuleName: SymbolizerPath.str(),
1146 ModuleOffset: getSectionedAddress(Address)),
1147 Args&: SymbolizerPath);
1148 decltype(NameStrings)::iterator Iter;
1149 std::tie(args&: Iter, args: std::ignore) = NameStrings.insert(x: DataDIGlobal.Name);
1150 return StringRef(*Iter);
1151}
1152
1153void ProfiledBinary::computeInlinedContextSizeForRange(uint64_t RangeBegin,
1154 uint64_t RangeEnd) {
1155 InstructionPointer IP(this, RangeBegin, true);
1156
1157 if (IP.Address != RangeBegin)
1158 WithColor::warning() << "Invalid start instruction at "
1159 << format(Fmt: "%8" PRIx64, Vals: RangeBegin) << "\n";
1160
1161 if (IP.Address >= RangeEnd)
1162 return;
1163
1164 do {
1165 const SampleContextFrameVector SymbolizedCallStack =
1166 getFrameLocationStack(Address: IP.Address, UseProbeDiscriminator: usePseudoProbes());
1167 uint64_t Size = AddressToInstSizeMap[IP.Address];
1168 // Record instruction size for the corresponding context
1169 FuncSizeTracker.addInstructionForContext(Context: SymbolizedCallStack, InstrSize: Size);
1170
1171 } while (IP.advance() && IP.Address < RangeEnd);
1172}
1173
1174void ProfiledBinary::computeInlinedContextSizeForFunc(
1175 const BinaryFunction *Func) {
1176 // Note that a function can be spilt into multiple ranges, so compute for all
1177 // ranges of the function.
1178 for (const auto &Range : Func->Ranges)
1179 computeInlinedContextSizeForRange(RangeBegin: Range.first, RangeEnd: Range.second);
1180
1181 // Track optimized-away inlinee for probed binary. A function inlined and then
1182 // optimized away should still have their probes left over in places.
1183 if (usePseudoProbes()) {
1184 auto I = TopLevelProbeFrameMap.find(Key: Func->FuncName);
1185 if (I != TopLevelProbeFrameMap.end()) {
1186 BinarySizeContextTracker::ProbeFrameStack ProbeContext;
1187 FuncSizeTracker.trackInlineesOptimizedAway(ProbeDecoder, ProbeNode: *I->second,
1188 ProbeContext);
1189 }
1190 }
1191}
1192
1193void ProfiledBinary::loadSymbolsFromPseudoProbe() {
1194 if (!usePseudoProbes())
1195 return;
1196
1197 const AddressProbesMap &Address2ProbesMap = getAddress2ProbesMap();
1198 for (auto *Func : ProfiledFunctions) {
1199 if (Func->NameStatus != DwarfNameStatus::Mismatch)
1200 continue;
1201 for (auto &[StartAddr, EndAddr] : Func->Ranges) {
1202 auto Range = findFuncRangeForStartAddr(Address: StartAddr);
1203 if (!Range->IsFuncEntry)
1204 continue;
1205 const auto &Probe = Address2ProbesMap.find(From: StartAddr, To: EndAddr);
1206 if (Probe.begin() != Probe.end()) {
1207 const MCDecodedPseudoProbeInlineTree *InlineTreeNode =
1208 Probe.begin()->get().getInlineTreeNode();
1209 while (!InlineTreeNode->isTopLevelFunc())
1210 InlineTreeNode = static_cast<MCDecodedPseudoProbeInlineTree *>(
1211 InlineTreeNode->Parent);
1212
1213 auto TopLevelProbes = InlineTreeNode->getProbes();
1214 [[maybe_unused]] auto TopProbe = TopLevelProbes.begin();
1215 assert(TopProbe != TopLevelProbes.end() &&
1216 TopProbe->getAddress() >= StartAddr &&
1217 TopProbe->getAddress() < EndAddr &&
1218 "Top level pseudo probe does not match function range");
1219
1220 const auto *ProbeDesc = getFuncDescForGUID(GUID: InlineTreeNode->Guid);
1221 auto Ret = PseudoProbeNames.emplace(args&: Func, args: ProbeDesc->FuncName);
1222 if (!Ret.second && Ret.first->second != ProbeDesc->FuncName &&
1223 ShowDetailedWarning)
1224 WithColor::warning()
1225 << "Mismatched pseudo probe names in function " << Func->FuncName
1226 << " at range: (" << format(Fmt: "%8" PRIx64, Vals: StartAddr) << ", "
1227 << format(Fmt: "%8" PRIx64, Vals: EndAddr) << "). "
1228 << "The previously found pseudo probe name is "
1229 << Ret.first->second << " but it conflicts with name "
1230 << ProbeDesc->FuncName
1231 << " This likely indicates a DWARF error that produces "
1232 "conflicting symbols at the same starting address.\n";
1233 }
1234 }
1235 }
1236}
1237
1238StringRef ProfiledBinary::findPseudoProbeName(const BinaryFunction *Func) {
1239 auto ProbeName = PseudoProbeNames.find(x: Func);
1240 if (ProbeName == PseudoProbeNames.end())
1241 return StringRef();
1242 return ProbeName->second;
1243}
1244
1245void ProfiledBinary::inferMissingFrames(
1246 const SmallVectorImpl<uint64_t> &Context,
1247 SmallVectorImpl<uint64_t> &NewContext) {
1248 MissingContextInferrer->inferMissingFrames(Context, NewContext);
1249}
1250
1251InstructionPointer::InstructionPointer(const ProfiledBinary *Binary,
1252 uint64_t Address, bool RoundToNext)
1253 : Binary(Binary), Address(Address) {
1254 Index = Binary->getIndexForAddr(Address);
1255 if (RoundToNext) {
1256 // we might get address which is not the code
1257 // it should round to the next valid address
1258 if (Index >= Binary->getCodeAddrVecSize())
1259 this->Address = UINT64_MAX;
1260 else
1261 this->Address = Binary->getAddressforIndex(Index);
1262 }
1263}
1264
1265bool InstructionPointer::advance() {
1266 Index++;
1267 if (Index >= Binary->getCodeAddrVecSize()) {
1268 Address = UINT64_MAX;
1269 return false;
1270 }
1271 Address = Binary->getAddressforIndex(Index);
1272 return true;
1273}
1274
1275bool InstructionPointer::backward() {
1276 if (Index == 0) {
1277 Address = 0;
1278 return false;
1279 }
1280 Index--;
1281 Address = Binary->getAddressforIndex(Index);
1282 return true;
1283}
1284
1285void InstructionPointer::update(uint64_t Addr) {
1286 Address = Addr;
1287 Index = Binary->getIndexForAddr(Address);
1288}
1289
1290} // end namespace sampleprof
1291} // end namespace llvm
1292