Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 1 | #ifndef LLVM_PROFILEDATA_MEMPROF_H_ |
2 | #define LLVM_PROFILEDATA_MEMPROF_H_ |
||
3 | |||
4 | #include "llvm/ADT/DenseMap.h" |
||
5 | #include "llvm/ADT/STLFunctionalExtras.h" |
||
6 | #include "llvm/ADT/SmallVector.h" |
||
7 | #include "llvm/IR/GlobalValue.h" |
||
8 | #include "llvm/ProfileData/MemProfData.inc" |
||
9 | #include "llvm/Support/Endian.h" |
||
10 | #include "llvm/Support/EndianStream.h" |
||
11 | #include "llvm/Support/raw_ostream.h" |
||
12 | |||
13 | #include <cstdint> |
||
14 | #include <optional> |
||
15 | |||
16 | namespace llvm { |
||
17 | namespace memprof { |
||
18 | |||
19 | enum class Meta : uint64_t { |
||
20 | Start = 0, |
||
21 | #define MIBEntryDef(NameTag, Name, Type) NameTag, |
||
22 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
23 | #undef MIBEntryDef |
||
24 | Size |
||
25 | }; |
||
26 | |||
27 | using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>; |
||
28 | |||
29 | // Holds the actual MemInfoBlock data with all fields. Contents may be read or |
||
30 | // written partially by providing an appropriate schema to the serialize and |
||
31 | // deserialize methods. |
||
32 | struct PortableMemInfoBlock { |
||
33 | PortableMemInfoBlock() = default; |
||
34 | explicit PortableMemInfoBlock(const MemInfoBlock &Block) { |
||
35 | #define MIBEntryDef(NameTag, Name, Type) Name = Block.Name; |
||
36 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
37 | #undef MIBEntryDef |
||
38 | } |
||
39 | |||
40 | PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) { |
||
41 | deserialize(Schema, Ptr); |
||
42 | } |
||
43 | |||
44 | // Read the contents of \p Ptr based on the \p Schema to populate the |
||
45 | // MemInfoBlock member. |
||
46 | void deserialize(const MemProfSchema &Schema, const unsigned char *Ptr) { |
||
47 | using namespace support; |
||
48 | |||
49 | for (const Meta Id : Schema) { |
||
50 | switch (Id) { |
||
51 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
52 | case Meta::Name: { \ |
||
53 | Name = endian::readNext<Type, little, unaligned>(Ptr); \ |
||
54 | } break; |
||
55 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
56 | #undef MIBEntryDef |
||
57 | default: |
||
58 | llvm_unreachable("Unknown meta type id, is the profile collected from " |
||
59 | "a newer version of the runtime?"); |
||
60 | } |
||
61 | } |
||
62 | } |
||
63 | |||
64 | // Write the contents of the MemInfoBlock based on the \p Schema provided to |
||
65 | // the raw_ostream \p OS. |
||
66 | void serialize(const MemProfSchema &Schema, raw_ostream &OS) const { |
||
67 | using namespace support; |
||
68 | |||
69 | endian::Writer LE(OS, little); |
||
70 | for (const Meta Id : Schema) { |
||
71 | switch (Id) { |
||
72 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
73 | case Meta::Name: { \ |
||
74 | LE.write<Type>(Name); \ |
||
75 | } break; |
||
76 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
77 | #undef MIBEntryDef |
||
78 | default: |
||
79 | llvm_unreachable("Unknown meta type id, invalid input?"); |
||
80 | } |
||
81 | } |
||
82 | } |
||
83 | |||
84 | // Print out the contents of the MemInfoBlock in YAML format. |
||
85 | void printYAML(raw_ostream &OS) const { |
||
86 | OS << " MemInfoBlock:\n"; |
||
87 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
88 | OS << " " << #Name << ": " << Name << "\n"; |
||
89 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
90 | #undef MIBEntryDef |
||
91 | } |
||
92 | |||
93 | // Define getters for each type which can be called by analyses. |
||
94 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
95 | Type get##Name() const { return Name; } |
||
96 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
97 | #undef MIBEntryDef |
||
98 | |||
99 | void clear() { *this = PortableMemInfoBlock(); } |
||
100 | |||
101 | // Returns the full schema currently in use. |
||
102 | static MemProfSchema getSchema() { |
||
103 | MemProfSchema List; |
||
104 | #define MIBEntryDef(NameTag, Name, Type) List.push_back(Meta::Name); |
||
105 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
106 | #undef MIBEntryDef |
||
107 | return List; |
||
108 | } |
||
109 | |||
110 | bool operator==(const PortableMemInfoBlock &Other) const { |
||
111 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
112 | if (Other.get##Name() != get##Name()) \ |
||
113 | return false; |
||
114 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
115 | #undef MIBEntryDef |
||
116 | return true; |
||
117 | } |
||
118 | |||
119 | bool operator!=(const PortableMemInfoBlock &Other) const { |
||
120 | return !operator==(Other); |
||
121 | } |
||
122 | |||
123 | static constexpr size_t serializedSize() { |
||
124 | size_t Result = 0; |
||
125 | #define MIBEntryDef(NameTag, Name, Type) Result += sizeof(Type); |
||
126 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
127 | #undef MIBEntryDef |
||
128 | return Result; |
||
129 | } |
||
130 | |||
131 | private: |
||
132 | #define MIBEntryDef(NameTag, Name, Type) Type Name = Type(); |
||
133 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
134 | #undef MIBEntryDef |
||
135 | }; |
||
136 | |||
137 | // A type representing the id generated by hashing the contents of the Frame. |
||
138 | using FrameId = uint64_t; |
||
139 | // Describes a call frame for a dynamic allocation context. The contents of |
||
140 | // the frame are populated by symbolizing the stack depot call frame from the |
||
141 | // compiler runtime. |
||
142 | struct Frame { |
||
143 | // A uuid (uint64_t) identifying the function. It is obtained by |
||
144 | // llvm::md5(FunctionName) which returns the lower 64 bits. |
||
145 | GlobalValue::GUID Function; |
||
146 | // The symbol name for the function. Only populated in the Frame by the reader |
||
147 | // if requested during initialization. This field should not be serialized. |
||
148 | std::optional<std::string> SymbolName; |
||
149 | // The source line offset of the call from the beginning of parent function. |
||
150 | uint32_t LineOffset; |
||
151 | // The source column number of the call to help distinguish multiple calls |
||
152 | // on the same line. |
||
153 | uint32_t Column; |
||
154 | // Whether the current frame is inlined. |
||
155 | bool IsInlineFrame; |
||
156 | |||
157 | Frame(const Frame &Other) { |
||
158 | Function = Other.Function; |
||
159 | SymbolName = Other.SymbolName; |
||
160 | LineOffset = Other.LineOffset; |
||
161 | Column = Other.Column; |
||
162 | IsInlineFrame = Other.IsInlineFrame; |
||
163 | } |
||
164 | |||
165 | Frame(uint64_t Hash, uint32_t Off, uint32_t Col, bool Inline) |
||
166 | : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {} |
||
167 | |||
168 | bool operator==(const Frame &Other) const { |
||
169 | // Ignore the SymbolName field to avoid a string compare. Comparing the |
||
170 | // function hash serves the same purpose. |
||
171 | return Other.Function == Function && Other.LineOffset == LineOffset && |
||
172 | Other.Column == Column && Other.IsInlineFrame == IsInlineFrame; |
||
173 | } |
||
174 | |||
175 | Frame &operator=(const Frame &Other) { |
||
176 | Function = Other.Function; |
||
177 | SymbolName = Other.SymbolName; |
||
178 | LineOffset = Other.LineOffset; |
||
179 | Column = Other.Column; |
||
180 | IsInlineFrame = Other.IsInlineFrame; |
||
181 | return *this; |
||
182 | } |
||
183 | |||
184 | bool operator!=(const Frame &Other) const { return !operator==(Other); } |
||
185 | |||
186 | // Write the contents of the frame to the ostream \p OS. |
||
187 | void serialize(raw_ostream &OS) const { |
||
188 | using namespace support; |
||
189 | |||
190 | endian::Writer LE(OS, little); |
||
191 | |||
192 | // If the type of the GlobalValue::GUID changes, then we need to update |
||
193 | // the reader and the writer. |
||
194 | static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value, |
||
195 | "Expect GUID to be uint64_t."); |
||
196 | LE.write<uint64_t>(Function); |
||
197 | |||
198 | LE.write<uint32_t>(LineOffset); |
||
199 | LE.write<uint32_t>(Column); |
||
200 | LE.write<bool>(IsInlineFrame); |
||
201 | } |
||
202 | |||
203 | // Read a frame from char data which has been serialized as little endian. |
||
204 | static Frame deserialize(const unsigned char *Ptr) { |
||
205 | using namespace support; |
||
206 | |||
207 | const uint64_t F = endian::readNext<uint64_t, little, unaligned>(Ptr); |
||
208 | const uint32_t L = endian::readNext<uint32_t, little, unaligned>(Ptr); |
||
209 | const uint32_t C = endian::readNext<uint32_t, little, unaligned>(Ptr); |
||
210 | const bool I = endian::readNext<bool, little, unaligned>(Ptr); |
||
211 | return Frame(/*Function=*/F, /*LineOffset=*/L, /*Column=*/C, |
||
212 | /*IsInlineFrame=*/I); |
||
213 | } |
||
214 | |||
215 | // Returns the size of the frame information. |
||
216 | static constexpr size_t serializedSize() { |
||
217 | return sizeof(Frame::Function) + sizeof(Frame::LineOffset) + |
||
218 | sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame); |
||
219 | } |
||
220 | |||
221 | // Print the frame information in YAML format. |
||
222 | void printYAML(raw_ostream &OS) const { |
||
223 | OS << " -\n" |
||
224 | << " Function: " << Function << "\n" |
||
225 | << " SymbolName: " << SymbolName.value_or("<None>") << "\n" |
||
226 | << " LineOffset: " << LineOffset << "\n" |
||
227 | << " Column: " << Column << "\n" |
||
228 | << " Inline: " << IsInlineFrame << "\n"; |
||
229 | } |
||
230 | |||
231 | // Return a hash value based on the contents of the frame. Here we don't use |
||
232 | // hashing from llvm ADT since we are going to persist the hash id, the hash |
||
233 | // combine algorithm in ADT uses a new randomized seed each time. |
||
234 | inline FrameId hash() const { |
||
235 | auto HashCombine = [](auto Value, size_t Seed) { |
||
236 | std::hash<decltype(Value)> Hasher; |
||
237 | // The constant used below is the 64 bit representation of the fractional |
||
238 | // part of the golden ratio. Used here for the randomness in their bit |
||
239 | // pattern. |
||
240 | return Hasher(Value) + 0x9e3779b97f4a7c15 + (Seed << 6) + (Seed >> 2); |
||
241 | }; |
||
242 | |||
243 | size_t Result = 0; |
||
244 | Result ^= HashCombine(Function, Result); |
||
245 | Result ^= HashCombine(LineOffset, Result); |
||
246 | Result ^= HashCombine(Column, Result); |
||
247 | Result ^= HashCombine(IsInlineFrame, Result); |
||
248 | return static_cast<FrameId>(Result); |
||
249 | } |
||
250 | }; |
||
251 | |||
252 | // Holds allocation information in a space efficient format where frames are |
||
253 | // represented using unique identifiers. |
||
254 | struct IndexedAllocationInfo { |
||
255 | // The dynamic calling context for the allocation in bottom-up (leaf-to-root) |
||
256 | // order. Frame contents are stored out-of-line. |
||
257 | llvm::SmallVector<FrameId> CallStack; |
||
258 | // The statistics obtained from the runtime for the allocation. |
||
259 | PortableMemInfoBlock Info; |
||
260 | |||
261 | IndexedAllocationInfo() = default; |
||
262 | IndexedAllocationInfo(ArrayRef<FrameId> CS, const MemInfoBlock &MB) |
||
263 | : CallStack(CS.begin(), CS.end()), Info(MB) {} |
||
264 | |||
265 | // Returns the size in bytes when this allocation info struct is serialized. |
||
266 | size_t serializedSize() const { |
||
267 | return sizeof(uint64_t) + // The number of frames to serialize. |
||
268 | sizeof(FrameId) * CallStack.size() + // The callstack frame ids. |
||
269 | PortableMemInfoBlock::serializedSize(); // The size of the payload. |
||
270 | } |
||
271 | |||
272 | bool operator==(const IndexedAllocationInfo &Other) const { |
||
273 | if (Other.Info != Info) |
||
274 | return false; |
||
275 | |||
276 | if (Other.CallStack.size() != CallStack.size()) |
||
277 | return false; |
||
278 | |||
279 | for (size_t J = 0; J < Other.CallStack.size(); J++) { |
||
280 | if (Other.CallStack[J] != CallStack[J]) |
||
281 | return false; |
||
282 | } |
||
283 | return true; |
||
284 | } |
||
285 | |||
286 | bool operator!=(const IndexedAllocationInfo &Other) const { |
||
287 | return !operator==(Other); |
||
288 | } |
||
289 | }; |
||
290 | |||
291 | // Holds allocation information with frame contents inline. The type should |
||
292 | // be used for temporary in-memory instances. |
||
293 | struct AllocationInfo { |
||
294 | // Same as IndexedAllocationInfo::CallStack with the frame contents inline. |
||
295 | llvm::SmallVector<Frame> CallStack; |
||
296 | // Same as IndexedAllocationInfo::Info; |
||
297 | PortableMemInfoBlock Info; |
||
298 | |||
299 | AllocationInfo() = default; |
||
300 | AllocationInfo( |
||
301 | const IndexedAllocationInfo &IndexedAI, |
||
302 | llvm::function_ref<const Frame(const FrameId)> IdToFrameCallback) { |
||
303 | for (const FrameId &Id : IndexedAI.CallStack) { |
||
304 | CallStack.push_back(IdToFrameCallback(Id)); |
||
305 | } |
||
306 | Info = IndexedAI.Info; |
||
307 | } |
||
308 | |||
309 | void printYAML(raw_ostream &OS) const { |
||
310 | OS << " -\n"; |
||
311 | OS << " Callstack:\n"; |
||
312 | // TODO: Print out the frame on one line with to make it easier for deep |
||
313 | // callstacks once we have a test to check valid YAML is generated. |
||
314 | for (const Frame &F : CallStack) { |
||
315 | F.printYAML(OS); |
||
316 | } |
||
317 | Info.printYAML(OS); |
||
318 | } |
||
319 | }; |
||
320 | |||
321 | // Holds the memprof profile information for a function. The internal |
||
322 | // representation stores frame ids for efficiency. This representation should |
||
323 | // be used in the profile conversion and manipulation tools. |
||
324 | struct IndexedMemProfRecord { |
||
325 | // Memory allocation sites in this function for which we have memory |
||
326 | // profiling data. |
||
327 | llvm::SmallVector<IndexedAllocationInfo> AllocSites; |
||
328 | // Holds call sites in this function which are part of some memory |
||
329 | // allocation context. We store this as a list of locations, each with its |
||
330 | // list of inline locations in bottom-up order i.e. from leaf to root. The |
||
331 | // inline location list may include additional entries, users should pick |
||
332 | // the last entry in the list with the same function GUID. |
||
333 | llvm::SmallVector<llvm::SmallVector<FrameId>> CallSites; |
||
334 | |||
335 | void clear() { |
||
336 | AllocSites.clear(); |
||
337 | CallSites.clear(); |
||
338 | } |
||
339 | |||
340 | void merge(const IndexedMemProfRecord &Other) { |
||
341 | // TODO: Filter out duplicates which may occur if multiple memprof |
||
342 | // profiles are merged together using llvm-profdata. |
||
343 | AllocSites.append(Other.AllocSites); |
||
344 | CallSites.append(Other.CallSites); |
||
345 | } |
||
346 | |||
347 | size_t serializedSize() const { |
||
348 | size_t Result = sizeof(GlobalValue::GUID); |
||
349 | for (const IndexedAllocationInfo &N : AllocSites) |
||
350 | Result += N.serializedSize(); |
||
351 | |||
352 | // The number of callsites we have information for. |
||
353 | Result += sizeof(uint64_t); |
||
354 | for (const auto &Frames : CallSites) { |
||
355 | // The number of frame ids to serialize. |
||
356 | Result += sizeof(uint64_t); |
||
357 | Result += Frames.size() * sizeof(FrameId); |
||
358 | } |
||
359 | return Result; |
||
360 | } |
||
361 | |||
362 | bool operator==(const IndexedMemProfRecord &Other) const { |
||
363 | if (Other.AllocSites.size() != AllocSites.size()) |
||
364 | return false; |
||
365 | |||
366 | if (Other.CallSites.size() != CallSites.size()) |
||
367 | return false; |
||
368 | |||
369 | for (size_t I = 0; I < AllocSites.size(); I++) { |
||
370 | if (AllocSites[I] != Other.AllocSites[I]) |
||
371 | return false; |
||
372 | } |
||
373 | |||
374 | for (size_t I = 0; I < CallSites.size(); I++) { |
||
375 | if (CallSites[I] != Other.CallSites[I]) |
||
376 | return false; |
||
377 | } |
||
378 | return true; |
||
379 | } |
||
380 | |||
381 | // Serializes the memprof records in \p Records to the ostream \p OS based |
||
382 | // on the schema provided in \p Schema. |
||
383 | void serialize(const MemProfSchema &Schema, raw_ostream &OS); |
||
384 | |||
385 | // Deserializes memprof records from the Buffer. |
||
386 | static IndexedMemProfRecord deserialize(const MemProfSchema &Schema, |
||
387 | const unsigned char *Buffer); |
||
388 | |||
389 | // Returns the GUID for the function name after canonicalization. For |
||
390 | // memprof, we remove any .llvm suffix added by LTO. MemProfRecords are |
||
391 | // mapped to functions using this GUID. |
||
392 | static GlobalValue::GUID getGUID(const StringRef FunctionName); |
||
393 | }; |
||
394 | |||
395 | // Holds the memprof profile information for a function. The internal |
||
396 | // representation stores frame contents inline. This representation should |
||
397 | // be used for small amount of temporary, in memory instances. |
||
398 | struct MemProfRecord { |
||
399 | // Same as IndexedMemProfRecord::AllocSites with frame contents inline. |
||
400 | llvm::SmallVector<AllocationInfo> AllocSites; |
||
401 | // Same as IndexedMemProfRecord::CallSites with frame contents inline. |
||
402 | llvm::SmallVector<llvm::SmallVector<Frame>> CallSites; |
||
403 | |||
404 | MemProfRecord() = default; |
||
405 | MemProfRecord( |
||
406 | const IndexedMemProfRecord &Record, |
||
407 | llvm::function_ref<const Frame(const FrameId Id)> IdToFrameCallback) { |
||
408 | for (const IndexedAllocationInfo &IndexedAI : Record.AllocSites) { |
||
409 | AllocSites.emplace_back(IndexedAI, IdToFrameCallback); |
||
410 | } |
||
411 | for (const ArrayRef<FrameId> Site : Record.CallSites) { |
||
412 | llvm::SmallVector<Frame> Frames; |
||
413 | for (const FrameId Id : Site) { |
||
414 | Frames.push_back(IdToFrameCallback(Id)); |
||
415 | } |
||
416 | CallSites.push_back(Frames); |
||
417 | } |
||
418 | } |
||
419 | |||
420 | // Prints out the contents of the memprof record in YAML. |
||
421 | void print(llvm::raw_ostream &OS) const { |
||
422 | if (!AllocSites.empty()) { |
||
423 | OS << " AllocSites:\n"; |
||
424 | for (const AllocationInfo &N : AllocSites) |
||
425 | N.printYAML(OS); |
||
426 | } |
||
427 | |||
428 | if (!CallSites.empty()) { |
||
429 | OS << " CallSites:\n"; |
||
430 | for (const llvm::SmallVector<Frame> &Frames : CallSites) { |
||
431 | for (const Frame &F : Frames) { |
||
432 | OS << " -\n"; |
||
433 | F.printYAML(OS); |
||
434 | } |
||
435 | } |
||
436 | } |
||
437 | } |
||
438 | }; |
||
439 | |||
440 | // Reads a memprof schema from a buffer. All entries in the buffer are |
||
441 | // interpreted as uint64_t. The first entry in the buffer denotes the number of |
||
442 | // ids in the schema. Subsequent entries are integers which map to memprof::Meta |
||
443 | // enum class entries. After successfully reading the schema, the pointer is one |
||
444 | // byte past the schema contents. |
||
445 | Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer); |
||
446 | |||
447 | // Trait for reading IndexedMemProfRecord data from the on-disk hash table. |
||
448 | class RecordLookupTrait { |
||
449 | public: |
||
450 | using data_type = const IndexedMemProfRecord &; |
||
451 | using internal_key_type = uint64_t; |
||
452 | using external_key_type = uint64_t; |
||
453 | using hash_value_type = uint64_t; |
||
454 | using offset_type = uint64_t; |
||
455 | |||
456 | RecordLookupTrait() = delete; |
||
457 | RecordLookupTrait(const MemProfSchema &S) : Schema(S) {} |
||
458 | |||
459 | static bool EqualKey(uint64_t A, uint64_t B) { return A == B; } |
||
460 | static uint64_t GetInternalKey(uint64_t K) { return K; } |
||
461 | static uint64_t GetExternalKey(uint64_t K) { return K; } |
||
462 | |||
463 | hash_value_type ComputeHash(uint64_t K) { return K; } |
||
464 | |||
465 | static std::pair<offset_type, offset_type> |
||
466 | ReadKeyDataLength(const unsigned char *&D) { |
||
467 | using namespace support; |
||
468 | |||
469 | offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D); |
||
470 | offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D); |
||
471 | return std::make_pair(KeyLen, DataLen); |
||
472 | } |
||
473 | |||
474 | uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) { |
||
475 | using namespace support; |
||
476 | return endian::readNext<external_key_type, little, unaligned>(D); |
||
477 | } |
||
478 | |||
479 | data_type ReadData(uint64_t K, const unsigned char *D, |
||
480 | offset_type /*Unused*/) { |
||
481 | Record = IndexedMemProfRecord::deserialize(Schema, D); |
||
482 | return Record; |
||
483 | } |
||
484 | |||
485 | private: |
||
486 | // Holds the memprof schema used to deserialize records. |
||
487 | MemProfSchema Schema; |
||
488 | // Holds the records from one function deserialized from the indexed format. |
||
489 | IndexedMemProfRecord Record; |
||
490 | }; |
||
491 | |||
492 | // Trait for writing IndexedMemProfRecord data to the on-disk hash table. |
||
493 | class RecordWriterTrait { |
||
494 | public: |
||
495 | using key_type = uint64_t; |
||
496 | using key_type_ref = uint64_t; |
||
497 | |||
498 | using data_type = IndexedMemProfRecord; |
||
499 | using data_type_ref = IndexedMemProfRecord &; |
||
500 | |||
501 | using hash_value_type = uint64_t; |
||
502 | using offset_type = uint64_t; |
||
503 | |||
504 | // Pointer to the memprof schema to use for the generator. Unlike the reader |
||
505 | // we must use a default constructor with no params for the writer trait so we |
||
506 | // have a public member which must be initialized by the user. |
||
507 | MemProfSchema *Schema = nullptr; |
||
508 | |||
509 | RecordWriterTrait() = default; |
||
510 | |||
511 | static hash_value_type ComputeHash(key_type_ref K) { return K; } |
||
512 | |||
513 | static std::pair<offset_type, offset_type> |
||
514 | EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) { |
||
515 | using namespace support; |
||
516 | |||
517 | endian::Writer LE(Out, little); |
||
518 | offset_type N = sizeof(K); |
||
519 | LE.write<offset_type>(N); |
||
520 | offset_type M = V.serializedSize(); |
||
521 | LE.write<offset_type>(M); |
||
522 | return std::make_pair(N, M); |
||
523 | } |
||
524 | |||
525 | void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) { |
||
526 | using namespace support; |
||
527 | endian::Writer LE(Out, little); |
||
528 | LE.write<uint64_t>(K); |
||
529 | } |
||
530 | |||
531 | void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V, |
||
532 | offset_type /*Unused*/) { |
||
533 | assert(Schema != nullptr && "MemProf schema is not initialized!"); |
||
534 | V.serialize(*Schema, Out); |
||
535 | } |
||
536 | }; |
||
537 | |||
538 | // Trait for writing frame mappings to the on-disk hash table. |
||
539 | class FrameWriterTrait { |
||
540 | public: |
||
541 | using key_type = FrameId; |
||
542 | using key_type_ref = FrameId; |
||
543 | |||
544 | using data_type = Frame; |
||
545 | using data_type_ref = Frame &; |
||
546 | |||
547 | using hash_value_type = FrameId; |
||
548 | using offset_type = uint64_t; |
||
549 | |||
550 | static hash_value_type ComputeHash(key_type_ref K) { return K; } |
||
551 | |||
552 | static std::pair<offset_type, offset_type> |
||
553 | EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) { |
||
554 | using namespace support; |
||
555 | endian::Writer LE(Out, little); |
||
556 | offset_type N = sizeof(K); |
||
557 | LE.write<offset_type>(N); |
||
558 | offset_type M = V.serializedSize(); |
||
559 | LE.write<offset_type>(M); |
||
560 | return std::make_pair(N, M); |
||
561 | } |
||
562 | |||
563 | void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) { |
||
564 | using namespace support; |
||
565 | endian::Writer LE(Out, little); |
||
566 | LE.write<key_type>(K); |
||
567 | } |
||
568 | |||
569 | void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V, |
||
570 | offset_type /*Unused*/) { |
||
571 | V.serialize(Out); |
||
572 | } |
||
573 | }; |
||
574 | |||
575 | // Trait for reading frame mappings from the on-disk hash table. |
||
576 | class FrameLookupTrait { |
||
577 | public: |
||
578 | using data_type = const Frame; |
||
579 | using internal_key_type = FrameId; |
||
580 | using external_key_type = FrameId; |
||
581 | using hash_value_type = FrameId; |
||
582 | using offset_type = uint64_t; |
||
583 | |||
584 | static bool EqualKey(internal_key_type A, internal_key_type B) { |
||
585 | return A == B; |
||
586 | } |
||
587 | static uint64_t GetInternalKey(internal_key_type K) { return K; } |
||
588 | static uint64_t GetExternalKey(external_key_type K) { return K; } |
||
589 | |||
590 | hash_value_type ComputeHash(internal_key_type K) { return K; } |
||
591 | |||
592 | static std::pair<offset_type, offset_type> |
||
593 | ReadKeyDataLength(const unsigned char *&D) { |
||
594 | using namespace support; |
||
595 | |||
596 | offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D); |
||
597 | offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D); |
||
598 | return std::make_pair(KeyLen, DataLen); |
||
599 | } |
||
600 | |||
601 | uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) { |
||
602 | using namespace support; |
||
603 | return endian::readNext<external_key_type, little, unaligned>(D); |
||
604 | } |
||
605 | |||
606 | data_type ReadData(uint64_t K, const unsigned char *D, |
||
607 | offset_type /*Unused*/) { |
||
608 | return Frame::deserialize(D); |
||
609 | } |
||
610 | }; |
||
611 | } // namespace memprof |
||
612 | } // namespace llvm |
||
613 | |||
614 | #endif // LLVM_PROFILEDATA_MEMPROF_H_ |