LLVM 19.0.0git
MemProfReader.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>(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>(Ptr);
105 for (uint64_t I = 0; I < NumItemsToRead; I++) {
106 const uint64_t Id =
107 endian::readNext<uint64_t, llvm::endianness::little>(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>(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>(Ptr);
126 const uint64_t NumPCs =
127 endian::readNext<uint64_t, llvm::endianness::little>(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>(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 &[Id, Stack] : From) {
146 auto I = To.find(Id);
147 if (I == To.end()) {
148 To[Id] = Stack;
149 } else {
150 // Check that the PCs are the same (in order).
151 if (Stack != 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
189 : IdToFrame(std::move(FrameIdMap)),
190 FunctionProfileData(std::move(ProfData)) {
191 // Populate CSId in each IndexedAllocationInfo and IndexedMemProfRecord
192 // while storing CallStack in CSIdToCallStack.
193 for (auto &KV : FunctionProfileData) {
194 IndexedMemProfRecord &Record = KV.second;
195 for (auto &AS : Record.AllocSites) {
196 CallStackId CSId = hashCallStack(AS.CallStack);
197 AS.CSId = CSId;
198 CSIdToCallStack.insert({CSId, AS.CallStack});
199 }
200 for (auto &CS : Record.CallSites) {
201 CallStackId CSId = hashCallStack(CS);
202 Record.CallSiteIds.push_back(CSId);
203 CSIdToCallStack.insert({CSId, CS});
204 }
205 }
206}
207
209RawMemProfReader::create(const Twine &Path, const StringRef ProfiledBinary,
210 bool KeepName) {
211 auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
212 if (std::error_code EC = BufferOr.getError())
213 return report(errorCodeToError(EC), Path.getSingleStringRef());
214
215 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
216 return create(std::move(Buffer), ProfiledBinary, KeepName);
217}
218
220RawMemProfReader::create(std::unique_ptr<MemoryBuffer> Buffer,
221 const StringRef ProfiledBinary, bool KeepName) {
222 if (Error E = checkBuffer(*Buffer))
223 return report(std::move(E), Buffer->getBufferIdentifier());
224
225 if (ProfiledBinary.empty()) {
226 // Peek the build ids to print a helpful error message.
227 const std::vector<std::string> BuildIds = peekBuildIds(Buffer.get());
228 std::string ErrorMessage(
229 R"(Path to profiled binary is empty, expected binary with one of the following build ids:
230)");
231 for (const auto &Id : BuildIds) {
232 ErrorMessage += "\n BuildId: ";
233 ErrorMessage += Id;
234 }
235 return report(
236 make_error<StringError>(ErrorMessage, inconvertibleErrorCode()),
237 /*Context=*/"");
238 }
239
240 auto BinaryOr = llvm::object::createBinary(ProfiledBinary);
241 if (!BinaryOr) {
242 return report(BinaryOr.takeError(), ProfiledBinary);
243 }
244
245 // Use new here since constructor is private.
246 std::unique_ptr<RawMemProfReader> Reader(
247 new RawMemProfReader(std::move(BinaryOr.get()), KeepName));
248 if (Error E = Reader->initialize(std::move(Buffer))) {
249 return std::move(E);
250 }
251 return std::move(Reader);
252}
254bool RawMemProfReader::hasFormat(const StringRef Path) {
255 auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
256 if (!BufferOr)
257 return false;
258
259 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
260 return hasFormat(*Buffer);
261}
263bool RawMemProfReader::hasFormat(const MemoryBuffer &Buffer) {
264 if (Buffer.getBufferSize() < sizeof(uint64_t))
265 return false;
266 // Aligned read to sanity check that the buffer was allocated with at least 8b
267 // alignment.
268 const uint64_t Magic = alignedRead(Buffer.getBufferStart());
269 return Magic == MEMPROF_RAW_MAGIC_64;
270}
273 uint64_t NumAllocFunctions = 0, NumMibInfo = 0;
274 for (const auto &KV : FunctionProfileData) {
275 const size_t NumAllocSites = KV.second.AllocSites.size();
276 if (NumAllocSites > 0) {
277 NumAllocFunctions++;
278 NumMibInfo += NumAllocSites;
279 }
280 }
281
282 OS << "MemprofProfile:\n";
283 OS << " Summary:\n";
284 OS << " Version: " << MEMPROF_RAW_VERSION << "\n";
285 OS << " NumSegments: " << SegmentInfo.size() << "\n";
286 OS << " NumMibInfo: " << NumMibInfo << "\n";
287 OS << " NumAllocFunctions: " << NumAllocFunctions << "\n";
288 OS << " NumStackOffsets: " << StackMap.size() << "\n";
289 // Print out the segment information.
290 OS << " Segments:\n";
291 for (const auto &Entry : SegmentInfo) {
292 OS << " -\n";
293 OS << " BuildId: " << getBuildIdString(Entry) << "\n";
294 OS << " Start: 0x" << llvm::utohexstr(Entry.Start) << "\n";
295 OS << " End: 0x" << llvm::utohexstr(Entry.End) << "\n";
296 OS << " Offset: 0x" << llvm::utohexstr(Entry.Offset) << "\n";
297 }
298 // Print out the merged contents of the profiles.
299 OS << " Records:\n";
300 for (const auto &[GUID, Record] : *this) {
301 OS << " -\n";
302 OS << " FunctionGUID: " << GUID << "\n";
303 Record.print(OS);
304 }
305}
306
307Error RawMemProfReader::initialize(std::unique_ptr<MemoryBuffer> DataBuffer) {
308 const StringRef FileName = Binary.getBinary()->getFileName();
309
310 auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Binary.getBinary());
311 if (!ElfObject) {
312 return report(make_error<StringError>(Twine("Not an ELF file: "),
314 FileName);
315 }
316
317 // Check whether the profiled binary was built with position independent code
318 // (PIC). Perform sanity checks for assumptions we rely on to simplify
319 // symbolization.
320 auto *Elf64LEObject = llvm::cast<llvm::object::ELF64LEObjectFile>(ElfObject);
321 const llvm::object::ELF64LEFile &ElfFile = Elf64LEObject->getELFFile();
322 auto PHdrsOr = ElfFile.program_headers();
323 if (!PHdrsOr)
324 return report(
325 make_error<StringError>(Twine("Could not read program headers: "),
327 FileName);
328
329 int NumExecutableSegments = 0;
330 for (const auto &Phdr : *PHdrsOr) {
331 if (Phdr.p_type == ELF::PT_LOAD) {
332 if (Phdr.p_flags & ELF::PF_X) {
333 // We assume only one text segment in the main binary for simplicity and
334 // reduce the overhead of checking multiple ranges during symbolization.
335 if (++NumExecutableSegments > 1) {
336 return report(
337 make_error<StringError>(
338 "Expect only one executable load segment in the binary",
340 FileName);
341 }
342 // Segment will always be loaded at a page boundary, expect it to be
343 // aligned already. Assume 4K pagesize for the machine from which the
344 // profile has been collected. This should be fine for now, in case we
345 // want to support other pagesizes it can be recorded in the raw profile
346 // during collection.
347 PreferredTextSegmentAddress = Phdr.p_vaddr;
348 assert(Phdr.p_vaddr == (Phdr.p_vaddr & ~(0x1000 - 1U)) &&
349 "Expect p_vaddr to always be page aligned");
350 assert(Phdr.p_offset == 0 && "Expect p_offset = 0 for symbolization.");
351 }
352 }
353 }
354
355 auto Triple = ElfObject->makeTriple();
356 if (!Triple.isX86())
357 return report(make_error<StringError>(Twine("Unsupported target: ") +
358 Triple.getArchName(),
360 FileName);
361
362 // Process the raw profile.
363 if (Error E = readRawProfile(std::move(DataBuffer)))
364 return E;
365
366 if (Error E = setupForSymbolization())
367 return E;
368
369 auto *Object = cast<object::ObjectFile>(Binary.getBinary());
370 std::unique_ptr<DIContext> Context = DWARFContext::create(
372
374 Object, std::move(Context), /*UntagAddresses=*/false);
375 if (!SOFOr)
376 return report(SOFOr.takeError(), FileName);
377 auto Symbolizer = std::move(SOFOr.get());
378
379 // The symbolizer ownership is moved into symbolizeAndFilterStackFrames so
380 // that it is freed automatically at the end, when it is no longer used. This
381 // reduces peak memory since it won't be live while also mapping the raw
382 // profile into records afterwards.
383 if (Error E = symbolizeAndFilterStackFrames(std::move(Symbolizer)))
384 return E;
385
386 return mapRawProfileToRecords();
387}
388
389Error RawMemProfReader::setupForSymbolization() {
390 auto *Object = cast<object::ObjectFile>(Binary.getBinary());
391 object::BuildIDRef BinaryId = object::getBuildID(Object);
392 if (BinaryId.empty())
393 return make_error<StringError>(Twine("No build id found in binary ") +
394 Binary.getBinary()->getFileName(),
396
397 int NumMatched = 0;
398 for (const auto &Entry : SegmentInfo) {
399 llvm::ArrayRef<uint8_t> SegmentId(Entry.BuildId, Entry.BuildIdSize);
400 if (BinaryId == SegmentId) {
401 // We assume only one text segment in the main binary for simplicity and
402 // reduce the overhead of checking multiple ranges during symbolization.
403 if (++NumMatched > 1) {
404 return make_error<StringError>(
405 "We expect only one executable segment in the profiled binary",
407 }
408 ProfiledTextSegmentStart = Entry.Start;
409 ProfiledTextSegmentEnd = Entry.End;
410 }
411 }
412 assert(NumMatched != 0 && "No matching executable segments in segment info.");
413 assert((PreferredTextSegmentAddress == 0 ||
414 (PreferredTextSegmentAddress == ProfiledTextSegmentStart)) &&
415 "Expect text segment address to be 0 or equal to profiled text "
416 "segment start.");
417 return Error::success();
418}
419
420Error RawMemProfReader::mapRawProfileToRecords() {
421 // Hold a mapping from function to each callsite location we encounter within
422 // it that is part of some dynamic allocation context. The location is stored
423 // as a pointer to a symbolized list of inline frames.
424 using LocationPtr = const llvm::SmallVector<FrameId> *;
426 PerFunctionCallSites;
427
428 // Convert the raw profile callstack data into memprof records. While doing so
429 // keep track of related contexts so that we can fill these in later.
430 for (const auto &[StackId, MIB] : CallstackProfileData) {
431 auto It = StackMap.find(StackId);
432 if (It == StackMap.end())
433 return make_error<InstrProfError>(
435 "memprof callstack record does not contain id: " + Twine(StackId));
436
437 // Construct the symbolized callstack.
439 Callstack.reserve(It->getSecond().size());
440
441 llvm::ArrayRef<uint64_t> Addresses = It->getSecond();
442 for (size_t I = 0; I < Addresses.size(); I++) {
443 const uint64_t Address = Addresses[I];
444 assert(SymbolizedFrame.count(Address) > 0 &&
445 "Address not found in SymbolizedFrame map");
446 const SmallVector<FrameId> &Frames = SymbolizedFrame[Address];
447
448 assert(!idToFrame(Frames.back()).IsInlineFrame &&
449 "The last frame should not be inlined");
450
451 // Record the callsites for each function. Skip the first frame of the
452 // first address since it is the allocation site itself that is recorded
453 // as an alloc site.
454 for (size_t J = 0; J < Frames.size(); J++) {
455 if (I == 0 && J == 0)
456 continue;
457 // We attach the entire bottom-up frame here for the callsite even
458 // though we only need the frames up to and including the frame for
459 // Frames[J].Function. This will enable better deduplication for
460 // compression in the future.
461 const GlobalValue::GUID Guid = idToFrame(Frames[J]).Function;
462 PerFunctionCallSites[Guid].insert(&Frames);
463 }
464
465 // Add all the frames to the current allocation callstack.
466 Callstack.append(Frames.begin(), Frames.end());
467 }
468
469 CallStackId CSId = hashCallStack(Callstack);
470 CSIdToCallStack.insert({CSId, Callstack});
471
472 // We attach the memprof record to each function bottom-up including the
473 // first non-inline frame.
474 for (size_t I = 0; /*Break out using the condition below*/; I++) {
475 const Frame &F = idToFrame(Callstack[I]);
476 auto Result =
477 FunctionProfileData.insert({F.Function, IndexedMemProfRecord()});
478 IndexedMemProfRecord &Record = Result.first->second;
479 Record.AllocSites.emplace_back(Callstack, CSId, MIB);
480
481 if (!F.IsInlineFrame)
482 break;
483 }
484 }
485
486 // Fill in the related callsites per function.
487 for (const auto &[Id, Locs] : PerFunctionCallSites) {
488 // Some functions may have only callsite data and no allocation data. Here
489 // we insert a new entry for callsite data if we need to.
490 auto Result = FunctionProfileData.insert({Id, IndexedMemProfRecord()});
491 IndexedMemProfRecord &Record = Result.first->second;
492 for (LocationPtr Loc : Locs) {
493 CallStackId CSId = hashCallStack(*Loc);
494 CSIdToCallStack.insert({CSId, *Loc});
495 Record.CallSites.push_back(*Loc);
496 Record.CallSiteIds.push_back(CSId);
497 }
498 }
499
501
502 return Error::success();
503}
504
505Error RawMemProfReader::symbolizeAndFilterStackFrames(
506 std::unique_ptr<llvm::symbolize::SymbolizableModule> Symbolizer) {
507 // The specifier to use when symbolization is requested.
508 const DILineInfoSpecifier Specifier(
509 DILineInfoSpecifier::FileLineInfoKind::RawValue,
510 DILineInfoSpecifier::FunctionNameKind::LinkageName);
511
512 // For entries where all PCs in the callstack are discarded, we erase the
513 // entry from the stack map.
514 llvm::SmallVector<uint64_t> EntriesToErase;
515 // We keep track of all prior discarded entries so that we can avoid invoking
516 // the symbolizer for such entries.
517 llvm::DenseSet<uint64_t> AllVAddrsToDiscard;
518 for (auto &Entry : StackMap) {
519 for (const uint64_t VAddr : Entry.getSecond()) {
520 // Check if we have already symbolized and cached the result or if we
521 // don't want to attempt symbolization since we know this address is bad.
522 // In this case the address is also removed from the current callstack.
523 if (SymbolizedFrame.count(VAddr) > 0 ||
524 AllVAddrsToDiscard.contains(VAddr))
525 continue;
526
527 Expected<DIInliningInfo> DIOr = Symbolizer->symbolizeInlinedCode(
528 getModuleOffset(VAddr), Specifier, /*UseSymbolTable=*/false);
529 if (!DIOr)
530 return DIOr.takeError();
531 DIInliningInfo DI = DIOr.get();
532
533 // Drop frames which we can't symbolize or if they belong to the runtime.
534 if (DI.getFrame(0).FunctionName == DILineInfo::BadString ||
535 isRuntimePath(DI.getFrame(0).FileName)) {
536 AllVAddrsToDiscard.insert(VAddr);
537 continue;
538 }
539
540 for (size_t I = 0, NumFrames = DI.getNumberOfFrames(); I < NumFrames;
541 I++) {
542 const auto &DIFrame = DI.getFrame(I);
543 const uint64_t Guid =
544 IndexedMemProfRecord::getGUID(DIFrame.FunctionName);
545 const Frame F(Guid, DIFrame.Line - DIFrame.StartLine, DIFrame.Column,
546 // Only the last entry is not an inlined location.
547 I != NumFrames - 1);
548 // Here we retain a mapping from the GUID to canonical symbol name
549 // instead of adding it to the frame object directly to reduce memory
550 // overhead. This is because there can be many unique frames,
551 // particularly for callsite frames.
552 if (KeepSymbolName) {
553 StringRef CanonicalName =
555 DIFrame.FunctionName);
556 GuidToSymbolName.insert({Guid, CanonicalName.str()});
557 }
558
559 const FrameId Hash = F.hash();
560 IdToFrame.insert({Hash, F});
561 SymbolizedFrame[VAddr].push_back(Hash);
562 }
563 }
564
565 auto &CallStack = Entry.getSecond();
566 llvm::erase_if(CallStack, [&AllVAddrsToDiscard](const uint64_t A) {
567 return AllVAddrsToDiscard.contains(A);
568 });
569 if (CallStack.empty())
570 EntriesToErase.push_back(Entry.getFirst());
571 }
572
573 // Drop the entries where the callstack is empty.
574 for (const uint64_t Id : EntriesToErase) {
575 StackMap.erase(Id);
576 CallstackProfileData.erase(Id);
577 }
578
579 if (StackMap.empty())
580 return make_error<InstrProfError>(
582 "no entries in callstack map after symbolization");
583
584 return Error::success();
585}
586
587std::vector<std::string>
589 const char *Next = DataBuffer->getBufferStart();
590 // Use a set + vector since a profile file may contain multiple raw profile
591 // dumps, each with segment information. We want them unique and in order they
592 // were stored in the profile; the profiled binary should be the first entry.
593 // The runtime uses dl_iterate_phdr and the "... first object visited by
594 // callback is the main program."
595 // https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html
596 std::vector<std::string> BuildIds;
598 while (Next < DataBuffer->getBufferEnd()) {
599 auto *Header = reinterpret_cast<const memprof::Header *>(Next);
600
601 const llvm::SmallVector<SegmentEntry> Entries =
602 readSegmentEntries(Next + Header->SegmentOffset);
603
604 for (const auto &Entry : Entries) {
605 const std::string Id = getBuildIdString(Entry);
606 if (BuildIdsSet.contains(Id))
607 continue;
608 BuildIds.push_back(Id);
609 BuildIdsSet.insert(Id);
610 }
611
612 Next += Header->TotalSize;
613 }
614 return BuildIds;
615}
616
617Error RawMemProfReader::readRawProfile(
618 std::unique_ptr<MemoryBuffer> DataBuffer) {
619 const char *Next = DataBuffer->getBufferStart();
620
621 while (Next < DataBuffer->getBufferEnd()) {
622 auto *Header = reinterpret_cast<const memprof::Header *>(Next);
623
624 // Read in the segment information, check whether its the same across all
625 // profiles in this binary file.
626 const llvm::SmallVector<SegmentEntry> Entries =
627 readSegmentEntries(Next + Header->SegmentOffset);
628 if (!SegmentInfo.empty() && SegmentInfo != Entries) {
629 // We do not expect segment information to change when deserializing from
630 // the same binary profile file. This can happen if dynamic libraries are
631 // loaded/unloaded between profile dumping.
632 return make_error<InstrProfError>(
634 "memprof raw profile has different segment information");
635 }
636 SegmentInfo.assign(Entries.begin(), Entries.end());
637
638 // Read in the MemInfoBlocks. Merge them based on stack id - we assume that
639 // raw profiles in the same binary file are from the same process so the
640 // stackdepot ids are the same.
641 for (const auto &[Id, MIB] : readMemInfoBlocks(Next + Header->MIBOffset)) {
642 if (CallstackProfileData.count(Id)) {
643 CallstackProfileData[Id].Merge(MIB);
644 } else {
645 CallstackProfileData[Id] = MIB;
646 }
647 }
648
649 // Read in the callstack for each ids. For multiple raw profiles in the same
650 // file, we expect that the callstack is the same for a unique id.
651 const CallStackMap CSM = readStackInfo(Next + Header->StackOffset);
652 if (StackMap.empty()) {
653 StackMap = CSM;
654 } else {
655 if (mergeStackMap(CSM, StackMap))
656 return make_error<InstrProfError>(
658 "memprof raw profile got different call stack for same id");
659 }
660
661 Next += Header->TotalSize;
662 }
663
664 return Error::success();
665}
666
667object::SectionedAddress
668RawMemProfReader::getModuleOffset(const uint64_t VirtualAddress) {
669 if (VirtualAddress > ProfiledTextSegmentStart &&
670 VirtualAddress <= ProfiledTextSegmentEnd) {
671 // For PIE binaries, the preferred address is zero and we adjust the virtual
672 // address by start of the profiled segment assuming that the offset of the
673 // segment in the binary is zero. For non-PIE binaries the preferred and
674 // profiled segment addresses should be equal and this is a no-op.
675 const uint64_t AdjustedAddress =
676 VirtualAddress + PreferredTextSegmentAddress - ProfiledTextSegmentStart;
677 return object::SectionedAddress{AdjustedAddress};
678 }
679 // Addresses which do not originate from the profiled text segment in the
680 // binary are not adjusted. These will fail symbolization and be filtered out
681 // during processing.
682 return object::SectionedAddress{VirtualAddress};
683}
686 GuidMemProfRecordPair &GuidRecord,
687 std::function<const Frame(const FrameId)> Callback) {
688 // Create a new callback for the RawMemProfRecord iterator so that we can
689 // provide the symbol name if the reader was initialized with KeepSymbolName =
690 // true. This is useful for debugging and testing.
691 auto IdToFrameCallback = [this](const FrameId Id) {
692 Frame F = this->idToFrame(Id);
693 if (!this->KeepSymbolName)
694 return F;
695 auto Iter = this->GuidToSymbolName.find(F.Function);
696 assert(Iter != this->GuidToSymbolName.end());
697 F.SymbolName = Iter->getSecond();
698 return F;
699 };
700 return MemProfReader::readNextRecord(GuidRecord, IdToFrameCallback);
701}
702} // namespace memprof
703} // 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:587
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
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)
Definition: MemProfReader.h:67
llvm::DenseMap< FrameId, Frame > IdToFrame
llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord >::iterator Iter
llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord > FunctionProfileData
std::pair< GlobalValue::GUID, MemProfRecord > GuidMemProfRecordPair
Definition: MemProfReader.h:41
llvm::DenseMap< CallStackId, llvm::SmallVector< FrameId > > CSIdToCallStack
void printYAML(raw_ostream &OS)
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:305
uint64_t FrameId
Definition: MemProf.h:188
uint64_t CallStackId
Definition: MemProf.h:306
llvm::DenseMap< uint64_t, llvm::SmallVector< uint64_t > > CallStackMap
void verifyFunctionProfileData(const llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord > &FunctionProfileData)
Definition: MemProf.cpp:323
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
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1849
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:2051
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:103
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
static constexpr const char *const BadString
Definition: DIContext.h:34
GlobalValue::GUID Function
Definition: MemProf.h:195
static GlobalValue::GUID getGUID(const StringRef FunctionName)
Definition: MemProf.cpp:263