Skip to content

Commit 9b67165

Browse files
committed
[memprof] Introduce a wrapper around MemInfoBlock.
Use the macro based format to add a wrapper around the MemInfoBlock when stored in the MemProfRecord. This wrapped block can then be serialized/deserialized based on a schema specified by a list of enums. Differential Revision: https://reviews.llvm.org/D117256
1 parent 9def83c commit 9b67165

File tree

4 files changed

+168
-47
lines changed

4 files changed

+168
-47
lines changed

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 125 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,134 @@
55
#include <string>
66
#include <vector>
77

8+
#include "llvm/ADT/SmallVector.h"
89
#include "llvm/ProfileData/MemProfData.inc"
10+
#include "llvm/ProfileData/ProfileCommon.h"
11+
#include "llvm/Support/Endian.h"
12+
#include "llvm/Support/EndianStream.h"
913
#include "llvm/Support/raw_ostream.h"
1014

1115
namespace llvm {
1216
namespace memprof {
1317

18+
enum class Meta : uint64_t {
19+
Start = 0,
20+
#define MIBEntryDef(NameTag, Name, Type) NameTag,
21+
#include "llvm/ProfileData/MIBEntryDef.inc"
22+
#undef MIBEntryDef
23+
Size
24+
};
25+
26+
using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>;
27+
28+
// Holds the actual MemInfoBlock data with all fields. Contents may be read or
29+
// written partially by providing an appropriate schema to the serialize and
30+
// deserialize methods.
31+
struct PortableMemInfoBlock {
32+
PortableMemInfoBlock() = default;
33+
explicit PortableMemInfoBlock(const MemInfoBlock &Block) {
34+
#define MIBEntryDef(NameTag, Name, Type) Name = Block.Name;
35+
#include "llvm/ProfileData/MIBEntryDef.inc"
36+
#undef MIBEntryDef
37+
}
38+
39+
PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) {
40+
deserialize(Schema, Ptr);
41+
}
42+
43+
// Read the contents of \p Ptr based on the \p Schema to populate the
44+
// MemInfoBlock member.
45+
void deserialize(const MemProfSchema &Schema, const unsigned char *Ptr) {
46+
using namespace support;
47+
48+
for (const Meta Id : Schema) {
49+
switch (Id) {
50+
#define MIBEntryDef(NameTag, Name, Type) \
51+
case Meta::Name: { \
52+
Name = endian::readNext<Type, little, unaligned>(Ptr); \
53+
} break;
54+
#include "llvm/ProfileData/MIBEntryDef.inc"
55+
#undef MIBEntryDef
56+
default:
57+
llvm_unreachable("Unknown meta type id, is the profile collected from "
58+
"a newer version of the runtime?");
59+
}
60+
}
61+
}
62+
63+
// Write the contents of the MemInfoBlock based on the \p Schema provided to
64+
// the raw_ostream \p OS.
65+
void serialize(const MemProfSchema &Schema, raw_ostream &OS) const {
66+
using namespace support;
67+
68+
endian::Writer LE(OS, little);
69+
for (const Meta Id : Schema) {
70+
switch (Id) {
71+
#define MIBEntryDef(NameTag, Name, Type) \
72+
case Meta::Name: { \
73+
LE.write<Type>(Name); \
74+
} break;
75+
#include "llvm/ProfileData/MIBEntryDef.inc"
76+
#undef MIBEntryDef
77+
default:
78+
llvm_unreachable("Unknown meta type id, invalid input?");
79+
}
80+
}
81+
}
82+
83+
// Print out the contents of the MemInfoBlock in YAML format.
84+
void printYAML(raw_ostream &OS) const {
85+
OS << " MemInfoBlock:\n";
86+
#define MIBEntryDef(NameTag, Name, Type) \
87+
OS << " " << #Name << ": " << Name << "\n";
88+
#include "llvm/ProfileData/MIBEntryDef.inc"
89+
#undef MIBEntryDef
90+
}
91+
92+
// Define getters for each type which can be called by analyses.
93+
#define MIBEntryDef(NameTag, Name, Type) \
94+
Type get##Name() const { return Name; }
95+
#include "llvm/ProfileData/MIBEntryDef.inc"
96+
#undef MIBEntryDef
97+
98+
void clear() { *this = PortableMemInfoBlock(); }
99+
100+
// Returns the full schema currently in use.
101+
static MemProfSchema getSchema() {
102+
MemProfSchema List;
103+
#define MIBEntryDef(NameTag, Name, Type) List.push_back(Meta::Name);
104+
#include "llvm/ProfileData/MIBEntryDef.inc"
105+
#undef MIBEntryDef
106+
return List;
107+
}
108+
109+
bool operator==(const PortableMemInfoBlock &Other) const {
110+
#define MIBEntryDef(NameTag, Name, Type) \
111+
if (Other.get##Name() != get##Name()) \
112+
return false;
113+
#include "llvm/ProfileData/MIBEntryDef.inc"
114+
#undef MIBEntryDef
115+
return true;
116+
}
117+
118+
bool operator!=(const PortableMemInfoBlock &Other) const {
119+
return !operator==(Other);
120+
}
121+
122+
static constexpr size_t serializedSize() {
123+
size_t Result = 0;
124+
#define MIBEntryDef(NameTag, Name, Type) Result += sizeof(Type);
125+
#include "llvm/ProfileData/MIBEntryDef.inc"
126+
#undef MIBEntryDef
127+
return Result;
128+
}
129+
130+
private:
131+
#define MIBEntryDef(NameTag, Name, Type) Type Name = Type();
132+
#include "llvm/ProfileData/MIBEntryDef.inc"
133+
#undef MIBEntryDef
134+
};
135+
14136
struct MemProfRecord {
15137
struct Frame {
16138
std::string Function;
@@ -24,14 +146,11 @@ struct MemProfRecord {
24146
};
25147

26148
std::vector<Frame> CallStack;
27-
// TODO: Replace this with the entry format described in the RFC so
28-
// that the InstrProfRecord reader and writer do not have to be concerned
29-
// about backwards compat.
30-
MemInfoBlock Info;
149+
PortableMemInfoBlock Info;
31150

32151
void clear() {
33152
CallStack.clear();
34-
Info = MemInfoBlock();
153+
Info.clear();
35154
}
36155

37156
// Prints out the contents of the memprof record in YAML.
@@ -47,45 +166,7 @@ struct MemProfRecord {
47166
<< " Inline: " << Frame.IsInlineFrame << "\n";
48167
}
49168

50-
OS << " MemInfoBlock:\n";
51-
52-
// TODO: Replace this once the format is updated to be version agnostic.
53-
OS << " "
54-
<< "AllocCount: " << Info.AllocCount << "\n";
55-
OS << " "
56-
<< "TotalAccessCount: " << Info.TotalAccessCount << "\n";
57-
OS << " "
58-
<< "MinAccessCount: " << Info.MinAccessCount << "\n";
59-
OS << " "
60-
<< "MaxAccessCount: " << Info.MaxAccessCount << "\n";
61-
OS << " "
62-
<< "TotalSize: " << Info.TotalSize << "\n";
63-
OS << " "
64-
<< "MinSize: " << Info.MinSize << "\n";
65-
OS << " "
66-
<< "MaxSize: " << Info.MaxSize << "\n";
67-
OS << " "
68-
<< "AllocTimestamp: " << Info.AllocTimestamp << "\n";
69-
OS << " "
70-
<< "DeallocTimestamp: " << Info.DeallocTimestamp << "\n";
71-
OS << " "
72-
<< "TotalLifetime: " << Info.TotalLifetime << "\n";
73-
OS << " "
74-
<< "MinLifetime: " << Info.MinLifetime << "\n";
75-
OS << " "
76-
<< "MaxLifetime: " << Info.MaxLifetime << "\n";
77-
OS << " "
78-
<< "AllocCpuId: " << Info.AllocCpuId << "\n";
79-
OS << " "
80-
<< "DeallocCpuId: " << Info.DeallocCpuId << "\n";
81-
OS << " "
82-
<< "NumMigratedCpu: " << Info.NumMigratedCpu << "\n";
83-
OS << " "
84-
<< "NumLifetimeOverlaps: " << Info.NumLifetimeOverlaps << "\n";
85-
OS << " "
86-
<< "NumSameAllocCpu: " << Info.NumSameAllocCpu << "\n";
87-
OS << " "
88-
<< "NumSameDeallocCpu: " << Info.NumSameDeallocCpu << "\n";
169+
Info.printYAML(OS);
89170
}
90171
};
91172

llvm/lib/ProfileData/RawMemProfReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ Error RawMemProfReader::fillRecord(const uint64_t Id, const MemInfoBlock &MIB,
368368
I != 0);
369369
}
370370
}
371-
Record.Info = MIB;
371+
Record.Info = PortableMemInfoBlock(MIB);
372372
return Error::success();
373373
}
374374

