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/Constants.h" 12 #include "llvm/IR/Instructions.h" 13 #include "llvm/IR/LLVMContext.h" 14 #include "llvm/IR/Module.h" 15 #include "llvm/IR/ModuleSummaryIndex.h" 16 #include "llvm/Support/CommandLine.h" 17 #include "llvm/Support/SourceMgr.h" 18 #include "gtest/gtest.h" 19 #include <cstring> 20 #include <sys/types.h> 21 22 using namespace llvm; 23 using namespace llvm::memprof; 24 25 extern cl::opt<float> MemProfLifetimeAccessDensityColdThreshold; 26 extern cl::opt<unsigned> MemProfAveLifetimeColdThreshold; 27 extern cl::opt<unsigned> MemProfMinAveLifetimeAccessDensityHotThreshold; 28 extern cl::opt<bool> MemProfUseHotHints; 29 30 namespace { 31 32 class MemoryProfileInfoTest : public testing::Test { 33 protected: 34 std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) { 35 SMDiagnostic Err; 36 std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C); 37 if (!Mod) 38 Err.print("MemoryProfileInfoTest", errs()); 39 return Mod; 40 } 41 42 std::unique_ptr<ModuleSummaryIndex> makeLLVMIndex(const char *Summary) { 43 SMDiagnostic Err; 44 std::unique_ptr<ModuleSummaryIndex> Index = 45 parseSummaryIndexAssemblyString(Summary, Err); 46 if (!Index) 47 Err.print("MemoryProfileInfoTest", errs()); 48 return Index; 49 } 50 51 // This looks for a call that has the given value name, which 52 // is the name of the value being assigned the call return value. 53 CallBase *findCall(Function &F, const char *Name = nullptr) { 54 for (auto &BB : F) 55 for (auto &I : BB) 56 if (auto *CB = dyn_cast<CallBase>(&I)) 57 if (!Name || CB->getName() == Name) 58 return CB; 59 return nullptr; 60 } 61 }; 62 63 // Test getAllocType helper. 64 // Basic checks on the allocation type for values just above and below 65 // the thresholds. 66 TEST_F(MemoryProfileInfoTest, GetAllocType) { 67 const uint64_t AllocCount = 2; 68 // To be cold we require that 69 // ((float)TotalLifetimeAccessDensity) / AllocCount / 100 < 70 // MemProfLifetimeAccessDensityColdThreshold 71 // so compute the ColdTotalLifetimeAccessDensityThreshold at the threshold. 72 const uint64_t ColdTotalLifetimeAccessDensityThreshold = 73 (uint64_t)(MemProfLifetimeAccessDensityColdThreshold * AllocCount * 100); 74 // To be cold we require that 75 // ((float)TotalLifetime) / AllocCount >= 76 // MemProfAveLifetimeColdThreshold * 1000 77 // so compute the TotalLifetime right at the threshold. 78 const uint64_t ColdTotalLifetimeThreshold = 79 MemProfAveLifetimeColdThreshold * AllocCount * 1000; 80 // To be hot we require that 81 // ((float)TotalLifetimeAccessDensity) / AllocCount / 100 > 82 // MemProfMinAveLifetimeAccessDensityHotThreshold 83 // so compute the HotTotalLifetimeAccessDensityThreshold at the threshold. 84 const uint64_t HotTotalLifetimeAccessDensityThreshold = 85 (uint64_t)(MemProfMinAveLifetimeAccessDensityHotThreshold * AllocCount * 86 100); 87 88 // Make sure the option for detecting hot allocations is set. 89 MemProfUseHotHints = true; 90 // Test Hot 91 // More accesses per byte per sec than hot threshold is hot. 92 EXPECT_EQ(getAllocType(HotTotalLifetimeAccessDensityThreshold + 1, AllocCount, 93 ColdTotalLifetimeThreshold + 1), 94 AllocationType::Hot); 95 // Undo the manual set of the option above. 96 cl::ResetAllOptionOccurrences(); 97 98 // Without MemProfUseHotHints (default) we should treat simply as NotCold. 99 EXPECT_EQ(getAllocType(HotTotalLifetimeAccessDensityThreshold + 1, AllocCount, 100 ColdTotalLifetimeThreshold + 1), 101 AllocationType::NotCold); 102 103 // Test Cold 104 // Long lived with less accesses per byte per sec than cold threshold is cold. 105 EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold - 1, AllocCount, 106 ColdTotalLifetimeThreshold + 1), 107 AllocationType::Cold); 108 109 // Test NotCold 110 // Long lived with more accesses per byte per sec than cold threshold is not cold. 111 EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold + 1, AllocCount, 112 ColdTotalLifetimeThreshold + 1), 113 AllocationType::NotCold); 114 // Short lived with more accesses per byte per sec than cold threshold is not cold. 115 EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold + 1, AllocCount, 116 ColdTotalLifetimeThreshold - 1), 117 AllocationType::NotCold); 118 // Short lived with less accesses per byte per sec than cold threshold is not cold. 119 EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold - 1, AllocCount, 120 ColdTotalLifetimeThreshold - 1), 121 AllocationType::NotCold); 122 } 123 124 // Test the hasSingleAllocType helper. 125 TEST_F(MemoryProfileInfoTest, SingleAllocType) { 126 uint8_t NotCold = (uint8_t)AllocationType::NotCold; 127 uint8_t Cold = (uint8_t)AllocationType::Cold; 128 uint8_t Hot = (uint8_t)AllocationType::Hot; 129 EXPECT_TRUE(hasSingleAllocType(NotCold)); 130 EXPECT_TRUE(hasSingleAllocType(Cold)); 131 EXPECT_TRUE(hasSingleAllocType(Hot)); 132 EXPECT_FALSE(hasSingleAllocType(NotCold | Cold)); 133 EXPECT_FALSE(hasSingleAllocType(NotCold | Hot)); 134 EXPECT_FALSE(hasSingleAllocType(Cold | Hot)); 135 EXPECT_FALSE(hasSingleAllocType(NotCold | Cold | Hot)); 136 } 137 138 // Test buildCallstackMetadata helper. 139 TEST_F(MemoryProfileInfoTest, BuildCallStackMD) { 140 LLVMContext C; 141 MDNode *CallStack = buildCallstackMetadata({1, 2, 3}, C); 142 ASSERT_EQ(CallStack->getNumOperands(), 3u); 143 unsigned ExpectedId = 1; 144 for (auto &Op : CallStack->operands()) { 145 auto *StackId = mdconst::dyn_extract<ConstantInt>(Op); 146 EXPECT_EQ(StackId->getZExtValue(), ExpectedId++); 147 } 148 } 149 150 // Test CallStackTrie::addCallStack interface taking allocation type and list of 151 // call stack ids. 152 // Check that allocations with a single allocation type along all call stacks 153 // get an attribute instead of memprof metadata. 154 TEST_F(MemoryProfileInfoTest, Attribute) { 155 LLVMContext C; 156 std::unique_ptr<Module> M = makeLLVMModule(C, 157 R"IR( 158 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 159 target triple = "x86_64-pc-linux-gnu" 160 define i32* @test() { 161 entry: 162 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 163 %0 = bitcast i8* %call1 to i32* 164 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 165 %1 = bitcast i8* %call2 to i32* 166 %call3 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 167 %2 = bitcast i8* %call3 to i32* 168 ret i32* %1 169 } 170 declare dso_local noalias noundef i8* @malloc(i64 noundef) 171 )IR"); 172 173 Function *Func = M->getFunction("test"); 174 175 // First call has all cold contexts. 176 CallStackTrie Trie1; 177 Trie1.addCallStack(AllocationType::Cold, {1, 2}); 178 Trie1.addCallStack(AllocationType::Cold, {1, 3, 4}); 179 CallBase *Call1 = findCall(*Func, "call1"); 180 Trie1.buildAndAttachMIBMetadata(Call1); 181 182 EXPECT_FALSE(Call1->hasMetadata(LLVMContext::MD_memprof)); 183 EXPECT_TRUE(Call1->hasFnAttr("memprof")); 184 EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold"); 185 186 // Second call has all non-cold contexts. 187 CallStackTrie Trie2; 188 Trie2.addCallStack(AllocationType::NotCold, {5, 6}); 189 Trie2.addCallStack(AllocationType::NotCold, {5, 7, 8}); 190 CallBase *Call2 = findCall(*Func, "call2"); 191 Trie2.buildAndAttachMIBMetadata(Call2); 192 193 EXPECT_FALSE(Call2->hasMetadata(LLVMContext::MD_memprof)); 194 EXPECT_TRUE(Call2->hasFnAttr("memprof")); 195 EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold"); 196 197 // Third call has all hot contexts. 198 CallStackTrie Trie3; 199 Trie3.addCallStack(AllocationType::Hot, {9, 10}); 200 Trie3.addCallStack(AllocationType::Hot, {9, 11, 12}); 201 CallBase *Call3 = findCall(*Func, "call3"); 202 Trie3.buildAndAttachMIBMetadata(Call3); 203 204 EXPECT_FALSE(Call3->hasMetadata(LLVMContext::MD_memprof)); 205 EXPECT_TRUE(Call3->hasFnAttr("memprof")); 206 EXPECT_EQ(Call3->getFnAttr("memprof").getValueAsString(), "hot"); 207 } 208 209 // Test CallStackTrie::addCallStack interface taking allocation type and list of 210 // call stack ids. 211 // Test that an allocation call reached by both cold and non cold call stacks 212 // gets memprof metadata representing the different allocation type contexts. 213 TEST_F(MemoryProfileInfoTest, ColdAndNotColdMIB) { 214 LLVMContext C; 215 std::unique_ptr<Module> M = makeLLVMModule(C, 216 R"IR( 217 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 218 target triple = "x86_64-pc-linux-gnu" 219 define i32* @test() { 220 entry: 221 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 222 %0 = bitcast i8* %call to i32* 223 ret i32* %0 224 } 225 declare dso_local noalias noundef i8* @malloc(i64 noundef) 226 )IR"); 227 228 Function *Func = M->getFunction("test"); 229 230 CallStackTrie Trie; 231 Trie.addCallStack(AllocationType::Cold, {1, 2}); 232 Trie.addCallStack(AllocationType::NotCold, {1, 3}); 233 234 CallBase *Call = findCall(*Func, "call"); 235 Trie.buildAndAttachMIBMetadata(Call); 236 237 EXPECT_FALSE(Call->hasFnAttr("memprof")); 238 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 239 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 240 ASSERT_EQ(MemProfMD->getNumOperands(), 2u); 241 for (auto &MIBOp : MemProfMD->operands()) { 242 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 243 MDNode *StackMD = getMIBStackNode(MIB); 244 ASSERT_NE(StackMD, nullptr); 245 ASSERT_EQ(StackMD->getNumOperands(), 2u); 246 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 247 ASSERT_EQ(StackId->getZExtValue(), 1u); 248 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 249 if (StackId->getZExtValue() == 2u) 250 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 251 else { 252 ASSERT_EQ(StackId->getZExtValue(), 3u); 253 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 254 } 255 } 256 } 257 258 // Test CallStackTrie::addCallStack interface taking allocation type and list of 259 // call stack ids. 260 // Test that an allocation call reached by both cold and hot call stacks 261 // gets memprof metadata representing the different allocation type contexts. 262 TEST_F(MemoryProfileInfoTest, ColdAndHotMIB) { 263 LLVMContext C; 264 std::unique_ptr<Module> M = makeLLVMModule(C, 265 R"IR( 266 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 267 target triple = "x86_64-pc-linux-gnu" 268 define i32* @test() { 269 entry: 270 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 271 %0 = bitcast i8* %call to i32* 272 ret i32* %0 273 } 274 declare dso_local noalias noundef i8* @malloc(i64 noundef) 275 )IR"); 276 277 Function *Func = M->getFunction("test"); 278 279 CallStackTrie Trie; 280 Trie.addCallStack(AllocationType::Cold, {1, 2}); 281 Trie.addCallStack(AllocationType::Hot, {1, 3}); 282 283 CallBase *Call = findCall(*Func, "call"); 284 Trie.buildAndAttachMIBMetadata(Call); 285 286 EXPECT_FALSE(Call->hasFnAttr("memprof")); 287 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 288 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 289 ASSERT_EQ(MemProfMD->getNumOperands(), 2u); 290 for (auto &MIBOp : MemProfMD->operands()) { 291 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 292 MDNode *StackMD = getMIBStackNode(MIB); 293 ASSERT_NE(StackMD, nullptr); 294 ASSERT_EQ(StackMD->getNumOperands(), 2u); 295 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 296 ASSERT_EQ(StackId->getZExtValue(), 1u); 297 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 298 if (StackId->getZExtValue() == 2u) 299 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 300 else { 301 ASSERT_EQ(StackId->getZExtValue(), 3u); 302 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Hot); 303 } 304 } 305 } 306 307 // Test CallStackTrie::addCallStack interface taking allocation type and list of 308 // call stack ids. 309 // Test that an allocation call reached by both non cold and hot call stacks 310 // gets memprof metadata representing the different allocation type contexts. 311 TEST_F(MemoryProfileInfoTest, NotColdAndHotMIB) { 312 LLVMContext C; 313 std::unique_ptr<Module> M = makeLLVMModule(C, 314 R"IR( 315 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 316 target triple = "x86_64-pc-linux-gnu" 317 define i32* @test() { 318 entry: 319 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 320 %0 = bitcast i8* %call to i32* 321 ret i32* %0 322 } 323 declare dso_local noalias noundef i8* @malloc(i64 noundef) 324 )IR"); 325 326 Function *Func = M->getFunction("test"); 327 328 CallStackTrie Trie; 329 Trie.addCallStack(AllocationType::NotCold, {1, 2}); 330 Trie.addCallStack(AllocationType::Hot, {1, 3}); 331 332 CallBase *Call = findCall(*Func, "call"); 333 Trie.buildAndAttachMIBMetadata(Call); 334 335 EXPECT_FALSE(Call->hasFnAttr("memprof")); 336 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 337 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 338 ASSERT_EQ(MemProfMD->getNumOperands(), 2u); 339 for (auto &MIBOp : MemProfMD->operands()) { 340 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 341 MDNode *StackMD = getMIBStackNode(MIB); 342 ASSERT_NE(StackMD, nullptr); 343 ASSERT_EQ(StackMD->getNumOperands(), 2u); 344 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 345 ASSERT_EQ(StackId->getZExtValue(), 1u); 346 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 347 if (StackId->getZExtValue() == 2u) 348 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 349 else { 350 ASSERT_EQ(StackId->getZExtValue(), 3u); 351 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Hot); 352 } 353 } 354 } 355 356 // Test CallStackTrie::addCallStack interface taking allocation type and list of 357 // call stack ids. 358 // Test that an allocation call reached by both cold, non cold and hot call 359 // stacks gets memprof metadata representing the different allocation type 360 // contexts. 361 TEST_F(MemoryProfileInfoTest, ColdAndNotColdAndHotMIB) { 362 LLVMContext C; 363 std::unique_ptr<Module> M = makeLLVMModule(C, 364 R"IR( 365 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 366 target triple = "x86_64-pc-linux-gnu" 367 define i32* @test() { 368 entry: 369 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 370 %0 = bitcast i8* %call to i32* 371 ret i32* %0 372 } 373 declare dso_local noalias noundef i8* @malloc(i64 noundef) 374 )IR"); 375 376 Function *Func = M->getFunction("test"); 377 378 CallStackTrie Trie; 379 Trie.addCallStack(AllocationType::Cold, {1, 2}); 380 Trie.addCallStack(AllocationType::NotCold, {1, 3}); 381 Trie.addCallStack(AllocationType::Hot, {1, 4}); 382 383 CallBase *Call = findCall(*Func, "call"); 384 Trie.buildAndAttachMIBMetadata(Call); 385 386 EXPECT_FALSE(Call->hasFnAttr("memprof")); 387 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 388 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 389 ASSERT_EQ(MemProfMD->getNumOperands(), 3u); 390 for (auto &MIBOp : MemProfMD->operands()) { 391 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 392 MDNode *StackMD = getMIBStackNode(MIB); 393 ASSERT_NE(StackMD, nullptr); 394 ASSERT_EQ(StackMD->getNumOperands(), 2u); 395 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 396 ASSERT_EQ(StackId->getZExtValue(), 1u); 397 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 398 if (StackId->getZExtValue() == 2u) { 399 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 400 } else if (StackId->getZExtValue() == 3u) { 401 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 402 } else { 403 ASSERT_EQ(StackId->getZExtValue(), 4u); 404 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Hot); 405 } 406 } 407 } 408 409 // Test CallStackTrie::addCallStack interface taking allocation type and list of 410 // call stack ids. 411 // Test that an allocation call reached by multiple call stacks has memprof 412 // metadata with the contexts trimmed to the minimum context required to 413 // identify the allocation type. 414 TEST_F(MemoryProfileInfoTest, TrimmedMIBContext) { 415 LLVMContext C; 416 std::unique_ptr<Module> M = makeLLVMModule(C, 417 R"IR( 418 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 419 target triple = "x86_64-pc-linux-gnu" 420 define i32* @test() { 421 entry: 422 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40) 423 %0 = bitcast i8* %call to i32* 424 ret i32* %0 425 } 426 declare dso_local noalias noundef i8* @malloc(i64 noundef) 427 )IR"); 428 429 Function *Func = M->getFunction("test"); 430 431 CallStackTrie Trie; 432 // We should be able to trim the following two and combine into a single MIB 433 // with the cold context {1, 2}. 434 Trie.addCallStack(AllocationType::Cold, {1, 2, 3}); 435 Trie.addCallStack(AllocationType::Cold, {1, 2, 4}); 436 // We should be able to trim the following two and combine into a single MIB 437 // with the non-cold context {1, 5}. 438 Trie.addCallStack(AllocationType::NotCold, {1, 5, 6}); 439 Trie.addCallStack(AllocationType::NotCold, {1, 5, 7}); 440 // We should be able to trim the following two and combine into a single MIB 441 // with the hot context {1, 8}. 442 Trie.addCallStack(AllocationType::Hot, {1, 8, 9}); 443 Trie.addCallStack(AllocationType::Hot, {1, 8, 10}); 444 445 CallBase *Call = findCall(*Func, "call"); 446 Trie.buildAndAttachMIBMetadata(Call); 447 448 EXPECT_FALSE(Call->hasFnAttr("memprof")); 449 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 450 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 451 ASSERT_EQ(MemProfMD->getNumOperands(), 3u); 452 for (auto &MIBOp : MemProfMD->operands()) { 453 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 454 MDNode *StackMD = getMIBStackNode(MIB); 455 ASSERT_NE(StackMD, nullptr); 456 ASSERT_EQ(StackMD->getNumOperands(), 2u); 457 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 458 EXPECT_EQ(StackId->getZExtValue(), 1u); 459 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 460 if (StackId->getZExtValue() == 2u) 461 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 462 else if (StackId->getZExtValue() == 5u) 463 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 464 else { 465 ASSERT_EQ(StackId->getZExtValue(), 8u); 466 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Hot); 467 } 468 } 469 } 470 471 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata. 472 // Check that allocations annotated with memprof metadata with a single 473 // allocation type get simplified to an attribute. 474 TEST_F(MemoryProfileInfoTest, SimplifyMIBToAttribute) { 475 LLVMContext C; 476 std::unique_ptr<Module> M = makeLLVMModule(C, 477 R"IR( 478 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 479 target triple = "x86_64-pc-linux-gnu" 480 define i32* @test() { 481 entry: 482 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0 483 %0 = bitcast i8* %call1 to i32* 484 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3 485 %1 = bitcast i8* %call2 to i32* 486 %call3 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !6 487 %2 = bitcast i8* %call3 to i32* 488 ret i32* %1 489 } 490 declare dso_local noalias noundef i8* @malloc(i64 noundef) 491 !0 = !{!1} 492 !1 = !{!2, !"cold"} 493 !2 = !{i64 1, i64 2, i64 3} 494 !3 = !{!4} 495 !4 = !{!5, !"notcold"} 496 !5 = !{i64 4, i64 5, i64 6, i64 7} 497 !6 = !{!7} 498 !7 = !{!8, !"hot"} 499 !8 = !{i64 8, i64 9, i64 10} 500 )IR"); 501 502 Function *Func = M->getFunction("test"); 503 504 // First call has all cold contexts. 505 CallStackTrie Trie1; 506 CallBase *Call1 = findCall(*Func, "call1"); 507 MDNode *MemProfMD1 = Call1->getMetadata(LLVMContext::MD_memprof); 508 ASSERT_EQ(MemProfMD1->getNumOperands(), 1u); 509 MDNode *MIB1 = dyn_cast<MDNode>(MemProfMD1->getOperand(0)); 510 Trie1.addCallStack(MIB1); 511 Trie1.buildAndAttachMIBMetadata(Call1); 512 513 EXPECT_TRUE(Call1->hasFnAttr("memprof")); 514 EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold"); 515 516 // Second call has all non-cold contexts. 517 CallStackTrie Trie2; 518 CallBase *Call2 = findCall(*Func, "call2"); 519 MDNode *MemProfMD2 = Call2->getMetadata(LLVMContext::MD_memprof); 520 ASSERT_EQ(MemProfMD2->getNumOperands(), 1u); 521 MDNode *MIB2 = dyn_cast<MDNode>(MemProfMD2->getOperand(0)); 522 Trie2.addCallStack(MIB2); 523 Trie2.buildAndAttachMIBMetadata(Call2); 524 525 EXPECT_TRUE(Call2->hasFnAttr("memprof")); 526 EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold"); 527 528 // Third call has all hot contexts. 529 CallStackTrie Trie3; 530 CallBase *Call3 = findCall(*Func, "call3"); 531 MDNode *MemProfMD3 = Call3->getMetadata(LLVMContext::MD_memprof); 532 ASSERT_EQ(MemProfMD2->getNumOperands(), 1u); 533 MDNode *MIB3 = dyn_cast<MDNode>(MemProfMD3->getOperand(0)); 534 Trie3.addCallStack(MIB3); 535 Trie3.buildAndAttachMIBMetadata(Call3); 536 537 EXPECT_TRUE(Call3->hasFnAttr("memprof")); 538 EXPECT_EQ(Call3->getFnAttr("memprof").getValueAsString(), "hot"); 539 } 540 541 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata. 542 // Test that allocations annotated with memprof metadata with multiple call 543 // stacks gets new memprof metadata with the contexts trimmed to the minimum 544 // context required to identify the allocation type. 545 TEST_F(MemoryProfileInfoTest, ReTrimMIBContext) { 546 LLVMContext C; 547 std::unique_ptr<Module> M = makeLLVMModule(C, 548 R"IR( 549 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 550 target triple = "x86_64-pc-linux-gnu" 551 define i32* @test() { 552 entry: 553 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0 554 %0 = bitcast i8* %call to i32* 555 ret i32* %0 556 } 557 declare dso_local noalias noundef i8* @malloc(i64 noundef) 558 !0 = !{!1, !3, !5, !7, !9, !11} 559 !1 = !{!2, !"cold"} 560 !2 = !{i64 1, i64 2, i64 3} 561 !3 = !{!4, !"cold"} 562 !4 = !{i64 1, i64 2, i64 4} 563 !5 = !{!6, !"notcold"} 564 !6 = !{i64 1, i64 5, i64 6} 565 !7 = !{!8, !"notcold"} 566 !8 = !{i64 1, i64 5, i64 7} 567 !9 = !{!10, !"hot"} 568 !10 = !{i64 1, i64 8, i64 9} 569 !11 = !{!12, !"hot"} 570 !12 = !{i64 1, i64 8, i64 10} 571 )IR"); 572 573 Function *Func = M->getFunction("test"); 574 575 CallStackTrie Trie; 576 ASSERT_TRUE(Trie.empty()); 577 CallBase *Call = findCall(*Func, "call"); 578 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 579 for (auto &MIBOp : MemProfMD->operands()) { 580 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 581 Trie.addCallStack(MIB); 582 } 583 ASSERT_FALSE(Trie.empty()); 584 Trie.buildAndAttachMIBMetadata(Call); 585 586 // We should be able to trim the first two and combine into a single MIB 587 // with the cold context {1, 2}. 588 // We should be able to trim the second two and combine into a single MIB 589 // with the non-cold context {1, 5}. 590 591 EXPECT_FALSE(Call->hasFnAttr("memprof")); 592 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof)); 593 MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 594 ASSERT_EQ(MemProfMD->getNumOperands(), 3u); 595 for (auto &MIBOp : MemProfMD->operands()) { 596 MDNode *MIB = dyn_cast<MDNode>(MIBOp); 597 MDNode *StackMD = getMIBStackNode(MIB); 598 ASSERT_NE(StackMD, nullptr); 599 ASSERT_EQ(StackMD->getNumOperands(), 2u); 600 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0)); 601 EXPECT_EQ(StackId->getZExtValue(), 1u); 602 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1)); 603 if (StackId->getZExtValue() == 2u) 604 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold); 605 else if (StackId->getZExtValue() == 5u) 606 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold); 607 else { 608 ASSERT_EQ(StackId->getZExtValue(), 8u); 609 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Hot); 610 } 611 } 612 } 613 614 TEST_F(MemoryProfileInfoTest, CallStackTestIR) { 615 LLVMContext C; 616 std::unique_ptr<Module> M = makeLLVMModule(C, 617 R"IR( 618 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 619 target triple = "x86_64-pc-linux-gnu" 620 define ptr @test() { 621 entry: 622 %call = call noalias noundef nonnull dereferenceable(10) ptr @_Znam(i64 noundef 10), !memprof !1, !callsite !6 623 ret ptr %call 624 } 625 declare noundef nonnull ptr @_Znam(i64 noundef) 626 !1 = !{!2, !4, !7} 627 !2 = !{!3, !"notcold"} 628 !3 = !{i64 1, i64 2, i64 3, i64 4} 629 !4 = !{!5, !"cold"} 630 !5 = !{i64 1, i64 2, i64 3, i64 5} 631 !6 = !{i64 1} 632 !7 = !{!8, !"hot"} 633 !8 = !{i64 1, i64 2, i64 3, i64 6} 634 )IR"); 635 636 Function *Func = M->getFunction("test"); 637 CallBase *Call = findCall(*Func, "call"); 638 639 CallStack<MDNode, MDNode::op_iterator> InstCallsite( 640 Call->getMetadata(LLVMContext::MD_callsite)); 641 642 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof); 643 unsigned Idx = 0; 644 for (auto &MIBOp : MemProfMD->operands()) { 645 auto *MIBMD = cast<const MDNode>(MIBOp); 646 MDNode *StackNode = getMIBStackNode(MIBMD); 647 CallStack<MDNode, MDNode::op_iterator> StackContext(StackNode); 648 EXPECT_EQ(StackContext.back(), 4 + Idx); 649 std::vector<uint64_t> StackIds; 650 for (auto ContextIter = StackContext.beginAfterSharedPrefix(InstCallsite); 651 ContextIter != StackContext.end(); ++ContextIter) 652 StackIds.push_back(*ContextIter); 653 if (Idx == 0) { 654 std::vector<uint64_t> Expected = {2, 3, 4}; 655 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 656 } else if (Idx == 1) { 657 std::vector<uint64_t> Expected = {2, 3, 5}; 658 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 659 } else { 660 std::vector<uint64_t> Expected = {2, 3, 6}; 661 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 662 } 663 Idx++; 664 } 665 } 666 667 TEST_F(MemoryProfileInfoTest, CallStackTestSummary) { 668 std::unique_ptr<ModuleSummaryIndex> Index = makeLLVMIndex(R"Summary( 669 ^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0)) 670 ^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)), (type: hot, stackIds: (1, 2, 3, 6)))))))) 671 ^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)), (callee: ^1, clones: (0), stackIds: (3, 6)))))) 672 )Summary"); 673 674 ASSERT_NE(Index, nullptr); 675 auto *CallsiteSummary = 676 cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/25)); 677 unsigned Idx = 0; 678 for (auto &CI : CallsiteSummary->callsites()) { 679 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator> InstCallsite( 680 &CI); 681 std::vector<uint64_t> StackIds; 682 for (auto StackIdIndex : InstCallsite) 683 StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex)); 684 if (Idx == 0) { 685 std::vector<uint64_t> Expected = {3, 4}; 686 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 687 } else if (Idx == 1) { 688 std::vector<uint64_t> Expected = {3, 5}; 689 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 690 } else { 691 std::vector<uint64_t> Expected = {3, 6}; 692 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 693 } 694 Idx++; 695 } 696 697 auto *AllocSummary = 698 cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/23)); 699 for (auto &AI : AllocSummary->allocs()) { 700 unsigned Idx = 0; 701 for (auto &MIB : AI.MIBs) { 702 CallStack<MIBInfo, SmallVector<unsigned>::const_iterator> StackContext( 703 &MIB); 704 EXPECT_EQ(Index->getStackIdAtIndex(StackContext.back()), 4 + Idx); 705 std::vector<uint64_t> StackIds; 706 for (auto StackIdIndex : StackContext) 707 StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex)); 708 if (Idx == 0) { 709 std::vector<uint64_t> Expected = {1, 2, 3, 4}; 710 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 711 } else if (Idx == 1) { 712 std::vector<uint64_t> Expected = {1, 2, 3, 5}; 713 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 714 } else { 715 std::vector<uint64_t> Expected = {1, 2, 3, 6}; 716 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected)); 717 } 718 Idx++; 719 } 720 } 721 } 722 } // end anonymous namespace 723