xref: /llvm-project/llvm/lib/ProfileData/MemProfReader.cpp (revision 1d5154663509b6200038a2f0b0ac958ea556fa9e)
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 <cstdint>
14 #include <memory>
15 #include <type_traits>
16 
17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/ADT/DenseMap.h"
19 #include "llvm/ADT/SetVector.h"
20 #include "llvm/ADT/SmallSet.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/StringExtras.h"
23 #include "llvm/ADT/Twine.h"
24 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
25 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
26 #include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h"
27 #include "llvm/Object/Binary.h"
28 #include "llvm/Object/BuildID.h"
29 #include "llvm/Object/ELFObjectFile.h"
30 #include "llvm/Object/ObjectFile.h"
31 #include "llvm/ProfileData/InstrProf.h"
32 #include "llvm/ProfileData/MemProf.h"
33 #include "llvm/ProfileData/MemProfData.inc"
34 #include "llvm/ProfileData/MemProfReader.h"
35 #include "llvm/ProfileData/MemProfYAML.h"
36 #include "llvm/ProfileData/SampleProf.h"
37 #include "llvm/Support/Debug.h"
38 #include "llvm/Support/Endian.h"
39 #include "llvm/Support/Error.h"
40 #include "llvm/Support/MemoryBuffer.h"
41 #include "llvm/Support/Path.h"
42 
43 #define DEBUG_TYPE "memprof"
44 
45 namespace llvm {
46 namespace memprof {
47 namespace {
48 template <class T = uint64_t> inline T alignedRead(const char *Ptr) {
49   static_assert(std::is_integral_v<T>, "Not an integral type");
50   assert(reinterpret_cast<size_t>(Ptr) % sizeof(T) == 0 && "Unaligned Read");
51   return *reinterpret_cast<const T *>(Ptr);
52 }
53 
54 Error 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     const auto *H = reinterpret_cast<const Header *>(Next);
71 
72     // Check if the version in header is among the supported versions.
73     bool IsSupported = false;
74     for (auto SupportedVersion : MEMPROF_RAW_SUPPORTED_VERSIONS) {
75       if (H->Version == SupportedVersion)
76         IsSupported = true;
77     }
78     if (!IsSupported) {
79       return make_error<InstrProfError>(instrprof_error::unsupported_version);
80     }
81 
82     TotalSize += H->TotalSize;
83     Next += H->TotalSize;
84   }
85 
86   if (Buffer.getBufferSize() != TotalSize) {
87     return make_error<InstrProfError>(instrprof_error::malformed);
88   }
89   return Error::success();
90 }
91 
92 llvm::SmallVector<SegmentEntry> readSegmentEntries(const char *Ptr) {
93   using namespace support;
94 
95   const uint64_t NumItemsToRead =
96       endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
97   llvm::SmallVector<SegmentEntry> Items;
98   for (uint64_t I = 0; I < NumItemsToRead; I++) {
99     Items.push_back(*reinterpret_cast<const SegmentEntry *>(
100         Ptr + I * sizeof(SegmentEntry)));
101   }
102   return Items;
103 }
104 
105 llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>>
106 readMemInfoBlocksV3(const char *Ptr) {
107   using namespace support;
108 
109   const uint64_t NumItemsToRead =
110       endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
111 
112   llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>> Items;
113   for (uint64_t I = 0; I < NumItemsToRead; I++) {
114     const uint64_t Id =
115         endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
116 
117     // We cheat a bit here and remove the const from cast to set the
118     // Histogram Pointer to newly allocated buffer. We also cheat, since V3 and
119     // V4 do not have the same fields. V3 is missing AccessHistogramSize and
120     // AccessHistogram. This means we read "dirty" data in here, but it should
121     // not segfault, since there will be callstack data placed after this in the
122     // binary format.
123     MemInfoBlock MIB = *reinterpret_cast<const MemInfoBlock *>(Ptr);
124     // Overwrite dirty data.
125     MIB.AccessHistogramSize = 0;
126     MIB.AccessHistogram = 0;
127 
128     Items.push_back({Id, MIB});
129     // Only increment by the size of MIB in V3.
130     Ptr += MEMPROF_V3_MIB_SIZE;
131   }
132   return Items;
133 }
134 
135 llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>>
136 readMemInfoBlocksV4(const char *Ptr) {
137   using namespace support;
138 
139   const uint64_t NumItemsToRead =
140       endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
141 
142   llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>> Items;
143   for (uint64_t I = 0; I < NumItemsToRead; I++) {
144     const uint64_t Id =
145         endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
146     // We cheat a bit here and remove the const from cast to set the
147     // Histogram Pointer to newly allocated buffer.
148     MemInfoBlock MIB = *reinterpret_cast<const MemInfoBlock *>(Ptr);
149 
150     // Only increment by size of MIB since readNext implicitly increments.
151     Ptr += sizeof(MemInfoBlock);
152 
153     if (MIB.AccessHistogramSize > 0) {
154       MIB.AccessHistogram =
155           (uintptr_t)malloc(MIB.AccessHistogramSize * sizeof(uint64_t));
156     }
157 
158     for (uint64_t J = 0; J < MIB.AccessHistogramSize; J++) {
159       ((uint64_t *)MIB.AccessHistogram)[J] =
160           endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr);
161     }
162     Items.push_back({Id, MIB});
163   }
164   return Items;
165 }
166 
167 CallStackMap readStackInfo(const char *Ptr) {
168   using namespace support;
169 
170   const uint64_t NumItemsToRead =
171       endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
172   CallStackMap Items;
173 
174   for (uint64_t I = 0; I < NumItemsToRead; I++) {
175     const uint64_t StackId =
176         endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
177     const uint64_t NumPCs =
178         endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
179 
180     SmallVector<uint64_t> CallStack;
181     CallStack.reserve(NumPCs);
182     for (uint64_t J = 0; J < NumPCs; J++) {
183       CallStack.push_back(
184           endian::readNext<uint64_t, llvm::endianness::little>(Ptr));
185     }
186 
187     Items[StackId] = CallStack;
188   }
189   return Items;
190 }
191 
192 // Merges the contents of stack information in \p From to \p To. Returns true if
193 // any stack ids observed previously map to a different set of program counter
194 // addresses.
195 bool mergeStackMap(const CallStackMap &From, CallStackMap &To) {
196   for (const auto &[Id, Stack] : From) {
197     auto [It, Inserted] = To.try_emplace(Id, Stack);
198     // Check that the PCs are the same (in order).
199     if (!Inserted && Stack != It->second)
200       return true;
201   }
202   return false;
203 }
204 
205 Error report(Error E, const StringRef Context) {
206   return joinErrors(createStringError(inconvertibleErrorCode(), Context),
207                     std::move(E));
208 }
209 
210 bool isRuntimePath(const StringRef Path) {
211   const StringRef Filename = llvm::sys::path::filename(Path);
212   // This list should be updated in case new files with additional interceptors
213   // are added to the memprof runtime.
214   return Filename == "memprof_malloc_linux.cpp" ||
215          Filename == "memprof_interceptors.cpp" ||
216          Filename == "memprof_new_delete.cpp";
217 }
218 
219 std::string getBuildIdString(const SegmentEntry &Entry) {
220   // If the build id is unset print a helpful string instead of all zeros.
221   if (Entry.BuildIdSize == 0)
222     return "<None>";
223 
224   std::string Str;
225   raw_string_ostream OS(Str);
226   for (size_t I = 0; I < Entry.BuildIdSize; I++) {
227     OS << format_hex_no_prefix(Entry.BuildId[I], 2);
228   }
229   return OS.str();
230 }
231 } // namespace
232 
233 Expected<std::unique_ptr<RawMemProfReader>>
234 RawMemProfReader::create(const Twine &Path, const StringRef ProfiledBinary,
235                          bool KeepName) {
236   auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
237   if (std::error_code EC = BufferOr.getError())
238     return report(errorCodeToError(EC), Path.getSingleStringRef());
239 
240   std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
241   return create(std::move(Buffer), ProfiledBinary, KeepName);
242 }
243 
244 Expected<std::unique_ptr<RawMemProfReader>>
245 RawMemProfReader::create(std::unique_ptr<MemoryBuffer> Buffer,
246                          const StringRef ProfiledBinary, bool KeepName) {
247   if (Error E = checkBuffer(*Buffer))
248     return report(std::move(E), Buffer->getBufferIdentifier());
249 
250   if (ProfiledBinary.empty()) {
251     // Peek the build ids to print a helpful error message.
252     const std::vector<std::string> BuildIds = peekBuildIds(Buffer.get());
253     std::string ErrorMessage(
254         R"(Path to profiled binary is empty, expected binary with one of the following build ids:
255 )");
256     for (const auto &Id : BuildIds) {
257       ErrorMessage += "\n BuildId: ";
258       ErrorMessage += Id;
259     }
260     return report(
261         make_error<StringError>(ErrorMessage, inconvertibleErrorCode()),
262         /*Context=*/"");
263   }
264 
265   auto BinaryOr = llvm::object::createBinary(ProfiledBinary);
266   if (!BinaryOr) {
267     return report(BinaryOr.takeError(), ProfiledBinary);
268   }
269 
270   // Use new here since constructor is private.
271   std::unique_ptr<RawMemProfReader> Reader(
272       new RawMemProfReader(std::move(BinaryOr.get()), KeepName));
273   if (Error E = Reader->initialize(std::move(Buffer))) {
274     return std::move(E);
275   }
276   return std::move(Reader);
277 }
278 
279 // We need to make sure that all leftover MIB histograms that have not been
280 // freed by merge are freed here.
281 RawMemProfReader::~RawMemProfReader() {
282   for (auto &[_, MIB] : CallstackProfileData) {
283     if (MemprofRawVersion >= 4ULL && MIB.AccessHistogramSize > 0) {
284       free((void *)MIB.AccessHistogram);
285     }
286   }
287 }
288 
289 bool RawMemProfReader::hasFormat(const StringRef Path) {
290   auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
291   if (!BufferOr)
292     return false;
293 
294   std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
295   return hasFormat(*Buffer);
296 }
297 
298 bool RawMemProfReader::hasFormat(const MemoryBuffer &Buffer) {
299   if (Buffer.getBufferSize() < sizeof(uint64_t))
300     return false;
301   // Aligned read to sanity check that the buffer was allocated with at least 8b
302   // alignment.
303   const uint64_t Magic = alignedRead(Buffer.getBufferStart());
304   return Magic == MEMPROF_RAW_MAGIC_64;
305 }
306 
307 void RawMemProfReader::printYAML(raw_ostream &OS) {
308   uint64_t NumAllocFunctions = 0, NumMibInfo = 0;
309   for (const auto &KV : MemProfData.Records) {
310     const size_t NumAllocSites = KV.second.AllocSites.size();
311     if (NumAllocSites > 0) {
312       NumAllocFunctions++;
313       NumMibInfo += NumAllocSites;
314     }
315   }
316 
317   OS << "MemprofProfile:\n";
318   OS << "  Summary:\n";
319   OS << "    Version: " << MemprofRawVersion << "\n";
320   OS << "    NumSegments: " << SegmentInfo.size() << "\n";
321   OS << "    NumMibInfo: " << NumMibInfo << "\n";
322   OS << "    NumAllocFunctions: " << NumAllocFunctions << "\n";
323   OS << "    NumStackOffsets: " << StackMap.size() << "\n";
324   // Print out the segment information.
325   OS << "  Segments:\n";
326   for (const auto &Entry : SegmentInfo) {
327     OS << "  -\n";
328     OS << "    BuildId: " << getBuildIdString(Entry) << "\n";
329     OS << "    Start: 0x" << llvm::utohexstr(Entry.Start) << "\n";
330     OS << "    End: 0x" << llvm::utohexstr(Entry.End) << "\n";
331     OS << "    Offset: 0x" << llvm::utohexstr(Entry.Offset) << "\n";
332   }
333   // Print out the merged contents of the profiles.
334   OS << "  Records:\n";
335   for (const auto &[GUID, Record] : *this) {
336     OS << "  -\n";
337     OS << "    FunctionGUID: " << GUID << "\n";
338     Record.print(OS);
339   }
340 }
341 
342 Error RawMemProfReader::initialize(std::unique_ptr<MemoryBuffer> DataBuffer) {
343   const StringRef FileName = Binary.getBinary()->getFileName();
344 
345   auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Binary.getBinary());
346   if (!ElfObject) {
347     return report(make_error<StringError>(Twine("Not an ELF file: "),
348                                           inconvertibleErrorCode()),
349                   FileName);
350   }
351 
352   // Check whether the profiled binary was built with position independent code
353   // (PIC). Perform sanity checks for assumptions we rely on to simplify
354   // symbolization.
355   auto *Elf64LEObject = llvm::cast<llvm::object::ELF64LEObjectFile>(ElfObject);
356   const llvm::object::ELF64LEFile &ElfFile = Elf64LEObject->getELFFile();
357   auto PHdrsOr = ElfFile.program_headers();
358   if (!PHdrsOr)
359     return report(
360         make_error<StringError>(Twine("Could not read program headers: "),
361                                 inconvertibleErrorCode()),
362         FileName);
363 
364   int NumExecutableSegments = 0;
365   for (const auto &Phdr : *PHdrsOr) {
366     if (Phdr.p_type == ELF::PT_LOAD) {
367       if (Phdr.p_flags & ELF::PF_X) {
368         // We assume only one text segment in the main binary for simplicity and
369         // reduce the overhead of checking multiple ranges during symbolization.
370         if (++NumExecutableSegments > 1) {
371           return report(
372               make_error<StringError>(
373                   "Expect only one executable load segment in the binary",
374                   inconvertibleErrorCode()),
375               FileName);
376         }
377         // Segment will always be loaded at a page boundary, expect it to be
378         // aligned already. Assume 4K pagesize for the machine from which the
379         // profile has been collected. This should be fine for now, in case we
380         // want to support other pagesizes it can be recorded in the raw profile
381         // during collection.
382         PreferredTextSegmentAddress = Phdr.p_vaddr;
383         assert(Phdr.p_vaddr == (Phdr.p_vaddr & ~(0x1000 - 1U)) &&
384                "Expect p_vaddr to always be page aligned");
385         assert(Phdr.p_offset == 0 && "Expect p_offset = 0 for symbolization.");
386       }
387     }
388   }
389 
390   auto Triple = ElfObject->makeTriple();
391   if (!Triple.isX86())
392     return report(make_error<StringError>(Twine("Unsupported target: ") +
393                                               Triple.getArchName(),
394                                           inconvertibleErrorCode()),
395                   FileName);
396 
397   // Process the raw profile.
398   if (Error E = readRawProfile(std::move(DataBuffer)))
399     return E;
400 
401   if (Error E = setupForSymbolization())
402     return E;
403 
404   auto *Object = cast<object::ObjectFile>(Binary.getBinary());
405   std::unique_ptr<DIContext> Context = DWARFContext::create(
406       *Object, DWARFContext::ProcessDebugRelocations::Process);
407 
408   auto SOFOr = symbolize::SymbolizableObjectFile::create(
409       Object, std::move(Context), /*UntagAddresses=*/false);
410   if (!SOFOr)
411     return report(SOFOr.takeError(), FileName);
412   auto Symbolizer = std::move(SOFOr.get());
413 
414   // The symbolizer ownership is moved into symbolizeAndFilterStackFrames so
415   // that it is freed automatically at the end, when it is no longer used. This
416   // reduces peak memory since it won't be live while also mapping the raw
417   // profile into records afterwards.
418   if (Error E = symbolizeAndFilterStackFrames(std::move(Symbolizer)))
419     return E;
420 
421   return mapRawProfileToRecords();
422 }
423 
424 Error RawMemProfReader::setupForSymbolization() {
425   auto *Object = cast<object::ObjectFile>(Binary.getBinary());
426   object::BuildIDRef BinaryId = object::getBuildID(Object);
427   if (BinaryId.empty())
428     return make_error<StringError>(Twine("No build id found in binary ") +
429                                        Binary.getBinary()->getFileName(),
430                                    inconvertibleErrorCode());
431 
432   int NumMatched = 0;
433   for (const auto &Entry : SegmentInfo) {
434     llvm::ArrayRef<uint8_t> SegmentId(Entry.BuildId, Entry.BuildIdSize);
435     if (BinaryId == SegmentId) {
436       // We assume only one text segment in the main binary for simplicity and
437       // reduce the overhead of checking multiple ranges during symbolization.
438       if (++NumMatched > 1) {
439         return make_error<StringError>(
440             "We expect only one executable segment in the profiled binary",
441             inconvertibleErrorCode());
442       }
443       ProfiledTextSegmentStart = Entry.Start;
444       ProfiledTextSegmentEnd = Entry.End;
445     }
446   }
447   assert(NumMatched != 0 && "No matching executable segments in segment info.");
448   assert((PreferredTextSegmentAddress == 0 ||
449           (PreferredTextSegmentAddress == ProfiledTextSegmentStart)) &&
450          "Expect text segment address to be 0 or equal to profiled text "
451          "segment start.");
452   return Error::success();
453 }
454 
455 Error RawMemProfReader::mapRawProfileToRecords() {
456   // Hold a mapping from function to each callsite location we encounter within
457   // it that is part of some dynamic allocation context. The location is stored
458   // as a pointer to a symbolized list of inline frames.
459   using LocationPtr = const llvm::SmallVector<FrameId> *;
460   llvm::MapVector<GlobalValue::GUID, llvm::SetVector<LocationPtr>>
461       PerFunctionCallSites;
462 
463   // Convert the raw profile callstack data into memprof records. While doing so
464   // keep track of related contexts so that we can fill these in later.
465   for (const auto &[StackId, MIB] : CallstackProfileData) {
466     auto It = StackMap.find(StackId);
467     if (It == StackMap.end())
468       return make_error<InstrProfError>(
469           instrprof_error::malformed,
470           "memprof callstack record does not contain id: " + Twine(StackId));
471 
472     // Construct the symbolized callstack.
473     llvm::SmallVector<FrameId> Callstack;
474     Callstack.reserve(It->getSecond().size());
475 
476     llvm::ArrayRef<uint64_t> Addresses = It->getSecond();
477     for (size_t I = 0; I < Addresses.size(); I++) {
478       const uint64_t Address = Addresses[I];
479       assert(SymbolizedFrame.count(Address) > 0 &&
480              "Address not found in SymbolizedFrame map");
481       const SmallVector<FrameId> &Frames = SymbolizedFrame[Address];
482 
483       assert(!idToFrame(Frames.back()).IsInlineFrame &&
484              "The last frame should not be inlined");
485 
486       // Record the callsites for each function. Skip the first frame of the
487       // first address since it is the allocation site itself that is recorded
488       // as an alloc site.
489       for (size_t J = 0; J < Frames.size(); J++) {
490         if (I == 0 && J == 0)
491           continue;
492         // We attach the entire bottom-up frame here for the callsite even
493         // though we only need the frames up to and including the frame for
494         // Frames[J].Function. This will enable better deduplication for
495         // compression in the future.
496         const GlobalValue::GUID Guid = idToFrame(Frames[J]).Function;
497         PerFunctionCallSites[Guid].insert(&Frames);
498       }
499 
500       // Add all the frames to the current allocation callstack.
501       Callstack.append(Frames.begin(), Frames.end());
502     }
503 
504     CallStackId CSId = MemProfData.addCallStack(Callstack);
505 
506     // We attach the memprof record to each function bottom-up including the
507     // first non-inline frame.
508     for (size_t I = 0; /*Break out using the condition below*/; I++) {
509       const Frame &F = idToFrame(Callstack[I]);
510       IndexedMemProfRecord &Record = MemProfData.Records[F.Function];
511       Record.AllocSites.emplace_back(CSId, MIB);
512 
513       if (!F.IsInlineFrame)
514         break;
515     }
516   }
517 
518   // Fill in the related callsites per function.
519   for (const auto &[Id, Locs] : PerFunctionCallSites) {
520     // Some functions may have only callsite data and no allocation data. Here
521     // we insert a new entry for callsite data if we need to.
522     IndexedMemProfRecord &Record = MemProfData.Records[Id];
523     for (LocationPtr Loc : Locs)
524       Record.CallSiteIds.push_back(MemProfData.addCallStack(*Loc));
525   }
526 
527   return Error::success();
528 }
529 
530 Error RawMemProfReader::symbolizeAndFilterStackFrames(
531     std::unique_ptr<llvm::symbolize::SymbolizableModule> Symbolizer) {
532   // The specifier to use when symbolization is requested.
533   const DILineInfoSpecifier Specifier(
534       DILineInfoSpecifier::FileLineInfoKind::RawValue,
535       DILineInfoSpecifier::FunctionNameKind::LinkageName);
536 
537   // For entries where all PCs in the callstack are discarded, we erase the
538   // entry from the stack map.
539   llvm::SmallVector<uint64_t> EntriesToErase;
540   // We keep track of all prior discarded entries so that we can avoid invoking
541   // the symbolizer for such entries.
542   llvm::DenseSet<uint64_t> AllVAddrsToDiscard;
543   for (auto &Entry : StackMap) {
544     for (const uint64_t VAddr : Entry.getSecond()) {
545       // Check if we have already symbolized and cached the result or if we
546       // don't want to attempt symbolization since we know this address is bad.
547       // In this case the address is also removed from the current callstack.
548       if (SymbolizedFrame.count(VAddr) > 0 ||
549           AllVAddrsToDiscard.contains(VAddr))
550         continue;
551 
552       Expected<DIInliningInfo> DIOr = Symbolizer->symbolizeInlinedCode(
553           getModuleOffset(VAddr), Specifier, /*UseSymbolTable=*/false);
554       if (!DIOr)
555         return DIOr.takeError();
556       DIInliningInfo DI = DIOr.get();
557 
558       // Drop frames which we can't symbolize or if they belong to the runtime.
559       if (DI.getFrame(0).FunctionName == DILineInfo::BadString ||
560           isRuntimePath(DI.getFrame(0).FileName)) {
561         AllVAddrsToDiscard.insert(VAddr);
562         continue;
563       }
564 
565       for (size_t I = 0, NumFrames = DI.getNumberOfFrames(); I < NumFrames;
566            I++) {
567         const auto &DIFrame = DI.getFrame(I);
568         const uint64_t Guid =
569             IndexedMemProfRecord::getGUID(DIFrame.FunctionName);
570         const Frame F(Guid, DIFrame.Line - DIFrame.StartLine, DIFrame.Column,
571                       // Only the last entry is not an inlined location.
572                       I != NumFrames - 1);
573         // Here we retain a mapping from the GUID to canonical symbol name
574         // instead of adding it to the frame object directly to reduce memory
575         // overhead. This is because there can be many unique frames,
576         // particularly for callsite frames.
577         if (KeepSymbolName) {
578           StringRef CanonicalName =
579               sampleprof::FunctionSamples::getCanonicalFnName(
580                   DIFrame.FunctionName);
581           GuidToSymbolName.insert({Guid, CanonicalName.str()});
582         }
583 
584         SymbolizedFrame[VAddr].push_back(MemProfData.addFrame(F));
585       }
586     }
587 
588     auto &CallStack = Entry.getSecond();
589     llvm::erase_if(CallStack, [&AllVAddrsToDiscard](const uint64_t A) {
590       return AllVAddrsToDiscard.contains(A);
591     });
592     if (CallStack.empty())
593       EntriesToErase.push_back(Entry.getFirst());
594   }
595 
596   // Drop the entries where the callstack is empty.
597   for (const uint64_t Id : EntriesToErase) {
598     StackMap.erase(Id);
599     if (CallstackProfileData[Id].AccessHistogramSize > 0)
600       free((void *)CallstackProfileData[Id].AccessHistogram);
601     CallstackProfileData.erase(Id);
602   }
603 
604   if (StackMap.empty())
605     return make_error<InstrProfError>(
606         instrprof_error::malformed,
607         "no entries in callstack map after symbolization");
608 
609   return Error::success();
610 }
611 
612 std::vector<std::string>
613 RawMemProfReader::peekBuildIds(MemoryBuffer *DataBuffer) {
614   const char *Next = DataBuffer->getBufferStart();
615   // Use a SetVector since a profile file may contain multiple raw profile
616   // dumps, each with segment information. We want them unique and in order they
617   // were stored in the profile; the profiled binary should be the first entry.
618   // The runtime uses dl_iterate_phdr and the "... first object visited by
619   // callback is the main program."
620   // https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html
621   llvm::SetVector<std::string, std::vector<std::string>,
622                   llvm::SmallSet<std::string, 10>>
623       BuildIds;
624   while (Next < DataBuffer->getBufferEnd()) {
625     const auto *Header = reinterpret_cast<const memprof::Header *>(Next);
626 
627     const llvm::SmallVector<SegmentEntry> Entries =
628         readSegmentEntries(Next + Header->SegmentOffset);
629 
630     for (const auto &Entry : Entries)
631       BuildIds.insert(getBuildIdString(Entry));
632 
633     Next += Header->TotalSize;
634   }
635   return BuildIds.takeVector();
636 }
637 
638 // FIXME: Add a schema for serializing similiar to IndexedMemprofReader. This
639 // will help being able to deserialize different versions raw memprof versions
640 // more easily.
641 llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>>
642 RawMemProfReader::readMemInfoBlocks(const char *Ptr) {
643   if (MemprofRawVersion == 3ULL)
644     return readMemInfoBlocksV3(Ptr);
645   if (MemprofRawVersion == 4ULL)
646     return readMemInfoBlocksV4(Ptr);
647   llvm_unreachable(
648       "Panic: Unsupported version number when reading MemInfoBlocks");
649 }
650 
651 Error RawMemProfReader::readRawProfile(
652     std::unique_ptr<MemoryBuffer> DataBuffer) {
653   const char *Next = DataBuffer->getBufferStart();
654 
655   while (Next < DataBuffer->getBufferEnd()) {
656     const auto *Header = reinterpret_cast<const memprof::Header *>(Next);
657 
658     // Set Reader version to memprof raw version of profile. Checking if version
659     // is supported is checked before creating the reader.
660     MemprofRawVersion = Header->Version;
661 
662     // Read in the segment information, check whether its the same across all
663     // profiles in this binary file.
664     const llvm::SmallVector<SegmentEntry> Entries =
665         readSegmentEntries(Next + Header->SegmentOffset);
666     if (!SegmentInfo.empty() && SegmentInfo != Entries) {
667       // We do not expect segment information to change when deserializing from
668       // the same binary profile file. This can happen if dynamic libraries are
669       // loaded/unloaded between profile dumping.
670       return make_error<InstrProfError>(
671           instrprof_error::malformed,
672           "memprof raw profile has different segment information");
673     }
674     SegmentInfo.assign(Entries.begin(), Entries.end());
675 
676     // Read in the MemInfoBlocks. Merge them based on stack id - we assume that
677     // raw profiles in the same binary file are from the same process so the
678     // stackdepot ids are the same.
679     for (const auto &[Id, MIB] : readMemInfoBlocks(Next + Header->MIBOffset)) {
680       if (CallstackProfileData.count(Id)) {
681 
682         if (MemprofRawVersion >= 4ULL &&
683             (CallstackProfileData[Id].AccessHistogramSize > 0 ||
684              MIB.AccessHistogramSize > 0)) {
685           uintptr_t ShorterHistogram;
686           if (CallstackProfileData[Id].AccessHistogramSize >
687               MIB.AccessHistogramSize)
688             ShorterHistogram = MIB.AccessHistogram;
689           else
690             ShorterHistogram = CallstackProfileData[Id].AccessHistogram;
691           CallstackProfileData[Id].Merge(MIB);
692           free((void *)ShorterHistogram);
693         } else {
694           CallstackProfileData[Id].Merge(MIB);
695         }
696       } else {
697         CallstackProfileData[Id] = MIB;
698       }
699     }
700 
701     // Read in the callstack for each ids. For multiple raw profiles in the same
702     // file, we expect that the callstack is the same for a unique id.
703     const CallStackMap CSM = readStackInfo(Next + Header->StackOffset);
704     if (StackMap.empty()) {
705       StackMap = CSM;
706     } else {
707       if (mergeStackMap(CSM, StackMap))
708         return make_error<InstrProfError>(
709             instrprof_error::malformed,
710             "memprof raw profile got different call stack for same id");
711     }
712 
713     Next += Header->TotalSize;
714   }
715 
716   return Error::success();
717 }
718 
719 object::SectionedAddress
720 RawMemProfReader::getModuleOffset(const uint64_t VirtualAddress) {
721   if (VirtualAddress > ProfiledTextSegmentStart &&
722       VirtualAddress <= ProfiledTextSegmentEnd) {
723     // For PIE binaries, the preferred address is zero and we adjust the virtual
724     // address by start of the profiled segment assuming that the offset of the
725     // segment in the binary is zero. For non-PIE binaries the preferred and
726     // profiled segment addresses should be equal and this is a no-op.
727     const uint64_t AdjustedAddress =
728         VirtualAddress + PreferredTextSegmentAddress - ProfiledTextSegmentStart;
729     return object::SectionedAddress{AdjustedAddress};
730   }
731   // Addresses which do not originate from the profiled text segment in the
732   // binary are not adjusted. These will fail symbolization and be filtered out
733   // during processing.
734   return object::SectionedAddress{VirtualAddress};
735 }
736 
737 Error RawMemProfReader::readNextRecord(
738     GuidMemProfRecordPair &GuidRecord,
739     std::function<const Frame(const FrameId)> Callback) {
740   // Create a new callback for the RawMemProfRecord iterator so that we can
741   // provide the symbol name if the reader was initialized with KeepSymbolName =
742   // true. This is useful for debugging and testing.
743   auto IdToFrameCallback = [this](const FrameId Id) {
744     Frame F = this->idToFrame(Id);
745     if (!this->KeepSymbolName)
746       return F;
747     auto Iter = this->GuidToSymbolName.find(F.Function);
748     assert(Iter != this->GuidToSymbolName.end());
749     F.SymbolName = std::make_unique<std::string>(Iter->getSecond());
750     return F;
751   };
752   return MemProfReader::readNextRecord(GuidRecord, IdToFrameCallback);
753 }
754 
755 Expected<std::unique_ptr<YAMLMemProfReader>>
756 YAMLMemProfReader::create(const Twine &Path) {
757   auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path, /*IsText=*/true);
758   if (std::error_code EC = BufferOr.getError())
759     return report(errorCodeToError(EC), Path.getSingleStringRef());
760 
761   std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
762   return create(std::move(Buffer));
763 }
764 
765 Expected<std::unique_ptr<YAMLMemProfReader>>
766 YAMLMemProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
767   auto Reader = std::make_unique<YAMLMemProfReader>();
768   Reader->parse(Buffer->getBuffer());
769   return std::move(Reader);
770 }
771 
772 bool YAMLMemProfReader::hasFormat(const StringRef Path) {
773   auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path, /*IsText=*/true);
774   if (!BufferOr)
775     return false;
776 
777   std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
778   return hasFormat(*Buffer);
779 }
780 
781 bool YAMLMemProfReader::hasFormat(const MemoryBuffer &Buffer) {
782   return Buffer.getBuffer().starts_with("---");
783 }
784 
785 void YAMLMemProfReader::parse(StringRef YAMLData) {
786   memprof::AllMemProfData Doc;
787   yaml::Input Yin(YAMLData);
788 
789   Yin >> Doc;
790   if (Yin.error())
791     return;
792 
793   // Add a call stack to MemProfData.CallStacks and return its CallStackId.
794   auto AddCallStack = [&](ArrayRef<Frame> CallStack) -> CallStackId {
795     SmallVector<FrameId> IndexedCallStack;
796     IndexedCallStack.reserve(CallStack.size());
797     for (const Frame &F : CallStack)
798       IndexedCallStack.push_back(MemProfData.addFrame(F));
799     return MemProfData.addCallStack(std::move(IndexedCallStack));
800   };
801 
802   for (const auto &[GUID, Record] : Doc.HeapProfileRecords) {
803     IndexedMemProfRecord IndexedRecord;
804 
805     // Convert AllocationInfo to IndexedAllocationInfo.
806     for (const AllocationInfo &AI : Record.AllocSites) {
807       CallStackId CSId = AddCallStack(AI.CallStack);
808       IndexedRecord.AllocSites.emplace_back(CSId, AI.Info);
809     }
810 
811     // Populate CallSiteIds.
812     for (const auto &CallSite : Record.CallSites) {
813       CallStackId CSId = AddCallStack(CallSite);
814       IndexedRecord.CallSiteIds.push_back(CSId);
815     }
816 
817     MemProfData.Records.try_emplace(GUID, std::move(IndexedRecord));
818   }
819 }
820 } // namespace memprof
821 } // namespace llvm
822