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