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