123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- #ifndef LLVM_PROFILEDATA_MEMPROF_H_
- #define LLVM_PROFILEDATA_MEMPROF_H_
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/STLFunctionalExtras.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/IR/GlobalValue.h"
- #include "llvm/ProfileData/MemProfData.inc"
- #include "llvm/Support/Endian.h"
- #include "llvm/Support/EndianStream.h"
- #include "llvm/Support/raw_ostream.h"
- #include <cstdint>
- #include <optional>
- namespace llvm {
- namespace memprof {
- enum class Meta : uint64_t {
- Start = 0,
- #define MIBEntryDef(NameTag, Name, Type) NameTag,
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- Size
- };
- using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>;
- // Holds the actual MemInfoBlock data with all fields. Contents may be read or
- // written partially by providing an appropriate schema to the serialize and
- // deserialize methods.
- struct PortableMemInfoBlock {
- PortableMemInfoBlock() = default;
- explicit PortableMemInfoBlock(const MemInfoBlock &Block) {
- #define MIBEntryDef(NameTag, Name, Type) Name = Block.Name;
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- }
- PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) {
- deserialize(Schema, Ptr);
- }
- // Read the contents of \p Ptr based on the \p Schema to populate the
- // MemInfoBlock member.
- void deserialize(const MemProfSchema &Schema, const unsigned char *Ptr) {
- using namespace support;
- for (const Meta Id : Schema) {
- switch (Id) {
- #define MIBEntryDef(NameTag, Name, Type) \
- case Meta::Name: { \
- Name = endian::readNext<Type, little, unaligned>(Ptr); \
- } break;
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- default:
- llvm_unreachable("Unknown meta type id, is the profile collected from "
- "a newer version of the runtime?");
- }
- }
- }
- // Write the contents of the MemInfoBlock based on the \p Schema provided to
- // the raw_ostream \p OS.
- void serialize(const MemProfSchema &Schema, raw_ostream &OS) const {
- using namespace support;
- endian::Writer LE(OS, little);
- for (const Meta Id : Schema) {
- switch (Id) {
- #define MIBEntryDef(NameTag, Name, Type) \
- case Meta::Name: { \
- LE.write<Type>(Name); \
- } break;
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- default:
- llvm_unreachable("Unknown meta type id, invalid input?");
- }
- }
- }
- // Print out the contents of the MemInfoBlock in YAML format.
- void printYAML(raw_ostream &OS) const {
- OS << " MemInfoBlock:\n";
- #define MIBEntryDef(NameTag, Name, Type) \
- OS << " " << #Name << ": " << Name << "\n";
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- }
- // Define getters for each type which can be called by analyses.
- #define MIBEntryDef(NameTag, Name, Type) \
- Type get##Name() const { return Name; }
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- void clear() { *this = PortableMemInfoBlock(); }
- // Returns the full schema currently in use.
- static MemProfSchema getSchema() {
- MemProfSchema List;
- #define MIBEntryDef(NameTag, Name, Type) List.push_back(Meta::Name);
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- return List;
- }
- bool operator==(const PortableMemInfoBlock &Other) const {
- #define MIBEntryDef(NameTag, Name, Type) \
- if (Other.get##Name() != get##Name()) \
- return false;
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- return true;
- }
- bool operator!=(const PortableMemInfoBlock &Other) const {
- return !operator==(Other);
- }
- static constexpr size_t serializedSize() {
- size_t Result = 0;
- #define MIBEntryDef(NameTag, Name, Type) Result += sizeof(Type);
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- return Result;
- }
- private:
- #define MIBEntryDef(NameTag, Name, Type) Type Name = Type();
- #include "llvm/ProfileData/MIBEntryDef.inc"
- #undef MIBEntryDef
- };
- // A type representing the id generated by hashing the contents of the Frame.
- using FrameId = uint64_t;
- // Describes a call frame for a dynamic allocation context. The contents of
- // the frame are populated by symbolizing the stack depot call frame from the
- // compiler runtime.
- struct Frame {
- // A uuid (uint64_t) identifying the function. It is obtained by
- // llvm::md5(FunctionName) which returns the lower 64 bits.
- GlobalValue::GUID Function;
- // The symbol name for the function. Only populated in the Frame by the reader
- // if requested during initialization. This field should not be serialized.
- std::optional<std::string> SymbolName;
- // The source line offset of the call from the beginning of parent function.
- uint32_t LineOffset;
- // The source column number of the call to help distinguish multiple calls
- // on the same line.
- uint32_t Column;
- // Whether the current frame is inlined.
- bool IsInlineFrame;
- Frame(const Frame &Other) {
- Function = Other.Function;
- SymbolName = Other.SymbolName;
- LineOffset = Other.LineOffset;
- Column = Other.Column;
- IsInlineFrame = Other.IsInlineFrame;
- }
- Frame(uint64_t Hash, uint32_t Off, uint32_t Col, bool Inline)
- : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {}
- bool operator==(const Frame &Other) const {
- // Ignore the SymbolName field to avoid a string compare. Comparing the
- // function hash serves the same purpose.
- return Other.Function == Function && Other.LineOffset == LineOffset &&
- Other.Column == Column && Other.IsInlineFrame == IsInlineFrame;
- }
- Frame &operator=(const Frame &Other) {
- Function = Other.Function;
- SymbolName = Other.SymbolName;
- LineOffset = Other.LineOffset;
- Column = Other.Column;
- IsInlineFrame = Other.IsInlineFrame;
- return *this;
- }
- bool operator!=(const Frame &Other) const { return !operator==(Other); }
- // Write the contents of the frame to the ostream \p OS.
- void serialize(raw_ostream &OS) const {
- using namespace support;
- endian::Writer LE(OS, little);
- // If the type of the GlobalValue::GUID changes, then we need to update
- // the reader and the writer.
- static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value,
- "Expect GUID to be uint64_t.");
- LE.write<uint64_t>(Function);
- LE.write<uint32_t>(LineOffset);
- LE.write<uint32_t>(Column);
- LE.write<bool>(IsInlineFrame);
- }
- // Read a frame from char data which has been serialized as little endian.
- static Frame deserialize(const unsigned char *Ptr) {
- using namespace support;
- const uint64_t F = endian::readNext<uint64_t, little, unaligned>(Ptr);
- const uint32_t L = endian::readNext<uint32_t, little, unaligned>(Ptr);
- const uint32_t C = endian::readNext<uint32_t, little, unaligned>(Ptr);
- const bool I = endian::readNext<bool, little, unaligned>(Ptr);
- return Frame(/*Function=*/F, /*LineOffset=*/L, /*Column=*/C,
- /*IsInlineFrame=*/I);
- }
- // Returns the size of the frame information.
- static constexpr size_t serializedSize() {
- return sizeof(Frame::Function) + sizeof(Frame::LineOffset) +
- sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame);
- }
- // Print the frame information in YAML format.
- void printYAML(raw_ostream &OS) const {
- OS << " -\n"
- << " Function: " << Function << "\n"
- << " SymbolName: " << SymbolName.value_or("<None>") << "\n"
- << " LineOffset: " << LineOffset << "\n"
- << " Column: " << Column << "\n"
- << " Inline: " << IsInlineFrame << "\n";
- }
- // Return a hash value based on the contents of the frame. Here we don't use
- // hashing from llvm ADT since we are going to persist the hash id, the hash
- // combine algorithm in ADT uses a new randomized seed each time.
- inline FrameId hash() const {
- auto HashCombine = [](auto Value, size_t Seed) {
- std::hash<decltype(Value)> Hasher;
- // The constant used below is the 64 bit representation of the fractional
- // part of the golden ratio. Used here for the randomness in their bit
- // pattern.
- return Hasher(Value) + 0x9e3779b97f4a7c15 + (Seed << 6) + (Seed >> 2);
- };
- size_t Result = 0;
- Result ^= HashCombine(Function, Result);
- Result ^= HashCombine(LineOffset, Result);
- Result ^= HashCombine(Column, Result);
- Result ^= HashCombine(IsInlineFrame, Result);
- return static_cast<FrameId>(Result);
- }
- };
- // Holds allocation information in a space efficient format where frames are
- // represented using unique identifiers.
- struct IndexedAllocationInfo {
- // The dynamic calling context for the allocation in bottom-up (leaf-to-root)
- // order. Frame contents are stored out-of-line.
- llvm::SmallVector<FrameId> CallStack;
- // The statistics obtained from the runtime for the allocation.
- PortableMemInfoBlock Info;
- IndexedAllocationInfo() = default;
- IndexedAllocationInfo(ArrayRef<FrameId> CS, const MemInfoBlock &MB)
- : CallStack(CS.begin(), CS.end()), Info(MB) {}
- // Returns the size in bytes when this allocation info struct is serialized.
- size_t serializedSize() const {
- return sizeof(uint64_t) + // The number of frames to serialize.
- sizeof(FrameId) * CallStack.size() + // The callstack frame ids.
- PortableMemInfoBlock::serializedSize(); // The size of the payload.
- }
- bool operator==(const IndexedAllocationInfo &Other) const {
- if (Other.Info != Info)
- return false;
- if (Other.CallStack.size() != CallStack.size())
- return false;
- for (size_t J = 0; J < Other.CallStack.size(); J++) {
- if (Other.CallStack[J] != CallStack[J])
- return false;
- }
- return true;
- }
- bool operator!=(const IndexedAllocationInfo &Other) const {
- return !operator==(Other);
- }
- };
- // Holds allocation information with frame contents inline. The type should
- // be used for temporary in-memory instances.
- struct AllocationInfo {
- // Same as IndexedAllocationInfo::CallStack with the frame contents inline.
- llvm::SmallVector<Frame> CallStack;
- // Same as IndexedAllocationInfo::Info;
- PortableMemInfoBlock Info;
- AllocationInfo() = default;
- AllocationInfo(
- const IndexedAllocationInfo &IndexedAI,
- llvm::function_ref<const Frame(const FrameId)> IdToFrameCallback) {
- for (const FrameId &Id : IndexedAI.CallStack) {
- CallStack.push_back(IdToFrameCallback(Id));
- }
- Info = IndexedAI.Info;
- }
- void printYAML(raw_ostream &OS) const {
- OS << " -\n";
- OS << " Callstack:\n";
- // TODO: Print out the frame on one line with to make it easier for deep
- // callstacks once we have a test to check valid YAML is generated.
- for (const Frame &F : CallStack) {
- F.printYAML(OS);
- }
- Info.printYAML(OS);
- }
- };
- // Holds the memprof profile information for a function. The internal
- // representation stores frame ids for efficiency. This representation should
- // be used in the profile conversion and manipulation tools.
- struct IndexedMemProfRecord {
- // Memory allocation sites in this function for which we have memory
- // profiling data.
- llvm::SmallVector<IndexedAllocationInfo> AllocSites;
- // Holds call sites in this function which are part of some memory
- // allocation context. We store this as a list of locations, each with its
- // list of inline locations in bottom-up order i.e. from leaf to root. The
- // inline location list may include additional entries, users should pick
- // the last entry in the list with the same function GUID.
- llvm::SmallVector<llvm::SmallVector<FrameId>> CallSites;
- void clear() {
- AllocSites.clear();
- CallSites.clear();
- }
- void merge(const IndexedMemProfRecord &Other) {
- // TODO: Filter out duplicates which may occur if multiple memprof
- // profiles are merged together using llvm-profdata.
- AllocSites.append(Other.AllocSites);
- CallSites.append(Other.CallSites);
- }
- size_t serializedSize() const {
- size_t Result = sizeof(GlobalValue::GUID);
- for (const IndexedAllocationInfo &N : AllocSites)
- Result += N.serializedSize();
- // The number of callsites we have information for.
- Result += sizeof(uint64_t);
- for (const auto &Frames : CallSites) {
- // The number of frame ids to serialize.
- Result += sizeof(uint64_t);
- Result += Frames.size() * sizeof(FrameId);
- }
- return Result;
- }
- bool operator==(const IndexedMemProfRecord &Other) const {
- if (Other.AllocSites.size() != AllocSites.size())
- return false;
- if (Other.CallSites.size() != CallSites.size())
- return false;
- for (size_t I = 0; I < AllocSites.size(); I++) {
- if (AllocSites[I] != Other.AllocSites[I])
- return false;
- }
- for (size_t I = 0; I < CallSites.size(); I++) {
- if (CallSites[I] != Other.CallSites[I])
- return false;
- }
- return true;
- }
- // Serializes the memprof records in \p Records to the ostream \p OS based
- // on the schema provided in \p Schema.
- void serialize(const MemProfSchema &Schema, raw_ostream &OS);
- // Deserializes memprof records from the Buffer.
- static IndexedMemProfRecord deserialize(const MemProfSchema &Schema,
- const unsigned char *Buffer);
- // Returns the GUID for the function name after canonicalization. For
- // memprof, we remove any .llvm suffix added by LTO. MemProfRecords are
- // mapped to functions using this GUID.
- static GlobalValue::GUID getGUID(const StringRef FunctionName);
- };
- // Holds the memprof profile information for a function. The internal
- // representation stores frame contents inline. This representation should
- // be used for small amount of temporary, in memory instances.
- struct MemProfRecord {
- // Same as IndexedMemProfRecord::AllocSites with frame contents inline.
- llvm::SmallVector<AllocationInfo> AllocSites;
- // Same as IndexedMemProfRecord::CallSites with frame contents inline.
- llvm::SmallVector<llvm::SmallVector<Frame>> CallSites;
- MemProfRecord() = default;
- MemProfRecord(
- const IndexedMemProfRecord &Record,
- llvm::function_ref<const Frame(const FrameId Id)> IdToFrameCallback) {
- for (const IndexedAllocationInfo &IndexedAI : Record.AllocSites) {
- AllocSites.emplace_back(IndexedAI, IdToFrameCallback);
- }
- for (const ArrayRef<FrameId> Site : Record.CallSites) {
- llvm::SmallVector<Frame> Frames;
- for (const FrameId Id : Site) {
- Frames.push_back(IdToFrameCallback(Id));
- }
- CallSites.push_back(Frames);
- }
- }
- // Prints out the contents of the memprof record in YAML.
- void print(llvm::raw_ostream &OS) const {
- if (!AllocSites.empty()) {
- OS << " AllocSites:\n";
- for (const AllocationInfo &N : AllocSites)
- N.printYAML(OS);
- }
- if (!CallSites.empty()) {
- OS << " CallSites:\n";
- for (const llvm::SmallVector<Frame> &Frames : CallSites) {
- for (const Frame &F : Frames) {
- OS << " -\n";
- F.printYAML(OS);
- }
- }
- }
- }
- };
- // Reads a memprof schema from a buffer. All entries in the buffer are
- // interpreted as uint64_t. The first entry in the buffer denotes the number of
- // ids in the schema. Subsequent entries are integers which map to memprof::Meta
- // enum class entries. After successfully reading the schema, the pointer is one
- // byte past the schema contents.
- Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer);
- // Trait for reading IndexedMemProfRecord data from the on-disk hash table.
- class RecordLookupTrait {
- public:
- using data_type = const IndexedMemProfRecord &;
- using internal_key_type = uint64_t;
- using external_key_type = uint64_t;
- using hash_value_type = uint64_t;
- using offset_type = uint64_t;
- RecordLookupTrait() = delete;
- RecordLookupTrait(const MemProfSchema &S) : Schema(S) {}
- static bool EqualKey(uint64_t A, uint64_t B) { return A == B; }
- static uint64_t GetInternalKey(uint64_t K) { return K; }
- static uint64_t GetExternalKey(uint64_t K) { return K; }
- hash_value_type ComputeHash(uint64_t K) { return K; }
- static std::pair<offset_type, offset_type>
- ReadKeyDataLength(const unsigned char *&D) {
- using namespace support;
- offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D);
- offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D);
- return std::make_pair(KeyLen, DataLen);
- }
- uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
- using namespace support;
- return endian::readNext<external_key_type, little, unaligned>(D);
- }
- data_type ReadData(uint64_t K, const unsigned char *D,
- offset_type /*Unused*/) {
- Record = IndexedMemProfRecord::deserialize(Schema, D);
- return Record;
- }
- private:
- // Holds the memprof schema used to deserialize records.
- MemProfSchema Schema;
- // Holds the records from one function deserialized from the indexed format.
- IndexedMemProfRecord Record;
- };
- // Trait for writing IndexedMemProfRecord data to the on-disk hash table.
- class RecordWriterTrait {
- public:
- using key_type = uint64_t;
- using key_type_ref = uint64_t;
- using data_type = IndexedMemProfRecord;
- using data_type_ref = IndexedMemProfRecord &;
- using hash_value_type = uint64_t;
- using offset_type = uint64_t;
- // Pointer to the memprof schema to use for the generator. Unlike the reader
- // we must use a default constructor with no params for the writer trait so we
- // have a public member which must be initialized by the user.
- MemProfSchema *Schema = nullptr;
- RecordWriterTrait() = default;
- static hash_value_type ComputeHash(key_type_ref K) { return K; }
- static std::pair<offset_type, offset_type>
- EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
- using namespace support;
- endian::Writer LE(Out, little);
- offset_type N = sizeof(K);
- LE.write<offset_type>(N);
- offset_type M = V.serializedSize();
- LE.write<offset_type>(M);
- return std::make_pair(N, M);
- }
- void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
- using namespace support;
- endian::Writer LE(Out, little);
- LE.write<uint64_t>(K);
- }
- void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
- offset_type /*Unused*/) {
- assert(Schema != nullptr && "MemProf schema is not initialized!");
- V.serialize(*Schema, Out);
- }
- };
- // Trait for writing frame mappings to the on-disk hash table.
- class FrameWriterTrait {
- public:
- using key_type = FrameId;
- using key_type_ref = FrameId;
- using data_type = Frame;
- using data_type_ref = Frame &;
- using hash_value_type = FrameId;
- using offset_type = uint64_t;
- static hash_value_type ComputeHash(key_type_ref K) { return K; }
- static std::pair<offset_type, offset_type>
- EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
- using namespace support;
- endian::Writer LE(Out, little);
- offset_type N = sizeof(K);
- LE.write<offset_type>(N);
- offset_type M = V.serializedSize();
- LE.write<offset_type>(M);
- return std::make_pair(N, M);
- }
- void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
- using namespace support;
- endian::Writer LE(Out, little);
- LE.write<key_type>(K);
- }
- void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
- offset_type /*Unused*/) {
- V.serialize(Out);
- }
- };
- // Trait for reading frame mappings from the on-disk hash table.
- class FrameLookupTrait {
- public:
- using data_type = const Frame;
- using internal_key_type = FrameId;
- using external_key_type = FrameId;
- using hash_value_type = FrameId;
- using offset_type = uint64_t;
- static bool EqualKey(internal_key_type A, internal_key_type B) {
- return A == B;
- }
- static uint64_t GetInternalKey(internal_key_type K) { return K; }
- static uint64_t GetExternalKey(external_key_type K) { return K; }
- hash_value_type ComputeHash(internal_key_type K) { return K; }
- static std::pair<offset_type, offset_type>
- ReadKeyDataLength(const unsigned char *&D) {
- using namespace support;
- offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D);
- offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D);
- return std::make_pair(KeyLen, DataLen);
- }
- uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
- using namespace support;
- return endian::readNext<external_key_type, little, unaligned>(D);
- }
- data_type ReadData(uint64_t K, const unsigned char *D,
- offset_type /*Unused*/) {
- return Frame::deserialize(D);
- }
- };
- } // namespace memprof
- } // namespace llvm
- #endif // LLVM_PROFILEDATA_MEMPROF_H_
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|