#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>
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)>;
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);
}
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?");
}
}
}
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?");
}
}
}
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 MIBEntryDef(NameTag, Name, Type) \
Type get##Name() const { return Name; }
#include "llvm/ProfileData/MIBEntryDef.inc"
#undef MIBEntryDef
void clear() { *this = PortableMemInfoBlock(); }
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
};
using FrameId = uint64_t;
struct Frame {
GlobalValue::GUID Function;
llvm::Optional<std::string> SymbolName;
uint32_t LineOffset;
uint32_t Column;
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 {
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); }
void serialize(raw_ostream &OS) const {
using namespace support;
endian::Writer LE(OS, little);
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);
}
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(F, L, C,
I);
}
static constexpr size_t serializedSize() {
return sizeof(Frame::Function) + sizeof(Frame::LineOffset) +
sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame);
}
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";
}
inline FrameId hash() const {
auto HashCombine = [](auto Value, size_t Seed) {
std::hash<decltype(Value)> Hasher;
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);
}
};
struct IndexedAllocationInfo {
llvm::SmallVector<FrameId> CallStack;
PortableMemInfoBlock Info;
IndexedAllocationInfo() = default;
IndexedAllocationInfo(ArrayRef<FrameId> CS, const MemInfoBlock &MB)
: CallStack(CS.begin(), CS.end()), Info(MB) {}
size_t serializedSize() const {
return sizeof(uint64_t) + sizeof(FrameId) * CallStack.size() + PortableMemInfoBlock::serializedSize(); }
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);
}
};
struct AllocationInfo {
llvm::SmallVector<Frame> CallStack;
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";
for (const Frame &F : CallStack) {
F.printYAML(OS);
}
Info.printYAML(OS);
}
};
struct IndexedMemProfRecord {
llvm::SmallVector<IndexedAllocationInfo> AllocSites;
llvm::SmallVector<llvm::SmallVector<FrameId>> CallSites;
void clear() {
AllocSites.clear();
CallSites.clear();
}
void merge(const IndexedMemProfRecord &Other) {
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();
Result += sizeof(uint64_t);
for (const auto &Frames : CallSites) {
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;
}
void serialize(const MemProfSchema &Schema, raw_ostream &OS);
static IndexedMemProfRecord deserialize(const MemProfSchema &Schema,
const unsigned char *Buffer);
static GlobalValue::GUID getGUID(const StringRef FunctionName);
};
struct MemProfRecord {
llvm::SmallVector<AllocationInfo> AllocSites;
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);
}
}
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);
}
}
}
}
};
Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer);
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 ) {
using namespace support;
return endian::readNext<external_key_type, little, unaligned>(D);
}
data_type ReadData(uint64_t K, const unsigned char *D,
offset_type ) {
Record = IndexedMemProfRecord::deserialize(Schema, D);
return Record;
}
private:
MemProfSchema Schema;
IndexedMemProfRecord Record;
};
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;
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 ) {
using namespace support;
endian::Writer LE(Out, little);
LE.write<uint64_t>(K);
}
void EmitData(raw_ostream &Out, key_type_ref , data_type_ref V,
offset_type ) {
assert(Schema != nullptr && "MemProf schema is not initialized!");
V.serialize(*Schema, Out);
}
};
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 ) {
using namespace support;
endian::Writer LE(Out, little);
LE.write<key_type>(K);
}
void EmitData(raw_ostream &Out, key_type_ref , data_type_ref V,
offset_type ) {
V.serialize(Out);
}
};
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 ) {
using namespace support;
return endian::readNext<external_key_type, little, unaligned>(D);
}
data_type ReadData(uint64_t K, const unsigned char *D,
offset_type ) {
return Frame::deserialize(D);
}
};
} }
#endif