55#include < string>
66#include < vector>
77
8+ #include " llvm/ADT/DenseMap.h"
89#include " llvm/ADT/SmallVector.h"
910#include " llvm/ProfileData/MemProfData.inc"
1011#include " llvm/ProfileData/ProfileCommon.h"
@@ -134,25 +135,65 @@ struct PortableMemInfoBlock {
134135};
135136
136137struct 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.
139146 uint32_t LineOffset;
147+ // The source column number of the call to help distinguish multiple calls
148+ // on the same line.
140149 uint32_t Column;
150+ // Whether the current frame is inlined.
141151 bool IsInlineFrame;
142152
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) {}
147155
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.
148182 std::vector<Frame> CallStack;
183+ // The statistics obtained from the runtime for the allocation.
149184 PortableMemInfoBlock Info;
150185
151186 void clear () {
152187 CallStack.clear ();
153188 Info.clear ();
154189 }
155190
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+
156197 // Prints out the contents of the memprof record in YAML.
157198 void print (llvm::raw_ostream &OS) const {
158199 OS << " Callstack:\n " ;
@@ -168,6 +209,138 @@ struct MemProfRecord {
168209
169210 Info.printYAML (OS);
170211 }
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+ }
171344};
172345
173346} // namespace memprof
0 commit comments