1//===-- ProfiledBinary.h - 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#ifndef LLVM_TOOLS_LLVM_PROFGEN_PROFILEDBINARY_H
10#define LLVM_TOOLS_LLVM_PROFGEN_PROFILEDBINARY_H
11
12#include "CallContext.h"
13#include "ErrorHandling.h"
14#include "llvm/ADT/DenseMap.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/ADT/StringSet.h"
17#include "llvm/DebugInfo/DWARF/DWARFContext.h"
18#include "llvm/DebugInfo/Symbolize/Symbolize.h"
19#include "llvm/MC/MCAsmInfo.h"
20#include "llvm/MC/MCContext.h"
21#include "llvm/MC/MCDisassembler/MCDisassembler.h"
22#include "llvm/MC/MCInst.h"
23#include "llvm/MC/MCInstPrinter.h"
24#include "llvm/MC/MCInstrAnalysis.h"
25#include "llvm/MC/MCInstrInfo.h"
26#include "llvm/MC/MCObjectFileInfo.h"
27#include "llvm/MC/MCPseudoProbe.h"
28#include "llvm/MC/MCRegisterInfo.h"
29#include "llvm/MC/MCSubtargetInfo.h"
30#include "llvm/MC/MCTargetOptions.h"
31#include "llvm/Object/ELFObjectFile.h"
32#include "llvm/ProfileData/SampleProf.h"
33#include "llvm/Support/CommandLine.h"
34#include "llvm/Support/Path.h"
35#include "llvm/Transforms/IPO/SampleContextTracker.h"
36#include <map>
37#include <set>
38#include <sstream>
39#include <string>
40#include <unordered_map>
41#include <unordered_set>
42#include <vector>
43
44namespace llvm {
45extern cl::opt<bool> EnableCSPreInliner;
46extern cl::opt<bool> UseContextCostForPreInliner;
47} // namespace llvm
48
49using namespace llvm;
50using namespace sampleprof;
51using namespace llvm::object;
52
53namespace llvm {
54namespace sampleprof {
55
56class ProfiledBinary;
57class MissingFrameInferrer;
58
59struct InstructionPointer {
60 const ProfiledBinary *Binary;
61 // Address of the executable segment of the binary.
62 uint64_t Address;
63 // Index to the sorted code address array of the binary.
64 uint64_t Index = 0;
65 InstructionPointer(const ProfiledBinary *Binary, uint64_t Address,
66 bool RoundToNext = false);
67 bool advance();
68 bool backward();
69 void update(uint64_t Addr);
70};
71
72// The special frame addresses.
73enum SpecialFrameAddr {
74 // Dummy root of frame trie.
75 DummyRoot = 0,
76 // Represent all the addresses outside of current binary.
77 // This's also used to indicate the call stack should be truncated since this
78 // isn't a real call context the compiler will see.
79 ExternalAddr = 1,
80};
81
82using RangesTy = std::vector<std::pair<uint64_t, uint64_t>>;
83
84struct BinaryFunction {
85 StringRef FuncName;
86 // End of range is an exclusive bound.
87 RangesTy Ranges;
88
89 uint64_t getFuncSize() {
90 uint64_t Sum = 0;
91 for (auto &R : Ranges) {
92 Sum += R.second - R.first;
93 }
94 return Sum;
95 }
96};
97
98// Info about function range. A function can be split into multiple
99// non-continuous ranges, each range corresponds to one FuncRange.
100struct FuncRange {
101 uint64_t StartAddress;
102 // EndAddress is an exclusive bound.
103 uint64_t EndAddress;
104 // Function the range belongs to
105 BinaryFunction *Func;
106 // Whether the start address is the real entry of the function.
107 bool IsFuncEntry = false;
108
109 StringRef getFuncName() { return Func->FuncName; }
110};
111
112// PrologEpilog address tracker, used to filter out broken stack samples
113// Currently we use a heuristic size (two) to infer prolog and epilog
114// based on the start address and return address. In the future,
115// we will switch to Dwarf CFI based tracker
116struct PrologEpilogTracker {
117 // A set of prolog and epilog addresses. Used by virtual unwinding.
118 std::unordered_set<uint64_t> PrologEpilogSet;
119 ProfiledBinary *Binary;
120 PrologEpilogTracker(ProfiledBinary *Bin) : Binary(Bin){};
121
122 // Take the two addresses from the start of function as prolog
123 void
124 inferPrologAddresses(std::map<uint64_t, FuncRange> &FuncStartAddressMap) {
125 for (auto I : FuncStartAddressMap) {
126 PrologEpilogSet.insert(x: I.first);
127 InstructionPointer IP(Binary, I.first);
128 if (!IP.advance())
129 break;
130 PrologEpilogSet.insert(x: IP.Address);
131 }
132 }
133
134 // Take the last two addresses before the return address as epilog
135 void inferEpilogAddresses(std::unordered_set<uint64_t> &RetAddrs) {
136 for (auto Addr : RetAddrs) {
137 PrologEpilogSet.insert(x: Addr);
138 InstructionPointer IP(Binary, Addr);
139 if (!IP.backward())
140 break;
141 PrologEpilogSet.insert(x: IP.Address);
142 }
143 }
144};
145
146// Track function byte size under different context (outlined version as well as
147// various inlined versions). It also provides query support to get function
148// size with the best matching context, which is used to help pre-inliner use
149// accurate post-optimization size to make decisions.
150// TODO: If an inlinee is completely optimized away, ideally we should have zero
151// for its context size, currently we would misss such context since it doesn't
152// have instructions. To fix this, we need to mark all inlinee with entry probe
153// but without instructions as having zero size.
154class BinarySizeContextTracker {
155public:
156 // Add instruction with given size to a context
157 void addInstructionForContext(const SampleContextFrameVector &Context,
158 uint32_t InstrSize);
159
160 // Get function size with a specific context. When there's no exact match
161 // for the given context, try to retrieve the size of that function from
162 // closest matching context.
163 uint32_t getFuncSizeForContext(const ContextTrieNode *Context);
164
165 // For inlinees that are full optimized away, we can establish zero size using
166 // their remaining probes.
167 void trackInlineesOptimizedAway(MCPseudoProbeDecoder &ProbeDecoder);
168
169 using ProbeFrameStack = SmallVector<std::pair<StringRef, uint32_t>>;
170 void
171 trackInlineesOptimizedAway(MCPseudoProbeDecoder &ProbeDecoder,
172 const MCDecodedPseudoProbeInlineTree &ProbeNode,
173 ProbeFrameStack &Context);
174
175 void dump() { RootContext.dumpTree(); }
176
177private:
178 // Root node for context trie tree, node that this is a reverse context trie
179 // with callee as parent and caller as child. This way we can traverse from
180 // root to find the best/longest matching context if an exact match does not
181 // exist. It gives us the best possible estimate for function's post-inline,
182 // post-optimization byte size.
183 ContextTrieNode RootContext;
184};
185
186using AddressRange = std::pair<uint64_t, uint64_t>;
187
188class ProfiledBinary {
189 // Absolute path of the executable binary.
190 std::string Path;
191 // Path of the debug info binary.
192 std::string DebugBinaryPath;
193 // The target triple.
194 Triple TheTriple;
195 // Path of symbolizer path which should be pointed to binary with debug info.
196 StringRef SymbolizerPath;
197 // Options used to configure the symbolizer
198 symbolize::LLVMSymbolizer::Options SymbolizerOpts;
199 // The runtime base address that the first executable segment is loaded at.
200 uint64_t BaseAddress = 0;
201 // The runtime base address that the first loadabe segment is loaded at.
202 uint64_t FirstLoadableAddress = 0;
203 // The preferred load address of each executable segment.
204 std::vector<uint64_t> PreferredTextSegmentAddresses;
205 // The file offset of each executable segment.
206 std::vector<uint64_t> TextSegmentOffsets;
207
208 // Mutiple MC component info
209 std::unique_ptr<const MCRegisterInfo> MRI;
210 std::unique_ptr<const MCAsmInfo> AsmInfo;
211 std::unique_ptr<const MCSubtargetInfo> STI;
212 std::unique_ptr<const MCInstrInfo> MII;
213 std::unique_ptr<MCDisassembler> DisAsm;
214 std::unique_ptr<const MCInstrAnalysis> MIA;
215 std::unique_ptr<MCInstPrinter> IPrinter;
216 // A list of text sections sorted by start RVA and size. Used to check
217 // if a given RVA is a valid code address.
218 std::set<std::pair<uint64_t, uint64_t>> TextSections;
219
220 // A map of mapping function name to BinaryFunction info.
221 std::unordered_map<std::string, BinaryFunction> BinaryFunctions;
222
223 // Lookup BinaryFunctions using the function name's MD5 hash. Needed if the
224 // profile is using MD5.
225 std::unordered_map<uint64_t, BinaryFunction *> HashBinaryFunctions;
226
227 // A list of binary functions that have samples.
228 std::unordered_set<const BinaryFunction *> ProfiledFunctions;
229
230 // GUID to Elf symbol start address map
231 DenseMap<uint64_t, uint64_t> SymbolStartAddrs;
232
233 // These maps are for temporary use of warning diagnosis.
234 DenseSet<int64_t> AddrsWithMultipleSymbols;
235 DenseSet<std::pair<uint64_t, uint64_t>> AddrsWithInvalidInstruction;
236
237 // Start address to Elf symbol GUID map
238 std::unordered_multimap<uint64_t, uint64_t> StartAddrToSymMap;
239
240 // An ordered map of mapping function's start address to function range
241 // relevant info. Currently to determine if the offset of ELF is the start of
242 // a real function, we leverage the function range info from DWARF.
243 std::map<uint64_t, FuncRange> StartAddrToFuncRangeMap;
244
245 // Address to context location map. Used to expand the context.
246 std::unordered_map<uint64_t, SampleContextFrameVector> AddressToLocStackMap;
247
248 // Address to instruction size map. Also used for quick Address lookup.
249 std::unordered_map<uint64_t, uint64_t> AddressToInstSizeMap;
250
251 // An array of Addresses of all instructions sorted in increasing order. The
252 // sorting is needed to fast advance to the next forward/backward instruction.
253 std::vector<uint64_t> CodeAddressVec;
254 // A set of call instruction addresses. Used by virtual unwinding.
255 std::unordered_set<uint64_t> CallAddressSet;
256 // A set of return instruction addresses. Used by virtual unwinding.
257 std::unordered_set<uint64_t> RetAddressSet;
258 // An ordered set of unconditional branch instruction addresses.
259 std::set<uint64_t> UncondBranchAddrSet;
260 // A set of branch instruction addresses.
261 std::unordered_set<uint64_t> BranchAddressSet;
262
263 // Estimate and track function prolog and epilog ranges.
264 PrologEpilogTracker ProEpilogTracker;
265
266 // Infer missing frames due to compiler optimizations such as tail call
267 // elimination.
268 std::unique_ptr<MissingFrameInferrer> MissingContextInferrer;
269
270 // Track function sizes under different context
271 BinarySizeContextTracker FuncSizeTracker;
272
273 // The symbolizer used to get inline context for an instruction.
274 std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
275
276 // String table owning function name strings created from the symbolizer.
277 std::unordered_set<std::string> NameStrings;
278
279 // A collection of functions to print disassembly for.
280 StringSet<> DisassembleFunctionSet;
281
282 // Pseudo probe decoder
283 MCPseudoProbeDecoder ProbeDecoder;
284
285 // Function name to probe frame map for top-level outlined functions.
286 StringMap<MCDecodedPseudoProbeInlineTree *> TopLevelProbeFrameMap;
287
288 bool UsePseudoProbes = false;
289
290 bool UseFSDiscriminator = false;
291
292 // Whether we need to symbolize all instructions to get function context size.
293 bool TrackFuncContextSize = false;
294
295 // Whether this is a kernel image;
296 bool IsKernel = false;
297
298 // Indicate if the base loading address is parsed from the mmap event or uses
299 // the preferred address
300 bool IsLoadedByMMap = false;
301 // Use to avoid redundant warning.
302 bool MissingMMapWarned = false;
303
304 bool IsCOFF = false;
305
306 void setPreferredTextSegmentAddresses(const ObjectFile *O);
307
308 template <class ELFT>
309 void setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj,
310 StringRef FileName);
311 void setPreferredTextSegmentAddresses(const COFFObjectFile *Obj,
312 StringRef FileName);
313
314 void checkPseudoProbe(const ELFObjectFileBase *Obj);
315
316 void decodePseudoProbe(const ELFObjectFileBase *Obj);
317
318 void
319 checkUseFSDiscriminator(const ObjectFile *Obj,
320 std::map<SectionRef, SectionSymbolsTy> &AllSymbols);
321
322 // Set up disassembler and related components.
323 void setUpDisassembler(const ObjectFile *Obj);
324 symbolize::LLVMSymbolizer::Options getSymbolizerOpts() const;
325
326 // Load debug info of subprograms from DWARF section.
327 void loadSymbolsFromDWARF(ObjectFile &Obj);
328
329 // Load debug info from DWARF unit.
330 void loadSymbolsFromDWARFUnit(DWARFUnit &CompilationUnit);
331
332 // Create elf symbol to its start address mapping.
333 void populateElfSymbolAddressList(const ELFObjectFileBase *O);
334
335 // A function may be spilt into multiple non-continuous address ranges. We use
336 // this to set whether start a function range is the real entry of the
337 // function and also set false to the non-function label.
338 void setIsFuncEntry(FuncRange *FRange, StringRef RangeSymName);
339
340 // Warn if no entry range exists in the function.
341 void warnNoFuncEntry();
342
343 /// Dissassemble the text section and build various address maps.
344 void disassemble(const ObjectFile *O);
345
346 /// Helper function to dissassemble the symbol and extract info for unwinding
347 bool dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
348 SectionSymbolsTy &Symbols, const SectionRef &Section);
349 /// Symbolize a given instruction pointer and return a full call context.
350 SampleContextFrameVector symbolize(const InstructionPointer &IP,
351 bool UseCanonicalFnName = false,
352 bool UseProbeDiscriminator = false);
353 /// Decode the interesting parts of the binary and build internal data
354 /// structures. On high level, the parts of interest are:
355 /// 1. Text sections, including the main code section and the PLT
356 /// entries that will be used to handle cross-module call transitions.
357 /// 2. The .debug_line section, used by Dwarf-based profile generation.
358 /// 3. Pseudo probe related sections, used by probe-based profile
359 /// generation.
360 void load();
361
362public:
363 ProfiledBinary(const StringRef ExeBinPath, const StringRef DebugBinPath);
364 ~ProfiledBinary();
365
366 void decodePseudoProbe();
367
368 StringRef getPath() const { return Path; }
369 StringRef getName() const { return llvm::sys::path::filename(path: Path); }
370 uint64_t getBaseAddress() const { return BaseAddress; }
371 void setBaseAddress(uint64_t Address) { BaseAddress = Address; }
372
373 bool isCOFF() const { return IsCOFF; }
374
375 // Canonicalize to use preferred load address as base address.
376 uint64_t canonicalizeVirtualAddress(uint64_t Address) {
377 return Address - BaseAddress + getPreferredBaseAddress();
378 }
379 // Return the preferred load address for the first executable segment.
380 uint64_t getPreferredBaseAddress() const {
381 return PreferredTextSegmentAddresses[0];
382 }
383 // Return the preferred load address for the first loadable segment.
384 uint64_t getFirstLoadableAddress() const { return FirstLoadableAddress; }
385 // Return the file offset for the first executable segment.
386 uint64_t getTextSegmentOffset() const { return TextSegmentOffsets[0]; }
387 const std::vector<uint64_t> &getPreferredTextSegmentAddresses() const {
388 return PreferredTextSegmentAddresses;
389 }
390 const std::vector<uint64_t> &getTextSegmentOffsets() const {
391 return TextSegmentOffsets;
392 }
393
394 uint64_t getInstSize(uint64_t Address) const {
395 auto I = AddressToInstSizeMap.find(x: Address);
396 if (I == AddressToInstSizeMap.end())
397 return 0;
398 return I->second;
399 }
400
401 bool addressIsCode(uint64_t Address) const {
402 return AddressToInstSizeMap.find(x: Address) != AddressToInstSizeMap.end();
403 }
404
405 bool addressIsCall(uint64_t Address) const {
406 return CallAddressSet.count(x: Address);
407 }
408 bool addressIsReturn(uint64_t Address) const {
409 return RetAddressSet.count(x: Address);
410 }
411 bool addressInPrologEpilog(uint64_t Address) const {
412 return ProEpilogTracker.PrologEpilogSet.count(x: Address);
413 }
414
415 bool addressIsTransfer(uint64_t Address) {
416 return BranchAddressSet.count(x: Address) || RetAddressSet.count(x: Address) ||
417 CallAddressSet.count(x: Address);
418 }
419
420 bool rangeCrossUncondBranch(uint64_t Start, uint64_t End) {
421 if (Start >= End)
422 return false;
423 auto R = UncondBranchAddrSet.lower_bound(x: Start);
424 return R != UncondBranchAddrSet.end() && *R < End;
425 }
426
427 uint64_t getAddressforIndex(uint64_t Index) const {
428 return CodeAddressVec[Index];
429 }
430
431 size_t getCodeAddrVecSize() const { return CodeAddressVec.size(); }
432
433 bool usePseudoProbes() const { return UsePseudoProbes; }
434 bool useFSDiscriminator() const { return UseFSDiscriminator; }
435 bool isKernel() const { return IsKernel; }
436
437 static bool isKernelImageName(StringRef BinaryName) {
438 return BinaryName == "[kernel.kallsyms]" ||
439 BinaryName == "[kernel.kallsyms]_stext" ||
440 BinaryName == "[kernel.kallsyms]_text";
441 }
442
443 // Get the index in CodeAddressVec for the address
444 // As we might get an address which is not the code
445 // here it would round to the next valid code address by
446 // using lower bound operation
447 uint32_t getIndexForAddr(uint64_t Address) const {
448 auto Low = llvm::lower_bound(Range: CodeAddressVec, Value&: Address);
449 return Low - CodeAddressVec.begin();
450 }
451
452 uint64_t getCallAddrFromFrameAddr(uint64_t FrameAddr) const {
453 if (FrameAddr == ExternalAddr)
454 return ExternalAddr;
455 auto I = getIndexForAddr(Address: FrameAddr);
456 FrameAddr = I ? getAddressforIndex(Index: I - 1) : 0;
457 if (FrameAddr && addressIsCall(Address: FrameAddr))
458 return FrameAddr;
459 return 0;
460 }
461
462 FuncRange *findFuncRangeForStartAddr(uint64_t Address) {
463 auto I = StartAddrToFuncRangeMap.find(x: Address);
464 if (I == StartAddrToFuncRangeMap.end())
465 return nullptr;
466 return &I->second;
467 }
468
469 // Binary search the function range which includes the input address.
470 FuncRange *findFuncRange(uint64_t Address) {
471 auto I = StartAddrToFuncRangeMap.upper_bound(x: Address);
472 if (I == StartAddrToFuncRangeMap.begin())
473 return nullptr;
474 I--;
475
476 if (Address >= I->second.EndAddress)
477 return nullptr;
478
479 return &I->second;
480 }
481
482 // Get all ranges of one function.
483 RangesTy getRanges(uint64_t Address) {
484 auto *FRange = findFuncRange(Address);
485 // Ignore the range which falls into plt section or system lib.
486 if (!FRange)
487 return RangesTy();
488
489 return FRange->Func->Ranges;
490 }
491
492 const std::unordered_map<std::string, BinaryFunction> &
493 getAllBinaryFunctions() {
494 return BinaryFunctions;
495 }
496
497 std::unordered_set<const BinaryFunction *> &getProfiledFunctions() {
498 return ProfiledFunctions;
499 }
500
501 void setProfiledFunctions(std::unordered_set<const BinaryFunction *> &Funcs) {
502 ProfiledFunctions = Funcs;
503 }
504
505 BinaryFunction *getBinaryFunction(FunctionId FName) {
506 if (FName.isStringRef()) {
507 auto I = BinaryFunctions.find(x: FName.str());
508 if (I == BinaryFunctions.end())
509 return nullptr;
510 return &I->second;
511 }
512 auto I = HashBinaryFunctions.find(x: FName.getHashCode());
513 if (I == HashBinaryFunctions.end())
514 return nullptr;
515 return I->second;
516 }
517
518 uint32_t getFuncSizeForContext(const ContextTrieNode *ContextNode) {
519 return FuncSizeTracker.getFuncSizeForContext(Context: ContextNode);
520 }
521
522 void inferMissingFrames(const SmallVectorImpl<uint64_t> &Context,
523 SmallVectorImpl<uint64_t> &NewContext);
524
525 // Load the symbols from debug table and populate into symbol list.
526 void populateSymbolListFromDWARF(ProfileSymbolList &SymbolList);
527
528 SampleContextFrameVector
529 getFrameLocationStack(uint64_t Address, bool UseProbeDiscriminator = false) {
530 InstructionPointer IP(this, Address);
531 return symbolize(IP, UseCanonicalFnName: SymbolizerOpts.UseSymbolTable, UseProbeDiscriminator);
532 }
533
534 const SampleContextFrameVector &
535 getCachedFrameLocationStack(uint64_t Address,
536 bool UseProbeDiscriminator = false) {
537 auto I = AddressToLocStackMap.emplace(args&: Address, args: SampleContextFrameVector());
538 if (I.second) {
539 I.first->second = getFrameLocationStack(Address, UseProbeDiscriminator);
540 }
541 return I.first->second;
542 }
543
544 std::optional<SampleContextFrame> getInlineLeafFrameLoc(uint64_t Address) {
545 const auto &Stack = getCachedFrameLocationStack(Address);
546 if (Stack.empty())
547 return {};
548 return Stack.back();
549 }
550
551 void flushSymbolizer() { Symbolizer.reset(); }
552
553 MissingFrameInferrer *getMissingContextInferrer() {
554 return MissingContextInferrer.get();
555 }
556
557 // Compare two addresses' inline context
558 bool inlineContextEqual(uint64_t Add1, uint64_t Add2);
559
560 // Get the full context of the current stack with inline context filled in.
561 // It will search the disassembling info stored in AddressToLocStackMap. This
562 // is used as the key of function sample map
563 SampleContextFrameVector
564 getExpandedContext(const SmallVectorImpl<uint64_t> &Stack,
565 bool &WasLeafInlined);
566 // Go through instructions among the given range and record its size for the
567 // inline context.
568 void computeInlinedContextSizeForRange(uint64_t StartAddress,
569 uint64_t EndAddress);
570
571 void computeInlinedContextSizeForFunc(const BinaryFunction *Func);
572
573 const MCDecodedPseudoProbe *getCallProbeForAddr(uint64_t Address) const {
574 return ProbeDecoder.getCallProbeForAddr(Address);
575 }
576
577 void getInlineContextForProbe(const MCDecodedPseudoProbe *Probe,
578 SampleContextFrameVector &InlineContextStack,
579 bool IncludeLeaf = false) const {
580 SmallVector<MCPseudoProbeFrameLocation, 16> ProbeInlineContext;
581 ProbeDecoder.getInlineContextForProbe(Probe, InlineContextStack&: ProbeInlineContext,
582 IncludeLeaf);
583 for (uint32_t I = 0; I < ProbeInlineContext.size(); I++) {
584 auto &Callsite = ProbeInlineContext[I];
585 // Clear the current context for an unknown probe.
586 if (Callsite.second == 0 && I != ProbeInlineContext.size() - 1) {
587 InlineContextStack.clear();
588 continue;
589 }
590 InlineContextStack.emplace_back(Args: FunctionId(Callsite.first),
591 Args: LineLocation(Callsite.second, 0));
592 }
593 }
594 const AddressProbesMap &getAddress2ProbesMap() const {
595 return ProbeDecoder.getAddress2ProbesMap();
596 }
597 const MCPseudoProbeFuncDesc *getFuncDescForGUID(uint64_t GUID) {
598 return ProbeDecoder.getFuncDescForGUID(GUID);
599 }
600
601 const MCPseudoProbeFuncDesc *
602 getInlinerDescForProbe(const MCDecodedPseudoProbe *Probe) {
603 return ProbeDecoder.getInlinerDescForProbe(Probe);
604 }
605
606 bool getTrackFuncContextSize() { return TrackFuncContextSize; }
607
608 bool getIsLoadedByMMap() { return IsLoadedByMMap; }
609
610 void setIsLoadedByMMap(bool Value) { IsLoadedByMMap = Value; }
611
612 bool getMissingMMapWarned() { return MissingMMapWarned; }
613
614 void setMissingMMapWarned(bool Value) { MissingMMapWarned = Value; }
615};
616
617} // end namespace sampleprof
618} // end namespace llvm
619
620#endif
621