File indexing completed on 2026-05-10 08:44:24
0001 #ifndef LLVM_PROFILEDATA_MEMPROF_H_
0002 #define LLVM_PROFILEDATA_MEMPROF_H_
0003
0004 #include "llvm/ADT/BitVector.h"
0005 #include "llvm/ADT/MapVector.h"
0006 #include "llvm/ADT/STLForwardCompat.h"
0007 #include "llvm/ADT/STLFunctionalExtras.h"
0008 #include "llvm/ADT/SmallVector.h"
0009 #include "llvm/IR/GlobalValue.h"
0010 #include "llvm/ProfileData/MemProfData.inc"
0011 #include "llvm/Support/BLAKE3.h"
0012 #include "llvm/Support/Endian.h"
0013 #include "llvm/Support/EndianStream.h"
0014 #include "llvm/Support/HashBuilder.h"
0015 #include "llvm/Support/raw_ostream.h"
0016
0017 #include <bitset>
0018 #include <cstdint>
0019 #include <optional>
0020
0021 namespace llvm {
0022 namespace yaml {
0023 template <typename T> struct CustomMappingTraits;
0024 }
0025
0026 namespace memprof {
0027
0028 struct MemProfRecord;
0029
0030
0031 enum IndexedVersion : uint64_t {
0032
0033 Version2 = 2,
0034
0035
0036 Version3 = 3,
0037 };
0038
0039 constexpr uint64_t MinimumSupportedVersion = Version2;
0040 constexpr uint64_t MaximumSupportedVersion = Version3;
0041
0042
0043 static_assert(MinimumSupportedVersion <= MaximumSupportedVersion);
0044
0045 enum class Meta : uint64_t {
0046 Start = 0,
0047 #define MIBEntryDef(NameTag, Name, Type) NameTag,
0048 #include "llvm/ProfileData/MIBEntryDef.inc"
0049 #undef MIBEntryDef
0050 Size
0051 };
0052
0053 using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>;
0054
0055
0056 MemProfSchema getFullSchema();
0057
0058
0059 MemProfSchema getHotColdSchema();
0060
0061
0062
0063
0064 struct PortableMemInfoBlock {
0065 PortableMemInfoBlock() = default;
0066 explicit PortableMemInfoBlock(const MemInfoBlock &Block,
0067 const MemProfSchema &IncomingSchema) {
0068 for (const Meta Id : IncomingSchema)
0069 Schema.set(llvm::to_underlying(Id));
0070 #define MIBEntryDef(NameTag, Name, Type) Name = Block.Name;
0071 #include "llvm/ProfileData/MIBEntryDef.inc"
0072 #undef MIBEntryDef
0073 }
0074
0075 PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) {
0076 deserialize(Schema, Ptr);
0077 }
0078
0079
0080
0081 void deserialize(const MemProfSchema &IncomingSchema,
0082 const unsigned char *Ptr) {
0083 using namespace support;
0084
0085 Schema.reset();
0086 for (const Meta Id : IncomingSchema) {
0087 switch (Id) {
0088 #define MIBEntryDef(NameTag, Name, Type) \
0089 case Meta::Name: { \
0090 Name = endian::readNext<Type, llvm::endianness::little>(Ptr); \
0091 } break;
0092 #include "llvm/ProfileData/MIBEntryDef.inc"
0093 #undef MIBEntryDef
0094 default:
0095 llvm_unreachable("Unknown meta type id, is the profile collected from "
0096 "a newer version of the runtime?");
0097 }
0098
0099 Schema.set(llvm::to_underlying(Id));
0100 }
0101 }
0102
0103
0104
0105 void serialize(const MemProfSchema &Schema, raw_ostream &OS) const {
0106 using namespace support;
0107
0108 endian::Writer LE(OS, llvm::endianness::little);
0109 for (const Meta Id : Schema) {
0110 switch (Id) {
0111 #define MIBEntryDef(NameTag, Name, Type) \
0112 case Meta::Name: { \
0113 LE.write<Type>(Name); \
0114 } break;
0115 #include "llvm/ProfileData/MIBEntryDef.inc"
0116 #undef MIBEntryDef
0117 default:
0118 llvm_unreachable("Unknown meta type id, invalid input?");
0119 }
0120 }
0121 }
0122
0123
0124 void printYAML(raw_ostream &OS) const {
0125 OS << " MemInfoBlock:\n";
0126 #define MIBEntryDef(NameTag, Name, Type) \
0127 OS << " " << #Name << ": " << Name << "\n";
0128 #include "llvm/ProfileData/MIBEntryDef.inc"
0129 #undef MIBEntryDef
0130 if (AccessHistogramSize > 0) {
0131 OS << " " << "AccessHistogramValues" << ":";
0132 for (uint32_t I = 0; I < AccessHistogramSize; ++I) {
0133 OS << " " << ((uint64_t *)AccessHistogram)[I];
0134 }
0135 OS << "\n";
0136 }
0137 }
0138
0139
0140 std::bitset<llvm::to_underlying(Meta::Size)> getSchema() const {
0141 return Schema;
0142 }
0143
0144
0145 #define MIBEntryDef(NameTag, Name, Type) \
0146 Type get##Name() const { \
0147 assert(Schema[llvm::to_underlying(Meta::Name)]); \
0148 return Name; \
0149 }
0150 #include "llvm/ProfileData/MIBEntryDef.inc"
0151 #undef MIBEntryDef
0152
0153
0154 #define MIBEntryDef(NameTag, Name, Type) \
0155 void set##Name(Type NewVal) { \
0156 assert(Schema[llvm::to_underlying(Meta::Name)]); \
0157 Name = NewVal; \
0158 }
0159 #include "llvm/ProfileData/MIBEntryDef.inc"
0160 #undef MIBEntryDef
0161
0162 void clear() { *this = PortableMemInfoBlock(); }
0163
0164 bool operator==(const PortableMemInfoBlock &Other) const {
0165 if (Other.Schema != Schema)
0166 return false;
0167
0168 #define MIBEntryDef(NameTag, Name, Type) \
0169 if (Schema[llvm::to_underlying(Meta::Name)] && \
0170 Other.get##Name() != get##Name()) \
0171 return false;
0172 #include "llvm/ProfileData/MIBEntryDef.inc"
0173 #undef MIBEntryDef
0174 return true;
0175 }
0176
0177 bool operator!=(const PortableMemInfoBlock &Other) const {
0178 return !operator==(Other);
0179 }
0180
0181 static size_t serializedSize(const MemProfSchema &Schema) {
0182 size_t Result = 0;
0183
0184 for (const Meta Id : Schema) {
0185 switch (Id) {
0186 #define MIBEntryDef(NameTag, Name, Type) \
0187 case Meta::Name: { \
0188 Result += sizeof(Type); \
0189 } break;
0190 #include "llvm/ProfileData/MIBEntryDef.inc"
0191 #undef MIBEntryDef
0192 default:
0193 llvm_unreachable("Unknown meta type id, invalid input?");
0194 }
0195 }
0196
0197 return Result;
0198 }
0199
0200
0201 friend struct yaml::CustomMappingTraits<memprof::PortableMemInfoBlock>;
0202
0203 private:
0204
0205 std::bitset<llvm::to_underlying(Meta::Size)> Schema;
0206
0207 #define MIBEntryDef(NameTag, Name, Type) Type Name = Type();
0208 #include "llvm/ProfileData/MIBEntryDef.inc"
0209 #undef MIBEntryDef
0210 };
0211
0212
0213 using FrameId = uint64_t;
0214
0215 using LinearFrameId = uint32_t;
0216
0217
0218
0219 struct Frame {
0220
0221
0222 GlobalValue::GUID Function = 0;
0223
0224
0225 std::unique_ptr<std::string> SymbolName;
0226
0227 uint32_t LineOffset = 0;
0228
0229
0230 uint32_t Column = 0;
0231
0232 bool IsInlineFrame = false;
0233
0234 Frame() = default;
0235 Frame(const Frame &Other) {
0236 Function = Other.Function;
0237 SymbolName = Other.SymbolName
0238 ? std::make_unique<std::string>(*Other.SymbolName)
0239 : nullptr;
0240 LineOffset = Other.LineOffset;
0241 Column = Other.Column;
0242 IsInlineFrame = Other.IsInlineFrame;
0243 }
0244
0245 Frame(GlobalValue::GUID Hash, uint32_t Off, uint32_t Col, bool Inline)
0246 : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {}
0247
0248 bool operator==(const Frame &Other) const {
0249
0250
0251 return Other.Function == Function && Other.LineOffset == LineOffset &&
0252 Other.Column == Column && Other.IsInlineFrame == IsInlineFrame;
0253 }
0254
0255 Frame &operator=(const Frame &Other) {
0256 Function = Other.Function;
0257 SymbolName = Other.SymbolName
0258 ? std::make_unique<std::string>(*Other.SymbolName)
0259 : nullptr;
0260 LineOffset = Other.LineOffset;
0261 Column = Other.Column;
0262 IsInlineFrame = Other.IsInlineFrame;
0263 return *this;
0264 }
0265
0266 bool operator!=(const Frame &Other) const { return !operator==(Other); }
0267
0268 bool hasSymbolName() const { return !!SymbolName; }
0269
0270 StringRef getSymbolName() const {
0271 assert(hasSymbolName());
0272 return *SymbolName;
0273 }
0274
0275 std::string getSymbolNameOr(StringRef Alt) const {
0276 return std::string(hasSymbolName() ? getSymbolName() : Alt);
0277 }
0278
0279
0280 void serialize(raw_ostream &OS) const {
0281 using namespace support;
0282
0283 endian::Writer LE(OS, llvm::endianness::little);
0284
0285
0286
0287 static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value,
0288 "Expect GUID to be uint64_t.");
0289 LE.write<uint64_t>(Function);
0290
0291 LE.write<uint32_t>(LineOffset);
0292 LE.write<uint32_t>(Column);
0293 LE.write<bool>(IsInlineFrame);
0294 }
0295
0296
0297 static Frame deserialize(const unsigned char *Ptr) {
0298 using namespace support;
0299
0300 const uint64_t F =
0301 endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
0302 const uint32_t L =
0303 endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0304 const uint32_t C =
0305 endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0306 const bool I = endian::readNext<bool, llvm::endianness::little>(Ptr);
0307 return Frame(F, L, C,
0308 I);
0309 }
0310
0311
0312 static constexpr size_t serializedSize() {
0313 return sizeof(Frame::Function) + sizeof(Frame::LineOffset) +
0314 sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame);
0315 }
0316
0317
0318 void printYAML(raw_ostream &OS) const {
0319 OS << " -\n"
0320 << " Function: " << Function << "\n"
0321 << " SymbolName: " << getSymbolNameOr("<None>") << "\n"
0322 << " LineOffset: " << LineOffset << "\n"
0323 << " Column: " << Column << "\n"
0324 << " Inline: " << IsInlineFrame << "\n";
0325 }
0326 };
0327
0328
0329 using CallStackId = uint64_t;
0330
0331
0332 using LinearCallStackId = uint32_t;
0333
0334
0335
0336 struct IndexedAllocationInfo {
0337
0338
0339 CallStackId CSId = 0;
0340
0341 PortableMemInfoBlock Info;
0342
0343 IndexedAllocationInfo() = default;
0344 IndexedAllocationInfo(CallStackId CSId, const MemInfoBlock &MB,
0345 const MemProfSchema &Schema = getFullSchema())
0346 : CSId(CSId), Info(MB, Schema) {}
0347 IndexedAllocationInfo(CallStackId CSId, const PortableMemInfoBlock &MB)
0348 : CSId(CSId), Info(MB) {}
0349
0350
0351 size_t serializedSize(const MemProfSchema &Schema,
0352 IndexedVersion Version) const;
0353
0354 bool operator==(const IndexedAllocationInfo &Other) const {
0355 if (Other.Info != Info)
0356 return false;
0357
0358 if (Other.CSId != CSId)
0359 return false;
0360 return true;
0361 }
0362
0363 bool operator!=(const IndexedAllocationInfo &Other) const {
0364 return !operator==(Other);
0365 }
0366 };
0367
0368
0369
0370 struct AllocationInfo {
0371
0372 std::vector<Frame> CallStack;
0373
0374 PortableMemInfoBlock Info;
0375
0376 AllocationInfo() = default;
0377
0378 void printYAML(raw_ostream &OS) const {
0379 OS << " -\n";
0380 OS << " Callstack:\n";
0381
0382
0383 for (const Frame &F : CallStack) {
0384 F.printYAML(OS);
0385 }
0386 Info.printYAML(OS);
0387 }
0388 };
0389
0390
0391
0392
0393 struct IndexedMemProfRecord {
0394
0395
0396 llvm::SmallVector<IndexedAllocationInfo> AllocSites;
0397
0398
0399
0400
0401
0402 llvm::SmallVector<CallStackId> CallSiteIds;
0403
0404 void clear() { *this = IndexedMemProfRecord(); }
0405
0406 void merge(const IndexedMemProfRecord &Other) {
0407
0408
0409 AllocSites.append(Other.AllocSites);
0410 }
0411
0412 size_t serializedSize(const MemProfSchema &Schema,
0413 IndexedVersion Version) const;
0414
0415 bool operator==(const IndexedMemProfRecord &Other) const {
0416 if (Other.AllocSites != AllocSites)
0417 return false;
0418
0419 if (Other.CallSiteIds != CallSiteIds)
0420 return false;
0421 return true;
0422 }
0423
0424
0425
0426 void serialize(const MemProfSchema &Schema, raw_ostream &OS,
0427 IndexedVersion Version,
0428 llvm::DenseMap<CallStackId, LinearCallStackId>
0429 *MemProfCallStackIndexes = nullptr) const;
0430
0431
0432 static IndexedMemProfRecord deserialize(const MemProfSchema &Schema,
0433 const unsigned char *Buffer,
0434 IndexedVersion Version);
0435
0436
0437
0438 MemProfRecord toMemProfRecord(
0439 llvm::function_ref<std::vector<Frame>(const CallStackId)> Callback) const;
0440
0441
0442
0443
0444 static GlobalValue::GUID getGUID(const StringRef FunctionName);
0445 };
0446
0447
0448
0449
0450 struct MemProfRecord {
0451
0452 llvm::SmallVector<AllocationInfo> AllocSites;
0453
0454 llvm::SmallVector<std::vector<Frame>> CallSites;
0455
0456 MemProfRecord() = default;
0457
0458
0459 void print(llvm::raw_ostream &OS) const {
0460 if (!AllocSites.empty()) {
0461 OS << " AllocSites:\n";
0462 for (const AllocationInfo &N : AllocSites)
0463 N.printYAML(OS);
0464 }
0465
0466 if (!CallSites.empty()) {
0467 OS << " CallSites:\n";
0468 for (const std::vector<Frame> &Frames : CallSites) {
0469 for (const Frame &F : Frames) {
0470 OS << " -\n";
0471 F.printYAML(OS);
0472 }
0473 }
0474 }
0475 }
0476 };
0477
0478
0479
0480
0481
0482
0483 Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer);
0484
0485
0486 class RecordLookupTrait {
0487 public:
0488 using data_type = const IndexedMemProfRecord &;
0489 using internal_key_type = uint64_t;
0490 using external_key_type = uint64_t;
0491 using hash_value_type = uint64_t;
0492 using offset_type = uint64_t;
0493
0494 RecordLookupTrait() = delete;
0495 RecordLookupTrait(IndexedVersion V, const MemProfSchema &S)
0496 : Version(V), Schema(S) {}
0497
0498 static bool EqualKey(uint64_t A, uint64_t B) { return A == B; }
0499 static uint64_t GetInternalKey(uint64_t K) { return K; }
0500 static uint64_t GetExternalKey(uint64_t K) { return K; }
0501
0502 hash_value_type ComputeHash(uint64_t K) { return K; }
0503
0504 static std::pair<offset_type, offset_type>
0505 ReadKeyDataLength(const unsigned char *&D) {
0506 using namespace support;
0507
0508 offset_type KeyLen =
0509 endian::readNext<offset_type, llvm::endianness::little>(D);
0510 offset_type DataLen =
0511 endian::readNext<offset_type, llvm::endianness::little>(D);
0512 return std::make_pair(KeyLen, DataLen);
0513 }
0514
0515 uint64_t ReadKey(const unsigned char *D, offset_type ) {
0516 using namespace support;
0517 return endian::readNext<external_key_type, llvm::endianness::little>(D);
0518 }
0519
0520 data_type ReadData(uint64_t K, const unsigned char *D,
0521 offset_type ) {
0522 Record = IndexedMemProfRecord::deserialize(Schema, D, Version);
0523 return Record;
0524 }
0525
0526 private:
0527
0528 IndexedVersion Version;
0529
0530 MemProfSchema Schema;
0531
0532 IndexedMemProfRecord Record;
0533 };
0534
0535
0536 class RecordWriterTrait {
0537 public:
0538 using key_type = uint64_t;
0539 using key_type_ref = uint64_t;
0540
0541 using data_type = IndexedMemProfRecord;
0542 using data_type_ref = IndexedMemProfRecord &;
0543
0544 using hash_value_type = uint64_t;
0545 using offset_type = uint64_t;
0546
0547 private:
0548
0549 const MemProfSchema *Schema;
0550
0551 IndexedVersion Version;
0552
0553
0554 llvm::DenseMap<CallStackId, LinearCallStackId> *MemProfCallStackIndexes;
0555
0556 public:
0557
0558 RecordWriterTrait() = delete;
0559 RecordWriterTrait(
0560 const MemProfSchema *Schema, IndexedVersion V,
0561 llvm::DenseMap<CallStackId, LinearCallStackId> *MemProfCallStackIndexes)
0562 : Schema(Schema), Version(V),
0563 MemProfCallStackIndexes(MemProfCallStackIndexes) {}
0564
0565 static hash_value_type ComputeHash(key_type_ref K) { return K; }
0566
0567 std::pair<offset_type, offset_type>
0568 EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
0569 using namespace support;
0570
0571 endian::Writer LE(Out, llvm::endianness::little);
0572 offset_type N = sizeof(K);
0573 LE.write<offset_type>(N);
0574 offset_type M = V.serializedSize(*Schema, Version);
0575 LE.write<offset_type>(M);
0576 return std::make_pair(N, M);
0577 }
0578
0579 void EmitKey(raw_ostream &Out, key_type_ref K, offset_type ) {
0580 using namespace support;
0581 endian::Writer LE(Out, llvm::endianness::little);
0582 LE.write<uint64_t>(K);
0583 }
0584
0585 void EmitData(raw_ostream &Out, key_type_ref , data_type_ref V,
0586 offset_type ) {
0587 assert(Schema != nullptr && "MemProf schema is not initialized!");
0588 V.serialize(*Schema, Out, Version, MemProfCallStackIndexes);
0589
0590
0591
0592
0593 V.clear();
0594 }
0595 };
0596
0597
0598 class FrameWriterTrait {
0599 public:
0600 using key_type = FrameId;
0601 using key_type_ref = FrameId;
0602
0603 using data_type = Frame;
0604 using data_type_ref = Frame &;
0605
0606 using hash_value_type = FrameId;
0607 using offset_type = uint64_t;
0608
0609 static hash_value_type ComputeHash(key_type_ref K) { return K; }
0610
0611 static std::pair<offset_type, offset_type>
0612 EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
0613 using namespace support;
0614 endian::Writer LE(Out, llvm::endianness::little);
0615 offset_type N = sizeof(K);
0616 LE.write<offset_type>(N);
0617 offset_type M = V.serializedSize();
0618 LE.write<offset_type>(M);
0619 return std::make_pair(N, M);
0620 }
0621
0622 void EmitKey(raw_ostream &Out, key_type_ref K, offset_type ) {
0623 using namespace support;
0624 endian::Writer LE(Out, llvm::endianness::little);
0625 LE.write<key_type>(K);
0626 }
0627
0628 void EmitData(raw_ostream &Out, key_type_ref , data_type_ref V,
0629 offset_type ) {
0630 V.serialize(Out);
0631 }
0632 };
0633
0634
0635 class FrameLookupTrait {
0636 public:
0637 using data_type = const Frame;
0638 using internal_key_type = FrameId;
0639 using external_key_type = FrameId;
0640 using hash_value_type = FrameId;
0641 using offset_type = uint64_t;
0642
0643 static bool EqualKey(internal_key_type A, internal_key_type B) {
0644 return A == B;
0645 }
0646 static uint64_t GetInternalKey(internal_key_type K) { return K; }
0647 static uint64_t GetExternalKey(external_key_type K) { return K; }
0648
0649 hash_value_type ComputeHash(internal_key_type K) { return K; }
0650
0651 static std::pair<offset_type, offset_type>
0652 ReadKeyDataLength(const unsigned char *&D) {
0653 using namespace support;
0654
0655 offset_type KeyLen =
0656 endian::readNext<offset_type, llvm::endianness::little>(D);
0657 offset_type DataLen =
0658 endian::readNext<offset_type, llvm::endianness::little>(D);
0659 return std::make_pair(KeyLen, DataLen);
0660 }
0661
0662 uint64_t ReadKey(const unsigned char *D, offset_type ) {
0663 using namespace support;
0664 return endian::readNext<external_key_type, llvm::endianness::little>(D);
0665 }
0666
0667 data_type ReadData(uint64_t K, const unsigned char *D,
0668 offset_type ) {
0669 return Frame::deserialize(D);
0670 }
0671 };
0672
0673
0674 class CallStackWriterTrait {
0675 public:
0676 using key_type = CallStackId;
0677 using key_type_ref = CallStackId;
0678
0679 using data_type = llvm::SmallVector<FrameId>;
0680 using data_type_ref = llvm::SmallVector<FrameId> &;
0681
0682 using hash_value_type = CallStackId;
0683 using offset_type = uint64_t;
0684
0685 static hash_value_type ComputeHash(key_type_ref K) { return K; }
0686
0687 static std::pair<offset_type, offset_type>
0688 EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
0689 using namespace support;
0690 endian::Writer LE(Out, llvm::endianness::little);
0691
0692 offset_type N = sizeof(K);
0693 offset_type M = sizeof(FrameId) * V.size();
0694 LE.write<offset_type>(M);
0695 return std::make_pair(N, M);
0696 }
0697
0698 void EmitKey(raw_ostream &Out, key_type_ref K, offset_type ) {
0699 using namespace support;
0700 endian::Writer LE(Out, llvm::endianness::little);
0701 LE.write<key_type>(K);
0702 }
0703
0704 void EmitData(raw_ostream &Out, key_type_ref , data_type_ref V,
0705 offset_type ) {
0706 using namespace support;
0707 endian::Writer LE(Out, llvm::endianness::little);
0708
0709
0710 for (FrameId F : V)
0711 LE.write<FrameId>(F);
0712 }
0713 };
0714
0715
0716 class CallStackLookupTrait {
0717 public:
0718 using data_type = const llvm::SmallVector<FrameId>;
0719 using internal_key_type = CallStackId;
0720 using external_key_type = CallStackId;
0721 using hash_value_type = CallStackId;
0722 using offset_type = uint64_t;
0723
0724 static bool EqualKey(internal_key_type A, internal_key_type B) {
0725 return A == B;
0726 }
0727 static uint64_t GetInternalKey(internal_key_type K) { return K; }
0728 static uint64_t GetExternalKey(external_key_type K) { return K; }
0729
0730 hash_value_type ComputeHash(internal_key_type K) { return K; }
0731
0732 static std::pair<offset_type, offset_type>
0733 ReadKeyDataLength(const unsigned char *&D) {
0734 using namespace support;
0735
0736
0737 offset_type KeyLen = sizeof(external_key_type);
0738 offset_type DataLen =
0739 endian::readNext<offset_type, llvm::endianness::little>(D);
0740 return std::make_pair(KeyLen, DataLen);
0741 }
0742
0743 uint64_t ReadKey(const unsigned char *D, offset_type ) {
0744 using namespace support;
0745 return endian::readNext<external_key_type, llvm::endianness::little>(D);
0746 }
0747
0748 data_type ReadData(uint64_t K, const unsigned char *D, offset_type Length) {
0749 using namespace support;
0750 llvm::SmallVector<FrameId> CS;
0751
0752 uint64_t NumFrames = Length / sizeof(FrameId);
0753 assert(Length % sizeof(FrameId) == 0);
0754 CS.reserve(NumFrames);
0755 for (size_t I = 0; I != NumFrames; ++I) {
0756 FrameId F = endian::readNext<FrameId, llvm::endianness::little>(D);
0757 CS.push_back(F);
0758 }
0759 return CS;
0760 }
0761 };
0762
0763 namespace detail {
0764
0765
0766
0767 template <typename value_type, typename IterTy>
0768 value_type DerefIterator(IterTy Iter) {
0769 using deref_type = llvm::remove_cvref_t<decltype(*Iter)>;
0770 if constexpr (std::is_same_v<deref_type, value_type>)
0771 return *Iter;
0772 else
0773 return Iter->second;
0774 }
0775 }
0776
0777
0778 template <typename MapTy> struct FrameIdConverter {
0779 std::optional<FrameId> LastUnmappedId;
0780 MapTy ⤅
0781
0782 FrameIdConverter() = delete;
0783 FrameIdConverter(MapTy &Map) : Map(Map) {}
0784
0785
0786
0787
0788 FrameIdConverter(const FrameIdConverter &) = delete;
0789 FrameIdConverter &operator=(const FrameIdConverter &) = delete;
0790
0791 Frame operator()(FrameId Id) {
0792 auto Iter = Map.find(Id);
0793 if (Iter == Map.end()) {
0794 LastUnmappedId = Id;
0795 return Frame();
0796 }
0797 return detail::DerefIterator<Frame>(Iter);
0798 }
0799 };
0800
0801
0802 template <typename MapTy> struct CallStackIdConverter {
0803 std::optional<CallStackId> LastUnmappedId;
0804 MapTy ⤅
0805 llvm::function_ref<Frame(FrameId)> FrameIdToFrame;
0806
0807 CallStackIdConverter() = delete;
0808 CallStackIdConverter(MapTy &Map,
0809 llvm::function_ref<Frame(FrameId)> FrameIdToFrame)
0810 : Map(Map), FrameIdToFrame(FrameIdToFrame) {}
0811
0812
0813
0814
0815 CallStackIdConverter(const CallStackIdConverter &) = delete;
0816 CallStackIdConverter &operator=(const CallStackIdConverter &) = delete;
0817
0818 std::vector<Frame> operator()(CallStackId CSId) {
0819 std::vector<Frame> Frames;
0820 auto CSIter = Map.find(CSId);
0821 if (CSIter == Map.end()) {
0822 LastUnmappedId = CSId;
0823 } else {
0824 llvm::SmallVector<FrameId> CS =
0825 detail::DerefIterator<llvm::SmallVector<FrameId>>(CSIter);
0826 Frames.reserve(CS.size());
0827 for (FrameId Id : CS)
0828 Frames.push_back(FrameIdToFrame(Id));
0829 }
0830 return Frames;
0831 }
0832 };
0833
0834
0835
0836 struct LinearFrameIdConverter {
0837 const unsigned char *FrameBase;
0838
0839 LinearFrameIdConverter() = delete;
0840 LinearFrameIdConverter(const unsigned char *FrameBase)
0841 : FrameBase(FrameBase) {}
0842
0843 Frame operator()(LinearFrameId LinearId) {
0844 uint64_t Offset = static_cast<uint64_t>(LinearId) * Frame::serializedSize();
0845 return Frame::deserialize(FrameBase + Offset);
0846 }
0847 };
0848
0849
0850
0851 struct LinearCallStackIdConverter {
0852 const unsigned char *CallStackBase;
0853 llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame;
0854
0855 LinearCallStackIdConverter() = delete;
0856 LinearCallStackIdConverter(
0857 const unsigned char *CallStackBase,
0858 llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame)
0859 : CallStackBase(CallStackBase), FrameIdToFrame(FrameIdToFrame) {}
0860
0861 std::vector<Frame> operator()(LinearCallStackId LinearCSId) {
0862 std::vector<Frame> Frames;
0863
0864 const unsigned char *Ptr =
0865 CallStackBase +
0866 static_cast<uint64_t>(LinearCSId) * sizeof(LinearFrameId);
0867 uint32_t NumFrames =
0868 support::endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0869 Frames.reserve(NumFrames);
0870 for (; NumFrames; --NumFrames) {
0871 LinearFrameId Elem =
0872 support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0873
0874
0875 if (static_cast<std::make_signed_t<LinearFrameId>>(Elem) < 0) {
0876 Ptr += (-Elem) * sizeof(LinearFrameId);
0877 Elem =
0878 support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0879 }
0880
0881 assert(static_cast<std::make_signed_t<LinearFrameId>>(Elem) >= 0);
0882 Frames.push_back(FrameIdToFrame(Elem));
0883 Ptr += sizeof(LinearFrameId);
0884 }
0885
0886 return Frames;
0887 }
0888 };
0889
0890 struct LineLocation {
0891 LineLocation(uint32_t L, uint32_t D) : LineOffset(L), Column(D) {}
0892
0893 bool operator<(const LineLocation &O) const {
0894 return LineOffset < O.LineOffset ||
0895 (LineOffset == O.LineOffset && Column < O.Column);
0896 }
0897
0898 bool operator==(const LineLocation &O) const {
0899 return LineOffset == O.LineOffset && Column == O.Column;
0900 }
0901
0902 bool operator!=(const LineLocation &O) const {
0903 return LineOffset != O.LineOffset || Column != O.Column;
0904 }
0905
0906 uint64_t getHashCode() const { return ((uint64_t)Column << 32) | LineOffset; }
0907
0908 uint32_t LineOffset;
0909 uint32_t Column;
0910 };
0911
0912
0913 using CallEdgeTy = std::pair<LineLocation, uint64_t>;
0914
0915
0916
0917
0918
0919
0920
0921 struct CallerCalleePairExtractor {
0922
0923 const unsigned char *CallStackBase;
0924
0925 llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame;
0926
0927 DenseMap<uint64_t, SmallVector<CallEdgeTy, 0>> CallerCalleePairs;
0928
0929
0930 BitVector Visited;
0931
0932 CallerCalleePairExtractor() = delete;
0933 CallerCalleePairExtractor(
0934 const unsigned char *CallStackBase,
0935 llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame,
0936 unsigned RadixTreeSize)
0937 : CallStackBase(CallStackBase), FrameIdToFrame(FrameIdToFrame),
0938 Visited(RadixTreeSize) {}
0939
0940 void operator()(LinearCallStackId LinearCSId) {
0941 const unsigned char *Ptr =
0942 CallStackBase +
0943 static_cast<uint64_t>(LinearCSId) * sizeof(LinearFrameId);
0944 uint32_t NumFrames =
0945 support::endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0946
0947 uint64_t CalleeGUID = 0;
0948 for (; NumFrames; --NumFrames) {
0949 LinearFrameId Elem =
0950 support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0951
0952
0953 if (static_cast<std::make_signed_t<LinearFrameId>>(Elem) < 0) {
0954 Ptr += (-Elem) * sizeof(LinearFrameId);
0955 Elem =
0956 support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0957 }
0958
0959 assert(static_cast<std::make_signed_t<LinearFrameId>>(Elem) >= 0);
0960
0961
0962 Frame F = FrameIdToFrame(Elem);
0963 uint64_t CallerGUID = F.Function;
0964 LineLocation Loc(F.LineOffset, F.Column);
0965 CallerCalleePairs[CallerGUID].emplace_back(Loc, CalleeGUID);
0966
0967
0968
0969
0970 unsigned Offset =
0971 std::distance(CallStackBase, Ptr) / sizeof(LinearFrameId);
0972 if (Visited.test(Offset))
0973 break;
0974 Visited.set(Offset);
0975
0976 Ptr += sizeof(LinearFrameId);
0977 CalleeGUID = CallerGUID;
0978 }
0979 }
0980 };
0981
0982 struct IndexedMemProfData {
0983
0984
0985 llvm::MapVector<GlobalValue::GUID, IndexedMemProfRecord> Records;
0986
0987
0988
0989
0990 llvm::MapVector<FrameId, Frame> Frames;
0991
0992
0993 llvm::MapVector<CallStackId, llvm::SmallVector<FrameId>> CallStacks;
0994
0995 FrameId addFrame(const Frame &F) {
0996 const FrameId Id = hashFrame(F);
0997 Frames.try_emplace(Id, F);
0998 return Id;
0999 }
1000
1001 CallStackId addCallStack(ArrayRef<FrameId> CS) {
1002 CallStackId CSId = hashCallStack(CS);
1003 CallStacks.try_emplace(CSId, CS);
1004 return CSId;
1005 }
1006
1007 CallStackId addCallStack(SmallVector<FrameId> &&CS) {
1008 CallStackId CSId = hashCallStack(CS);
1009 CallStacks.try_emplace(CSId, std::move(CS));
1010 return CSId;
1011 }
1012
1013 private:
1014
1015
1016
1017
1018
1019 FrameId hashFrame(const Frame &F) const {
1020 llvm::HashBuilder<llvm::TruncatedBLAKE3<8>, llvm::endianness::little>
1021 HashBuilder;
1022 HashBuilder.add(F.Function, F.LineOffset, F.Column, F.IsInlineFrame);
1023 llvm::BLAKE3Result<8> Hash = HashBuilder.final();
1024 FrameId Id;
1025 std::memcpy(&Id, Hash.data(), sizeof(Hash));
1026 return Id;
1027 }
1028
1029
1030 CallStackId hashCallStack(ArrayRef<FrameId> CS) const;
1031 };
1032
1033
1034
1035 struct IndexedCallstackIdConveter {
1036 IndexedCallstackIdConveter() = delete;
1037 IndexedCallstackIdConveter(IndexedMemProfData &MemProfData)
1038 : FrameIdConv(MemProfData.Frames),
1039 CSIdConv(MemProfData.CallStacks, FrameIdConv) {}
1040
1041
1042
1043
1044 IndexedCallstackIdConveter(const IndexedCallstackIdConveter &) = delete;
1045 IndexedCallstackIdConveter &
1046 operator=(const IndexedCallstackIdConveter &) = delete;
1047
1048 std::vector<Frame> operator()(CallStackId CSId) { return CSIdConv(CSId); }
1049
1050 FrameIdConverter<decltype(IndexedMemProfData::Frames)> FrameIdConv;
1051 CallStackIdConverter<decltype(IndexedMemProfData::CallStacks)> CSIdConv;
1052 };
1053
1054 struct FrameStat {
1055
1056 uint64_t Count = 0;
1057
1058 uint64_t PositionSum = 0;
1059 };
1060
1061
1062 template <typename FrameIdTy>
1063 llvm::DenseMap<FrameIdTy, FrameStat>
1064 computeFrameHistogram(llvm::MapVector<CallStackId, llvm::SmallVector<FrameIdTy>>
1065 &MemProfCallStackData);
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122 template <typename FrameIdTy> class CallStackRadixTreeBuilder {
1123
1124 std::vector<LinearFrameId> RadixArray;
1125
1126
1127 llvm::DenseMap<CallStackId, LinearCallStackId> CallStackPos;
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147 std::vector<LinearCallStackId> Indexes;
1148
1149 using CSIdPair = std::pair<CallStackId, llvm::SmallVector<FrameIdTy>>;
1150
1151
1152
1153 LinearCallStackId encodeCallStack(
1154 const llvm::SmallVector<FrameIdTy> *CallStack,
1155 const llvm::SmallVector<FrameIdTy> *Prev,
1156 const llvm::DenseMap<FrameIdTy, LinearFrameId> *MemProfFrameIndexes);
1157
1158 public:
1159 CallStackRadixTreeBuilder() = default;
1160
1161
1162 void
1163 build(llvm::MapVector<CallStackId, llvm::SmallVector<FrameIdTy>>
1164 &&MemProfCallStackData,
1165 const llvm::DenseMap<FrameIdTy, LinearFrameId> *MemProfFrameIndexes,
1166 llvm::DenseMap<FrameIdTy, FrameStat> &FrameHistogram);
1167
1168 ArrayRef<LinearFrameId> getRadixArray() const { return RadixArray; }
1169
1170 llvm::DenseMap<CallStackId, LinearCallStackId> takeCallStackPos() {
1171 return std::move(CallStackPos);
1172 }
1173 };
1174 }
1175 }
1176
1177 #endif