diff --git a/llvm/include/llvm/ProfileData/MemProf.h b/llvm/include/llvm/ProfileData/MemProf.h --- a/llvm/include/llvm/ProfileData/MemProf.h +++ b/llvm/include/llvm/ProfileData/MemProf.h @@ -5,12 +5,77 @@ #include #include +#include "llvm/ADT/SmallVector.h" #include "llvm/ProfileData/MemProfData.inc" #include "llvm/Support/raw_ostream.h" namespace llvm { namespace memprof { +enum class Meta { + Start = 0, +#define MIBEntryDef(NameTag, Name, Type) NameTag, +#include "llvm/ProfileData/MIBEntryDef.inc" +#undef MIBEntryDef + Size +}; + +struct PortableMemInfoBlock { + PortableMemInfoBlock() {} + PortableMemInfoBlock(const MemInfoBlock &Block) : Info(Block) {} + PortableMemInfoBlock(const llvm::SmallVectorImpl &Schema, char *Ptr) { + for (const Meta Id : Schema) { + switch (Id) { +#define MIBEntryDef(NameTag, Name, Type) \ + case Meta::Name: { \ + Info.Name = *reinterpret_cast(Ptr); \ + Ptr += sizeof(Type); \ + } 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 llvm::SmallVectorImpl &Schema, char *Ptr) { + for (const Meta Id : Schema) { + switch (Id) { +#define MIBEntryDef(NameTag, Name, Type) \ + case Meta::Name: { \ + *(Type *)Ptr = Info.Name; \ + Ptr += sizeof(Type); \ + } 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 << ": " << Info.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() { return Info.Name; } +#include "llvm/ProfileData/MIBEntryDef.inc" +#undef MIBEntryDef + + void clear() { Info = MemInfoBlock(); } + +private: + MemInfoBlock Info; +}; + struct MemProfRecord { struct Frame { std::string Function; @@ -24,14 +89,11 @@ }; std::vector CallStack; - // TODO: Replace this with the entry format described in the RFC so - // that the InstrProfRecord reader and writer do not have to be concerned - // about backwards compat. - MemInfoBlock Info; + PortableMemInfoBlock Info; void clear() { CallStack.clear(); - Info = MemInfoBlock(); + Info.clear(); } // Prints out the contents of the memprof record in YAML. @@ -47,45 +109,7 @@ << " Inline: " << Frame.IsInlineFrame << "\n"; } - OS << " MemInfoBlock:\n"; - - // TODO: Replace this once the format is updated to be version agnostic. - OS << " " - << "AllocCount: " << Info.AllocCount << "\n"; - OS << " " - << "TotalAccessCount: " << Info.TotalAccessCount << "\n"; - OS << " " - << "MinAccessCount: " << Info.MinAccessCount << "\n"; - OS << " " - << "MaxAccessCount: " << Info.MaxAccessCount << "\n"; - OS << " " - << "TotalSize: " << Info.TotalSize << "\n"; - OS << " " - << "MinSize: " << Info.MinSize << "\n"; - OS << " " - << "MaxSize: " << Info.MaxSize << "\n"; - OS << " " - << "AllocTimestamp: " << Info.AllocTimestamp << "\n"; - OS << " " - << "DeallocTimestamp: " << Info.DeallocTimestamp << "\n"; - OS << " " - << "TotalLifetime: " << Info.TotalLifetime << "\n"; - OS << " " - << "MinLifetime: " << Info.MinLifetime << "\n"; - OS << " " - << "MaxLifetime: " << Info.MaxLifetime << "\n"; - OS << " " - << "AllocCpuId: " << Info.AllocCpuId << "\n"; - OS << " " - << "DeallocCpuId: " << Info.DeallocCpuId << "\n"; - OS << " " - << "NumMigratedCpu: " << Info.NumMigratedCpu << "\n"; - OS << " " - << "NumLifetimeOverlaps: " << Info.NumLifetimeOverlaps << "\n"; - OS << " " - << "NumSameAllocCpu: " << Info.NumSameAllocCpu << "\n"; - OS << " " - << "NumSameDeallocCpu: " << Info.NumSameDeallocCpu << "\n"; + Info.printYAML(OS); } }; diff --git a/llvm/test/tools/llvm-profdata/memprof-basic.test b/llvm/test/tools/llvm-profdata/memprof-basic.test --- a/llvm/test/tools/llvm-profdata/memprof-basic.test +++ b/llvm/test/tools/llvm-profdata/memprof-basic.test @@ -76,6 +76,7 @@ CHECK-NEXT: NumLifetimeOverlaps: 0 CHECK-NEXT: NumSameAllocCpu: 0 CHECK-NEXT: NumSameDeallocCpu: 0 +CHECK-NEXT: DataTypeId: {{[0-9]+}} CHECK-NEXT: - CHECK-NEXT: Callstack: CHECK-NEXT: - @@ -112,6 +113,7 @@ CHECK-NEXT: NumLifetimeOverlaps: 0 CHECK-NEXT: NumSameAllocCpu: 0 CHECK-NEXT: NumSameDeallocCpu: 0 +CHECK-NEXT: DataTypeId: {{[0-9]+}} CHECK-NEXT: - CHECK-NEXT: Callstack: CHECK-NEXT: - @@ -148,3 +150,4 @@ CHECK-NEXT: NumLifetimeOverlaps: 0 CHECK-NEXT: NumSameAllocCpu: 0 CHECK-NEXT: NumSameDeallocCpu: 0 +CHECK-NEXT: DataTypeId: {{[0-9]+}} diff --git a/llvm/unittests/ProfileData/MemProfTest.cpp b/llvm/unittests/ProfileData/MemProfTest.cpp --- a/llvm/unittests/ProfileData/MemProfTest.cpp +++ b/llvm/unittests/ProfileData/MemProfTest.cpp @@ -24,6 +24,8 @@ using ::llvm::memprof::CallStackMap; using ::llvm::memprof::MemInfoBlock; using ::llvm::memprof::MemProfRecord; +using ::llvm::memprof::Meta; +using ::llvm::memprof::PortableMemInfoBlock; using ::llvm::memprof::RawMemProfReader; using ::llvm::memprof::SegmentEntry; using ::llvm::object::SectionedAddress; @@ -135,8 +137,8 @@ } EXPECT_EQ(Records.size(), 2U); - EXPECT_EQ(Records[0].Info.AllocCount, 1U); - EXPECT_EQ(Records[1].Info.AllocCount, 2U); + EXPECT_EQ(Records[0].Info.getAllocCount(), 1U); + EXPECT_EQ(Records[1].Info.getAllocCount(), 2U); EXPECT_THAT(Records[0].CallStack[0], FrameContains("foo", 5U, 30U, false)); EXPECT_THAT(Records[0].CallStack[1], FrameContains("bar", 51U, 20U, true)); @@ -146,4 +148,26 @@ EXPECT_THAT(Records[1].CallStack[3], FrameContains("bar", 51U, 20U, true)); } +TEST(MemProf, PortableWrapper) { + MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000, + /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3, + /*dealloc_cpu=*/4); + + llvm::SmallVector(Meta::Size)> Schema; +#define MIBEntryDef(NameTag, Name, Type) Schema.push_back(Meta::Name); +#include "llvm/ProfileData/MIBEntryDef.inc" +#undef MIBEntryDef + + PortableMemInfoBlock Block(Schema, reinterpret_cast(&Info)); + + EXPECT_EQ(Block.getAllocCount(), Info.AllocCount); + EXPECT_EQ(Block.getTotalAccessCount(), Info.MaxAccessCount); + EXPECT_EQ(Block.getAllocCpuId(), Info.AllocCpuId); + + char Output[sizeof(MemInfoBlock)] = {0}; + Block.serialize(Schema, Output); + + EXPECT_EQ(*reinterpret_cast(Output), Info); +} + } // namespace