Skip to content

Commit 8c1bd67

Browse files
[MemProf] Optionally print or record the profiled sizes of allocations (llvm#98248)
This is the first step in being able to track the total profiled sizes of allocations successfully marked as cold. Under a new option -memprof-report-hinted-sizes: - For unambiguous (non-context-sensitive) allocations, print the profiled size and the allocation coldness, along with a hash of the allocation's location (to allow for deduplication across modules or inline instances). - For context sensitive allocations, add the size as a 3rd operand on the MIB metadata. A follow on patch will propagate this through to the thin link where the sizes will be reported for each context after cloning.
1 parent 3c8b18b commit 8c1bd67

File tree

6 files changed

+76
-20
lines changed

6 files changed

+76
-20
lines changed

llvm/include/llvm/Analysis/MemoryProfileInfo.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ MDNode *getMIBStackNode(const MDNode *MIB);
3737
/// Returns the allocation type from an MIB metadata node.
3838
AllocationType getMIBAllocType(const MDNode *MIB);
3939

40+
/// Returns the total size from an MIB metadata node, or 0 if it was not
41+
/// recorded.
42+
uint64_t getMIBTotalSize(const MDNode *MIB);
43+
4044
/// Returns the string to use in attributes with the given type.
4145
std::string getAllocTypeAttributeString(AllocationType Type);
4246

@@ -54,10 +58,11 @@ class CallStackTrie {
5458
// Allocation types for call context sharing the context prefix at this
5559
// node.
5660
uint8_t AllocTypes;
61+
uint64_t TotalSize;
5762
// Map of caller stack id to the corresponding child Trie node.
5863
std::map<uint64_t, CallStackTrieNode *> Callers;
59-
CallStackTrieNode(AllocationType Type)
60-
: AllocTypes(static_cast<uint8_t>(Type)) {}
64+
CallStackTrieNode(AllocationType Type, uint64_t TotalSize)
65+
: AllocTypes(static_cast<uint8_t>(Type)), TotalSize(TotalSize) {}
6166
};
6267

6368
// The node for the allocation at the root.
@@ -90,7 +95,8 @@ class CallStackTrie {
9095
/// matching via a debug location hash), expected to be in order from the
9196
/// allocation call down to the bottom of the call stack (i.e. callee to
9297
/// caller order).
93-
void addCallStack(AllocationType AllocType, ArrayRef<uint64_t> StackIds);
98+
void addCallStack(AllocationType AllocType, ArrayRef<uint64_t> StackIds,
99+
uint64_t TotalSize = 0);
94100

95101
/// Add the call stack context along with its allocation type from the MIB
96102
/// metadata to the Trie.

llvm/lib/Analysis/MemoryProfileInfo.cpp

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ cl::opt<unsigned> MemProfMinAveLifetimeAccessDensityHotThreshold(
4141
cl::desc("The minimum TotalLifetimeAccessDensity / AllocCount for an "
4242
"allocation to be considered hot"));
4343

44+
cl::opt<bool> MemProfReportHintedSizes(
45+
"memprof-report-hinted-sizes", cl::init(false), cl::Hidden,
46+
cl::desc("Report total allocation sizes of hinted allocations"));
47+
4448
AllocationType llvm::memprof::getAllocType(uint64_t TotalLifetimeAccessDensity,
4549
uint64_t AllocCount,
4650
uint64_t TotalLifetime) {
@@ -74,13 +78,13 @@ MDNode *llvm::memprof::buildCallstackMetadata(ArrayRef<uint64_t> CallStack,
7478
}
7579

7680
MDNode *llvm::memprof::getMIBStackNode(const MDNode *MIB) {
77-
assert(MIB->getNumOperands() == 2);
81+
assert(MIB->getNumOperands() >= 2);
7882
// The stack metadata is the first operand of each memprof MIB metadata.
7983
return cast<MDNode>(MIB->getOperand(0));
8084
}
8185

8286
AllocationType llvm::memprof::getMIBAllocType(const MDNode *MIB) {
83-
assert(MIB->getNumOperands() == 2);
87+
assert(MIB->getNumOperands() >= 2);
8488
// The allocation type is currently the second operand of each memprof
8589
// MIB metadata. This will need to change as we add additional allocation
8690
// types that can be applied based on the allocation profile data.
@@ -94,6 +98,12 @@ AllocationType llvm::memprof::getMIBAllocType(const MDNode *MIB) {
9498
return AllocationType::NotCold;
9599
}
96100

101+
uint64_t llvm::memprof::getMIBTotalSize(const MDNode *MIB) {
102+
if (MIB->getNumOperands() < 3)
103+
return 0;
104+
return mdconst::dyn_extract<ConstantInt>(MIB->getOperand(2))->getZExtValue();
105+
}
106+
97107
std::string llvm::memprof::getAllocTypeAttributeString(AllocationType Type) {
98108
switch (Type) {
99109
case AllocationType::NotCold:
@@ -125,7 +135,8 @@ bool llvm::memprof::hasSingleAllocType(uint8_t AllocTypes) {
125135
}
126136

127137
void CallStackTrie::addCallStack(AllocationType AllocType,
128-
ArrayRef<uint64_t> StackIds) {
138+
ArrayRef<uint64_t> StackIds,
139+
uint64_t TotalSize) {
129140
bool First = true;
130141
CallStackTrieNode *Curr = nullptr;
131142
for (auto StackId : StackIds) {
@@ -135,9 +146,10 @@ void CallStackTrie::addCallStack(AllocationType AllocType,
135146
if (Alloc) {
136147
assert(AllocStackId == StackId);
137148
Alloc->AllocTypes |= static_cast<uint8_t>(AllocType);
149+
Alloc->TotalSize += TotalSize;
138150
} else {
139151
AllocStackId = StackId;
140-
Alloc = new CallStackTrieNode(AllocType);
152+
Alloc = new CallStackTrieNode(AllocType, TotalSize);
141153
}
142154
Curr = Alloc;
143155
continue;
@@ -147,10 +159,11 @@ void CallStackTrie::addCallStack(AllocationType AllocType,
147159
if (Next != Curr->Callers.end()) {
148160
Curr = Next->second;
149161
Curr->AllocTypes |= static_cast<uint8_t>(AllocType);
162+
Curr->TotalSize += TotalSize;
150163
continue;
151164
}
152165
// Otherwise add a new caller node.
153-
auto *New = new CallStackTrieNode(AllocType);
166+
auto *New = new CallStackTrieNode(AllocType, TotalSize);
154167
Curr->Callers[StackId] = New;
155168
Curr = New;
156169
}
@@ -167,16 +180,19 @@ void CallStackTrie::addCallStack(MDNode *MIB) {
167180
assert(StackId);
168181
CallStack.push_back(StackId->getZExtValue());
169182
}
170-
addCallStack(getMIBAllocType(MIB), CallStack);
183+
addCallStack(getMIBAllocType(MIB), CallStack, getMIBTotalSize(MIB));
171184
}
172185

173186
static MDNode *createMIBNode(LLVMContext &Ctx,
174187
std::vector<uint64_t> &MIBCallStack,
175-
AllocationType AllocType) {
188+
AllocationType AllocType, uint64_t TotalSize) {
176189
std::vector<Metadata *> MIBPayload(
177190
{buildCallstackMetadata(MIBCallStack, Ctx)});
178191
MIBPayload.push_back(
179192
MDString::get(Ctx, getAllocTypeAttributeString(AllocType)));
193+
if (TotalSize)
194+
MIBPayload.push_back(ValueAsMetadata::get(
195+
ConstantInt::get(Type::getInt64Ty(Ctx), TotalSize)));
180196
return MDNode::get(Ctx, MIBPayload);
181197
}
182198

@@ -190,8 +206,8 @@ bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx,
190206
// Trim context below the first node in a prefix with a single alloc type.
191207
// Add an MIB record for the current call stack prefix.
192208
if (hasSingleAllocType(Node->AllocTypes)) {
193-
MIBNodes.push_back(
194-
createMIBNode(Ctx, MIBCallStack, (AllocationType)Node->AllocTypes));
209+
MIBNodes.push_back(createMIBNode(
210+
Ctx, MIBCallStack, (AllocationType)Node->AllocTypes, Node->TotalSize));
195211
return true;
196212
}
197213

@@ -227,7 +243,8 @@ bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx,
227243
// non-cold allocation type.
228244
if (!CalleeHasAmbiguousCallerContext)
229245
return false;
230-
MIBNodes.push_back(createMIBNode(Ctx, MIBCallStack, AllocationType::NotCold));
246+
MIBNodes.push_back(createMIBNode(Ctx, MIBCallStack, AllocationType::NotCold,
247+
Node->TotalSize));
231248
return true;
232249
}
233250

@@ -238,6 +255,13 @@ bool CallStackTrie::buildAndAttachMIBMetadata(CallBase *CI) {
238255
auto &Ctx = CI->getContext();
239256
if (hasSingleAllocType(Alloc->AllocTypes)) {
240257
addAllocTypeAttribute(Ctx, CI, (AllocationType)Alloc->AllocTypes);
258+
if (MemProfReportHintedSizes) {
259+
assert(Alloc->TotalSize);
260+
errs() << "Total size for allocation with location hash " << AllocStackId
261+
<< " and single alloc type "
262+
<< getAllocTypeAttributeString((AllocationType)Alloc->AllocTypes)
263+
<< ": " << Alloc->TotalSize << "\n";
264+
}
241265
return false;
242266
}
243267
std::vector<uint64_t> MIBCallStack;

llvm/lib/IR/Verifier.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4941,10 +4941,14 @@ void Verifier::visitMemProfMetadata(Instruction &I, MDNode *MD) {
49414941
MDNode *StackMD = dyn_cast<MDNode>(MIB->getOperand(0));
49424942
visitCallStackMetadata(StackMD);
49434943

4944-
// Check that remaining operands are MDString.
4945-
Check(llvm::all_of(llvm::drop_begin(MIB->operands()),
4944+
// Check that remaining operands, except possibly the last, are MDString.
4945+
Check(llvm::all_of(MIB->operands().drop_front().drop_back(),
49464946
[](const MDOperand &Op) { return isa<MDString>(Op); }),
4947-
"Not all !memprof MemInfoBlock operands 1 to N are MDString", MIB);
4947+
"Not all !memprof MemInfoBlock operands 1 to N-1 are MDString", MIB);
4948+
// The last operand might be the total profiled size so can be an integer.
4949+
auto &LastOperand = MIB->operands().back();
4950+
Check(isa<MDString>(LastOperand) || mdconst::hasa<ConstantInt>(LastOperand),
4951+
"Last !memprof MemInfoBlock operand not MDString or int", MIB);
49484952
}
49494953
}
49504954

llvm/lib/Transforms/Instrumentation/MemProfiler.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ static cl::opt<bool>
161161
"context in this module's profiles"),
162162
cl::Hidden, cl::init(false));
163163

164+
extern cl::opt<bool> MemProfReportHintedSizes;
165+
164166
// Instrumentation statistics
165167
STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
166168
STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
@@ -712,7 +714,12 @@ static AllocationType addCallStack(CallStackTrie &AllocTrie,
712714
auto AllocType = getAllocType(AllocInfo->Info.getTotalLifetimeAccessDensity(),
713715
AllocInfo->Info.getAllocCount(),
714716
AllocInfo->Info.getTotalLifetime());
715-
AllocTrie.addCallStack(AllocType, StackIds);
717+
uint64_t TotalSize = 0;
718+
if (MemProfReportHintedSizes) {
719+
TotalSize = AllocInfo->Info.getTotalSize();
720+
assert(TotalSize);
721+
}
722+
AllocTrie.addCallStack(AllocType, StackIds, TotalSize);
716723
return AllocType;
717724
}
718725

@@ -1055,4 +1062,4 @@ PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
10551062
}
10561063

10571064
return PreservedAnalyses::none();
1058-
}
1065+
}

llvm/test/Transforms/PGOProfile/memprof.ll

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@
6363
;; give both memprof and pgo metadata.
6464
; RUN: opt < %s -passes='pgo-instr-use,memprof-use<profile-filename=%t.pgomemprofdata>' -pgo-test-profile-file=%t.pgomemprofdata -pgo-warn-missing-function -S 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,PGO
6565

66+
;; Check that the total sizes are reported if requested.
67+
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.memprofdata>' -pgo-warn-missing-function -S -memprof-report-hinted-sizes 2>&1 | FileCheck %s --check-prefixes=TOTALSIZES
68+
6669
; MEMPROFMATCHINFO: MemProf notcold context with id 1093248920606587996 has total profiled size 10 is matched
6770
; MEMPROFMATCHINFO: MemProf notcold context with id 5725971306423925017 has total profiled size 10 is matched
6871
; MEMPROFMATCHINFO: MemProf notcold context with id 6792096022461663180 has total profiled size 10 is matched
@@ -331,6 +334,18 @@ for.end: ; preds = %for.cond
331334
; MEMPROF: ![[C10]] = !{i64 2061451396820446691}
332335
; MEMPROF: ![[C11]] = !{i64 1544787832369987002}
333336

337+
;; For non-context sensitive allocations that get attributes we emit a message
338+
;; with the allocation hash, type, and size in bytes.
339+
; TOTALSIZES: Total size for allocation with location hash 6792096022461663180 and single alloc type notcold: 10
340+
; TOTALSIZES: Total size for allocation with location hash 15737101490731057601 and single alloc type cold: 10
341+
;; For context sensitive allocations the size in bytes is included on the MIB
342+
;; metadata.
343+
; TOTALSIZES: !"cold", i64 10}
344+
; TOTALSIZES: !"cold", i64 10}
345+
; TOTALSIZES: !"notcold", i64 10}
346+
; TOTALSIZES: !"cold", i64 20}
347+
; TOTALSIZES: !"notcold", i64 10}
348+
334349

335350
; MEMPROFNOCOLINFO: #[[A1]] = { builtin allocsize(0) "memprof"="notcold" }
336351
; MEMPROFNOCOLINFO: #[[A2]] = { builtin allocsize(0) "memprof"="cold" }

llvm/test/Verifier/memprof-metadata-bad.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ declare dso_local noalias noundef ptr @malloc(i64 noundef)
4343
!6 = !{i64 0}
4444
!7 = !{!8}
4545
; CHECK: call stack metadata should have at least 1 operand
46-
; CHECK: Not all !memprof MemInfoBlock operands 1 to N are MDString
47-
!8 = !{!0, !"default", i64 0}
46+
; CHECK: Not all !memprof MemInfoBlock operands 1 to N-1 are MDString
47+
!8 = !{!0, !"default", i64 0, i64 5}
4848
!9 = !{i64 123}
4949
; CHECK: call stack metadata operand should be constant integer
5050
!10 = !{!"wrongtype"}

0 commit comments

Comments
 (0)