1 //===- MemoryProfileInfoTest.cpp - Memory Profile Info Unit Tests-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Analysis/MemoryProfileInfo.h"
10 #include "llvm/AsmParser/Parser.h"
11 #include "llvm/IR/Instructions.h"
12 #include "llvm/IR/LLVMContext.h"
13 #include "llvm/IR/Module.h"
14 #include "llvm/IR/ModuleSummaryIndex.h"
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "gtest/gtest.h"
21 using namespace llvm::memprof
;
23 extern cl::opt
<float> MemProfAccessesPerByteColdThreshold
;
24 extern cl::opt
<unsigned> MemProfMinLifetimeColdThreshold
;
28 class MemoryProfileInfoTest
: public testing::Test
{
30 std::unique_ptr
<Module
> makeLLVMModule(LLVMContext
&C
, const char *IR
) {
32 std::unique_ptr
<Module
> Mod
= parseAssemblyString(IR
, Err
, C
);
34 Err
.print("MemoryProfileInfoTest", errs());
38 std::unique_ptr
<ModuleSummaryIndex
> makeLLVMIndex(const char *Summary
) {
40 std::unique_ptr
<ModuleSummaryIndex
> Index
=
41 parseSummaryIndexAssemblyString(Summary
, Err
);
43 Err
.print("MemoryProfileInfoTest", errs());
47 // This looks for a call that has the given value name, which
48 // is the name of the value being assigned the call return value.
49 CallBase
*findCall(Function
&F
, const char *Name
= nullptr) {
52 if (auto *CB
= dyn_cast
<CallBase
>(&I
))
53 if (!Name
|| CB
->getName() == Name
)
59 // Test getAllocType helper.
60 // Basic checks on the allocation type for values just above and below
62 TEST_F(MemoryProfileInfoTest
, GetAllocType
) {
63 // Long lived with more accesses per byte than threshold is not cold.
65 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
+ 1,
67 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 + 1),
68 AllocationType::NotCold
);
69 // Long lived with less accesses per byte than threshold is cold.
71 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
- 1,
73 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 + 1),
74 AllocationType::Cold
);
75 // Short lived with more accesses per byte than threshold is not cold.
77 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
+ 1,
79 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 - 1),
80 AllocationType::NotCold
);
81 // Short lived with less accesses per byte than threshold is not cold.
83 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
- 1,
85 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 - 1),
86 AllocationType::NotCold
);
89 // Test the hasSingleAllocType helper.
90 TEST_F(MemoryProfileInfoTest
, SingleAllocType
) {
91 uint8_t NotCold
= (uint8_t)AllocationType::NotCold
;
92 uint8_t Cold
= (uint8_t)AllocationType::Cold
;
93 EXPECT_TRUE(hasSingleAllocType(NotCold
));
94 EXPECT_TRUE(hasSingleAllocType(Cold
));
95 EXPECT_FALSE(hasSingleAllocType(NotCold
| Cold
));
98 // Test buildCallstackMetadata helper.
99 TEST_F(MemoryProfileInfoTest
, BuildCallStackMD
) {
101 MDNode
*CallStack
= buildCallstackMetadata({1, 2, 3}, C
);
102 ASSERT_EQ(CallStack
->getNumOperands(), 3u);
103 unsigned ExpectedId
= 1;
104 for (auto &Op
: CallStack
->operands()) {
105 auto *StackId
= mdconst::dyn_extract
<ConstantInt
>(Op
);
106 EXPECT_EQ(StackId
->getZExtValue(), ExpectedId
++);
110 // Test CallStackTrie::addCallStack interface taking allocation type and list of
112 // Check that allocations with a single allocation type along all call stacks
113 // get an attribute instead of memprof metadata.
114 TEST_F(MemoryProfileInfoTest
, Attribute
) {
116 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
118 target datalayout = "e
-m
:e
-i64
:64-f80
:128-n8
:16:32:64-S128
"
119 target triple = "x86_64
-pc
-linux
-gnu
"
120 define i32* @test() {
122 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
123 %0 = bitcast i8* %call1 to i32*
124 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
125 %1 = bitcast i8* %call2 to i32*
128 declare dso_local noalias noundef i8* @malloc(i64 noundef)
131 Function
*Func
= M
->getFunction("test");
133 // First call has all cold contexts.
135 Trie1
.addCallStack(AllocationType::Cold
, {1, 2});
136 Trie1
.addCallStack(AllocationType::Cold
, {1, 3, 4});
137 CallBase
*Call1
= findCall(*Func
, "call1");
138 Trie1
.buildAndAttachMIBMetadata(Call1
);
140 EXPECT_FALSE(Call1
->hasMetadata(LLVMContext::MD_memprof
));
141 EXPECT_TRUE(Call1
->hasFnAttr("memprof"));
142 EXPECT_EQ(Call1
->getFnAttr("memprof").getValueAsString(), "cold");
144 // Second call has all non-cold contexts.
146 Trie2
.addCallStack(AllocationType::NotCold
, {5, 6});
147 Trie2
.addCallStack(AllocationType::NotCold
, {5, 7, 8});
148 CallBase
*Call2
= findCall(*Func
, "call2");
149 Trie2
.buildAndAttachMIBMetadata(Call2
);
151 EXPECT_FALSE(Call2
->hasMetadata(LLVMContext::MD_memprof
));
152 EXPECT_TRUE(Call2
->hasFnAttr("memprof"));
153 EXPECT_EQ(Call2
->getFnAttr("memprof").getValueAsString(), "notcold");
156 // Test CallStackTrie::addCallStack interface taking allocation type and list of
158 // Test that an allocation call reached by both cold and non cold call stacks
159 // gets memprof metadata representing the different allocation type contexts.
160 TEST_F(MemoryProfileInfoTest
, ColdAndNotColdMIB
) {
162 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
164 target datalayout = "e
-m
:e
-i64
:64-f80
:128-n8
:16:32:64-S128
"
165 target triple = "x86_64
-pc
-linux
-gnu
"
166 define i32* @test() {
168 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
169 %0 = bitcast i8* %call to i32*
172 declare dso_local noalias noundef i8* @malloc(i64 noundef)
175 Function
*Func
= M
->getFunction("test");
178 Trie
.addCallStack(AllocationType::Cold
, {1, 2});
179 Trie
.addCallStack(AllocationType::NotCold
, {1, 3});
181 CallBase
*Call
= findCall(*Func
, "call");
182 Trie
.buildAndAttachMIBMetadata(Call
);
184 EXPECT_FALSE(Call
->hasFnAttr("memprof"));
185 EXPECT_TRUE(Call
->hasMetadata(LLVMContext::MD_memprof
));
186 MDNode
*MemProfMD
= Call
->getMetadata(LLVMContext::MD_memprof
);
187 ASSERT_EQ(MemProfMD
->getNumOperands(), 2u);
188 for (auto &MIBOp
: MemProfMD
->operands()) {
189 MDNode
*MIB
= dyn_cast
<MDNode
>(MIBOp
);
190 MDNode
*StackMD
= getMIBStackNode(MIB
);
191 ASSERT_NE(StackMD
, nullptr);
192 ASSERT_EQ(StackMD
->getNumOperands(), 2u);
193 auto *StackId
= mdconst::dyn_extract
<ConstantInt
>(StackMD
->getOperand(0));
194 ASSERT_EQ(StackId
->getZExtValue(), 1u);
195 StackId
= mdconst::dyn_extract
<ConstantInt
>(StackMD
->getOperand(1));
196 if (StackId
->getZExtValue() == 2u)
197 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::Cold
);
199 ASSERT_EQ(StackId
->getZExtValue(), 3u);
200 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::NotCold
);
205 // Test CallStackTrie::addCallStack interface taking allocation type and list of
207 // Test that an allocation call reached by multiple call stacks has memprof
208 // metadata with the contexts trimmed to the minimum context required to
209 // identify the allocation type.
210 TEST_F(MemoryProfileInfoTest
, TrimmedMIBContext
) {
212 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
214 target datalayout = "e
-m
:e
-i64
:64-f80
:128-n8
:16:32:64-S128
"
215 target triple = "x86_64
-pc
-linux
-gnu
"
216 define i32* @test() {
218 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
219 %0 = bitcast i8* %call to i32*
222 declare dso_local noalias noundef i8* @malloc(i64 noundef)
225 Function
*Func
= M
->getFunction("test");
228 // We should be able to trim the following two and combine into a single MIB
229 // with the cold context {1, 2}.
230 Trie
.addCallStack(AllocationType::Cold
, {1, 2, 3});
231 Trie
.addCallStack(AllocationType::Cold
, {1, 2, 4});
232 // We should be able to trim the following two and combine into a single MIB
233 // with the non-cold context {1, 5}.
234 Trie
.addCallStack(AllocationType::NotCold
, {1, 5, 6});
235 Trie
.addCallStack(AllocationType::NotCold
, {1, 5, 7});
237 CallBase
*Call
= findCall(*Func
, "call");
238 Trie
.buildAndAttachMIBMetadata(Call
);
240 EXPECT_FALSE(Call
->hasFnAttr("memprof"));
241 EXPECT_TRUE(Call
->hasMetadata(LLVMContext::MD_memprof
));
242 MDNode
*MemProfMD
= Call
->getMetadata(LLVMContext::MD_memprof
);
243 ASSERT_EQ(MemProfMD
->getNumOperands(), 2u);
244 for (auto &MIBOp
: MemProfMD
->operands()) {
245 MDNode
*MIB
= dyn_cast
<MDNode
>(MIBOp
);
246 MDNode
*StackMD
= getMIBStackNode(MIB
);
247 ASSERT_NE(StackMD
, nullptr);
248 ASSERT_EQ(StackMD
->getNumOperands(), 2u);
249 auto *StackId
= mdconst::dyn_extract
<ConstantInt
>(StackMD
->getOperand(0));
250 EXPECT_EQ(StackId
->getZExtValue(), 1u);
251 StackId
= mdconst::dyn_extract
<ConstantInt
>(StackMD
->getOperand(1));
252 if (StackId
->getZExtValue() == 2u)
253 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::Cold
);
255 ASSERT_EQ(StackId
->getZExtValue(), 5u);
256 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::NotCold
);
261 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
262 // Check that allocations annotated with memprof metadata with a single
263 // allocation type get simplified to an attribute.
264 TEST_F(MemoryProfileInfoTest
, SimplifyMIBToAttribute
) {
266 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
268 target datalayout = "e
-m
:e
-i64
:64-f80
:128-n8
:16:32:64-S128
"
269 target triple = "x86_64
-pc
-linux
-gnu
"
270 define i32* @test() {
272 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
273 %0 = bitcast i8* %call1 to i32*
274 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3
275 %1 = bitcast i8* %call2 to i32*
278 declare dso_local noalias noundef i8* @malloc(i64 noundef)
281 !2 = !{i64 1, i64 2, i64 3}
283 !4 = !{!5, !"notcold
"}
284 !5 = !{i64 4, i64 5, i64 6, i64 7}
287 Function
*Func
= M
->getFunction("test");
289 // First call has all cold contexts.
291 CallBase
*Call1
= findCall(*Func
, "call1");
292 MDNode
*MemProfMD1
= Call1
->getMetadata(LLVMContext::MD_memprof
);
293 ASSERT_EQ(MemProfMD1
->getNumOperands(), 1u);
294 MDNode
*MIB1
= dyn_cast
<MDNode
>(MemProfMD1
->getOperand(0));
295 Trie1
.addCallStack(MIB1
);
296 Trie1
.buildAndAttachMIBMetadata(Call1
);
298 EXPECT_TRUE(Call1
->hasFnAttr("memprof"));
299 EXPECT_EQ(Call1
->getFnAttr("memprof").getValueAsString(), "cold");
301 // Second call has all non-cold contexts.
303 CallBase
*Call2
= findCall(*Func
, "call2");
304 MDNode
*MemProfMD2
= Call2
->getMetadata(LLVMContext::MD_memprof
);
305 ASSERT_EQ(MemProfMD2
->getNumOperands(), 1u);
306 MDNode
*MIB2
= dyn_cast
<MDNode
>(MemProfMD2
->getOperand(0));
307 Trie2
.addCallStack(MIB2
);
308 Trie2
.buildAndAttachMIBMetadata(Call2
);
310 EXPECT_TRUE(Call2
->hasFnAttr("memprof"));
311 EXPECT_EQ(Call2
->getFnAttr("memprof").getValueAsString(), "notcold");
314 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
315 // Test that allocations annotated with memprof metadata with multiple call
316 // stacks gets new memprof metadata with the contexts trimmed to the minimum
317 // context required to identify the allocation type.
318 TEST_F(MemoryProfileInfoTest
, ReTrimMIBContext
) {
320 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
322 target datalayout = "e
-m
:e
-i64
:64-f80
:128-n8
:16:32:64-S128
"
323 target triple = "x86_64
-pc
-linux
-gnu
"
324 define i32* @test() {
326 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
327 %0 = bitcast i8* %call to i32*
330 declare dso_local noalias noundef i8* @malloc(i64 noundef)
331 !0 = !{!1, !3, !5, !7}
333 !2 = !{i64 1, i64 2, i64 3}
335 !4 = !{i64 1, i64 2, i64 4}
336 !5 = !{!6, !"notcold
"}
337 !6 = !{i64 1, i64 5, i64 6}
338 !7 = !{!8, !"notcold
"}
339 !8 = !{i64 1, i64 5, i64 7}
342 Function
*Func
= M
->getFunction("test");
345 ASSERT_TRUE(Trie
.empty());
346 CallBase
*Call
= findCall(*Func
, "call");
347 MDNode
*MemProfMD
= Call
->getMetadata(LLVMContext::MD_memprof
);
348 for (auto &MIBOp
: MemProfMD
->operands()) {
349 MDNode
*MIB
= dyn_cast
<MDNode
>(MIBOp
);
350 Trie
.addCallStack(MIB
);
352 ASSERT_FALSE(Trie
.empty());
353 Trie
.buildAndAttachMIBMetadata(Call
);
355 // We should be able to trim the first two and combine into a single MIB
356 // with the cold context {1, 2}.
357 // We should be able to trim the second two and combine into a single MIB
358 // with the non-cold context {1, 5}.
360 EXPECT_FALSE(Call
->hasFnAttr("memprof"));
361 EXPECT_TRUE(Call
->hasMetadata(LLVMContext::MD_memprof
));
362 MemProfMD
= Call
->getMetadata(LLVMContext::MD_memprof
);
363 ASSERT_EQ(MemProfMD
->getNumOperands(), 2u);
364 for (auto &MIBOp
: MemProfMD
->operands()) {
365 MDNode
*MIB
= dyn_cast
<MDNode
>(MIBOp
);
366 MDNode
*StackMD
= getMIBStackNode(MIB
);
367 ASSERT_NE(StackMD
, nullptr);
368 ASSERT_EQ(StackMD
->getNumOperands(), 2u);
369 auto *StackId
= mdconst::dyn_extract
<ConstantInt
>(StackMD
->getOperand(0));
370 EXPECT_EQ(StackId
->getZExtValue(), 1u);
371 StackId
= mdconst::dyn_extract
<ConstantInt
>(StackMD
->getOperand(1));
372 if (StackId
->getZExtValue() == 2u)
373 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::Cold
);
375 ASSERT_EQ(StackId
->getZExtValue(), 5u);
376 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::NotCold
);
381 TEST_F(MemoryProfileInfoTest
, CallStackTestIR
) {
383 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
385 target datalayout = "e
-m
:e
-i64
:64-f80
:128-n8
:16:32:64-S128
"
386 target triple = "x86_64
-pc
-linux
-gnu
"
389 %call = call noalias noundef nonnull dereferenceable(10) ptr @_Znam(i64 noundef 10), !memprof !1, !callsite !6
392 declare noundef nonnull ptr @_Znam(i64 noundef)
394 !2 = !{!3, !"notcold
"}
395 !3 = !{i64 1, i64 2, i64 3, i64 4}
397 !5 = !{i64 1, i64 2, i64 3, i64 5}
401 Function
*Func
= M
->getFunction("test");
402 CallBase
*Call
= findCall(*Func
, "call");
404 CallStack
<MDNode
, MDNode::op_iterator
> InstCallsite(
405 Call
->getMetadata(LLVMContext::MD_callsite
));
407 MDNode
*MemProfMD
= Call
->getMetadata(LLVMContext::MD_memprof
);
409 for (auto &MIBOp
: MemProfMD
->operands()) {
410 auto *MIBMD
= cast
<const MDNode
>(MIBOp
);
411 MDNode
*StackNode
= getMIBStackNode(MIBMD
);
412 CallStack
<MDNode
, MDNode::op_iterator
> StackContext(StackNode
);
413 uint64_t ExpectedBack
= First
? 4 : 5;
414 EXPECT_EQ(StackContext
.back(), ExpectedBack
);
415 std::vector
<uint64_t> StackIds
;
416 for (auto ContextIter
= StackContext
.beginAfterSharedPrefix(InstCallsite
);
417 ContextIter
!= StackContext
.end(); ++ContextIter
)
418 StackIds
.push_back(*ContextIter
);
420 std::vector
<uint64_t> Expected
= {2, 3, 4};
421 EXPECT_EQ(ArrayRef(StackIds
), ArrayRef(Expected
));
423 std::vector
<uint64_t> Expected
= {2, 3, 5};
424 EXPECT_EQ(ArrayRef(StackIds
), ArrayRef(Expected
));
430 TEST_F(MemoryProfileInfoTest
, CallStackTestSummary
) {
431 std::unique_ptr
<ModuleSummaryIndex
> Index
= makeLLVMIndex(R
"Summary(
432 ^0 = module: (path: "test
.o
", hash: (0, 0, 0, 0, 0))
433 ^1 = gv: (guid: 23, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), allocs: ((versions: (none), memProf: ((type: notcold, stackIds: (1, 2, 3, 4)), (type: cold, stackIds: (1, 2, 3, 5))))))))
434 ^2 = gv: (guid: 25, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 22, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^1)), callsites: ((callee: ^1, clones: (0), stackIds: (3, 4)), (callee: ^1, clones: (0), stackIds: (3, 5))))))
437 ASSERT_NE(Index
, nullptr);
438 auto *CallsiteSummary
=
439 cast
<FunctionSummary
>(Index
->getGlobalValueSummary(/*guid=*/25));
441 for (auto &CI
: CallsiteSummary
->callsites()) {
442 CallStack
<CallsiteInfo
, SmallVector
<unsigned>::const_iterator
> InstCallsite(
444 std::vector
<uint64_t> StackIds
;
445 for (auto StackIdIndex
: InstCallsite
)
446 StackIds
.push_back(Index
->getStackIdAtIndex(StackIdIndex
));
448 std::vector
<uint64_t> Expected
= {3, 4};
449 EXPECT_EQ(ArrayRef(StackIds
), ArrayRef(Expected
));
451 std::vector
<uint64_t> Expected
= {3, 5};
452 EXPECT_EQ(ArrayRef(StackIds
), ArrayRef(Expected
));
458 cast
<FunctionSummary
>(Index
->getGlobalValueSummary(/*guid=*/23));
459 for (auto &AI
: AllocSummary
->allocs()) {
461 for (auto &MIB
: AI
.MIBs
) {
462 CallStack
<MIBInfo
, SmallVector
<unsigned>::const_iterator
> StackContext(
464 uint64_t ExpectedBack
= First
? 4 : 5;
465 EXPECT_EQ(Index
->getStackIdAtIndex(StackContext
.back()), ExpectedBack
);
466 std::vector
<uint64_t> StackIds
;
467 for (auto StackIdIndex
: StackContext
)
468 StackIds
.push_back(Index
->getStackIdAtIndex(StackIdIndex
));
470 std::vector
<uint64_t> Expected
= {1, 2, 3, 4};
471 EXPECT_EQ(ArrayRef(StackIds
), ArrayRef(Expected
));
473 std::vector
<uint64_t> Expected
= {1, 2, 3, 5};
474 EXPECT_EQ(ArrayRef(StackIds
), ArrayRef(Expected
));
480 } // end anonymous namespace