xref: /llvm-project/llvm/unittests/Transforms/Instrumentation/MemProfUseTest.cpp (revision ad635b4168213293feda4c0925c6df4501e41d52)
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