llvm/test/tools/llvm-profdata/memprof-basic.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ CHECK-NEXT: NumMigratedCpu: 1
7676
CHECK-NEXT: NumLifetimeOverlaps: 0
7777
CHECK-NEXT: NumSameAllocCpu: 0
7878
CHECK-NEXT: NumSameDeallocCpu: 0
79+
CHECK-NEXT: DataTypeId: {{[0-9]+}}
7980
CHECK-NEXT: -
8081
CHECK-NEXT: Callstack:
8182
CHECK-NEXT: -
@@ -112,6 +113,7 @@ CHECK-NEXT: NumMigratedCpu: 0
112113
CHECK-NEXT: NumLifetimeOverlaps: 0
113114
CHECK-NEXT: NumSameAllocCpu: 0
114115
CHECK-NEXT: NumSameDeallocCpu: 0
116+
CHECK-NEXT: DataTypeId: {{[0-9]+}}
115117
CHECK-NEXT: -
116118
CHECK-NEXT: Callstack:
117119
CHECK-NEXT: -
@@ -148,3 +150,4 @@ CHECK-NEXT: NumMigratedCpu: 0
148150
CHECK-NEXT: NumLifetimeOverlaps: 0
149151
CHECK-NEXT: NumSameAllocCpu: 0
150152
CHECK-NEXT: NumSameDeallocCpu: 0
153+
CHECK-NEXT: DataTypeId: {{[0-9]+}}

