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