LLVM 19.0.0git
RawMemProfReader.cpp
Go to the documentation of this file.
1//===- RawMemProfReader.cpp - Instrumented memory profiling reader --------===//
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// This file contains support for reading MemProf profiling data.
10//
11//===----------------------------------------------------------------------===//
12
13#include <algorithm>
14#include <cstdint>
15#include <memory>
16#include <type_traits>
17
18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/DenseMap.h"
20#include "llvm/ADT/SetVector.h"
21#include "llvm/ADT/SmallSet.h"
24#include "llvm/ADT/Twine.h"
28#include "llvm/Object/Binary.h"
29#include "llvm/Object/BuildID.h"
37#include "llvm/Support/Debug.h"
38#include "llvm/Support/Endian.h"
39#include "llvm/Support/Error.h"
41#include "llvm/Support/Path.h"
42
43#define DEBUG_TYPE "memprof"
44
45namespace llvm {
46namespace memprof {
47namespace {
48template <class T = uint64_t> inline T alignedRead(const char *Ptr) {
49 static_assert(std::is_pod<T>::value, "Not a pod type.");
50 assert(reinterpret_cast<size_t>(Ptr) % sizeof(T) == 0 && "Unaligned Read");
51 return *reinterpret_cast<const T *>(Ptr);
52}
53
54Error checkBuffer(const MemoryBuffer &Buffer) {
55 if (!RawMemProfReader::hasFormat(Buffer))
56 return make_error<InstrProfError>(instrprof_error::bad_magic);
57
58 if (Buffer.getBufferSize() == 0)
59 return make_error<InstrProfError>(instrprof_error::empty_raw_profile);
60
61 if (Buffer.getBufferSize() < sizeof(Header)) {
62 return make_error<InstrProfError>(instrprof_error::truncated);
63 }
64
65 // The size of the buffer can be > header total size since we allow repeated
66 // serialization of memprof profiles to the same file.
67 uint64_t TotalSize = 0;
68 const char *Next = Buffer.getBufferStart();
69 while (Next < Buffer.getBufferEnd()) {
70 auto *H = reinterpret_cast<const Header *>(Next);
71 if (H->Version != MEMPROF_RAW_VERSION) {
72 return make_error<InstrProfError>(instrprof_error::unsupported_version);
73 }
74
75 TotalSize += H->TotalSize;
76 Next += H->TotalSize;
77 }
78
79 if (Buffer.getBufferSize() != TotalSize) {
80 return make_error<InstrProfError>(instrprof_error::malformed);
81 }
82 return Error::success();
83}
84
85llvm::SmallVector<SegmentEntry> readSegmentEntries(const char *Ptr) {
86 using namespace support;
87
88 const uint64_t NumItemsToRead =
89 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
91 for (uint64_t I = 0; I < NumItemsToRead; I++) {
92 Items.push_back(*reinterpret_cast<const SegmentEntry *>(
93 Ptr + I * sizeof(SegmentEntry)));
94 }
95 return Items;
96}
97
99readMemInfoBlocks(const char *Ptr) {
100 using namespace support;
101
102 const uint64_t NumItemsToRead =
103 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
105 for (uint64_t I = 0; I < NumItemsToRead; I++) {
106 const uint64_t Id =
107 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
108 const MemInfoBlock MIB = *reinterpret_cast<const MemInfoBlock *>(Ptr);
109 Items.push_back({Id, MIB});
110 // Only increment by size of MIB since readNext implicitly increments.
111 Ptr += sizeof(MemInfoBlock);
112 }
113 return Items;
114}
115
116CallStackMap readStackInfo(const char *Ptr) {
117 using namespace support;
118
119 const uint64_t NumItemsToRead =
120 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
121 CallStackMap Items;
122
123 for (uint64_t I = 0; I < NumItemsToRead; I++) {
124 const uint64_t StackId =
125 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
126 const uint64_t NumPCs =
127 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
128
129 SmallVector<uint64_t> CallStack;
130 CallStack.reserve(NumPCs);
131 for (uint64_t J = 0; J < NumPCs; J++) {
132 CallStack.push_back(
133 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr));
134 }
135
136 Items[StackId] = CallStack;
137 }
138 return Items;
139}
140
141// Merges the contents of stack information in \p From to \p To. Returns true if
142// any stack ids observed previously map to a different set of program counter
143// addresses.
144bool mergeStackMap(const CallStackMap &From, CallStackMap &To) {
145 for (const auto &IdStack : From) {
146 auto I = To.find(IdStack.first);
147 if (I == To.end()) {
148 To[IdStack.first] = IdStack.second;
149 } else {
150 // Check that the PCs are the same (in order).
151 if (IdStack.second != I->second)
152 return true;
153 }
154 }
155 return false;
156}
157
158Error report(Error E, const StringRef Context) {
160 std::move(E));
161}
162
163bool isRuntimePath(const StringRef Path) {
164 const StringRef Filename = llvm::sys::path::filename(Path);
165 // This list should be updated in case new files with additional interceptors
166 // are added to the memprof runtime.
167 return Filename.equals("memprof_malloc_linux.cpp") ||
168 Filename.equals("memprof_interceptors.cpp") ||
169 Filename.equals("memprof_new_delete.cpp");
170}
171
172std::string getBuildIdString(const SegmentEntry &Entry) {
173 // If the build id is unset print a helpful string instead of all zeros.
174 if (Entry.BuildIdSize == 0)
175 return "<None>";
176
177 std::string Str;
178 raw_string_ostream OS(Str);
179 for (size_t I = 0; I < Entry.BuildIdSize; I++) {
180 OS << format_hex_no_prefix(Entry.BuildId[I], 2);
181 }
182 return OS.str();
183}
184} // namespace
185
186Expected<std::unique_ptr<RawMemProfReader>>
187RawMemProfReader::create(const Twine &Path, const StringRef ProfiledBinary,
188 bool KeepName) {
189 auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
190 if (std::error_code EC = BufferOr.getError())
191 return report(errorCodeToError(EC), Path.getSingleStringRef());
192
193 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
194 return create(std::move(Buffer), ProfiledBinary, KeepName);
195}
196
198RawMemProfReader::create(std::unique_ptr<MemoryBuffer> Buffer,
199 const StringRef ProfiledBinary, bool KeepName) {
200 if (Error E = checkBuffer(*Buffer))
201 return report(std::move(E), Buffer->getBufferIdentifier());
202
203 if (ProfiledBinary.empty()) {
204 // Peek the build ids to print a helpful error message.
205 const std::vector<std::string> BuildIds = peekBuildIds(Buffer.get());
206 std::string ErrorMessage(
207 R"(Path to profiled binary is empty, expected binary with one of the following build ids:
208)");
209 for (const auto &Id : BuildIds) {
210 ErrorMessage += "\n BuildId: ";
211 ErrorMessage += Id;
212 }
213 return report(
214 make_error<StringError>(ErrorMessage, inconvertibleErrorCode()),
215 /*Context=*/"");
216 }
217
218 auto BinaryOr = llvm::object::createBinary(ProfiledBinary);
219 if (!BinaryOr) {
220 return report(BinaryOr.takeError(), ProfiledBinary);
221 }
222
223 // Use new here since constructor is private.
224 std::unique_ptr<RawMemProfReader> Reader(
225 new RawMemProfReader(std::move(BinaryOr.get()), KeepName));
226 if (Error E = Reader->initialize(std::move(Buffer))) {
227 return std::move(E);
228 }
229 return std::move(Reader);
230}
232bool RawMemProfReader::hasFormat(const StringRef Path) {
233 auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
234 if (!BufferOr)
235 return false;
236
237 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
238 return hasFormat(*Buffer);
239}
241bool RawMemProfReader::hasFormat(const MemoryBuffer &Buffer) {
242 if (Buffer.getBufferSize() < sizeof(uint64_t))
243 return false;
244 // Aligned read to sanity check that the buffer was allocated with at least 8b
245 // alignment.
246 const uint64_t Magic = alignedRead(Buffer.getBufferStart());
247 return Magic == MEMPROF_RAW_MAGIC_64;
248}
251 uint64_t NumAllocFunctions = 0, NumMibInfo = 0;
252 for (const auto &KV : FunctionProfileData) {
253 const size_t NumAllocSites = KV.second.AllocSites.size();
254 if (NumAllocSites > 0) {
255 NumAllocFunctions++;
256 NumMibInfo += NumAllocSites;
257 }
258 }
259
260 OS << "MemprofProfile:\n";
261 OS << " Summary:\n";
262 OS << " Version: " << MEMPROF_RAW_VERSION << "\n";
263 OS << " NumSegments: " << SegmentInfo.size() << "\n";
264 OS << " NumMibInfo: " << NumMibInfo << "\n";
265 OS << " NumAllocFunctions: " << NumAllocFunctions << "\n";
266 OS << " NumStackOffsets: " << StackMap.size() << "\n";
267 // Print out the segment information.
268 OS << " Segments:\n";
269 for (const auto &Entry : SegmentInfo) {
270 OS << " -\n";
271 OS << " BuildId: " << getBuildIdString(Entry) << "\n";
272 OS << " Start: 0x" << llvm::utohexstr(Entry.Start) << "\n";
273 OS << " End: 0x" << llvm::utohexstr(Entry.End) << "\n";
274 OS << " Offset: 0x" << llvm::utohexstr(Entry.Offset) << "\n";
275 }
276 // Print out the merged contents of the profiles.
277 OS << " Records:\n";
278 for (const auto &Entry : *this) {
279 OS << " -\n";
280 OS << " FunctionGUID: " << Entry.first << "\n";
281 Entry.second.print(OS);
282 }
283}
284
285Error RawMemProfReader::initialize(std::unique_ptr<MemoryBuffer> DataBuffer) {
286 const StringRef FileName = Binary.getBinary()->getFileName();
287
288 auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Binary.getBinary());
289 if (!ElfObject) {
290 return report(make_error<StringError>(Twine("Not an ELF file: "),
292 FileName);
293 }
294
295 // Check whether the profiled binary was built with position independent code
296 // (PIC). Perform sanity checks for assumptions we rely on to simplify
297 // symbolization.
298 auto* Elf64LEObject = llvm::cast<llvm::object::ELF64LEObjectFile>(ElfObject);
299 const llvm::object::ELF64LEFile& ElfFile = Elf64LEObject->getELFFile();
300 auto PHdrsOr = ElfFile.program_headers();
301 if (!PHdrsOr)
302 return report(
303 make_error<StringError>(Twine("Could not read program headers: "),
305 FileName);
306
307 int NumExecutableSegments = 0;
308 for (const auto &Phdr : *PHdrsOr) {
309 if (Phdr.p_type == ELF::PT_LOAD) {
310 if (Phdr.p_flags & ELF::PF_X) {
311 // We assume only one text segment in the main binary for simplicity and
312 // reduce the overhead of checking multiple ranges during symbolization.
313 if (++NumExecutableSegments > 1) {
314 return report(
315 make_error<StringError>(
316 "Expect only one executable load segment in the binary",
318 FileName);
319 }
320 // Segment will always be loaded at a page boundary, expect it to be
321 // aligned already. Assume 4K pagesize for the machine from which the
322 // profile has been collected. This should be fine for now, in case we
323 // want to support other pagesizes it can be recorded in the raw profile
324 // during collection.
325 PreferredTextSegmentAddress = Phdr.p_vaddr;
326 assert(Phdr.p_vaddr == (Phdr.p_vaddr & ~(0x1000 - 1U)) &&
327 "Expect p_vaddr to always be page aligned");
328 assert(Phdr.p_offset == 0 && "Expect p_offset = 0 for symbolization.");
329 }
330 }
331 }
332
333 auto Triple = ElfObject->makeTriple();
334 if (!Triple.isX86())
335 return report(make_error<StringError>(Twine("Unsupported target: ") +
336 Triple.getArchName(),
338 FileName);
339
340 // Process the raw profile.
341 if (Error E = readRawProfile(std::move(DataBuffer)))
342 return E;
343
344 if (Error E = setupForSymbolization())
345 return E;
346
347 auto *Object = cast<object::ObjectFile>(Binary.getBinary());
348 std::unique_ptr<DIContext> Context = DWARFContext::create(
350
352 Object, std::move(Context), /*UntagAddresses=*/false);
353 if (!SOFOr)
354 return report(SOFOr.takeError(), FileName);
355 auto Symbolizer = std::move(SOFOr.get());
356
357 // The symbolizer ownership is moved into symbolizeAndFilterStackFrames so
358 // that it is freed automatically at the end, when it is no longer used. This
359 // reduces peak memory since it won't be live while also mapping the raw
360 // profile into records afterwards.
361 if (Error E = symbolizeAndFilterStackFrames(std::move(Symbolizer)))
362 return E;
363
364 return mapRawProfileToRecords();
365}
366
367Error RawMemProfReader::setupForSymbolization() {
368 auto *Object = cast<object::ObjectFile>(Binary.getBinary());
369 object::BuildIDRef BinaryId = object::getBuildID(Object);
370 if (BinaryId.empty())
371 return make_error<StringError>(Twine("No build id found in binary ") +
372 Binary.getBinary()->getFileName(),
374
375 int NumMatched = 0;
376 for (const auto &Entry : SegmentInfo) {
377 llvm::ArrayRef<uint8_t> SegmentId(Entry.BuildId, Entry.BuildIdSize);
378 if (BinaryId == SegmentId) {
379 // We assume only one text segment in the main binary for simplicity and
380 // reduce the overhead of checking multiple ranges during symbolization.
381 if (++NumMatched > 1) {
382 return make_error<StringError>(
383 "We expect only one executable segment in the profiled binary",
385 }
386 ProfiledTextSegmentStart = Entry.Start;
387 ProfiledTextSegmentEnd = Entry.End;
388 }
389 }
390 assert(NumMatched != 0 && "No matching executable segments in segment info.");
391 assert((PreferredTextSegmentAddress == 0 ||
392 (PreferredTextSegmentAddress == ProfiledTextSegmentStart)) &&
393 "Expect text segment address to be 0 or equal to profiled text "
394 "segment start.");
395 return Error::success();
396}
397
398Error RawMemProfReader::mapRawProfileToRecords() {
399 // Hold a mapping from function to each callsite location we encounter within
400 // it that is part of some dynamic allocation context. The location is stored
401 // as a pointer to a symbolized list of inline frames.
402 using LocationPtr = const llvm::SmallVector<FrameId> *;
404 PerFunctionCallSites;
405
406 // Convert the raw profile callstack data into memprof records. While doing so
407 // keep track of related contexts so that we can fill these in later.
408 for (const auto &Entry : CallstackProfileData) {
409 const uint64_t StackId = Entry.first;
410
411 auto It = StackMap.find(StackId);
412 if (It == StackMap.end())
413 return make_error<InstrProfError>(
415 "memprof callstack record does not contain id: " + Twine(StackId));
416
417 // Construct the symbolized callstack.
419 Callstack.reserve(It->getSecond().size());
420
421 llvm::ArrayRef<uint64_t> Addresses = It->getSecond();
422 for (size_t I = 0; I < Addresses.size(); I++) {
423 const uint64_t Address = Addresses[I];
424 assert(SymbolizedFrame.count(Address) > 0 &&
425 "Address not found in SymbolizedFrame map");
426 const SmallVector<FrameId> &Frames = SymbolizedFrame[Address];
427
428 assert(!idToFrame(Frames.back()).IsInlineFrame &&
429 "The last frame should not be inlined");
430
431 // Record the callsites for each function. Skip the first frame of the
432 // first address since it is the allocation site itself that is recorded
433 // as an alloc site.
434 for (size_t J = 0; J < Frames.size(); J++) {
435 if (I == 0 && J == 0)
436 continue;
437 // We attach the entire bottom-up frame here for the callsite even
438 // though we only need the frames up to and including the frame for
439 // Frames[J].Function. This will enable better deduplication for
440 // compression in the future.
441 const GlobalValue::GUID Guid = idToFrame(Frames[J]).Function;
442 PerFunctionCallSites[Guid].insert(&Frames);
443 }
444
445 // Add all the frames to the current allocation callstack.
446 Callstack.append(Frames.begin(), Frames.end());
447 }
448
449 CallStackId CSId = hashCallStack(Callstack);
450
451 // We attach the memprof record to each function bottom-up including the
452 // first non-inline frame.
453 for (size_t I = 0; /*Break out using the condition below*/; I++) {
454 const Frame &F = idToFrame(Callstack[I]);
455 auto Result =
456 FunctionProfileData.insert({F.Function, IndexedMemProfRecord()});
457 IndexedMemProfRecord &Record = Result.first->second;
458 Record.AllocSites.emplace_back(Callstack, CSId, Entry.second);
459
460 if (!F.IsInlineFrame)
461 break;
462 }
463 }
464
465 // Fill in the related callsites per function.
466 for (const auto &[Id, Locs] : PerFunctionCallSites) {
467 // Some functions may have only callsite data and no allocation data. Here
468 // we insert a new entry for callsite data if we need to.
469 auto Result = FunctionProfileData.insert({Id, IndexedMemProfRecord()});
470 IndexedMemProfRecord &Record = Result.first->second;
471 for (LocationPtr Loc : Locs) {
472 Record.CallSites.push_back(*Loc);
473 }
474 }
475
477
478 return Error::success();
479}
480
481Error RawMemProfReader::symbolizeAndFilterStackFrames(
482 std::unique_ptr<llvm::symbolize::SymbolizableModule> Symbolizer) {
483 // The specifier to use when symbolization is requested.
484 const DILineInfoSpecifier Specifier(
485 DILineInfoSpecifier::FileLineInfoKind::RawValue,
486 DILineInfoSpecifier::FunctionNameKind::LinkageName);
487
488 // For entries where all PCs in the callstack are discarded, we erase the
489 // entry from the stack map.
490 llvm::SmallVector<uint64_t> EntriesToErase;
491 // We keep track of all prior discarded entries so that we can avoid invoking
492 // the symbolizer for such entries.
493 llvm::DenseSet<uint64_t> AllVAddrsToDiscard;
494 for (auto &Entry : StackMap) {
495 for (const uint64_t VAddr : Entry.getSecond()) {
496 // Check if we have already symbolized and cached the result or if we
497 // don't want to attempt symbolization since we know this address is bad.
498 // In this case the address is also removed from the current callstack.
499 if (SymbolizedFrame.count(VAddr) > 0 ||
500 AllVAddrsToDiscard.contains(VAddr))
501 continue;
502
503 Expected<DIInliningInfo> DIOr = Symbolizer->symbolizeInlinedCode(
504 getModuleOffset(VAddr), Specifier, /*UseSymbolTable=*/false);
505 if (!DIOr)
506 return DIOr.takeError();
507 DIInliningInfo DI = DIOr.get();
508
509 // Drop frames which we can't symbolize or if they belong to the runtime.
510 if (DI.getFrame(0).FunctionName == DILineInfo::BadString ||
511 isRuntimePath(DI.getFrame(0).FileName)) {
512 AllVAddrsToDiscard.insert(VAddr);
513 continue;
514 }
515
516 for (size_t I = 0, NumFrames = DI.getNumberOfFrames(); I < NumFrames;
517 I++) {
518 const auto &DIFrame = DI.getFrame(I);
519 const uint64_t Guid =
520 IndexedMemProfRecord::getGUID(DIFrame.FunctionName);
521 const Frame F(Guid, DIFrame.Line - DIFrame.StartLine, DIFrame.Column,
522 // Only the last entry is not an inlined location.
523 I != NumFrames - 1);
524 // Here we retain a mapping from the GUID to canonical symbol name
525 // instead of adding it to the frame object directly to reduce memory
526 // overhead. This is because there can be many unique frames,
527 // particularly for callsite frames.
528 if (KeepSymbolName) {
529 StringRef CanonicalName =
531 DIFrame.FunctionName);
532 GuidToSymbolName.insert({Guid, CanonicalName.str()});
533 }
534
535 const FrameId Hash = F.hash();
536 IdToFrame.insert({Hash, F});
537 SymbolizedFrame[VAddr].push_back(Hash);
538 }
539 }
540
541 auto &CallStack = Entry.getSecond();
542 llvm::erase_if(CallStack, [&AllVAddrsToDiscard](const uint64_t A) {
543 return AllVAddrsToDiscard.contains(A);
544 });
545 if (CallStack.empty())
546 EntriesToErase.push_back(Entry.getFirst());
547 }
548
549 // Drop the entries where the callstack is empty.
550 for (const uint64_t Id : EntriesToErase) {
551 StackMap.erase(Id);
552 CallstackProfileData.erase(Id);
553 }
554
555 if (StackMap.empty())
556 return make_error<InstrProfError>(
558 "no entries in callstack map after symbolization");
559
560 return Error::success();
561}
562
563std::vector<std::string>
565 const char *Next = DataBuffer->getBufferStart();
566 // Use a set + vector since a profile file may contain multiple raw profile
567 // dumps, each with segment information. We want them unique and in order they
568 // were stored in the profile; the profiled binary should be the first entry.
569 // The runtime uses dl_iterate_phdr and the "... first object visited by
570 // callback is the main program."
571 // https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html
572 std::vector<std::string> BuildIds;
574 while (Next < DataBuffer->getBufferEnd()) {
575 auto *Header = reinterpret_cast<const memprof::Header *>(Next);
576
577 const llvm::SmallVector<SegmentEntry> Entries =
578 readSegmentEntries(Next + Header->SegmentOffset);
579
580 for (const auto &Entry : Entries) {
581 const std::string Id = getBuildIdString(Entry);
582 if (BuildIdsSet.contains(Id))
583 continue;
584 BuildIds.push_back(Id);
585 BuildIdsSet.insert(Id);
586 }
587
588 Next += Header->TotalSize;
589 }
590 return BuildIds;
591}
592
593Error RawMemProfReader::readRawProfile(
594 std::unique_ptr<MemoryBuffer> DataBuffer) {
595 const char *Next = DataBuffer->getBufferStart();
596
597 while (Next < DataBuffer->getBufferEnd()) {
598 auto *Header = reinterpret_cast<const memprof::Header *>(Next);
599
600 // Read in the segment information, check whether its the same across all
601 // profiles in this binary file.
602 const llvm::SmallVector<SegmentEntry> Entries =
603 readSegmentEntries(Next + Header->SegmentOffset);
604 if (!SegmentInfo.empty() && SegmentInfo != Entries) {
605 // We do not expect segment information to change when deserializing from
606 // the same binary profile file. This can happen if dynamic libraries are
607 // loaded/unloaded between profile dumping.
608 return make_error<InstrProfError>(
610 "memprof raw profile has different segment information");
611 }
612 SegmentInfo.assign(Entries.begin(), Entries.end());
613
614 // Read in the MemInfoBlocks. Merge them based on stack id - we assume that
615 // raw profiles in the same binary file are from the same process so the
616 // stackdepot ids are the same.
617 for (const auto &Value : readMemInfoBlocks(Next + Header->MIBOffset)) {
618 if (CallstackProfileData.count(Value.first)) {
619 CallstackProfileData[Value.first].Merge(Value.second);
620 } else {
621 CallstackProfileData[Value.first] = Value.second;
622 }
623 }
624
625 // Read in the callstack for each ids. For multiple raw profiles in the same
626 // file, we expect that the callstack is the same for a unique id.
627 const CallStackMap CSM = readStackInfo(Next + Header->StackOffset);
628 if (StackMap.empty()) {
629 StackMap = CSM;
630 } else {
631 if (mergeStackMap(CSM, StackMap))
632 return make_error<InstrProfError>(
634 "memprof raw profile got different call stack for same id");
635 }
636
637 Next += Header->TotalSize;
638 }
639
640 return Error::success();
641}
642
643object::SectionedAddress
644RawMemProfReader::getModuleOffset(const uint64_t VirtualAddress) {
645 if (VirtualAddress > ProfiledTextSegmentStart &&
646 VirtualAddress <= ProfiledTextSegmentEnd) {
647 // For PIE binaries, the preferred address is zero and we adjust the virtual
648 // address by start of the profiled segment assuming that the offset of the
649 // segment in the binary is zero. For non-PIE binaries the preferred and
650 // profiled segment addresses should be equal and this is a no-op.
651 const uint64_t AdjustedAddress =
652 VirtualAddress + PreferredTextSegmentAddress - ProfiledTextSegmentStart;
653 return object::SectionedAddress{AdjustedAddress};
654 }
655 // Addresses which do not originate from the profiled text segment in the
656 // binary are not adjusted. These will fail symbolization and be filtered out
657 // during processing.
658 return object::SectionedAddress{VirtualAddress};
659}
662 GuidMemProfRecordPair &GuidRecord,
663 std::function<const Frame(const FrameId)> Callback) {
664 // Create a new callback for the RawMemProfRecord iterator so that we can
665 // provide the symbol name if the reader was initialized with KeepSymbolName =
666 // true. This is useful for debugging and testing.
667 auto IdToFrameCallback = [this](const FrameId Id) {
668 Frame F = this->idToFrame(Id);
669 if (!this->KeepSymbolName)
670 return F;
671 auto Iter = this->GuidToSymbolName.find(F.Function);
672 assert(Iter != this->GuidToSymbolName.end());
673 F.SymbolName = Iter->getSecond();
674 return F;
675 };
676 return MemProfReader::readNextRecord(GuidRecord, IdToFrameCallback);
677}
678} // namespace memprof
679} // namespace llvm
BlockVerifier::State From
This file declares a library for handling Build IDs and using them to find debug info.
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the DenseMap class.
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
#define H(x, y, z)
Definition: MD5.cpp:57
LLVMContext & Context
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
This file implements a set that has insertion order iteration characteristics.
This file defines the SmallSet class.
This file defines the SmallVector class.
This file contains some functions that are useful when dealing with strings.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:165
static std::unique_ptr< DWARFContext > create(const object::ObjectFile &Obj, ProcessDebugRelocations RelocAction=ProcessDebugRelocations::Process, const LoadedObjectInfo *L=nullptr, std::string DWPName="", std::function< void(Error)> RecoverableErrorHandler=WithColor::defaultErrorHandler, std::function< void(Error)> WarningHandler=WithColor::defaultWarningHandler, bool ThreadSafe=false)
iterator find(const_arg_type_t< KeyT > Val)
Definition: DenseMap.h:155
unsigned size() const
Definition: DenseMap.h:99
iterator end()
Definition: DenseMap.h:84
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition: DenseMap.h:220
Implements a dense probed hash-table based set.
Definition: DenseSet.h:271
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
uint64_t GUID
Declare a type to represent a global unique identifier for a global value.
Definition: GlobalValue.h:586
This class implements a map that also provides access to all stored values in a deterministic order.
Definition: MapVector.h:36
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition: MapVector.h:141
This interface provides simple read-only access to a block of memory, and provides simple methods for...
Definition: MemoryBuffer.h:51
size_t getBufferSize() const
Definition: MemoryBuffer.h:68
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
const char * getBufferStart() const
Definition: MemoryBuffer.h:66
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:135
bool contains(const T &V) const
Check if the SmallSet contains the given element.
Definition: SmallSet.h:236
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition: SmallSet.h:179
size_t size() const
Definition: SmallVector.h:91
void reserve(size_type N)
Definition: SmallVector.h:676
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:696
void push_back(const T &Elt)
Definition: SmallVector.h:426
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:134
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
LLVM Value Representation.
Definition: Value.h:74
std::pair< iterator, bool > insert(const ValueT &V)
Definition: DenseSet.h:206
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
Definition: DenseSet.h:185
const Frame & idToFrame(const FrameId Id) const
virtual Error readNextRecord(GuidMemProfRecordPair &GuidRecord, std::function< const Frame(const FrameId)> Callback=nullptr)
llvm::DenseMap< FrameId, Frame > IdToFrame
llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord >::iterator Iter
llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord > FunctionProfileData
std::pair< GlobalValue::GUID, MemProfRecord > GuidMemProfRecordPair
static Expected< std::unique_ptr< RawMemProfReader > > create(const Twine &Path, StringRef ProfiledBinary, bool KeepName=false)
static std::vector< std::string > peekBuildIds(MemoryBuffer *DataBuffer)
virtual Error readNextRecord(GuidMemProfRecordPair &GuidRecord, std::function< const Frame(const FrameId)> Callback) override
static bool hasFormat(const MemoryBuffer &DataBuffer)
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
static StringRef getCanonicalFnName(const Function &F)
Return the canonical name for a function, taking into account suffix elision policy attributes.
Definition: SampleProf.h:1085
static Expected< std::unique_ptr< SymbolizableObjectFile > > create(const object::ObjectFile *Obj, std::unique_ptr< DIContext > DICtx, bool UntagAddresses)
@ PF_X
Definition: ELF.h:1505
@ PT_LOAD
Definition: ELF.h:1456
CallStackId hashCallStack(ArrayRef< FrameId > CS)
Definition: MemProf.cpp:123
uint64_t FrameId
Definition: MemProf.h:138
uint64_t CallStackId
Definition: MemProf.h:257
llvm::DenseMap< uint64_t, llvm::SmallVector< uint64_t > > CallStackMap
void verifyFunctionProfileData(const llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord > &FunctionProfileData)
Definition: MemProf.cpp:141
BuildIDRef getBuildID(const ObjectFile *Obj)
Returns the build ID, if any, contained in the given object file.
Definition: BuildID.cpp:56
ArrayRef< uint8_t > BuildIDRef
A reference to a BuildID in binary form.
Definition: BuildID.h:28
Expected< std::unique_ptr< Binary > > createBinary(MemoryBufferRef Source, LLVMContext *Context=nullptr, bool InitContent=true)
Create a Binary from Source, autodetecting the file type.
Definition: Binary.cpp:45
StringRef filename(StringRef path, Style style=Style::native)
Get filename.
Definition: Path.cpp:578
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:90
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition: Error.h:1258
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition: Error.h:431
FormattedNumber format_hex_no_prefix(uint64_t N, unsigned Width, bool Upper=false)
format_hex_no_prefix - Output N as a fixed width hexadecimal.
Definition: Format.h:200
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...
Definition: STLExtras.h:2060
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:103
static constexpr const char *const BadString
Definition: DIContext.h:34
GlobalValue::GUID Function
Definition: MemProf.h:145
static GlobalValue::GUID getGUID(const StringRef FunctionName)
Definition: MemProf.cpp:81