llvm/unittests/ProfileData/MemProfTest.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/ProfileData/RawMemProfReader.h"
1010
#include "llvm/Support/Error.h"
1111
#include "llvm/Support/MD5.h"
12+
#include "llvm/Support/raw_ostream.h"
1213
#include "gmock/gmock.h"
1314
#include "gtest/gtest.h"
1415

@@ -24,6 +25,9 @@ using ::llvm::DILocal;
2425
using ::llvm::memprof::CallStackMap;
2526
using ::llvm::memprof::MemInfoBlock;
2627
using ::llvm::memprof::MemProfRecord;
28+
using ::llvm::memprof::MemProfSchema;
29+
using ::llvm::memprof::Meta;
30+
using ::llvm::memprof::PortableMemInfoBlock;
2731
using ::llvm::memprof::RawMemProfReader;
2832
using ::llvm::memprof::SegmentEntry;
2933
using ::llvm::object::SectionedAddress;
@@ -99,6 +103,14 @@ MATCHER_P4(FrameContains, Function, LineOffset, Column, Inline, "") {
99103
return false;
100104
}
101105

106+
MemProfSchema getFullSchema() {
107+
MemProfSchema Schema;
108+
#define MIBEntryDef(NameTag, Name, Type) Schema.push_back(Meta::Name);
109+
#include "llvm/ProfileData/MIBEntryDef.inc"
110+
#undef MIBEntryDef
111+
return Schema;
112+
}
113+
102114
TEST(MemProf, FillsValue) {
103115
std::unique_ptr<MockSymbolizer> Symbolizer(new MockSymbolizer());
104116

@@ -136,8 +148,8 @@ TEST(MemProf, FillsValue) {
136148
}
137149
EXPECT_EQ(Records.size(), 2U);
138150

139-
EXPECT_EQ(Records[0].Info.AllocCount, 1U);
140-
EXPECT_EQ(Records[1].Info.AllocCount, 2U);
151+
EXPECT_EQ(Records[0].Info.getAllocCount(), 1U);
152+
EXPECT_EQ(Records[1].Info.getAllocCount(), 2U);
141153
EXPECT_THAT(Records[0].CallStack[0], FrameContains("foo", 5U, 30U, false));
142154
EXPECT_THAT(Records[0].CallStack[1], FrameContains("bar", 51U, 20U, true));
143155

@@ -147,4 +159,29 @@ TEST(MemProf, FillsValue) {
147159
EXPECT_THAT(Records[1].CallStack[3], FrameContains("bar", 51U, 20U, true));
148160
}
149161

162+
TEST(MemProf, PortableWrapper) {
163+
MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,
164+
/*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,
165+
/*dealloc_cpu=*/4);
166+
167+
const auto Schema = getFullSchema();
168+
PortableMemInfoBlock WriteBlock(Info);
169+
170+
std::string Buffer;
171+
llvm::raw_string_ostream OS(Buffer);
172+
WriteBlock.serialize(Schema, OS);
173+
OS.flush();
174+
175+
PortableMemInfoBlock ReadBlock(
176+
Schema, reinterpret_cast<const unsigned char *>(Buffer.data()));
177+
178+
EXPECT_EQ(ReadBlock, WriteBlock);
179+
// Here we compare directly with the actual counts instead of MemInfoBlock
180+
// members. Since the MemInfoBlock struct is packed and the EXPECT_EQ macros
181+
// take a reference to the params, this results in unaligned accesses.
182+
EXPECT_EQ(1, ReadBlock.getAllocCount());
183+
EXPECT_EQ(7, ReadBlock.getTotalAccessCount());
184+
EXPECT_EQ(3, ReadBlock.getAllocCpuId());
185+
}
186+
150187
} // namespace

0 commit comments

Comments
 (0)