1 //===- MemProfUseTest.cpp - MemProf use 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/TargetLibraryInfo.h" 10 #include "llvm/AsmParser/Parser.h" 11 #include "llvm/IR/LLVMContext.h" 12 #include "llvm/IR/Module.h" 13 #include "llvm/Passes/PassBuilder.h" 14 #include "llvm/ProfileData/InstrProfReader.h" 15 #include "llvm/ProfileData/InstrProfWriter.h" 16 #include "llvm/ProfileData/MemProf.h" 17 #include "llvm/Support/SourceMgr.h" 18 #include "llvm/Testing/Support/Error.h" 19 #include "llvm/Transforms/Instrumentation/MemProfiler.h" 20 21 #include "gmock/gmock.h" 22 #include "gtest/gtest.h" 23 24 namespace llvm { 25 namespace memprof { 26 namespace { 27 using testing::Contains; 28 using testing::ElementsAre; 29 using testing::Pair; 30 using testing::SizeIs; 31 using testing::UnorderedElementsAre; 32 33 TEST(MemProf, ExtractDirectCallsFromIR) { 34 // The following IR is generated from: 35 // 36 // void f1(); 37 // void f2(); 38 // void f3(); 39 // 40 // void foo() { 41 // f1(); 42 // f2(); f3(); 43 // } 44 StringRef IR = R"IR( 45 define dso_local void @_Z3foov() !dbg !10 { 46 entry: 47 call void @_Z2f1v(), !dbg !13 48 call void @_Z2f2v(), !dbg !14 49 call void @_Z2f3v(), !dbg !15 50 ret void, !dbg !16 51 } 52 53 declare !dbg !17 void @_Z2f1v() 54 55 declare !dbg !18 void @_Z2f2v() 56 57 declare !dbg !19 void @_Z2f3v() 58 59 !llvm.dbg.cu = !{!0} 60 !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} 61 !llvm.ident = !{!9} 62 63 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None) 64 !1 = !DIFile(filename: "foobar.cc", directory: "/") 65 !2 = !{i32 7, !"Dwarf Version", i32 5} 66 !3 = !{i32 2, !"Debug Info Version", i32 3} 67 !4 = !{i32 1, !"wchar_size", i32 4} 68 !5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"} 69 !6 = !{i32 8, !"PIC Level", i32 2} 70 !7 = !{i32 7, !"PIE Level", i32 2} 71 !8 = !{i32 7, !"uwtable", i32 2} 72 !9 = !{!"clang"} 73 !10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0) 74 !11 = !DISubroutineType(types: !12) 75 !12 = !{} 76 !13 = !DILocation(line: 6, column: 3, scope: !10) 77 !14 = !DILocation(line: 7, column: 3, scope: !10) 78 !15 = !DILocation(line: 7, column: 9, scope: !10) 79 !16 = !DILocation(line: 8, column: 1, scope: !10) 80 !17 = !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 81 !18 = !DISubprogram(name: "f2", linkageName: "_Z2f2v", scope: !1, file: !1, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 82 !19 = !DISubprogram(name: "f3", linkageName: "_Z2f3v", scope: !1, file: !1, line: 3, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 83 )IR"; 84 85 LLVMContext Ctx; 86 SMDiagnostic Err; 87 std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx); 88 ASSERT_TRUE(M); 89 90 auto *F = M->getFunction("_Z3foov"); 91 ASSERT_NE(F, nullptr); 92 93 TargetLibraryInfoWrapperPass WrapperPass; 94 auto &TLI = WrapperPass.getTLI(*F); 95 auto Calls = extractCallsFromIR(*M, TLI); 96 97 // Expect exactly one caller. 98 ASSERT_THAT(Calls, SizeIs(1)); 99 100 auto It = Calls.begin(); 101 ASSERT_NE(It, Calls.end()); 102 103 const auto &[CallerGUID, CallSites] = *It; 104 EXPECT_EQ(CallerGUID, IndexedMemProfRecord::getGUID("_Z3foov")); 105 106 // Verify that call sites show up in the ascending order of their source 107 // locations. 108 EXPECT_THAT( 109 CallSites, 110 ElementsAre( 111 Pair(LineLocation(1, 3), IndexedMemProfRecord::getGUID("_Z2f1v")), 112 Pair(LineLocation(2, 3), IndexedMemProfRecord::getGUID("_Z2f2v")), 113 Pair(LineLocation(2, 9), IndexedMemProfRecord::getGUID("_Z2f3v")))); 114 } 115 116 TEST(MemProf, ExtractDirectCallsFromIRInline) { 117 // The following IR is generated from: 118 // 119 // void f1(); 120 // static inline void f2() { 121 // // For an interesting line number. 122 // f1(); 123 // } 124 // static inline void f3() { 125 // /****/ f2(); // For an interesting column number. 126 // } 127 // 128 // void g1(); 129 // void g2(); 130 // static inline void g3() { 131 // /**/ g1(); // For an interesting column number. 132 // g2(); 133 // } 134 // 135 // void foo() { 136 // f3(); 137 // /***/ g3(); // For an interesting column number. 138 // } 139 StringRef IR = R"IR( 140 define dso_local void @_Z3foov() local_unnamed_addr !dbg !10 { 141 entry: 142 tail call void @_Z2f1v(), !dbg !13 143 tail call void @_Z2g1v(), !dbg !18 144 tail call void @_Z2g2v(), !dbg !21 145 ret void, !dbg !22 146 } 147 148 declare !dbg !23 void @_Z2f1v() local_unnamed_addr 149 150 declare !dbg !24 void @_Z2g1v() local_unnamed_addr 151 152 declare !dbg !25 void @_Z2g2v() local_unnamed_addr 153 154 !llvm.dbg.cu = !{!0} 155 !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} 156 !llvm.ident = !{!9} 157 158 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None) 159 !1 = !DIFile(filename: "foobar.cc", directory: "/") 160 !2 = !{i32 7, !"Dwarf Version", i32 5} 161 !3 = !{i32 2, !"Debug Info Version", i32 3} 162 !4 = !{i32 1, !"wchar_size", i32 4} 163 !5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"} 164 !6 = !{i32 8, !"PIC Level", i32 2} 165 !7 = !{i32 7, !"PIE Level", i32 2} 166 !8 = !{i32 7, !"uwtable", i32 2} 167 !9 = !{!"clang"} 168 !10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 17, type: !11, scopeLine: 17, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0) 169 !11 = !DISubroutineType(types: !12) 170 !12 = !{} 171 !13 = !DILocation(line: 4, column: 3, scope: !14, inlinedAt: !15) 172 !14 = distinct !DISubprogram(name: "f2", linkageName: "_ZL2f2v", scope: !1, file: !1, line: 2, type: !11, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) 173 !15 = distinct !DILocation(line: 7, column: 10, scope: !16, inlinedAt: !17) 174 !16 = distinct !DISubprogram(name: "f3", linkageName: "_ZL2f3v", scope: !1, file: !1, line: 6, type: !11, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) 175 !17 = distinct !DILocation(line: 18, column: 3, scope: !10) 176 !18 = !DILocation(line: 13, column: 8, scope: !19, inlinedAt: !20) 177 !19 = distinct !DISubprogram(name: "g3", linkageName: "_ZL2g3v", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) 178 !20 = distinct !DILocation(line: 19, column: 9, scope: !10) 179 !21 = !DILocation(line: 14, column: 3, scope: !19, inlinedAt: !20) 180 !22 = !DILocation(line: 20, column: 1, scope: !10) 181 !23 = !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 182 !24 = !DISubprogram(name: "g1", linkageName: "_Z2g1v", scope: !1, file: !1, line: 10, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 183 !25 = !DISubprogram(name: "g2", linkageName: "_Z2g2v", scope: !1, file: !1, line: 11, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 184 )IR"; 185 186 LLVMContext Ctx; 187 SMDiagnostic Err; 188 std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx); 189 ASSERT_TRUE(M); 190 191 auto *F = M->getFunction("_Z3foov"); 192 ASSERT_NE(F, nullptr); 193 194 TargetLibraryInfoWrapperPass WrapperPass; 195 auto &TLI = WrapperPass.getTLI(*F); 196 auto Calls = extractCallsFromIR(*M, TLI); 197 198 // Expect exactly 4 callers. 199 ASSERT_THAT(Calls, SizeIs(4)); 200 201 // Verify each key-value pair. 202 203 auto FooIt = Calls.find(IndexedMemProfRecord::getGUID("_Z3foov")); 204 ASSERT_NE(FooIt, Calls.end()); 205 const auto &[FooCallerGUID, FooCallSites] = *FooIt; 206 EXPECT_EQ(FooCallerGUID, IndexedMemProfRecord::getGUID("_Z3foov")); 207 EXPECT_THAT( 208 FooCallSites, 209 ElementsAre( 210 Pair(LineLocation(1, 3), IndexedMemProfRecord::getGUID("_ZL2f3v")), 211 Pair(LineLocation(2, 9), IndexedMemProfRecord::getGUID("_ZL2g3v")))); 212 213 auto F2It = Calls.find(IndexedMemProfRecord::getGUID("_ZL2f2v")); 214 ASSERT_NE(F2It, Calls.end()); 215 const auto &[F2CallerGUID, F2CallSites] = *F2It; 216 EXPECT_EQ(F2CallerGUID, IndexedMemProfRecord::getGUID("_ZL2f2v")); 217 EXPECT_THAT(F2CallSites, 218 ElementsAre(Pair(LineLocation(2, 3), 219 IndexedMemProfRecord::getGUID("_Z2f1v")))); 220 221 auto F3It = Calls.find(IndexedMemProfRecord::getGUID("_ZL2f3v")); 222 ASSERT_NE(F3It, Calls.end()); 223 const auto &[F3CallerGUID, F3CallSites] = *F3It; 224 EXPECT_EQ(F3CallerGUID, IndexedMemProfRecord::getGUID("_ZL2f3v")); 225 EXPECT_THAT(F3CallSites, 226 ElementsAre(Pair(LineLocation(1, 10), 227 IndexedMemProfRecord::getGUID("_ZL2f2v")))); 228 229 auto G3It = Calls.find(IndexedMemProfRecord::getGUID("_ZL2g3v")); 230 ASSERT_NE(G3It, Calls.end()); 231 const auto &[G3CallerGUID, G3CallSites] = *G3It; 232 EXPECT_EQ(G3CallerGUID, IndexedMemProfRecord::getGUID("_ZL2g3v")); 233 EXPECT_THAT( 234 G3CallSites, 235 ElementsAre( 236 Pair(LineLocation(1, 8), IndexedMemProfRecord::getGUID("_Z2g1v")), 237 Pair(LineLocation(2, 3), IndexedMemProfRecord::getGUID("_Z2g2v")))); 238 } 239 240 TEST(MemProf, ExtractDirectCallsFromIRCallingNew) { 241 // The following IR is generated from: 242 // 243 // int *foo() { 244 // return ::new (int); 245 // } 246 StringRef IR = R"IR( 247 define dso_local noundef ptr @_Z3foov() #0 !dbg !10 { 248 entry: 249 %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #2, !dbg !13 250 ret ptr %call, !dbg !14 251 } 252 253 ; Function Attrs: nobuiltin allocsize(0) 254 declare noundef nonnull ptr @_Znwm(i64 noundef) #1 255 256 attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } 257 attributes #1 = { nobuiltin allocsize(0) "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } 258 attributes #2 = { builtin allocsize(0) } 259 260 !llvm.dbg.cu = !{!0} 261 !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} 262 !llvm.ident = !{!9} 263 264 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None) 265 !1 = !DIFile(filename: "foobar.cc", directory: "/") 266 !2 = !{i32 7, !"Dwarf Version", i32 5} 267 !3 = !{i32 2, !"Debug Info Version", i32 3} 268 !4 = !{i32 1, !"wchar_size", i32 4} 269 !5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"} 270 !6 = !{i32 8, !"PIC Level", i32 2} 271 !7 = !{i32 7, !"PIE Level", i32 2} 272 !8 = !{i32 7, !"uwtable", i32 2} 273 !9 = !{!"clang"} 274 !10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0) 275 !11 = !DISubroutineType(types: !12) 276 !12 = !{} 277 !13 = !DILocation(line: 2, column: 10, scope: !10) 278 !14 = !DILocation(line: 2, column: 3, scope: !10) 279 )IR"; 280 281 LLVMContext Ctx; 282 SMDiagnostic Err; 283 std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx); 284 ASSERT_TRUE(M); 285 286 auto *F = M->getFunction("_Z3foov"); 287 ASSERT_NE(F, nullptr); 288 289 TargetLibraryInfoWrapperPass WrapperPass; 290 auto &TLI = WrapperPass.getTLI(*F); 291 auto Calls = extractCallsFromIR(*M, TLI); 292 293 // Expect exactly one caller. 294 ASSERT_THAT(Calls, SizeIs(1)); 295 296 // Verify each key-value pair. 297 298 auto FooIt = Calls.find(IndexedMemProfRecord::getGUID("_Z3foov")); 299 ASSERT_NE(FooIt, Calls.end()); 300 const auto &[FooCallerGUID, FooCallSites] = *FooIt; 301 EXPECT_EQ(FooCallerGUID, IndexedMemProfRecord::getGUID("_Z3foov")); 302 EXPECT_THAT(FooCallSites, ElementsAre(Pair(LineLocation(1, 10), 0))); 303 } 304 305 // Populate those fields returned by getHotColdSchema. 306 MemInfoBlock makePartialMIB() { 307 MemInfoBlock MIB; 308 MIB.AllocCount = 1; 309 MIB.TotalSize = 5; 310 MIB.TotalLifetime = 10; 311 MIB.TotalLifetimeAccessDensity = 23; 312 return MIB; 313 } 314 315 IndexedMemProfRecord 316 makeRecordV2(std::initializer_list<CallStackId> AllocFrames, 317 std::initializer_list<CallStackId> CallSiteFrames, 318 const MemInfoBlock &Block, const MemProfSchema &Schema) { 319 IndexedMemProfRecord MR; 320 for (const auto &CSId : AllocFrames) 321 MR.AllocSites.emplace_back(CSId, Block, Schema); 322 for (const auto &CSId : CallSiteFrames) 323 MR.CallSiteIds.push_back(CSId); 324 return MR; 325 } 326 327 static const auto Err = [](Error E) { 328 FAIL() << E; 329 consumeError(std::move(E)); 330 }; 331 332 // Make sure that we can undrift direct calls. 333 TEST(MemProf, ComputeUndriftingMap) { 334 // Suppose that the source code has changed from: 335 // 336 // void bar(); 337 // void baz(); 338 // void zzz(); 339 // 340 // void foo() { 341 // /**/ bar(); // LineLocation(1, 8) 342 // zzz(); // LineLocation(2, 3) 343 // baz(); // LineLocation(3, 3) 344 // } 345 // 346 // to: 347 // 348 // void bar(); 349 // void baz(); 350 // 351 // void foo() { 352 // bar(); // LineLocation(1, 3) 353 // /**/ baz(); // LineLocation(2, 8) 354 // } 355 // 356 // Notice that the calls to bar and baz have drifted while zzz has been 357 // removed. 358 StringRef IR = R"IR( 359 define dso_local void @_Z3foov() #0 !dbg !10 { 360 entry: 361 call void @_Z3barv(), !dbg !13 362 call void @_Z3bazv(), !dbg !14 363 ret void, !dbg !15 364 } 365 366 declare !dbg !16 void @_Z3barv() #1 367 368 declare !dbg !17 void @_Z3bazv() #1 369 370 attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } 371 attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } 372 373 !llvm.dbg.cu = !{!0} 374 !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} 375 !llvm.ident = !{!9} 376 377 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None) 378 !1 = !DIFile(filename: "foobar.cc", directory: "/") 379 !2 = !{i32 7, !"Dwarf Version", i32 5} 380 !3 = !{i32 2, !"Debug Info Version", i32 3} 381 !4 = !{i32 1, !"wchar_size", i32 4} 382 !5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"} 383 !6 = !{i32 8, !"PIC Level", i32 2} 384 !7 = !{i32 7, !"PIE Level", i32 2} 385 !8 = !{i32 7, !"uwtable", i32 2} 386 !9 = !{!"clang"} 387 !10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0) 388 !11 = !DISubroutineType(types: !12) 389 !12 = !{} 390 !13 = !DILocation(line: 5, column: 3, scope: !10) 391 !14 = !DILocation(line: 6, column: 8, scope: !10) 392 !15 = !DILocation(line: 7, column: 1, scope: !10) 393 !16 = !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 394 !17 = !DISubprogram(name: "baz", linkageName: "_Z3bazv", scope: !1, file: !1, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) 395 )IR"; 396 397 LLVMContext Ctx; 398 SMDiagnostic SMErr; 399 std::unique_ptr<Module> M = parseAssemblyString(IR, SMErr, Ctx); 400 ASSERT_TRUE(M); 401 402 auto *F = M->getFunction("_Z3foov"); 403 ASSERT_NE(F, nullptr); 404 405 TargetLibraryInfoWrapperPass WrapperPass; 406 auto &TLI = WrapperPass.getTLI(*F); 407 auto Calls = extractCallsFromIR(*M, TLI); 408 409 uint64_t GUIDFoo = IndexedMemProfRecord::getGUID("_Z3foov"); 410 uint64_t GUIDBar = IndexedMemProfRecord::getGUID("_Z3barv"); 411 uint64_t GUIDBaz = IndexedMemProfRecord::getGUID("_Z3bazv"); 412 uint64_t GUIDZzz = IndexedMemProfRecord::getGUID("_Z3zzzv"); 413 414 // Verify that extractCallsFromIR extracts caller-callee pairs as expected. 415 EXPECT_THAT(Calls, 416 UnorderedElementsAre(Pair( 417 GUIDFoo, ElementsAre(Pair(LineLocation(1, 3), GUIDBar), 418 Pair(LineLocation(2, 8), GUIDBaz))))); 419 420 llvm::InstrProfWriter Writer; 421 std::unique_ptr<IndexedInstrProfReader> Reader; 422 423 const MemInfoBlock MIB = makePartialMIB(); 424 425 Writer.setMemProfVersionRequested(Version3); 426 Writer.setMemProfFullSchema(false); 427 428 ASSERT_THAT_ERROR(Writer.mergeProfileKind(InstrProfKind::MemProf), 429 Succeeded()); 430 431 const IndexedMemProfRecord IndexedMR = makeRecordV2( 432 /*AllocFrames=*/{0x111, 0x222, 0x333}, 433 /*CallSiteFrames=*/{}, MIB, getHotColdSchema()); 434 435 IndexedMemProfData MemProfData; 436 // The call sites within foo. 437 MemProfData.Frames.try_emplace(0, GUIDFoo, 1, 8, false); 438 MemProfData.Frames.try_emplace(1, GUIDFoo, 2, 3, false); 439 MemProfData.Frames.try_emplace(2, GUIDFoo, 3, 3, false); 440 // Line/column numbers below don't matter. 441 MemProfData.Frames.try_emplace(3, GUIDBar, 9, 9, false); 442 MemProfData.Frames.try_emplace(4, GUIDZzz, 9, 9, false); 443 MemProfData.Frames.try_emplace(5, GUIDBaz, 9, 9, false); 444 MemProfData.CallStacks.try_emplace( 445 0x111, std::initializer_list<FrameId>{3, 0}); // bar called by foo 446 MemProfData.CallStacks.try_emplace( 447 0x222, std::initializer_list<FrameId>{4, 1}); // zzz called by foo 448 MemProfData.CallStacks.try_emplace( 449 0x333, std::initializer_list<FrameId>{5, 2}); // baz called by foo 450 MemProfData.Records.try_emplace(0x9999, IndexedMR); 451 Writer.addMemProfData(MemProfData, Err); 452 453 auto Profile = Writer.writeBuffer(); 454 455 auto ReaderOrErr = 456 IndexedInstrProfReader::create(std::move(Profile), nullptr); 457 EXPECT_THAT_ERROR(ReaderOrErr.takeError(), Succeeded()); 458 Reader = std::move(ReaderOrErr.get()); 459 460 // Verify that getMemProfCallerCalleePairs extracts caller-callee pairs as 461 // expected. 462 auto Pairs = Reader->getMemProfCallerCalleePairs(); 463 ASSERT_THAT(Pairs, SizeIs(4)); 464 ASSERT_THAT( 465 Pairs, 466 Contains(Pair(GUIDFoo, ElementsAre(Pair(LineLocation(1, 8), GUIDBar), 467 Pair(LineLocation(2, 3), GUIDZzz), 468 Pair(LineLocation(3, 3), GUIDBaz))))); 469 470 // Verify that computeUndriftMap identifies undrifting opportunities: 471 // 472 // Profile IR 473 // (Line: 1, Column: 8) -> (Line: 1, Column: 3) 474 // (Line: 3, Column: 3) -> (Line: 2, Column: 8) 475 auto UndriftMap = computeUndriftMap(*M, Reader.get(), TLI); 476 ASSERT_THAT(UndriftMap, 477 UnorderedElementsAre(Pair( 478 GUIDFoo, UnorderedElementsAre( 479 Pair(LineLocation(1, 8), LineLocation(1, 3)), 480 Pair(LineLocation(3, 3), LineLocation(2, 8)))))); 481 } 482 } // namespace 483 } // namespace memprof 484 } // namespace llvm 485