LLVM 24.0.0git
IndexedMemProfData.cpp
Go to the documentation of this file.
1//===- IndexedMemProfData.h - MemProf format support ------------*- C++ -*-===//
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// MemProf data is serialized in writeMemProf provided in this file.
10//
11//===----------------------------------------------------------------------===//
12
21
22namespace llvm {
23
24// Serialize Schema.
26 const memprof::MemProfSchema &Schema) {
27 OS.write(static_cast<uint64_t>(Schema.size()));
28 for (const auto Id : Schema)
29 OS.write(static_cast<uint64_t>(Id));
30}
31
32// Serialize MemProfRecordData. Return RecordTableOffset.
34 ProfOStream &OS,
36 &MemProfRecordData,
39 *MemProfCallStackIndexes = nullptr) {
40 memprof::RecordWriterTrait RecordWriter(Schema, Version,
41 MemProfCallStackIndexes);
43 RecordTableGenerator;
44 for (auto &[GUID, Record] : MemProfRecordData) {
45 // Insert the key (func hash) and value (memprof record).
46 RecordTableGenerator.insert(GUID, Record, RecordWriter);
47 }
48 // Release the memory of this MapVector as it is no longer needed.
49 MemProfRecordData.clear();
50
51 // The call to Emit invokes RecordWriterTrait::EmitData which destructs
52 // the memprof record copies owned by the RecordTableGenerator. This works
53 // because the RecordTableGenerator is not used after this point.
54 return RecordTableGenerator.Emit(OS.OS, RecordWriter);
55}
56
57// Serialize MemProfFrameData. Return the mapping from FrameIds to their
58// indexes within the frame array.
61 ProfOStream &OS,
64 // Mappings from FrameIds to array indexes.
66
67 // Compute the order in which we serialize Frames. The order does not matter
68 // in terms of correctness, but we still compute it for deserialization
69 // performance. Specifically, if we serialize frequently used Frames one
70 // after another, we have better cache utilization. For two Frames that
71 // appear equally frequently, we break a tie by serializing the one that tends
72 // to appear earlier in call stacks. We implement the tie-breaking mechanism
73 // by computing the sum of indexes within call stacks for each Frame. If we
74 // still have a tie, then we just resort to compare two FrameIds, which is
75 // just for stability of output.
76 std::vector<std::pair<memprof::FrameId, const memprof::Frame *>> FrameIdOrder;
77 FrameIdOrder.reserve(MemProfFrameData.size());
78 for (const auto &[Id, Frame] : MemProfFrameData)
79 FrameIdOrder.emplace_back(Id, &Frame);
80 assert(MemProfFrameData.size() == FrameIdOrder.size());
81 llvm::sort(FrameIdOrder,
82 [&](const std::pair<memprof::FrameId, const memprof::Frame *> &L,
83 const std::pair<memprof::FrameId, const memprof::Frame *> &R) {
84 const auto &SL = FrameHistogram[L.first];
85 const auto &SR = FrameHistogram[R.first];
86 // Popular FrameIds should come first.
87 if (SL.Count != SR.Count)
88 return SL.Count > SR.Count;
89 // If they are equally popular, then the one that tends to appear
90 // earlier in call stacks should come first.
91 if (SL.PositionSum != SR.PositionSum)
92 return SL.PositionSum < SR.PositionSum;
93 // Compare their FrameIds for sort stability.
94 return L.first < R.first;
95 });
96
97 // Serialize all frames while creating mappings from linear IDs to FrameIds.
98 uint64_t Index = 0;
99 MemProfFrameIndexes.reserve(FrameIdOrder.size());
100 for (const auto &[Id, F] : FrameIdOrder) {
101 F->serialize(OS.OS);
102 MemProfFrameIndexes.insert({Id, Index});
103 ++Index;
104 }
105 assert(MemProfFrameData.size() == Index);
106 assert(MemProfFrameData.size() == MemProfFrameIndexes.size());
107
108 // Release the memory of this MapVector as it is no longer needed.
109 MemProfFrameData.clear();
110
111 return MemProfFrameIndexes;
112}
113
116 ProfOStream &OS,
118 &MemProfCallStackData,
120 &MemProfFrameIndexes,
122 unsigned &NumElements) {
124 MemProfCallStackIndexes;
125
127 Builder.build(std::move(MemProfCallStackData), &MemProfFrameIndexes,
128 FrameHistogram);
129 for (auto I : Builder.getRadixArray())
130 OS.write32(I);
131 NumElements = Builder.getRadixArray().size();
132 MemProfCallStackIndexes = Builder.takeCallStackPos();
133
134 // Release the memory of this vector as it is no longer needed.
135 MemProfCallStackData.clear();
136
137 return MemProfCallStackIndexes;
138}
139
142 memprof::IndexedVersion Version, bool MemProfFullSchema,
143 std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData =
144 nullptr,
145 std::unique_ptr<memprof::MemProfSummary> MemProfSum = nullptr) {
147 "Unsupported version for radix tree format");
148
149 OS.write(Version); // Write the specific version (V3 or V4)
150 uint64_t HeaderUpdatePos = OS.tell();
151 OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
152 OS.write(0ULL); // Reserve space for the memprof record payload offset.
153 OS.write(0ULL); // Reserve space for the memprof record table offset.
154 if (Version >= memprof::Version4) {
155 OS.write(0ULL); // Reserve space for the data access profile offset.
156
157 MemProfSum->write(OS);
158 }
159
160 auto Schema = memprof::getHotColdSchema();
161 if (MemProfFullSchema)
162 Schema = memprof::getFullSchema();
163 writeMemProfSchema(OS, Schema);
164
167 assert(MemProfData.Frames.size() == FrameHistogram.size());
168
170 writeMemProfFrameArray(OS, MemProfData.Frames, FrameHistogram);
171
172 uint64_t CallStackPayloadOffset = OS.tell();
173 // The number of elements in the call stack array.
174 unsigned NumElements = 0;
176 MemProfCallStackIndexes =
178 MemProfFrameIndexes, FrameHistogram,
179 NumElements);
180
181 uint64_t RecordPayloadOffset = OS.tell();
182 uint64_t RecordTableOffset = writeMemProfRecords(
183 OS, MemProfData.Records, &Schema, Version, &MemProfCallStackIndexes);
184
185 uint64_t DataAccessProfOffset = 0;
186 if (DataAccessProfileData != nullptr) {
188 "Data access profiles are added starting from v4");
189 DataAccessProfOffset = OS.tell();
190 if (Error E = DataAccessProfileData->serialize(OS))
191 return E;
192 }
193
194 // Verify that the computation for the number of elements in the call stack
195 // array works.
196 assert(CallStackPayloadOffset +
197 NumElements * sizeof(memprof::LinearFrameId) ==
198 RecordPayloadOffset);
199
200 SmallVector<uint64_t, 4> Header = {
201 CallStackPayloadOffset,
202 RecordPayloadOffset,
203 RecordTableOffset,
204 };
206 Header.push_back(DataAccessProfOffset);
207
208 OS.patch({{HeaderUpdatePos, Header}});
209
210 return Error::success();
211}
212
213// Write out MemProf Version3
215 memprof::IndexedMemProfData &MemProfData,
216 bool MemProfFullSchema) {
217 return writeMemProfRadixTreeBased(OS, MemProfData, memprof::Version3,
218 MemProfFullSchema);
219}
220
221// Write out MemProf Version4
224 bool MemProfFullSchema,
225 std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,
226 std::unique_ptr<memprof::MemProfSummary> MemProfSum) {
228 OS, MemProfData, memprof::Version4, MemProfFullSchema,
229 std::move(DataAccessProfileData), std::move(MemProfSum));
230}
231
232// Write out the MemProf data in a requested version.
235 memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema,
236 std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,
237 std::unique_ptr<memprof::MemProfSummary> MemProfSum) {
238 switch (MemProfVersionRequested) {
240 return writeMemProfV3(OS, MemProfData, MemProfFullSchema);
242 return writeMemProfV4(OS, MemProfData, MemProfFullSchema,
243 std::move(DataAccessProfileData),
244 std::move(MemProfSum));
245 }
246
249 formatv("MemProf version {} not supported; "
250 "requires version between {} and {}, inclusive",
251 MemProfVersionRequested, memprof::MinimumSupportedVersion,
253}
254
255Error IndexedMemProfReader::deserializeRadixTreeBased(
256 const unsigned char *Start, const unsigned char *Ptr,
257 memprof::IndexedVersion Version) {
258 assert((Version == memprof::Version3 || Version == memprof::Version4) &&
259 "Unsupported version for radix tree format");
260 // The offset in the stream right before invoking
261 // CallStackTableGenerator.Emit.
262 const uint64_t CallStackPayloadOffset =
264 // The offset in the stream right before invoking RecordTableGenerator.Emit.
265 const uint64_t RecordPayloadOffset =
267 // The value returned from RecordTableGenerator.Emit.
268 const uint64_t RecordTableOffset =
270
271 uint64_t DataAccessProfOffset = 0;
272 if (Version >= memprof::Version4) {
273 DataAccessProfOffset =
275 MemProfSum = memprof::MemProfSummary::deserialize(Ptr);
276
277 if (DataAccessProfOffset > RecordTableOffset) {
278 DataAccessProfileData = std::make_unique<memprof::DataAccessProfData>();
279 const unsigned char *DAPPtr = Start + DataAccessProfOffset;
280 if (Error E = DataAccessProfileData->deserialize(DAPPtr))
281 return E;
282 MemProfSum->buildDataAccessSummary(*DataAccessProfileData);
283 }
284 }
285
286 assert((!DataAccessProfOffset || DataAccessProfOffset > RecordTableOffset) &&
287 "Data access profile is either empty or after the record table");
288
289 // Read the schema.
290 auto SchemaOr = memprof::readMemProfSchema(Ptr);
291 if (!SchemaOr)
292 return SchemaOr.takeError();
293 Schema = SchemaOr.get();
294
295 FrameBase = Ptr;
296 CallStackBase = Start + CallStackPayloadOffset;
297
298 // Compute the number of elements in the radix tree array. Since we use this
299 // to reserve enough bits in a BitVector, it's totally OK if we overestimate
300 // this number a little bit because of padding just before the next section.
301 RadixTreeSize = (RecordPayloadOffset - CallStackPayloadOffset) /
303
304 // Now initialize the table reader with a pointer into data buffer.
305 MemProfRecordTable.reset(MemProfRecordHashTable::Create(
306 /*Buckets=*/Start + RecordTableOffset,
307 /*Payload=*/Start + RecordPayloadOffset,
308 /*Base=*/Start, memprof::RecordLookupTrait(Version, Schema)));
309
310 return Error::success();
311}
312
313Error IndexedMemProfReader::deserialize(const unsigned char *Start,
314 uint64_t MemProfOffset) {
315 const unsigned char *Ptr = Start + MemProfOffset;
316
317 // Read the MemProf version number.
318 const uint64_t FirstWord =
320
321 // Check if the version is supported
322 if (FirstWord >= memprof::MinimumSupportedVersion &&
324 // Everything is good. We can proceed to deserialize the rest.
325 Version = static_cast<memprof::IndexedVersion>(FirstWord);
326 } else {
329 formatv("MemProf version {} not supported; "
330 "requires version between {} and {}, inclusive",
333 }
334
335 switch (Version) {
338 // V3 and V4 share the same high-level structure (radix tree, linear IDs).
339 if (Error E = deserializeRadixTreeBased(Start, Ptr, Version))
340 return E;
341 break;
342 }
343
344 return Error::success();
345}
346} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Defines facilities for reading and writing on-disk hash tables.
void reserve(size_type NumEntries)
Grow the densemap so that it can contain at least NumEntries items before resizing again.
Definition DenseMap.h:722
unsigned size() const
Definition DenseMap.h:718
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition DenseMap.h:828
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
LLVM_ABI Error deserialize(const unsigned char *Start, uint64_t MemProfOffset)
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
size_type size() const
Definition MapVector.h:58
Generates an on disk hash table.
offset_type Emit(raw_ostream &Out)
Emit the table to Out, which must not be at offset 0.
static OnDiskIterableChainedHashTable * Create(const unsigned char *Buckets, const unsigned char *const Payload, const unsigned char *const Base, const memprof::RecordLookupTrait &InfoObj=memprof::RecordLookupTrait())
raw_ostream & OS
Definition InstrProf.h:87
LLVM_ABI uint64_t tell() const
LLVM_ABI void patch(ArrayRef< PatchItem > P)
LLVM_ABI void write32(uint32_t V)
LLVM_ABI void write(uint64_t V)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
void build(llvm::MapVector< CallStackId, llvm::SmallVector< FrameIdTy > > &&MemProfCallStackData, const llvm::DenseMap< FrameIdTy, LinearFrameId > *MemProfFrameIndexes, llvm::DenseMap< FrameIdTy, FrameStat > &FrameHistogram)
static LLVM_ABI std::unique_ptr< MemProfSummary > deserialize(const unsigned char *&)
Read from indexed MemProf profile.
constexpr uint64_t MaximumSupportedVersion
Definition MemProf.h:51
LLVM_ABI MemProfSchema getHotColdSchema()
Definition MemProf.cpp:19
llvm::SmallVector< Meta, static_cast< int >(Meta::Size)> MemProfSchema
Definition MemProf.h:74
constexpr uint64_t MinimumSupportedVersion
Definition MemProf.h:50
LLVM_ABI MemProfSchema getFullSchema()
Definition MemProf.cpp:11
LLVM_ABI Expected< MemProfSchema > readMemProfSchema(const unsigned char *&Buffer)
Definition MemProf.cpp:278
uint32_t LinearFrameId
Definition MemProf.h:236
uint64_t CallStackId
Definition MemProf.h:353
llvm::DenseMap< FrameIdTy, FrameStat > computeFrameHistogram(llvm::MapVector< CallStackId, llvm::SmallVector< FrameIdTy > > &MemProfCallStackData)
value_type readNext(const CharT *&memory, endianness endian)
Read a value of a particular endianness from a buffer, and increment the buffer past that value.
Definition Endian.h:67
This is an optimization pass for GlobalISel generic memory operations.
static void writeMemProfSchema(ProfOStream &OS, const memprof::MemProfSchema &Schema)
static Error writeMemProfV3(ProfOStream &OS, memprof::IndexedMemProfData &MemProfData, bool MemProfFullSchema)
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1652
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)
static llvm::DenseMap< memprof::FrameId, memprof::LinearFrameId > writeMemProfFrameArray(ProfOStream &OS, llvm::MapVector< memprof::FrameId, memprof::Frame > &MemProfFrameData, llvm::DenseMap< memprof::FrameId, memprof::FrameStat > &FrameHistogram)
static Error writeMemProfV4(ProfOStream &OS, memprof::IndexedMemProfData &MemProfData, bool MemProfFullSchema, std::unique_ptr< memprof::DataAccessProfData > DataAccessProfileData, std::unique_ptr< memprof::MemProfSummary > MemProfSum)
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
static Error 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)
static uint64_t writeMemProfRecords(ProfOStream &OS, llvm::MapVector< GlobalValue::GUID, memprof::IndexedMemProfRecord > &MemProfRecordData, memprof::MemProfSchema *Schema, memprof::IndexedVersion Version, llvm::DenseMap< memprof::CallStackId, memprof::LinearCallStackId > *MemProfCallStackIndexes=nullptr)
LLVM_ABI Error writeMemProf(ProfOStream &OS, memprof::IndexedMemProfData &MemProfData, memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema, std::unique_ptr< memprof::DataAccessProfData > DataAccessProfileData, std::unique_ptr< memprof::MemProfSummary > MemProfSum)
llvm::MapVector< CallStackId, llvm::SmallVector< FrameId > > CallStacks
llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord > Records
llvm::MapVector< FrameId, Frame > Frames