5
5
#include < string>
6
6
#include < vector>
7
7
8
+ #include " llvm/ADT/DenseMap.h"
8
9
#include " llvm/ADT/SmallVector.h"
9
10
#include " llvm/ProfileData/MemProfData.inc"
10
11
#include " llvm/ProfileData/ProfileCommon.h"
@@ -134,25 +135,65 @@ struct PortableMemInfoBlock {
134
135
};
135
136
136
137
struct MemProfRecord {
137
- struct Frame {
138
- std::string Function;
138
+ // Describes a call frame for a dynamic allocation context. The contents of
139
+ // the frame are populated by symbolizing the stack depot call frame from the
140
+ // compiler runtime.
141
+ PACKED (struct Frame {
142
+ // A uuid (uint64_t) identifying the function. It is obtained by
143
+ // llvm::md5(FunctionName) which returns the lower 64 bits.
144
+ GlobalValue::GUID Function;
145
+ // The source line offset of the call from the beginning of parent function.
139
146
uint32_t LineOffset;
147
+ // The source column number of the call to help distinguish multiple calls
148
+ // on the same line.
140
149
uint32_t Column;
150
+ // Whether the current frame is inlined.
141
151
bool IsInlineFrame;
142
152
143
- Frame (std::string Str, uint32_t Off, uint32_t Col, bool Inline)
144
- : Function(std::move(Str)), LineOffset(Off), Column(Col),
145
- IsInlineFrame (Inline) {}
146
- };
153
+ Frame (uint64_t Hash, uint32_t Off, uint32_t Col, bool Inline)
154
+ : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {}
147
155
156
+ bool operator ==(const Frame &Other) const {
157
+ return Other.Function == Function && Other.LineOffset == LineOffset &&
158
+ Other.Column == Column && Other.IsInlineFrame == IsInlineFrame;
159
+ }
160
+
161
+ bool operator !=(const Frame &Other) const { return !operator ==(Other); }
162
+
163
+ // Write the contents of the frame to the ostream \p OS.
164
+ void write (raw_ostream & OS) const {
165
+ using namespace support ;
166
+
167
+ endian::Writer LE (OS, little);
168
+
169
+ // If the type of the GlobalValue::GUID changes, then we need to update
170
+ // the reader and the writer.
171
+ static_assert (std::is_same<GlobalValue::GUID, uint64_t >::value,
172
+ " Expect GUID to be uint64_t." );
173
+ LE.write <uint64_t >(Function);
174
+
175
+ LE.write <uint32_t >(LineOffset);
176
+ LE.write <uint32_t >(Column);
177
+ LE.write <bool >(IsInlineFrame);
178
+ }
179
+ });
180
+
181
+ // The dynamic calling context for the allocation.
148
182
std::vector<Frame> CallStack;
183
+ // The statistics obtained from the runtime for the allocation.
149
184
PortableMemInfoBlock Info;
150
185
151
186
void clear () {
152
187
CallStack.clear ();
153
188
Info.clear ();
154
189
}
155
190
191
+ size_t serializedSize () const {
192
+ return sizeof (uint64_t ) + // The number of frames to serialize.
193
+ sizeof (Frame) * CallStack.size () + // The contents of the frames.
194
+ PortableMemInfoBlock::serializedSize (); // The size of the payload.
195
+ }
196
+
156
197
// Prints out the contents of the memprof record in YAML.
157
198
void print (llvm::raw_ostream &OS) const {
158
199
OS << " Callstack:\n " ;
@@ -168,6 +209,138 @@ struct MemProfRecord {
168
209
169
210
Info.printYAML (OS);
170
211
}
212
+
213
+ bool operator ==(const MemProfRecord &Other) const {
214
+ if (Other.Info != Info)
215
+ return false ;
216
+
217
+ if (Other.CallStack .size () != CallStack.size ())
218
+ return false ;
219
+
220
+ for (size_t I = 0 ; I < Other.CallStack .size (); I++) {
221
+ if (Other.CallStack [I] != CallStack[I])
222
+ return false ;
223
+ }
224
+ return true ;
225
+ }
226
+ };
227
+
228
+ // Serializes the memprof records in \p Records to the ostream \p OS based on
229
+ // the schema provided in \p Schema.
230
+ void serializeRecords (const ArrayRef<MemProfRecord> Records,
231
+ const MemProfSchema &Schema, raw_ostream &OS);
232
+
233
+ // Deserializes memprof records from the Buffer
234
+ SmallVector<MemProfRecord, 4 > deserializeRecords (const MemProfSchema &Schema,
235
+ const unsigned char *Buffer);
236
+
237
+ // Reads a memprof schema from a buffer. All entries in the buffer are
238
+ // interpreted as uint64_t. The first entry in the buffer denotes the number of
239
+ // ids in the schema. Subsequent entries are integers which map to memprof::Meta
240
+ // enum class entries. After successfully reading the schema, the pointer is one
241
+ // byte past the schema contents.
242
+ Expected<MemProfSchema> readMemProfSchema (const unsigned char *&Buffer);
243
+
244
+ using FunctionMemProfMap =
245
+ DenseMap<uint64_t , SmallVector<memprof::MemProfRecord, 4 >>;
246
+
247
+ // / Trait for lookups into the on-disk hash table for memprof format in the
248
+ // / indexed profile.
249
+ class MemProfRecordLookupTrait {
250
+ public:
251
+ using data_type = ArrayRef<MemProfRecord>;
252
+ using internal_key_type = uint64_t ;
253
+ using external_key_type = uint64_t ;
254
+ using hash_value_type = uint64_t ;
255
+ using offset_type = uint64_t ;
256
+
257
+ MemProfRecordLookupTrait () = delete ;
258
+ MemProfRecordLookupTrait (const MemProfSchema &S) : Schema(S) {}
259
+
260
+ static bool EqualKey (uint64_t A, uint64_t B) { return A == B; }
261
+ static uint64_t GetInternalKey (uint64_t K) { return K; }
262
+ static uint64_t GetExternalKey (uint64_t K) { return K; }
263
+
264
+ hash_value_type ComputeHash (uint64_t K) { return K; }
265
+
266
+ static std::pair<offset_type, offset_type>
267
+ ReadKeyDataLength (const unsigned char *&D) {
268
+ using namespace support ;
269
+
270
+ offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D);
271
+ offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D);
272
+ return std::make_pair (KeyLen, DataLen);
273
+ }
274
+
275
+ uint64_t ReadKey (const unsigned char *D, offset_type /* Unused*/ ) {
276
+ using namespace support ;
277
+ return endian::readNext<external_key_type, little, unaligned>(D);
278
+ }
279
+
280
+ data_type ReadData (uint64_t K, const unsigned char *D,
281
+ offset_type /* Unused*/ ) {
282
+ Records = deserializeRecords (Schema, D);
283
+ return Records;
284
+ }
285
+
286
+ private:
287
+ // Holds the memprof schema used to deserialize records.
288
+ MemProfSchema Schema;
289
+ // Holds the records from one function deserialized from the indexed format.
290
+ llvm::SmallVector<MemProfRecord, 4 > Records;
291
+ };
292
+
293
+ class MemProfRecordWriterTrait {
294
+ public:
295
+ using key_type = uint64_t ;
296
+ using key_type_ref = uint64_t ;
297
+
298
+ using data_type = ArrayRef<MemProfRecord>;
299
+ using data_type_ref = ArrayRef<MemProfRecord>;
300
+
301
+ using hash_value_type = uint64_t ;
302
+ using offset_type = uint64_t ;
303
+
304
+ // Pointer to the memprof schema to use for the generator. Unlike the reader
305
+ // we must use a default constructor with no params for the writer trait so we
306
+ // have a public member which must be initialized by the user.
307
+ MemProfSchema *Schema = nullptr ;
308
+
309
+ MemProfRecordWriterTrait () = default ;
310
+
311
+ static hash_value_type ComputeHash (key_type_ref K) { return K; }
312
+
313
+ static std::pair<offset_type, offset_type>
314
+ EmitKeyDataLength (raw_ostream &Out, key_type_ref K, data_type_ref V) {
315
+ using namespace support ;
316
+
317
+ endian::Writer LE (Out, little);
318
+
319
+ offset_type N = sizeof (K);
320
+ LE.write <offset_type>(N);
321
+
322
+ offset_type M = 0 ;
323
+
324
+ M += sizeof (uint64_t );
325
+ for (const auto &Record : V) {
326
+ M += Record.serializedSize ();
327
+ }
328
+
329
+ LE.write <offset_type>(M);
330
+ return std::make_pair (N, M);
331
+ }
332
+
333
+ void EmitKey (raw_ostream &Out, key_type_ref K, offset_type /* Unused*/ ) {
334
+ using namespace support ;
335
+ endian::Writer LE (Out, little);
336
+ LE.write <uint64_t >(K);
337
+ }
338
+
339
+ void EmitData (raw_ostream &Out, key_type_ref /* Unused*/ , data_type_ref V,
340
+ offset_type /* Unused*/ ) {
341
+ assert (Schema != nullptr && " MemProf schema is not initialized!" );
342
+ serializeRecords (V, *Schema, Out);
343
+ }
171
344
};
172
345
173
346
} // namespace memprof
0 commit comments