1 //===- MemoryProfileInfoTest.cpp - Memory Profile Info Unit Tests-===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 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/Support/CommandLine.h" 15 #include "llvm/Support/SourceMgr.h" 16 #include "gtest/gtest.h" 17 #include <cstring> 18 19 using namespace llvm; 20 using namespace llvm::memprof; 21 22 extern cl::opt<float> MemProfAccessesPerByteColdThreshold; 23 extern cl::opt<unsigned> MemProfMinLifetimeColdThreshold; 24 25 namespace { 26 27 class MemoryProfileInfoTest : public testing::Test { 28 protected: 29 std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) { 30 SMDiagnostic Err; 31 std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C); 32 if (!Mod) 33 Err.print("MemoryProfileInfoTest", errs()); 34 return Mod; 35 } 36 37 // This looks for a call that has the given value name, which 38 // is the name of the value being assigned the call return value. 39 CallBase *findCall(Function &F, const char *Name = nullptr) { 40 for (auto &BB : F) 41 for (auto &I : BB) 42 if (auto *CB = dyn_cast<CallBase>(&I)) 43 if (!Name || CB->getName() == Name) 44 return CB; 45 return nullptr; 46 } 47 }; 48 49 // Test getAllocType helper. 50 // Basic checks on the allocation type for values just above and below 51 // the thresholds. 52 TEST_F(MemoryProfileInfoTest, GetAllocType) { 53 // Long lived with more accesses per byte than threshold is not cold. 54 EXPECT_EQ( 55 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1, 56 /*MinSize=*/1, 57 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1), 58 AllocationType::NotCold); 59 // Long lived with less accesses per byte than threshold is cold. 60 EXPECT_EQ( 61 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1, 62 /*MinSize=*/1, 63 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1), 64 AllocationType::Cold); 65 // Short lived with more accesses per byte than threshold is not cold. 66 EXPECT_EQ( 67 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1, 68 /*MinSize=*/1, 69 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1), 70 AllocationType::NotCold); 71 // Short lived with less accesses per byte than threshold is not cold. 72 EXPECT_EQ( 73 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1, 74 /*MinSize=*/1, 75 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1), 76 AllocationType::NotCold); 77 } 78 79 // Test buildCallstackMetadata helper. 80 TEST_F(MemoryProfileInfoTest, BuildCallStackMD) { 81 LLVMContext C; 82 MDNode *CallStack = buildCallstackMetadata({1, 2, 3}, C); 83 ASSERT_EQ(CallStack->getNumOperands(), 3u); 84 unsigned ExpectedId = 1; 85 for (auto &Op : CallStack->operands()) { 86 auto *StackId = mdconst::dyn_extract<ConstantInt>(Op); 87 EXPECT_EQ(StackId->getZExtValue(), ExpectedId++); 88 } 89 } 90 91 // Test CallStackTrie::addCallStack interface taking allocation type and list of 92 // call stack ids. 93 // Check that allocations with a single allocation type along all call stacks 94 // get an attribute instead of memprof metadata. 95 TEST_F(MemoryProfileInfoTest, Attribute) { 96 LLVMContext C; 97 std::unique_ptr<Module> M = makeLLVMModule(C, 98 R"IR( 99 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 100 target triple = "x86_64-pc-linux-gnu" 101 define i32* @test() { 102 entry: 103 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 104 %0 = bitcast i8* %call1 to i32* 105 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 106 %1 = bitcast i8* %call2 to i32* 107 ret i32* %1 108 } 109 declare dso_local noalias noundef i8* @malloc(i64 noundef) 110 )IR"); 111 112 Function *Func = M->getFunction("test"); 113 114 // First call has all cold contexts. 115 CallStackTrie Trie1; 116 Trie1.addCallStack(AllocationType::Cold, {1, 2}); 117 Trie1.addCallStack(AllocationType::Cold, {1, 3, 4}); 118 CallBase *Call1 = findCall(*Func, "call1"); 119 Trie1.buildAndAttachMIBMetadata(Call1); 120 121 EXPECT_FALSE(Call1->hasMetadata(LLVMContext::MD_memprof)); 122 EXPECT_TRUE(Call1->hasFnAttr("memprof")); 123 EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold"); 124 125 // Second call has all non-cold contexts. 126 CallStackTrie Trie2; 127 Trie2.addCallStack(AllocationType::NotCold, {5, 6}); 128 Trie2.addCallStack(AllocationType::NotCold, {5, 7, 8}); 129 CallBase *Call2 = findCall(*Func, "call2"); 130 Trie2.buildAndAttachMIBMetadata(Call2); 131 132 EXPECT_FALSE(Call2->hasMetadata(LLVMContext::MD_memprof)); 133 EXPECT_TRUE(Call2->hasFnAttr("memprof")); 134 EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold"); 135 } 136 137 // Test CallStackTrie::addCallStack interface taking allocation type and list of 138 // call stack ids. 139 // Test that an allocation call reached by both cold and non cold call stacks 140 // gets memprof metadata representing the different allocation type contexts. 141 TEST_F(MemoryProfileInfoTest, ColdAndNotColdMIB) { 142 LLVMContext C; 143 std::unique_ptr<Module> M = makeLLVMModule(C, 144 R"IR( 145 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 146 target triple = "x86_64-pc-linux-gnu" 147 define i32* @test() { 148 entry: 149 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 150 %0 = bitcast i8* %call to i32* 151 ret i32* %0 152 } 153 declare dso_local noalias noundef i8* @malloc(i64 noundef) 154 )IR"); 155 156 Function *Func = M->getFunction("test"); 157 158 CallStackTrie Trie; 159 Trie.addCallStack(AllocationType::Cold, {1, 2}); 160 Trie.addCallStack(AllocationType::NotCold, {1, 3}); 161 162 CallBase *Call = findCall(*Func, "call"); 163 Trie.buildAndAttachMIBMetadata(Call); 164 165 EXPECT_FALSE(Call->hasFnAttr("memprof")); 166 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 167 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 168 ASSERT_EQ(MemProfMD->getNumOperands(), 2u); 169 for (auto &MIBOp : MemProfMD->operands()) { 170 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 171 MDNode *StackMD = getMIBStackNode(MIB); 172 ASSERT_NE(StackMD, nullptr); 173 ASSERT_EQ(StackMD->getNumOperands(), 2u); 174 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 175 ASSERT_EQ(StackId->getZExtValue(), 1u); 176 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 177 if (StackId->getZExtValue() == 2u) 178 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 179 else { 180 ASSERT_EQ(StackId->getZExtValue(), 3u); 181 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 182 } 183 } 184 } 185 186 // Test CallStackTrie::addCallStack interface taking allocation type and list of 187 // call stack ids. 188 // Test that an allocation call reached by multiple call stacks has memprof 189 // metadata with the contexts trimmed to the minimum context required to 190 // identify the allocation type. 191 TEST_F(MemoryProfileInfoTest, TrimmedMIBContext) { 192 LLVMContext C; 193 std::unique_ptr<Module> M = makeLLVMModule(C, 194 R"IR( 195 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 196 target triple = "x86_64-pc-linux-gnu" 197 define i32* @test() { 198 entry: 199 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 200 %0 = bitcast i8* %call to i32* 201 ret i32* %0 202 } 203 declare dso_local noalias noundef i8* @malloc(i64 noundef) 204 )IR"); 205 206 Function *Func = M->getFunction("test"); 207 208 CallStackTrie Trie; 209 // We should be able to trim the following two and combine into a single MIB 210 // with the cold context {1, 2}. 211 Trie.addCallStack(AllocationType::Cold, {1, 2, 3}); 212 Trie.addCallStack(AllocationType::Cold, {1, 2, 4}); 213 // We should be able to trim the following two and combine into a single MIB 214 // with the non-cold context {1, 5}. 215 Trie.addCallStack(AllocationType::NotCold, {1, 5, 6}); 216 Trie.addCallStack(AllocationType::NotCold, {1, 5, 7}); 217 218 CallBase *Call = findCall(*Func, "call"); 219 Trie.buildAndAttachMIBMetadata(Call); 220 221 EXPECT_FALSE(Call->hasFnAttr("memprof")); 222 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 223 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 224 ASSERT_EQ(MemProfMD->getNumOperands(), 2u); 225 for (auto &MIBOp : MemProfMD->operands()) { 226 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 227 MDNode *StackMD = getMIBStackNode(MIB); 228 ASSERT_NE(StackMD, nullptr); 229 ASSERT_EQ(StackMD->getNumOperands(), 2u); 230 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 231 EXPECT_EQ(StackId->getZExtValue(), 1u); 232 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 233 if (StackId->getZExtValue() == 2u) 234 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 235 else { 236 ASSERT_EQ(StackId->getZExtValue(), 5u); 237 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 238 } 239 } 240 } 241 242 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata. 243 // Check that allocations annotated with memprof metadata with a single 244 // allocation type get simplified to an attribute. 245 TEST_F(MemoryProfileInfoTest, SimplifyMIBToAttribute) { 246 LLVMContext C; 247 std::unique_ptr<Module> M = makeLLVMModule(C, 248 R"IR( 249 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 250 target triple = "x86_64-pc-linux-gnu" 251 define i32* @test() { 252 entry: 253 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0 254 %0 = bitcast i8* %call1 to i32* 255 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3 256 %1 = bitcast i8* %call2 to i32* 257 ret i32* %1 258 } 259 declare dso_local noalias noundef i8* @malloc(i64 noundef) 260 !0 = !{!1} 261 !1 = !{!2, !"cold"} 262 !2 = !{i64 1, i64 2, i64 3} 263 !3 = !{!4} 264 !4 = !{!5, !"notcold"} 265 !5 = !{i64 4, i64 5, i64 6, i64 7} 266 )IR"); 267 268 Function *Func = M->getFunction("test"); 269 270 // First call has all cold contexts. 271 CallStackTrie Trie1; 272 CallBase *Call1 = findCall(*Func, "call1"); 273 MDNode *MemProfMD1 = Call1->getMetadata(LLVMContext::MD_memprof); 274 ASSERT_EQ(MemProfMD1->getNumOperands(), 1u); 275 MDNode *MIB1 = dyn_cast<MDNode>(MemProfMD1->getOperand(0)); 276 Trie1.addCallStack(MIB1); 277 Trie1.buildAndAttachMIBMetadata(Call1); 278 279 EXPECT_TRUE(Call1->hasFnAttr("memprof")); 280 EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold"); 281 282 // Second call has all non-cold contexts. 283 CallStackTrie Trie2; 284 CallBase *Call2 = findCall(*Func, "call2"); 285 MDNode *MemProfMD2 = Call2->getMetadata(LLVMContext::MD_memprof); 286 ASSERT_EQ(MemProfMD2->getNumOperands(), 1u); 287 MDNode *MIB2 = dyn_cast<MDNode>(MemProfMD2->getOperand(0)); 288 Trie2.addCallStack(MIB2); 289 Trie2.buildAndAttachMIBMetadata(Call2); 290 291 EXPECT_TRUE(Call2->hasFnAttr("memprof")); 292 EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold"); 293 } 294 295 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata. 296 // Test that allocations annotated with memprof metadata with multiple call 297 // stacks gets new memprof metadata with the contexts trimmed to the minimum 298 // context required to identify the allocation type. 299 TEST_F(MemoryProfileInfoTest, ReTrimMIBContext) { 300 LLVMContext C; 301 std::unique_ptr<Module> M = makeLLVMModule(C, 302 R"IR( 303 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 304 target triple = "x86_64-pc-linux-gnu" 305 define i32* @test() { 306 entry: 307 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0 308 %0 = bitcast i8* %call to i32* 309 ret i32* %0 310 } 311 declare dso_local noalias noundef i8* @malloc(i64 noundef) 312 !0 = !{!1, !3, !5, !7} 313 !1 = !{!2, !"cold"} 314 !2 = !{i64 1, i64 2, i64 3} 315 !3 = !{!4, !"cold"} 316 !4 = !{i64 1, i64 2, i64 4} 317 !5 = !{!6, !"notcold"} 318 !6 = !{i64 1, i64 5, i64 6} 319 !7 = !{!8, !"notcold"} 320 !8 = !{i64 1, i64 5, i64 7} 321 )IR"); 322 323 Function *Func = M->getFunction("test"); 324 325 CallStackTrie Trie; 326 ASSERT_TRUE(Trie.empty()); 327 CallBase *Call = findCall(*Func, "call"); 328 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 329 for (auto &MIBOp : MemProfMD->operands()) { 330 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 331 Trie.addCallStack(MIB); 332 } 333 ASSERT_FALSE(Trie.empty()); 334 Trie.buildAndAttachMIBMetadata(Call); 335 336 // We should be able to trim the first two and combine into a single MIB 337 // with the cold context {1, 2}. 338 // We should be able to trim the second two and combine into a single MIB 339 // with the non-cold context {1, 5}. 340 341 EXPECT_FALSE(Call->hasFnAttr("memprof")); 342 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 343 MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 344 ASSERT_EQ(MemProfMD->getNumOperands(), 2u); 345 for (auto &MIBOp : MemProfMD->operands()) { 346 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 347 MDNode *StackMD = getMIBStackNode(MIB); 348 ASSERT_NE(StackMD, nullptr); 349 ASSERT_EQ(StackMD->getNumOperands(), 2u); 350 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 351 EXPECT_EQ(StackId->getZExtValue(), 1u); 352 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 353 if (StackId->getZExtValue() == 2u) 354 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 355 else { 356 ASSERT_EQ(StackId->getZExtValue(), 5u); 357 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 358 } 359 } 360 } 361 362 } // end anonymous namespace 363