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