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