1 //===- MemProf.h - MemProf 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 // This file contains common definitions used in the reading and writing of
10 // memory profile data.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_PROFILEDATA_MEMPROF_H
15 #define LLVM_PROFILEDATA_MEMPROF_H
16
17 #include "llvm/ADT/BitVector.h"
18 #include "llvm/ADT/MapVector.h"
19 #include "llvm/ADT/STLForwardCompat.h"
20 #include "llvm/ADT/STLFunctionalExtras.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/IR/GlobalValue.h"
24 #include "llvm/ProfileData/MemProfData.inc"
25 #include "llvm/Support/BLAKE3.h"
26 #include "llvm/Support/Compiler.h"
27 #include "llvm/Support/Endian.h"
28 #include "llvm/Support/EndianStream.h"
29 #include "llvm/Support/HashBuilder.h"
30 #include "llvm/Support/raw_ostream.h"
31
32 #include <bitset>
33 #include <cstdint>
34 #include <optional>
35
36 namespace llvm {
37 namespace yaml {
38 template <typename T> struct CustomMappingTraits;
39 } // namespace yaml
40
41 namespace memprof {
42
43 struct MemProfRecord;
44
45 // The versions of the indexed MemProf format
46 enum IndexedVersion : uint64_t {
47 // Version 2: Added a call stack table.
48 Version2 = 2,
49 // Version 3: Added a radix tree for call stacks. Switched to linear IDs for
50 // frames and call stacks.
51 Version3 = 3,
52 // Version 4: Added CalleeGuids to call site info.
53 Version4 = 4,
54 };
55
56 constexpr uint64_t MinimumSupportedVersion = Version2;
57 constexpr uint64_t MaximumSupportedVersion = Version4;
58
59 // Verify that the minimum and maximum satisfy the obvious constraint.
60 static_assert(MinimumSupportedVersion <= MaximumSupportedVersion);
61
getMemprofOptionsSymbolDarwinLinkageName()62 inline llvm::StringRef getMemprofOptionsSymbolDarwinLinkageName() {
63 return "___memprof_default_options_str";
64 }
65
getMemprofOptionsSymbolName()66 inline llvm::StringRef getMemprofOptionsSymbolName() {
67 // Darwin linkage names are prefixed with an extra "_". See
68 // DataLayout::getGlobalPrefix().
69 return getMemprofOptionsSymbolDarwinLinkageName().drop_front();
70 }
71
72 enum class Meta : uint64_t {
73 Start = 0,
74 #define MIBEntryDef(NameTag, Name, Type) NameTag,
75 #include "llvm/ProfileData/MIBEntryDef.inc"
76 #undef MIBEntryDef
77 Size
78 };
79
80 using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>;
81
82 // Returns the full schema currently in use.
83 LLVM_ABI MemProfSchema getFullSchema();
84
85 // Returns the schema consisting of the fields used for hot cold memory hinting.
86 LLVM_ABI MemProfSchema getHotColdSchema();
87
88 // Holds the actual MemInfoBlock data with all fields. Contents may be read or
89 // written partially by providing an appropriate schema to the serialize and
90 // deserialize methods.
91 struct PortableMemInfoBlock {
92 PortableMemInfoBlock() = default;
PortableMemInfoBlockPortableMemInfoBlock93 explicit PortableMemInfoBlock(const MemInfoBlock &Block,
94 const MemProfSchema &IncomingSchema) {
95 for (const Meta Id : IncomingSchema)
96 Schema.set(llvm::to_underlying(Id));
97 #define MIBEntryDef(NameTag, Name, Type) Name = Block.Name;
98 #include "llvm/ProfileData/MIBEntryDef.inc"
99 #undef MIBEntryDef
100 }
101
PortableMemInfoBlockPortableMemInfoBlock102 PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) {
103 deserialize(Schema, Ptr);
104 }
105
106 // Read the contents of \p Ptr based on the \p Schema to populate the
107 // MemInfoBlock member.
deserializePortableMemInfoBlock108 void deserialize(const MemProfSchema &IncomingSchema,
109 const unsigned char *Ptr) {
110 using namespace support;
111
112 Schema.reset();
113 for (const Meta Id : IncomingSchema) {
114 switch (Id) {
115 #define MIBEntryDef(NameTag, Name, Type) \
116 case Meta::Name: { \
117 Name = endian::readNext<Type, llvm::endianness::little>(Ptr); \
118 } break;
119 #include "llvm/ProfileData/MIBEntryDef.inc"
120 #undef MIBEntryDef
121 default:
122 llvm_unreachable("Unknown meta type id, is the profile collected from "
123 "a newer version of the runtime?");
124 }
125
126 Schema.set(llvm::to_underlying(Id));
127 }
128 }
129
130 // Write the contents of the MemInfoBlock based on the \p Schema provided to
131 // the raw_ostream \p OS.
serializePortableMemInfoBlock132 void serialize(const MemProfSchema &Schema, raw_ostream &OS) const {
133 using namespace support;
134
135 endian::Writer LE(OS, llvm::endianness::little);
136 for (const Meta Id : Schema) {
137 switch (Id) {
138 #define MIBEntryDef(NameTag, Name, Type) \
139 case Meta::Name: { \
140 LE.write<Type>(Name); \
141 } break;
142 #include "llvm/ProfileData/MIBEntryDef.inc"
143 #undef MIBEntryDef
144 default:
145 llvm_unreachable("Unknown meta type id, invalid input?");
146 }
147 }
148 }
149
150 // Print out the contents of the MemInfoBlock in YAML format.
printYAMLPortableMemInfoBlock151 void printYAML(raw_ostream &OS) const {
152 OS << " MemInfoBlock:\n";
153 #define MIBEntryDef(NameTag, Name, Type) \
154 OS << " " << #Name << ": " << Name << "\n";
155 #include "llvm/ProfileData/MIBEntryDef.inc"
156 #undef MIBEntryDef
157 if (AccessHistogramSize > 0) {
158 OS << " " << "AccessHistogramValues" << ":";
159 for (uint32_t I = 0; I < AccessHistogramSize; ++I) {
160 OS << " " << ((uint64_t *)AccessHistogram)[I];
161 }
162 OS << "\n";
163 }
164 }
165
166 // Return the schema, only for unit tests.
getSchemaPortableMemInfoBlock167 std::bitset<llvm::to_underlying(Meta::Size)> getSchema() const {
168 return Schema;
169 }
170
171 // Define getters for each type which can be called by analyses.
172 #define MIBEntryDef(NameTag, Name, Type) \
173 Type get##Name() const { \
174 assert(Schema[llvm::to_underlying(Meta::Name)]); \
175 return Name; \
176 }
177 #include "llvm/ProfileData/MIBEntryDef.inc"
178 #undef MIBEntryDef
179
180 // Define setters for each type which can be called by the writer.
181 #define MIBEntryDef(NameTag, Name, Type) \
182 void set##Name(Type NewVal) { \
183 assert(Schema[llvm::to_underlying(Meta::Name)]); \
184 Name = NewVal; \
185 }
186 #include "llvm/ProfileData/MIBEntryDef.inc"
187 #undef MIBEntryDef
188
clearPortableMemInfoBlock189 void clear() { *this = PortableMemInfoBlock(); }
190
191 bool operator==(const PortableMemInfoBlock &Other) const {
192 if (Other.Schema != Schema)
193 return false;
194
195 #define MIBEntryDef(NameTag, Name, Type) \
196 if (Schema[llvm::to_underlying(Meta::Name)] && \
197 Other.get##Name() != get##Name()) \
198 return false;
199 #include "llvm/ProfileData/MIBEntryDef.inc"
200 #undef MIBEntryDef
201 return true;
202 }
203
204 bool operator!=(const PortableMemInfoBlock &Other) const {
205 return !operator==(Other);
206 }
207
serializedSizePortableMemInfoBlock208 static size_t serializedSize(const MemProfSchema &Schema) {
209 size_t Result = 0;
210
211 for (const Meta Id : Schema) {
212 switch (Id) {
213 #define MIBEntryDef(NameTag, Name, Type) \
214 case Meta::Name: { \
215 Result += sizeof(Type); \
216 } break;
217 #include "llvm/ProfileData/MIBEntryDef.inc"
218 #undef MIBEntryDef
219 default:
220 llvm_unreachable("Unknown meta type id, invalid input?");
221 }
222 }
223
224 return Result;
225 }
226
227 // Give YAML access to the individual MIB fields.
228 friend struct yaml::CustomMappingTraits<memprof::PortableMemInfoBlock>;
229
230 private:
231 // The set of available fields, indexed by Meta::Name.
232 std::bitset<llvm::to_underlying(Meta::Size)> Schema;
233
234 #define MIBEntryDef(NameTag, Name, Type) Type Name = Type();
235 #include "llvm/ProfileData/MIBEntryDef.inc"
236 #undef MIBEntryDef
237 };
238
239 // A type representing the id generated by hashing the contents of the Frame.
240 using FrameId = uint64_t;
241 // A type representing the id to index into the frame array.
242 using LinearFrameId = uint32_t;
243 // Describes a call frame for a dynamic allocation context. The contents of
244 // the frame are populated by symbolizing the stack depot call frame from the
245 // compiler runtime.
246 struct Frame {
247 // A uuid (uint64_t) identifying the function. It is obtained by
248 // llvm::md5(FunctionName) which returns the lower 64 bits.
249 GlobalValue::GUID Function = 0;
250 // The symbol name for the function. Only populated in the Frame by the reader
251 // if requested during initialization. This field should not be serialized.
252 std::unique_ptr<std::string> SymbolName;
253 // The source line offset of the call from the beginning of parent function.
254 uint32_t LineOffset = 0;
255 // The source column number of the call to help distinguish multiple calls
256 // on the same line.
257 uint32_t Column = 0;
258 // Whether the current frame is inlined.
259 bool IsInlineFrame = false;
260
261 Frame() = default;
262 Frame(const Frame &Other) {
263 Function = Other.Function;
264 SymbolName = Other.SymbolName
265 ? std::make_unique<std::string>(*Other.SymbolName)
266 : nullptr;
267 LineOffset = Other.LineOffset;
268 Column = Other.Column;
269 IsInlineFrame = Other.IsInlineFrame;
270 }
271
272 Frame(GlobalValue::GUID Hash, uint32_t Off, uint32_t Col, bool Inline)
273 : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {}
274
275 bool operator==(const Frame &Other) const {
276 // Ignore the SymbolName field to avoid a string compare. Comparing the
277 // function hash serves the same purpose.
278 return Other.Function == Function && Other.LineOffset == LineOffset &&
279 Other.Column == Column && Other.IsInlineFrame == IsInlineFrame;
280 }
281
282 Frame &operator=(const Frame &Other) {
283 Function = Other.Function;
284 SymbolName = Other.SymbolName
285 ? std::make_unique<std::string>(*Other.SymbolName)
286 : nullptr;
287 LineOffset = Other.LineOffset;
288 Column = Other.Column;
289 IsInlineFrame = Other.IsInlineFrame;
290 return *this;
291 }
292
293 bool operator!=(const Frame &Other) const { return !operator==(Other); }
294
295 bool hasSymbolName() const { return !!SymbolName; }
296
297 StringRef getSymbolName() const {
298 assert(hasSymbolName());
299 return *SymbolName;
300 }
301
302 std::string getSymbolNameOr(StringRef Alt) const {
303 return std::string(hasSymbolName() ? getSymbolName() : Alt);
304 }
305
306 // Write the contents of the frame to the ostream \p OS.
307 void serialize(raw_ostream &OS) const {
308 using namespace support;
309
310 endian::Writer LE(OS, llvm::endianness::little);
311
312 // If the type of the GlobalValue::GUID changes, then we need to update
313 // the reader and the writer.
314 static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value,
315 "Expect GUID to be uint64_t.");
316 LE.write<uint64_t>(Function);
317
318 LE.write<uint32_t>(LineOffset);
319 LE.write<uint32_t>(Column);
320 LE.write<bool>(IsInlineFrame);
321 }
322
323 // Read a frame from char data which has been serialized as little endian.
324 static Frame deserialize(const unsigned char *Ptr) {
325 using namespace support;
326
327 const uint64_t F =
328 endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
329 const uint32_t L =
330 endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
331 const uint32_t C =
332 endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
333 const bool I = endian::readNext<bool, llvm::endianness::little>(Ptr);
334 return Frame(/*Function=*/F, /*LineOffset=*/L, /*Column=*/C,
335 /*IsInlineFrame=*/I);
336 }
337
338 // Returns the size of the frame information.
339 static constexpr size_t serializedSize() {
340 return sizeof(Frame::Function) + sizeof(Frame::LineOffset) +
341 sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame);
342 }
343
344 // Print the frame information in YAML format.
345 void printYAML(raw_ostream &OS) const {
346 OS << " -\n"
347 << " Function: " << Function << "\n"
348 << " SymbolName: " << getSymbolNameOr("<None>") << "\n"
349 << " LineOffset: " << LineOffset << "\n"
350 << " Column: " << Column << "\n"
351 << " Inline: " << IsInlineFrame << "\n";
352 }
353 };
354
355 // A type representing the index into the table of call stacks.
356 using CallStackId = uint64_t;
357
358 // A type representing the index into the call stack array.
359 using LinearCallStackId = uint32_t;
360
361 // Holds call site information with indexed frame contents.
362 struct IndexedCallSiteInfo {
363 // The call stack ID for this call site
364 CallStackId CSId = 0;
365 // The GUIDs of the callees at this call site
366 SmallVector<GlobalValue::GUID, 1> CalleeGuids;
367
368 IndexedCallSiteInfo() = default;
369 IndexedCallSiteInfo(CallStackId CSId) : CSId(CSId) {}
370 IndexedCallSiteInfo(CallStackId CSId,
371 SmallVector<GlobalValue::GUID, 1> CalleeGuids)
372 : CSId(CSId), CalleeGuids(std::move(CalleeGuids)) {}
373
374 bool operator==(const IndexedCallSiteInfo &Other) const {
375 return CSId == Other.CSId && CalleeGuids == Other.CalleeGuids;
376 }
377
378 bool operator!=(const IndexedCallSiteInfo &Other) const {
379 return !operator==(Other);
380 }
381 };
382
383 // Holds allocation information in a space efficient format where frames are
384 // represented using unique identifiers.
385 struct IndexedAllocationInfo {
386 // The dynamic calling context for the allocation in bottom-up (leaf-to-root)
387 // order. Frame contents are stored out-of-line.
388 CallStackId CSId = 0;
389 // The statistics obtained from the runtime for the allocation.
390 PortableMemInfoBlock Info;
391
392 IndexedAllocationInfo() = default;
393 IndexedAllocationInfo(CallStackId CSId, const MemInfoBlock &MB,
394 const MemProfSchema &Schema = getFullSchema())
395 : CSId(CSId), Info(MB, Schema) {}
396 IndexedAllocationInfo(CallStackId CSId, const PortableMemInfoBlock &MB)
397 : CSId(CSId), Info(MB) {}
398
399 // Returns the size in bytes when this allocation info struct is serialized.
400 LLVM_ABI size_t serializedSize(const MemProfSchema &Schema,
401 IndexedVersion Version) const;
402
403 bool operator==(const IndexedAllocationInfo &Other) const {
404 if (Other.Info != Info)
405 return false;
406
407 if (Other.CSId != CSId)
408 return false;
409 return true;
410 }
411
412 bool operator!=(const IndexedAllocationInfo &Other) const {
413 return !operator==(Other);
414 }
415 };
416
417 // Holds allocation information with frame contents inline. The type should
418 // be used for temporary in-memory instances.
419 struct AllocationInfo {
420 // Same as IndexedAllocationInfo::CallStack with the frame contents inline.
421 std::vector<Frame> CallStack;
422 // Same as IndexedAllocationInfo::Info;
423 PortableMemInfoBlock Info;
424
425 AllocationInfo() = default;
426
427 void printYAML(raw_ostream &OS) const {
428 OS << " -\n";
429 OS << " Callstack:\n";
430 // TODO: Print out the frame on one line with to make it easier for deep
431 // callstacks once we have a test to check valid YAML is generated.
432 for (const Frame &F : CallStack) {
433 F.printYAML(OS);
434 }
435 Info.printYAML(OS);
436 }
437 };
438
439 // Holds the memprof profile information for a function. The internal
440 // representation stores frame ids for efficiency. This representation should
441 // be used in the profile conversion and manipulation tools.
442 struct IndexedMemProfRecord {
443 // Memory allocation sites in this function for which we have memory
444 // profiling data.
445 llvm::SmallVector<IndexedAllocationInfo> AllocSites;
446 // Holds call sites in this function which are part of some memory
447 // allocation context. We store this as a list of locations, each with its
448 // list of inline locations in bottom-up order i.e. from leaf to root. The
449 // inline location list may include additional entries, users should pick
450 // the last entry in the list with the same function GUID.
451 llvm::SmallVector<IndexedCallSiteInfo> CallSites;
452
453 void clear() { *this = IndexedMemProfRecord(); }
454
455 void merge(const IndexedMemProfRecord &Other) {
456 // TODO: Filter out duplicates which may occur if multiple memprof
457 // profiles are merged together using llvm-profdata.
458 AllocSites.append(Other.AllocSites);
459 }
460
461 LLVM_ABI size_t serializedSize(const MemProfSchema &Schema,
462 IndexedVersion Version) const;
463
464 bool operator==(const IndexedMemProfRecord &Other) const {
465 if (Other.AllocSites != AllocSites)
466 return false;
467
468 if (Other.CallSites != CallSites)
469 return false;
470 return true;
471 }
472
473 // Serializes the memprof records in \p Records to the ostream \p OS based
474 // on the schema provided in \p Schema.
475 LLVM_ABI void serialize(const MemProfSchema &Schema, raw_ostream &OS,
476 IndexedVersion Version,
477 llvm::DenseMap<CallStackId, LinearCallStackId>
478 *MemProfCallStackIndexes = nullptr) const;
479
480 // Deserializes memprof records from the Buffer.
481 LLVM_ABI static IndexedMemProfRecord deserialize(const MemProfSchema &Schema,
482 const unsigned char *Buffer,
483 IndexedVersion Version);
484
485 // Convert IndexedMemProfRecord to MemProfRecord. Callback is used to
486 // translate CallStackId to call stacks with frames inline.
487 LLVM_ABI MemProfRecord toMemProfRecord(
488 llvm::function_ref<std::vector<Frame>(const CallStackId)> Callback) const;
489 };
490
491 // Returns the GUID for the function name after canonicalization. For
492 // memprof, we remove any .llvm suffix added by LTO. MemProfRecords are
493 // mapped to functions using this GUID.
494 LLVM_ABI GlobalValue::GUID getGUID(const StringRef FunctionName);
495
496 // Holds call site information with frame contents inline.
497 struct CallSiteInfo {
498 // The frames in the call stack
499 std::vector<Frame> Frames;
500
501 // The GUIDs of the callees at this call site
502 SmallVector<GlobalValue::GUID, 1> CalleeGuids;
503
504 CallSiteInfo() = default;
505 CallSiteInfo(std::vector<Frame> Frames) : Frames(std::move(Frames)) {}
506 CallSiteInfo(std::vector<Frame> Frames,
507 SmallVector<GlobalValue::GUID, 1> CalleeGuids)
508 : Frames(std::move(Frames)), CalleeGuids(std::move(CalleeGuids)) {}
509
510 bool operator==(const CallSiteInfo &Other) const {
511 return Frames == Other.Frames && CalleeGuids == Other.CalleeGuids;
512 }
513
514 bool operator!=(const CallSiteInfo &Other) const {
515 return !operator==(Other);
516 }
517 };
518
519 // Holds the memprof profile information for a function. The internal
520 // representation stores frame contents inline. This representation should
521 // be used for small amount of temporary, in memory instances.
522 struct MemProfRecord {
523 // Same as IndexedMemProfRecord::AllocSites with frame contents inline.
524 llvm::SmallVector<AllocationInfo> AllocSites;
525 // Same as IndexedMemProfRecord::CallSites with frame contents inline.
526 llvm::SmallVector<CallSiteInfo> CallSites;
527
528 MemProfRecord() = default;
529
530 // Prints out the contents of the memprof record in YAML.
531 void print(llvm::raw_ostream &OS) const {
532 if (!AllocSites.empty()) {
533 OS << " AllocSites:\n";
534 for (const AllocationInfo &N : AllocSites)
535 N.printYAML(OS);
536 }
537
538 if (!CallSites.empty()) {
539 OS << " CallSites:\n";
540 for (const CallSiteInfo &CS : CallSites) {
541 for (const Frame &F : CS.Frames) {
542 OS << " -\n";
543 F.printYAML(OS);
544 }
545 }
546 }
547 }
548 };
549
550 // Reads a memprof schema from a buffer. All entries in the buffer are
551 // interpreted as uint64_t. The first entry in the buffer denotes the number of
552 // ids in the schema. Subsequent entries are integers which map to memprof::Meta
553 // enum class entries. After successfully reading the schema, the pointer is one
554 // byte past the schema contents.
555 LLVM_ABI Expected<MemProfSchema>
556 readMemProfSchema(const unsigned char *&Buffer);
557
558 // Trait for reading IndexedMemProfRecord data from the on-disk hash table.
559 class RecordLookupTrait {
560 public:
561 using data_type = const IndexedMemProfRecord &;
562 using internal_key_type = uint64_t;
563 using external_key_type = uint64_t;
564 using hash_value_type = uint64_t;
565 using offset_type = uint64_t;
566
567 RecordLookupTrait() = delete;
568 RecordLookupTrait(IndexedVersion V, const MemProfSchema &S)
569 : Version(V), Schema(S) {}
570
571 static bool EqualKey(uint64_t A, uint64_t B) { return A == B; }
572 static uint64_t GetInternalKey(uint64_t K) { return K; }
573 static uint64_t GetExternalKey(uint64_t K) { return K; }
574
575 hash_value_type ComputeHash(uint64_t K) { return K; }
576
577 static std::pair<offset_type, offset_type>
578 ReadKeyDataLength(const unsigned char *&D) {
579 using namespace support;
580
581 offset_type KeyLen =
582 endian::readNext<offset_type, llvm::endianness::little>(D);
583 offset_type DataLen =
584 endian::readNext<offset_type, llvm::endianness::little>(D);
585 return std::make_pair(KeyLen, DataLen);
586 }
587
588 uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
589 using namespace support;
590 return endian::readNext<external_key_type, llvm::endianness::little>(D);
591 }
592
593 data_type ReadData(uint64_t K, const unsigned char *D,
594 offset_type /*Unused*/) {
595 Record = IndexedMemProfRecord::deserialize(Schema, D, Version);
596 return Record;
597 }
598
599 private:
600 // Holds the MemProf version.
601 IndexedVersion Version;
602 // Holds the memprof schema used to deserialize records.
603 MemProfSchema Schema;
604 // Holds the records from one function deserialized from the indexed format.
605 IndexedMemProfRecord Record;
606 };
607
608 // Trait for writing IndexedMemProfRecord data to the on-disk hash table.
609 class RecordWriterTrait {
610 public:
611 using key_type = uint64_t;
612 using key_type_ref = uint64_t;
613
614 using data_type = IndexedMemProfRecord;
615 using data_type_ref = IndexedMemProfRecord &;
616
617 using hash_value_type = uint64_t;
618 using offset_type = uint64_t;
619
620 private:
621 // Pointer to the memprof schema to use for the generator.
622 const MemProfSchema *Schema;
623 // The MemProf version to use for the serialization.
624 IndexedVersion Version;
625
626 // Mappings from CallStackId to the indexes into the call stack array.
627 llvm::DenseMap<CallStackId, LinearCallStackId> *MemProfCallStackIndexes;
628
629 public:
630 // We do not support the default constructor, which does not set Version.
631 RecordWriterTrait() = delete;
632 RecordWriterTrait(
633 const MemProfSchema *Schema, IndexedVersion V,
634 llvm::DenseMap<CallStackId, LinearCallStackId> *MemProfCallStackIndexes)
635 : Schema(Schema), Version(V),
636 MemProfCallStackIndexes(MemProfCallStackIndexes) {}
637
638 static hash_value_type ComputeHash(key_type_ref K) { return K; }
639
640 std::pair<offset_type, offset_type>
641 EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
642 using namespace support;
643
644 endian::Writer LE(Out, llvm::endianness::little);
645 offset_type N = sizeof(K);
646 LE.write<offset_type>(N);
647 offset_type M = V.serializedSize(*Schema, Version);
648 LE.write<offset_type>(M);
649 return std::make_pair(N, M);
650 }
651
652 void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
653 using namespace support;
654 endian::Writer LE(Out, llvm::endianness::little);
655 LE.write<uint64_t>(K);
656 }
657
658 void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
659 offset_type /*Unused*/) {
660 assert(Schema != nullptr && "MemProf schema is not initialized!");
661 V.serialize(*Schema, Out, Version, MemProfCallStackIndexes);
662 // Clear the IndexedMemProfRecord which results in clearing/freeing its
663 // vectors of allocs and callsites. This is owned by the associated on-disk
664 // hash table, but unused after this point. See also the comment added to
665 // the client which constructs the on-disk hash table for this trait.
666 V.clear();
667 }
668 };
669
670 // Trait for writing frame mappings to the on-disk hash table.
671 class FrameWriterTrait {
672 public:
673 using key_type = FrameId;
674 using key_type_ref = FrameId;
675
676 using data_type = Frame;
677 using data_type_ref = Frame &;
678
679 using hash_value_type = FrameId;
680 using offset_type = uint64_t;
681
682 static hash_value_type ComputeHash(key_type_ref K) { return K; }
683
684 static std::pair<offset_type, offset_type>
685 EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
686 using namespace support;
687 endian::Writer LE(Out, llvm::endianness::little);
688 offset_type N = sizeof(K);
689 LE.write<offset_type>(N);
690 offset_type M = V.serializedSize();
691 LE.write<offset_type>(M);
692 return std::make_pair(N, M);
693 }
694
695 void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
696 using namespace support;
697 endian::Writer LE(Out, llvm::endianness::little);
698 LE.write<key_type>(K);
699 }
700
701 void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
702 offset_type /*Unused*/) {
703 V.serialize(Out);
704 }
705 };
706
707 // Trait for reading frame mappings from the on-disk hash table.
708 class FrameLookupTrait {
709 public:
710 using data_type = const Frame;
711 using internal_key_type = FrameId;
712 using external_key_type = FrameId;
713 using hash_value_type = FrameId;
714 using offset_type = uint64_t;
715
716 static bool EqualKey(internal_key_type A, internal_key_type B) {
717 return A == B;
718 }
719 static uint64_t GetInternalKey(internal_key_type K) { return K; }
720 static uint64_t GetExternalKey(external_key_type K) { return K; }
721
722 hash_value_type ComputeHash(internal_key_type K) { return K; }
723
724 static std::pair<offset_type, offset_type>
725 ReadKeyDataLength(const unsigned char *&D) {
726 using namespace support;
727
728 offset_type KeyLen =
729 endian::readNext<offset_type, llvm::endianness::little>(D);
730 offset_type DataLen =
731 endian::readNext<offset_type, llvm::endianness::little>(D);
732 return std::make_pair(KeyLen, DataLen);
733 }
734
735 uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
736 using namespace support;
737 return endian::readNext<external_key_type, llvm::endianness::little>(D);
738 }
739
740 data_type ReadData(uint64_t K, const unsigned char *D,
741 offset_type /*Unused*/) {
742 return Frame::deserialize(D);
743 }
744 };
745
746 // Trait for writing call stacks to the on-disk hash table.
747 class CallStackWriterTrait {
748 public:
749 using key_type = CallStackId;
750 using key_type_ref = CallStackId;
751
752 using data_type = llvm::SmallVector<FrameId>;
753 using data_type_ref = llvm::SmallVector<FrameId> &;
754
755 using hash_value_type = CallStackId;
756 using offset_type = uint64_t;
757
758 static hash_value_type ComputeHash(key_type_ref K) { return K; }
759
760 static std::pair<offset_type, offset_type>
761 EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
762 using namespace support;
763 endian::Writer LE(Out, llvm::endianness::little);
764 // We do not explicitly emit the key length because it is a constant.
765 offset_type N = sizeof(K);
766 offset_type M = sizeof(FrameId) * V.size();
767 LE.write<offset_type>(M);
768 return std::make_pair(N, M);
769 }
770
771 void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
772 using namespace support;
773 endian::Writer LE(Out, llvm::endianness::little);
774 LE.write<key_type>(K);
775 }
776
777 void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
778 offset_type /*Unused*/) {
779 using namespace support;
780 endian::Writer LE(Out, llvm::endianness::little);
781 // Emit the frames. We do not explicitly emit the length of the vector
782 // because it can be inferred from the data length.
783 for (FrameId F : V)
784 LE.write<FrameId>(F);
785 }
786 };
787
788 // Trait for reading call stack mappings from the on-disk hash table.
789 class CallStackLookupTrait {
790 public:
791 using data_type = const llvm::SmallVector<FrameId>;
792 using internal_key_type = CallStackId;
793 using external_key_type = CallStackId;
794 using hash_value_type = CallStackId;
795 using offset_type = uint64_t;
796
797 static bool EqualKey(internal_key_type A, internal_key_type B) {
798 return A == B;
799 }
800 static uint64_t GetInternalKey(internal_key_type K) { return K; }
801 static uint64_t GetExternalKey(external_key_type K) { return K; }
802
803 hash_value_type ComputeHash(internal_key_type K) { return K; }
804
805 static std::pair<offset_type, offset_type>
806 ReadKeyDataLength(const unsigned char *&D) {
807 using namespace support;
808
809 // We do not explicitly read the key length because it is a constant.
810 offset_type KeyLen = sizeof(external_key_type);
811 offset_type DataLen =
812 endian::readNext<offset_type, llvm::endianness::little>(D);
813 return std::make_pair(KeyLen, DataLen);
814 }
815
816 uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
817 using namespace support;
818 return endian::readNext<external_key_type, llvm::endianness::little>(D);
819 }
820
821 data_type ReadData(uint64_t K, const unsigned char *D, offset_type Length) {
822 using namespace support;
823 llvm::SmallVector<FrameId> CS;
824 // Derive the number of frames from the data length.
825 uint64_t NumFrames = Length / sizeof(FrameId);
826 assert(Length % sizeof(FrameId) == 0);
827 CS.reserve(NumFrames);
828 for (size_t I = 0; I != NumFrames; ++I) {
829 FrameId F = endian::readNext<FrameId, llvm::endianness::little>(D);
830 CS.push_back(F);
831 }
832 return CS;
833 }
834 };
835
836 struct LineLocation {
837 LineLocation(uint32_t L, uint32_t D) : LineOffset(L), Column(D) {}
838
839 bool operator<(const LineLocation &O) const {
840 return std::tie(LineOffset, Column) < std::tie(O.LineOffset, O.Column);
841 }
842
843 bool operator==(const LineLocation &O) const {
844 return LineOffset == O.LineOffset && Column == O.Column;
845 }
846
847 bool operator!=(const LineLocation &O) const {
848 return LineOffset != O.LineOffset || Column != O.Column;
849 }
850
851 uint64_t getHashCode() const { return ((uint64_t)Column << 32) | LineOffset; }
852
853 uint32_t LineOffset;
854 uint32_t Column;
855 };
856
857 // A pair of a call site location and its corresponding callee GUID.
858 using CallEdgeTy = std::pair<LineLocation, uint64_t>;
859 } // namespace memprof
860 } // namespace llvm
861 #endif // LLVM_PROFILEDATA_MEMPROF_H
862