xref: /freebsd/contrib/llvm-project/llvm/lib/ProfileData/IndexedMemProfData.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //===- IndexedMemProfData.h - MemProf format support ------------*- C++ -*-===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric //
9*700637cbSDimitry Andric // MemProf data is serialized in writeMemProf provided in this file.
10*700637cbSDimitry Andric //
11*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
12*700637cbSDimitry Andric 
13*700637cbSDimitry Andric #include "llvm/ProfileData/DataAccessProf.h"
14*700637cbSDimitry Andric #include "llvm/ProfileData/InstrProf.h"
15*700637cbSDimitry Andric #include "llvm/ProfileData/InstrProfReader.h"
16*700637cbSDimitry Andric #include "llvm/ProfileData/MemProf.h"
17*700637cbSDimitry Andric #include "llvm/ProfileData/MemProfRadixTree.h"
18*700637cbSDimitry Andric #include "llvm/ProfileData/MemProfSummary.h"
19*700637cbSDimitry Andric #include "llvm/Support/FormatVariadic.h"
20*700637cbSDimitry Andric #include "llvm/Support/OnDiskHashTable.h"
21*700637cbSDimitry Andric 
22*700637cbSDimitry Andric namespace llvm {
23*700637cbSDimitry Andric 
24*700637cbSDimitry Andric // Serialize Schema.
writeMemProfSchema(ProfOStream & OS,const memprof::MemProfSchema & Schema)25*700637cbSDimitry Andric static void writeMemProfSchema(ProfOStream &OS,
26*700637cbSDimitry Andric                                const memprof::MemProfSchema &Schema) {
27*700637cbSDimitry Andric   OS.write(static_cast<uint64_t>(Schema.size()));
28*700637cbSDimitry Andric   for (const auto Id : Schema)
29*700637cbSDimitry Andric     OS.write(static_cast<uint64_t>(Id));
30*700637cbSDimitry Andric }
31*700637cbSDimitry Andric 
32*700637cbSDimitry Andric // Serialize MemProfRecordData.  Return RecordTableOffset.
writeMemProfRecords(ProfOStream & OS,llvm::MapVector<GlobalValue::GUID,memprof::IndexedMemProfRecord> & MemProfRecordData,memprof::MemProfSchema * Schema,memprof::IndexedVersion Version,llvm::DenseMap<memprof::CallStackId,memprof::LinearCallStackId> * MemProfCallStackIndexes=nullptr)33*700637cbSDimitry Andric static uint64_t writeMemProfRecords(
34*700637cbSDimitry Andric     ProfOStream &OS,
35*700637cbSDimitry Andric     llvm::MapVector<GlobalValue::GUID, memprof::IndexedMemProfRecord>
36*700637cbSDimitry Andric         &MemProfRecordData,
37*700637cbSDimitry Andric     memprof::MemProfSchema *Schema, memprof::IndexedVersion Version,
38*700637cbSDimitry Andric     llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
39*700637cbSDimitry Andric         *MemProfCallStackIndexes = nullptr) {
40*700637cbSDimitry Andric   memprof::RecordWriterTrait RecordWriter(Schema, Version,
41*700637cbSDimitry Andric                                           MemProfCallStackIndexes);
42*700637cbSDimitry Andric   OnDiskChainedHashTableGenerator<memprof::RecordWriterTrait>
43*700637cbSDimitry Andric       RecordTableGenerator;
44*700637cbSDimitry Andric   for (auto &[GUID, Record] : MemProfRecordData) {
45*700637cbSDimitry Andric     // Insert the key (func hash) and value (memprof record).
46*700637cbSDimitry Andric     RecordTableGenerator.insert(GUID, Record, RecordWriter);
47*700637cbSDimitry Andric   }
48*700637cbSDimitry Andric   // Release the memory of this MapVector as it is no longer needed.
49*700637cbSDimitry Andric   MemProfRecordData.clear();
50*700637cbSDimitry Andric 
51*700637cbSDimitry Andric   // The call to Emit invokes RecordWriterTrait::EmitData which destructs
52*700637cbSDimitry Andric   // the memprof record copies owned by the RecordTableGenerator. This works
53*700637cbSDimitry Andric   // because the RecordTableGenerator is not used after this point.
54*700637cbSDimitry Andric   return RecordTableGenerator.Emit(OS.OS, RecordWriter);
55*700637cbSDimitry Andric }
56*700637cbSDimitry Andric 
57*700637cbSDimitry Andric // Serialize MemProfFrameData.  Return FrameTableOffset.
writeMemProfFrames(ProfOStream & OS,llvm::MapVector<memprof::FrameId,memprof::Frame> & MemProfFrameData)58*700637cbSDimitry Andric static uint64_t writeMemProfFrames(
59*700637cbSDimitry Andric     ProfOStream &OS,
60*700637cbSDimitry Andric     llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData) {
61*700637cbSDimitry Andric   OnDiskChainedHashTableGenerator<memprof::FrameWriterTrait>
62*700637cbSDimitry Andric       FrameTableGenerator;
63*700637cbSDimitry Andric   for (auto &[FrameId, Frame] : MemProfFrameData) {
64*700637cbSDimitry Andric     // Insert the key (frame id) and value (frame contents).
65*700637cbSDimitry Andric     FrameTableGenerator.insert(FrameId, Frame);
66*700637cbSDimitry Andric   }
67*700637cbSDimitry Andric   // Release the memory of this MapVector as it is no longer needed.
68*700637cbSDimitry Andric   MemProfFrameData.clear();
69*700637cbSDimitry Andric 
70*700637cbSDimitry Andric   return FrameTableGenerator.Emit(OS.OS);
71*700637cbSDimitry Andric }
72*700637cbSDimitry Andric 
73*700637cbSDimitry Andric // Serialize MemProfFrameData.  Return the mapping from FrameIds to their
74*700637cbSDimitry Andric // indexes within the frame array.
75*700637cbSDimitry Andric static llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId>
writeMemProfFrameArray(ProfOStream & OS,llvm::MapVector<memprof::FrameId,memprof::Frame> & MemProfFrameData,llvm::DenseMap<memprof::FrameId,memprof::FrameStat> & FrameHistogram)76*700637cbSDimitry Andric writeMemProfFrameArray(
77*700637cbSDimitry Andric     ProfOStream &OS,
78*700637cbSDimitry Andric     llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData,
79*700637cbSDimitry Andric     llvm::DenseMap<memprof::FrameId, memprof::FrameStat> &FrameHistogram) {
80*700637cbSDimitry Andric   // Mappings from FrameIds to array indexes.
81*700637cbSDimitry Andric   llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId> MemProfFrameIndexes;
82*700637cbSDimitry Andric 
83*700637cbSDimitry Andric   // Compute the order in which we serialize Frames.  The order does not matter
84*700637cbSDimitry Andric   // in terms of correctness, but we still compute it for deserialization
85*700637cbSDimitry Andric   // performance.  Specifically, if we serialize frequently used Frames one
86*700637cbSDimitry Andric   // after another, we have better cache utilization.  For two Frames that
87*700637cbSDimitry Andric   // appear equally frequently, we break a tie by serializing the one that tends
88*700637cbSDimitry Andric   // to appear earlier in call stacks.  We implement the tie-breaking mechanism
89*700637cbSDimitry Andric   // by computing the sum of indexes within call stacks for each Frame.  If we
90*700637cbSDimitry Andric   // still have a tie, then we just resort to compare two FrameIds, which is
91*700637cbSDimitry Andric   // just for stability of output.
92*700637cbSDimitry Andric   std::vector<std::pair<memprof::FrameId, const memprof::Frame *>> FrameIdOrder;
93*700637cbSDimitry Andric   FrameIdOrder.reserve(MemProfFrameData.size());
94*700637cbSDimitry Andric   for (const auto &[Id, Frame] : MemProfFrameData)
95*700637cbSDimitry Andric     FrameIdOrder.emplace_back(Id, &Frame);
96*700637cbSDimitry Andric   assert(MemProfFrameData.size() == FrameIdOrder.size());
97*700637cbSDimitry Andric   llvm::sort(FrameIdOrder,
98*700637cbSDimitry Andric              [&](const std::pair<memprof::FrameId, const memprof::Frame *> &L,
99*700637cbSDimitry Andric                  const std::pair<memprof::FrameId, const memprof::Frame *> &R) {
100*700637cbSDimitry Andric                const auto &SL = FrameHistogram[L.first];
101*700637cbSDimitry Andric                const auto &SR = FrameHistogram[R.first];
102*700637cbSDimitry Andric                // Popular FrameIds should come first.
103*700637cbSDimitry Andric                if (SL.Count != SR.Count)
104*700637cbSDimitry Andric                  return SL.Count > SR.Count;
105*700637cbSDimitry Andric                // If they are equally popular, then the one that tends to appear
106*700637cbSDimitry Andric                // earlier in call stacks should come first.
107*700637cbSDimitry Andric                if (SL.PositionSum != SR.PositionSum)
108*700637cbSDimitry Andric                  return SL.PositionSum < SR.PositionSum;
109*700637cbSDimitry Andric                // Compare their FrameIds for sort stability.
110*700637cbSDimitry Andric                return L.first < R.first;
111*700637cbSDimitry Andric              });
112*700637cbSDimitry Andric 
113*700637cbSDimitry Andric   // Serialize all frames while creating mappings from linear IDs to FrameIds.
114*700637cbSDimitry Andric   uint64_t Index = 0;
115*700637cbSDimitry Andric   MemProfFrameIndexes.reserve(FrameIdOrder.size());
116*700637cbSDimitry Andric   for (const auto &[Id, F] : FrameIdOrder) {
117*700637cbSDimitry Andric     F->serialize(OS.OS);
118*700637cbSDimitry Andric     MemProfFrameIndexes.insert({Id, Index});
119*700637cbSDimitry Andric     ++Index;
120*700637cbSDimitry Andric   }
121*700637cbSDimitry Andric   assert(MemProfFrameData.size() == Index);
122*700637cbSDimitry Andric   assert(MemProfFrameData.size() == MemProfFrameIndexes.size());
123*700637cbSDimitry Andric 
124*700637cbSDimitry Andric   // Release the memory of this MapVector as it is no longer needed.
125*700637cbSDimitry Andric   MemProfFrameData.clear();
126*700637cbSDimitry Andric 
127*700637cbSDimitry Andric   return MemProfFrameIndexes;
128*700637cbSDimitry Andric }
129*700637cbSDimitry Andric 
writeMemProfCallStacks(ProfOStream & OS,llvm::MapVector<memprof::CallStackId,llvm::SmallVector<memprof::FrameId>> & MemProfCallStackData)130*700637cbSDimitry Andric static uint64_t writeMemProfCallStacks(
131*700637cbSDimitry Andric     ProfOStream &OS,
132*700637cbSDimitry Andric     llvm::MapVector<memprof::CallStackId, llvm::SmallVector<memprof::FrameId>>
133*700637cbSDimitry Andric         &MemProfCallStackData) {
134*700637cbSDimitry Andric   OnDiskChainedHashTableGenerator<memprof::CallStackWriterTrait>
135*700637cbSDimitry Andric       CallStackTableGenerator;
136*700637cbSDimitry Andric   for (auto &[CSId, CallStack] : MemProfCallStackData)
137*700637cbSDimitry Andric     CallStackTableGenerator.insert(CSId, CallStack);
138*700637cbSDimitry Andric   // Release the memory of this vector as it is no longer needed.
139*700637cbSDimitry Andric   MemProfCallStackData.clear();
140*700637cbSDimitry Andric 
141*700637cbSDimitry Andric   return CallStackTableGenerator.Emit(OS.OS);
142*700637cbSDimitry Andric }
143*700637cbSDimitry Andric 
144*700637cbSDimitry Andric static llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
writeMemProfCallStackArray(ProfOStream & OS,llvm::MapVector<memprof::CallStackId,llvm::SmallVector<memprof::FrameId>> & MemProfCallStackData,llvm::DenseMap<memprof::FrameId,memprof::LinearFrameId> & MemProfFrameIndexes,llvm::DenseMap<memprof::FrameId,memprof::FrameStat> & FrameHistogram,unsigned & NumElements)145*700637cbSDimitry Andric writeMemProfCallStackArray(
146*700637cbSDimitry Andric     ProfOStream &OS,
147*700637cbSDimitry Andric     llvm::MapVector<memprof::CallStackId, llvm::SmallVector<memprof::FrameId>>
148*700637cbSDimitry Andric         &MemProfCallStackData,
149*700637cbSDimitry Andric     llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId>
150*700637cbSDimitry Andric         &MemProfFrameIndexes,
151*700637cbSDimitry Andric     llvm::DenseMap<memprof::FrameId, memprof::FrameStat> &FrameHistogram,
152*700637cbSDimitry Andric     unsigned &NumElements) {
153*700637cbSDimitry Andric   llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
154*700637cbSDimitry Andric       MemProfCallStackIndexes;
155*700637cbSDimitry Andric 
156*700637cbSDimitry Andric   memprof::CallStackRadixTreeBuilder<memprof::FrameId> Builder;
157*700637cbSDimitry Andric   Builder.build(std::move(MemProfCallStackData), &MemProfFrameIndexes,
158*700637cbSDimitry Andric                 FrameHistogram);
159*700637cbSDimitry Andric   for (auto I : Builder.getRadixArray())
160*700637cbSDimitry Andric     OS.write32(I);
161*700637cbSDimitry Andric   NumElements = Builder.getRadixArray().size();
162*700637cbSDimitry Andric   MemProfCallStackIndexes = Builder.takeCallStackPos();
163*700637cbSDimitry Andric 
164*700637cbSDimitry Andric   // Release the memory of this vector as it is no longer needed.
165*700637cbSDimitry Andric   MemProfCallStackData.clear();
166*700637cbSDimitry Andric 
167*700637cbSDimitry Andric   return MemProfCallStackIndexes;
168*700637cbSDimitry Andric }
169*700637cbSDimitry Andric 
170*700637cbSDimitry Andric // Write out MemProf Version2 as follows:
171*700637cbSDimitry Andric // uint64_t Version
172*700637cbSDimitry Andric // uint64_t RecordTableOffset = RecordTableGenerator.Emit
173*700637cbSDimitry Andric // uint64_t FramePayloadOffset = Offset for the frame payload
174*700637cbSDimitry Andric // uint64_t FrameTableOffset = FrameTableGenerator.Emit
175*700637cbSDimitry Andric // uint64_t CallStackPayloadOffset = Offset for the call stack payload (NEW V2)
176*700637cbSDimitry Andric // uint64_t CallStackTableOffset = CallStackTableGenerator.Emit (NEW in V2)
177*700637cbSDimitry Andric // uint64_t Num schema entries
178*700637cbSDimitry Andric // uint64_t Schema entry 0
179*700637cbSDimitry Andric // uint64_t Schema entry 1
180*700637cbSDimitry Andric // ....
181*700637cbSDimitry Andric // uint64_t Schema entry N - 1
182*700637cbSDimitry Andric // OnDiskChainedHashTable MemProfRecordData
183*700637cbSDimitry Andric // OnDiskChainedHashTable MemProfFrameData
184*700637cbSDimitry Andric // OnDiskChainedHashTable MemProfCallStackData (NEW in V2)
writeMemProfV2(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,bool MemProfFullSchema)185*700637cbSDimitry Andric static Error writeMemProfV2(ProfOStream &OS,
186*700637cbSDimitry Andric                             memprof::IndexedMemProfData &MemProfData,
187*700637cbSDimitry Andric                             bool MemProfFullSchema) {
188*700637cbSDimitry Andric   OS.write(memprof::Version2);
189*700637cbSDimitry Andric   uint64_t HeaderUpdatePos = OS.tell();
190*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record table offset.
191*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame payload offset.
192*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame table offset.
193*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
194*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof call stack table offset.
195*700637cbSDimitry Andric 
196*700637cbSDimitry Andric   auto Schema = memprof::getHotColdSchema();
197*700637cbSDimitry Andric   if (MemProfFullSchema)
198*700637cbSDimitry Andric     Schema = memprof::getFullSchema();
199*700637cbSDimitry Andric   writeMemProfSchema(OS, Schema);
200*700637cbSDimitry Andric 
201*700637cbSDimitry Andric   uint64_t RecordTableOffset =
202*700637cbSDimitry Andric       writeMemProfRecords(OS, MemProfData.Records, &Schema, memprof::Version2);
203*700637cbSDimitry Andric 
204*700637cbSDimitry Andric   uint64_t FramePayloadOffset = OS.tell();
205*700637cbSDimitry Andric   uint64_t FrameTableOffset = writeMemProfFrames(OS, MemProfData.Frames);
206*700637cbSDimitry Andric 
207*700637cbSDimitry Andric   uint64_t CallStackPayloadOffset = OS.tell();
208*700637cbSDimitry Andric   uint64_t CallStackTableOffset =
209*700637cbSDimitry Andric       writeMemProfCallStacks(OS, MemProfData.CallStacks);
210*700637cbSDimitry Andric 
211*700637cbSDimitry Andric   uint64_t Header[] = {
212*700637cbSDimitry Andric       RecordTableOffset,      FramePayloadOffset,   FrameTableOffset,
213*700637cbSDimitry Andric       CallStackPayloadOffset, CallStackTableOffset,
214*700637cbSDimitry Andric   };
215*700637cbSDimitry Andric   OS.patch({{HeaderUpdatePos, Header}});
216*700637cbSDimitry Andric 
217*700637cbSDimitry Andric   return Error::success();
218*700637cbSDimitry Andric }
219*700637cbSDimitry Andric 
writeMemProfRadixTreeBased(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,memprof::IndexedVersion Version,bool MemProfFullSchema,std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData=nullptr,std::unique_ptr<memprof::MemProfSummary> MemProfSum=nullptr)220*700637cbSDimitry Andric static Error writeMemProfRadixTreeBased(
221*700637cbSDimitry Andric     ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
222*700637cbSDimitry Andric     memprof::IndexedVersion Version, bool MemProfFullSchema,
223*700637cbSDimitry Andric     std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData =
224*700637cbSDimitry Andric         nullptr,
225*700637cbSDimitry Andric     std::unique_ptr<memprof::MemProfSummary> MemProfSum = nullptr) {
226*700637cbSDimitry Andric   assert((Version == memprof::Version3 || Version == memprof::Version4) &&
227*700637cbSDimitry Andric          "Unsupported version for radix tree format");
228*700637cbSDimitry Andric 
229*700637cbSDimitry Andric   OS.write(Version); // Write the specific version (V3 or V4)
230*700637cbSDimitry Andric   uint64_t HeaderUpdatePos = OS.tell();
231*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
232*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record payload offset.
233*700637cbSDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record table offset.
234*700637cbSDimitry Andric   if (Version >= memprof::Version4) {
235*700637cbSDimitry Andric     OS.write(0ULL); // Reserve space for the data access profile offset.
236*700637cbSDimitry Andric 
237*700637cbSDimitry Andric     MemProfSum->write(OS);
238*700637cbSDimitry Andric   }
239*700637cbSDimitry Andric 
240*700637cbSDimitry Andric   auto Schema = memprof::getHotColdSchema();
241*700637cbSDimitry Andric   if (MemProfFullSchema)
242*700637cbSDimitry Andric     Schema = memprof::getFullSchema();
243*700637cbSDimitry Andric   writeMemProfSchema(OS, Schema);
244*700637cbSDimitry Andric 
245*700637cbSDimitry Andric   llvm::DenseMap<memprof::FrameId, memprof::FrameStat> FrameHistogram =
246*700637cbSDimitry Andric       memprof::computeFrameHistogram(MemProfData.CallStacks);
247*700637cbSDimitry Andric   assert(MemProfData.Frames.size() == FrameHistogram.size());
248*700637cbSDimitry Andric 
249*700637cbSDimitry Andric   llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId> MemProfFrameIndexes =
250*700637cbSDimitry Andric       writeMemProfFrameArray(OS, MemProfData.Frames, FrameHistogram);
251*700637cbSDimitry Andric 
252*700637cbSDimitry Andric   uint64_t CallStackPayloadOffset = OS.tell();
253*700637cbSDimitry Andric   // The number of elements in the call stack array.
254*700637cbSDimitry Andric   unsigned NumElements = 0;
255*700637cbSDimitry Andric   llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
256*700637cbSDimitry Andric       MemProfCallStackIndexes =
257*700637cbSDimitry Andric           writeMemProfCallStackArray(OS, MemProfData.CallStacks,
258*700637cbSDimitry Andric                                      MemProfFrameIndexes, FrameHistogram,
259*700637cbSDimitry Andric                                      NumElements);
260*700637cbSDimitry Andric 
261*700637cbSDimitry Andric   uint64_t RecordPayloadOffset = OS.tell();
262*700637cbSDimitry Andric   uint64_t RecordTableOffset = writeMemProfRecords(
263*700637cbSDimitry Andric       OS, MemProfData.Records, &Schema, Version, &MemProfCallStackIndexes);
264*700637cbSDimitry Andric 
265*700637cbSDimitry Andric   uint64_t DataAccessProfOffset = 0;
266*700637cbSDimitry Andric   if (DataAccessProfileData != nullptr) {
267*700637cbSDimitry Andric     assert(Version >= memprof::Version4 &&
268*700637cbSDimitry Andric            "Data access profiles are added starting from v4");
269*700637cbSDimitry Andric     DataAccessProfOffset = OS.tell();
270*700637cbSDimitry Andric     if (Error E = DataAccessProfileData->serialize(OS))
271*700637cbSDimitry Andric       return E;
272*700637cbSDimitry Andric   }
273*700637cbSDimitry Andric 
274*700637cbSDimitry Andric   // Verify that the computation for the number of elements in the call stack
275*700637cbSDimitry Andric   // array works.
276*700637cbSDimitry Andric   assert(CallStackPayloadOffset +
277*700637cbSDimitry Andric              NumElements * sizeof(memprof::LinearFrameId) ==
278*700637cbSDimitry Andric          RecordPayloadOffset);
279*700637cbSDimitry Andric 
280*700637cbSDimitry Andric   SmallVector<uint64_t, 4> Header = {
281*700637cbSDimitry Andric       CallStackPayloadOffset,
282*700637cbSDimitry Andric       RecordPayloadOffset,
283*700637cbSDimitry Andric       RecordTableOffset,
284*700637cbSDimitry Andric   };
285*700637cbSDimitry Andric   if (Version >= memprof::Version4)
286*700637cbSDimitry Andric     Header.push_back(DataAccessProfOffset);
287*700637cbSDimitry Andric 
288*700637cbSDimitry Andric   OS.patch({{HeaderUpdatePos, Header}});
289*700637cbSDimitry Andric 
290*700637cbSDimitry Andric   return Error::success();
291*700637cbSDimitry Andric }
292*700637cbSDimitry Andric 
293*700637cbSDimitry Andric // Write out MemProf Version3
writeMemProfV3(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,bool MemProfFullSchema)294*700637cbSDimitry Andric static Error writeMemProfV3(ProfOStream &OS,
295*700637cbSDimitry Andric                             memprof::IndexedMemProfData &MemProfData,
296*700637cbSDimitry Andric                             bool MemProfFullSchema) {
297*700637cbSDimitry Andric   return writeMemProfRadixTreeBased(OS, MemProfData, memprof::Version3,
298*700637cbSDimitry Andric                                     MemProfFullSchema);
299*700637cbSDimitry Andric }
300*700637cbSDimitry Andric 
301*700637cbSDimitry Andric // Write out MemProf Version4
writeMemProfV4(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,bool MemProfFullSchema,std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,std::unique_ptr<memprof::MemProfSummary> MemProfSum)302*700637cbSDimitry Andric static Error writeMemProfV4(
303*700637cbSDimitry Andric     ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
304*700637cbSDimitry Andric     bool MemProfFullSchema,
305*700637cbSDimitry Andric     std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,
306*700637cbSDimitry Andric     std::unique_ptr<memprof::MemProfSummary> MemProfSum) {
307*700637cbSDimitry Andric   return writeMemProfRadixTreeBased(
308*700637cbSDimitry Andric       OS, MemProfData, memprof::Version4, MemProfFullSchema,
309*700637cbSDimitry Andric       std::move(DataAccessProfileData), std::move(MemProfSum));
310*700637cbSDimitry Andric }
311*700637cbSDimitry Andric 
312*700637cbSDimitry Andric // Write out the MemProf data in a requested version.
writeMemProf(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,memprof::IndexedVersion MemProfVersionRequested,bool MemProfFullSchema,std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,std::unique_ptr<memprof::MemProfSummary> MemProfSum)313*700637cbSDimitry Andric Error writeMemProf(
314*700637cbSDimitry Andric     ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
315*700637cbSDimitry Andric     memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema,
316*700637cbSDimitry Andric     std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,
317*700637cbSDimitry Andric     std::unique_ptr<memprof::MemProfSummary> MemProfSum) {
318*700637cbSDimitry Andric   switch (MemProfVersionRequested) {
319*700637cbSDimitry Andric   case memprof::Version2:
320*700637cbSDimitry Andric     return writeMemProfV2(OS, MemProfData, MemProfFullSchema);
321*700637cbSDimitry Andric   case memprof::Version3:
322*700637cbSDimitry Andric     return writeMemProfV3(OS, MemProfData, MemProfFullSchema);
323*700637cbSDimitry Andric   case memprof::Version4:
324*700637cbSDimitry Andric     return writeMemProfV4(OS, MemProfData, MemProfFullSchema,
325*700637cbSDimitry Andric                           std::move(DataAccessProfileData),
326*700637cbSDimitry Andric                           std::move(MemProfSum));
327*700637cbSDimitry Andric   }
328*700637cbSDimitry Andric 
329*700637cbSDimitry Andric   return make_error<InstrProfError>(
330*700637cbSDimitry Andric       instrprof_error::unsupported_version,
331*700637cbSDimitry Andric       formatv("MemProf version {} not supported; "
332*700637cbSDimitry Andric               "requires version between {} and {}, inclusive",
333*700637cbSDimitry Andric               MemProfVersionRequested, memprof::MinimumSupportedVersion,
334*700637cbSDimitry Andric               memprof::MaximumSupportedVersion));
335*700637cbSDimitry Andric }
336*700637cbSDimitry Andric 
deserializeV2(const unsigned char * Start,const unsigned char * Ptr)337*700637cbSDimitry Andric Error IndexedMemProfReader::deserializeV2(const unsigned char *Start,
338*700637cbSDimitry Andric                                           const unsigned char *Ptr) {
339*700637cbSDimitry Andric   // The value returned from RecordTableGenerator.Emit.
340*700637cbSDimitry Andric   const uint64_t RecordTableOffset =
341*700637cbSDimitry Andric       support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
342*700637cbSDimitry Andric   // The offset in the stream right before invoking
343*700637cbSDimitry Andric   // FrameTableGenerator.Emit.
344*700637cbSDimitry Andric   const uint64_t FramePayloadOffset =
345*700637cbSDimitry Andric       support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
346*700637cbSDimitry Andric   // The value returned from FrameTableGenerator.Emit.
347*700637cbSDimitry Andric   const uint64_t FrameTableOffset =
348*700637cbSDimitry Andric       support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
349*700637cbSDimitry Andric 
350*700637cbSDimitry Andric   // The offset in the stream right before invoking
351*700637cbSDimitry Andric   // CallStackTableGenerator.Emit.
352*700637cbSDimitry Andric   uint64_t CallStackPayloadOffset = 0;
353*700637cbSDimitry Andric   // The value returned from CallStackTableGenerator.Emit.
354*700637cbSDimitry Andric   uint64_t CallStackTableOffset = 0;
355*700637cbSDimitry Andric   if (Version >= memprof::Version2) {
356*700637cbSDimitry Andric     CallStackPayloadOffset =
357*700637cbSDimitry Andric         support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
358*700637cbSDimitry Andric     CallStackTableOffset =
359*700637cbSDimitry Andric         support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
360*700637cbSDimitry Andric   }
361*700637cbSDimitry Andric 
362*700637cbSDimitry Andric   // Read the schema.
363*700637cbSDimitry Andric   auto SchemaOr = memprof::readMemProfSchema(Ptr);
364*700637cbSDimitry Andric   if (!SchemaOr)
365*700637cbSDimitry Andric     return SchemaOr.takeError();
366*700637cbSDimitry Andric   Schema = SchemaOr.get();
367*700637cbSDimitry Andric 
368*700637cbSDimitry Andric   // Now initialize the table reader with a pointer into data buffer.
369*700637cbSDimitry Andric   MemProfRecordTable.reset(MemProfRecordHashTable::Create(
370*700637cbSDimitry Andric       /*Buckets=*/Start + RecordTableOffset,
371*700637cbSDimitry Andric       /*Payload=*/Ptr,
372*700637cbSDimitry Andric       /*Base=*/Start, memprof::RecordLookupTrait(Version, Schema)));
373*700637cbSDimitry Andric 
374*700637cbSDimitry Andric   // Initialize the frame table reader with the payload and bucket offsets.
375*700637cbSDimitry Andric   MemProfFrameTable.reset(MemProfFrameHashTable::Create(
376*700637cbSDimitry Andric       /*Buckets=*/Start + FrameTableOffset,
377*700637cbSDimitry Andric       /*Payload=*/Start + FramePayloadOffset,
378*700637cbSDimitry Andric       /*Base=*/Start));
379*700637cbSDimitry Andric 
380*700637cbSDimitry Andric   if (Version >= memprof::Version2)
381*700637cbSDimitry Andric     MemProfCallStackTable.reset(MemProfCallStackHashTable::Create(
382*700637cbSDimitry Andric         /*Buckets=*/Start + CallStackTableOffset,
383*700637cbSDimitry Andric         /*Payload=*/Start + CallStackPayloadOffset,
384*700637cbSDimitry Andric         /*Base=*/Start));
385*700637cbSDimitry Andric 
386*700637cbSDimitry Andric   return Error::success();
387*700637cbSDimitry Andric }
388*700637cbSDimitry Andric 
deserializeRadixTreeBased(const unsigned char * Start,const unsigned char * Ptr,memprof::IndexedVersion Version)389*700637cbSDimitry Andric Error IndexedMemProfReader::deserializeRadixTreeBased(
390*700637cbSDimitry Andric     const unsigned char *Start, const unsigned char *Ptr,
391*700637cbSDimitry Andric     memprof::IndexedVersion Version) {
392*700637cbSDimitry Andric   assert((Version == memprof::Version3 || Version == memprof::Version4) &&
393*700637cbSDimitry Andric          "Unsupported version for radix tree format");
394*700637cbSDimitry Andric   // The offset in the stream right before invoking
395*700637cbSDimitry Andric   // CallStackTableGenerator.Emit.
396*700637cbSDimitry Andric   const uint64_t CallStackPayloadOffset =
397*700637cbSDimitry Andric       support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
398*700637cbSDimitry Andric   // The offset in the stream right before invoking RecordTableGenerator.Emit.
399*700637cbSDimitry Andric   const uint64_t RecordPayloadOffset =
400*700637cbSDimitry Andric       support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
401*700637cbSDimitry Andric   // The value returned from RecordTableGenerator.Emit.
402*700637cbSDimitry Andric   const uint64_t RecordTableOffset =
403*700637cbSDimitry Andric       support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
404*700637cbSDimitry Andric 
405*700637cbSDimitry Andric   uint64_t DataAccessProfOffset = 0;
406*700637cbSDimitry Andric   if (Version >= memprof::Version4) {
407*700637cbSDimitry Andric     DataAccessProfOffset =
408*700637cbSDimitry Andric         support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
409*700637cbSDimitry Andric     MemProfSum = memprof::MemProfSummary::deserialize(Ptr);
410*700637cbSDimitry Andric   }
411*700637cbSDimitry Andric 
412*700637cbSDimitry Andric   // Read the schema.
413*700637cbSDimitry Andric   auto SchemaOr = memprof::readMemProfSchema(Ptr);
414*700637cbSDimitry Andric   if (!SchemaOr)
415*700637cbSDimitry Andric     return SchemaOr.takeError();
416*700637cbSDimitry Andric   Schema = SchemaOr.get();
417*700637cbSDimitry Andric 
418*700637cbSDimitry Andric   FrameBase = Ptr;
419*700637cbSDimitry Andric   CallStackBase = Start + CallStackPayloadOffset;
420*700637cbSDimitry Andric 
421*700637cbSDimitry Andric   // Compute the number of elements in the radix tree array.  Since we use this
422*700637cbSDimitry Andric   // to reserve enough bits in a BitVector, it's totally OK if we overestimate
423*700637cbSDimitry Andric   // this number a little bit because of padding just before the next section.
424*700637cbSDimitry Andric   RadixTreeSize = (RecordPayloadOffset - CallStackPayloadOffset) /
425*700637cbSDimitry Andric                   sizeof(memprof::LinearFrameId);
426*700637cbSDimitry Andric 
427*700637cbSDimitry Andric   // Now initialize the table reader with a pointer into data buffer.
428*700637cbSDimitry Andric   MemProfRecordTable.reset(MemProfRecordHashTable::Create(
429*700637cbSDimitry Andric       /*Buckets=*/Start + RecordTableOffset,
430*700637cbSDimitry Andric       /*Payload=*/Start + RecordPayloadOffset,
431*700637cbSDimitry Andric       /*Base=*/Start, memprof::RecordLookupTrait(Version, Schema)));
432*700637cbSDimitry Andric 
433*700637cbSDimitry Andric   assert((!DataAccessProfOffset || DataAccessProfOffset > RecordTableOffset) &&
434*700637cbSDimitry Andric          "Data access profile is either empty or after the record table");
435*700637cbSDimitry Andric   if (DataAccessProfOffset > RecordTableOffset) {
436*700637cbSDimitry Andric     DataAccessProfileData = std::make_unique<memprof::DataAccessProfData>();
437*700637cbSDimitry Andric     const unsigned char *DAPPtr = Start + DataAccessProfOffset;
438*700637cbSDimitry Andric     if (Error E = DataAccessProfileData->deserialize(DAPPtr))
439*700637cbSDimitry Andric       return E;
440*700637cbSDimitry Andric   }
441*700637cbSDimitry Andric 
442*700637cbSDimitry Andric   return Error::success();
443*700637cbSDimitry Andric }
444*700637cbSDimitry Andric 
deserialize(const unsigned char * Start,uint64_t MemProfOffset)445*700637cbSDimitry Andric Error IndexedMemProfReader::deserialize(const unsigned char *Start,
446*700637cbSDimitry Andric                                         uint64_t MemProfOffset) {
447*700637cbSDimitry Andric   const unsigned char *Ptr = Start + MemProfOffset;
448*700637cbSDimitry Andric 
449*700637cbSDimitry Andric   // Read the MemProf version number.
450*700637cbSDimitry Andric   const uint64_t FirstWord =
451*700637cbSDimitry Andric       support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
452*700637cbSDimitry Andric 
453*700637cbSDimitry Andric   // Check if the version is supported
454*700637cbSDimitry Andric   if (FirstWord >= memprof::MinimumSupportedVersion &&
455*700637cbSDimitry Andric       FirstWord <= memprof::MaximumSupportedVersion) {
456*700637cbSDimitry Andric     // Everything is good. We can proceed to deserialize the rest.
457*700637cbSDimitry Andric     Version = static_cast<memprof::IndexedVersion>(FirstWord);
458*700637cbSDimitry Andric   } else {
459*700637cbSDimitry Andric     return make_error<InstrProfError>(
460*700637cbSDimitry Andric         instrprof_error::unsupported_version,
461*700637cbSDimitry Andric         formatv("MemProf version {} not supported; "
462*700637cbSDimitry Andric                 "requires version between {} and {}, inclusive",
463*700637cbSDimitry Andric                 FirstWord, memprof::MinimumSupportedVersion,
464*700637cbSDimitry Andric                 memprof::MaximumSupportedVersion));
465*700637cbSDimitry Andric   }
466*700637cbSDimitry Andric 
467*700637cbSDimitry Andric   switch (Version) {
468*700637cbSDimitry Andric   case memprof::Version2:
469*700637cbSDimitry Andric     if (Error E = deserializeV2(Start, Ptr))
470*700637cbSDimitry Andric       return E;
471*700637cbSDimitry Andric     break;
472*700637cbSDimitry Andric   case memprof::Version3:
473*700637cbSDimitry Andric   case memprof::Version4:
474*700637cbSDimitry Andric     // V3 and V4 share the same high-level structure (radix tree, linear IDs).
475*700637cbSDimitry Andric     if (Error E = deserializeRadixTreeBased(Start, Ptr, Version))
476*700637cbSDimitry Andric       return E;
477*700637cbSDimitry Andric     break;
478*700637cbSDimitry Andric   }
479*700637cbSDimitry Andric 
480*700637cbSDimitry Andric   return Error::success();
481*700637cbSDimitry Andric }
482*700637cbSDimitry Andric } // namespace llvm
483