xref: /llvm-project/llvm/unittests/ProfileData/MemProfTest.cpp (revision 10d054e95413f0e98e4aeed9dbd4605f6f03b3fa)
1181e2e8fSMircea Trofin //===- unittests/Support/MemProfTest.cpp ----------------------------------===//
2181e2e8fSMircea Trofin //
3181e2e8fSMircea Trofin // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4181e2e8fSMircea Trofin // See https://llvm.org/LICENSE.txt for license information.
5181e2e8fSMircea Trofin // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6181e2e8fSMircea Trofin //
7181e2e8fSMircea Trofin //===----------------------------------------------------------------------===//
8181e2e8fSMircea Trofin 
9216575e5SSnehasish Kumar #include "llvm/ProfileData/MemProf.h"
10216575e5SSnehasish Kumar #include "llvm/ADT/DenseMap.h"
11216575e5SSnehasish Kumar #include "llvm/ADT/MapVector.h"
12c9dae434SKazu Hirata #include "llvm/ADT/STLForwardCompat.h"
13216575e5SSnehasish Kumar #include "llvm/DebugInfo/DIContext.h"
14216575e5SSnehasish Kumar #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
1527a4f254SSnehasish Kumar #include "llvm/IR/Value.h"
16216575e5SSnehasish Kumar #include "llvm/Object/ObjectFile.h"
17216575e5SSnehasish Kumar #include "llvm/ProfileData/MemProfData.inc"
182bede687SKazu Hirata #include "llvm/ProfileData/MemProfReader.h"
1966edefaeSKazu Hirata #include "llvm/ProfileData/MemProfYAML.h"
2050713461SSnehasish Kumar #include "llvm/Support/raw_ostream.h"
21216575e5SSnehasish Kumar #include "gmock/gmock.h"
22216575e5SSnehasish Kumar #include "gtest/gtest.h"
23216575e5SSnehasish Kumar 
24216575e5SSnehasish Kumar #include <initializer_list>
25216575e5SSnehasish Kumar 
266a137fbeSKazu Hirata namespace llvm {
276a137fbeSKazu Hirata namespace memprof {
28216575e5SSnehasish Kumar namespace {
29216575e5SSnehasish Kumar 
30216575e5SSnehasish Kumar using ::llvm::DIGlobal;
31216575e5SSnehasish Kumar using ::llvm::DIInliningInfo;
32216575e5SSnehasish Kumar using ::llvm::DILineInfo;
33216575e5SSnehasish Kumar using ::llvm::DILineInfoSpecifier;
34216575e5SSnehasish Kumar using ::llvm::DILocal;
35e144ae54SSerge Pavlov using ::llvm::StringRef;
36216575e5SSnehasish Kumar using ::llvm::object::SectionedAddress;
37216575e5SSnehasish Kumar using ::llvm::symbolize::SymbolizableModule;
38e98396f4SKazu Hirata using ::testing::ElementsAre;
3976b49312SKazu Hirata using ::testing::IsEmpty;
40e98396f4SKazu Hirata using ::testing::Pair;
41216575e5SSnehasish Kumar using ::testing::Return;
428137bd9eSKazu Hirata using ::testing::SizeIs;
43e98396f4SKazu Hirata using ::testing::UnorderedElementsAre;
44216575e5SSnehasish Kumar 
45216575e5SSnehasish Kumar class MockSymbolizer : public SymbolizableModule {
46216575e5SSnehasish Kumar public:
47216575e5SSnehasish Kumar   MOCK_CONST_METHOD3(symbolizeInlinedCode,
48216575e5SSnehasish Kumar                      DIInliningInfo(SectionedAddress, DILineInfoSpecifier,
49216575e5SSnehasish Kumar                                     bool));
50216575e5SSnehasish Kumar   // Most of the methods in the interface are unused. We only mock the
51216575e5SSnehasish Kumar   // method that we expect to be called from the memprof reader.
52216575e5SSnehasish Kumar   virtual DILineInfo symbolizeCode(SectionedAddress, DILineInfoSpecifier,
53216575e5SSnehasish Kumar                                    bool) const {
54216575e5SSnehasish Kumar     llvm_unreachable("unused");
55216575e5SSnehasish Kumar   }
56216575e5SSnehasish Kumar   virtual DIGlobal symbolizeData(SectionedAddress) const {
57216575e5SSnehasish Kumar     llvm_unreachable("unused");
58216575e5SSnehasish Kumar   }
59216575e5SSnehasish Kumar   virtual std::vector<DILocal> symbolizeFrame(SectionedAddress) const {
60216575e5SSnehasish Kumar     llvm_unreachable("unused");
61216575e5SSnehasish Kumar   }
62cb1a7d28SSerge Pavlov   virtual std::vector<SectionedAddress> findSymbol(StringRef Symbol,
63cb1a7d28SSerge Pavlov                                                    uint64_t Offset) const {
64e144ae54SSerge Pavlov     llvm_unreachable("unused");
65e144ae54SSerge Pavlov   }
66216575e5SSnehasish Kumar   virtual bool isWin32Module() const { llvm_unreachable("unused"); }
67216575e5SSnehasish Kumar   virtual uint64_t getModulePreferredBase() const {
68216575e5SSnehasish Kumar     llvm_unreachable("unused");
69216575e5SSnehasish Kumar   }
70216575e5SSnehasish Kumar };
71216575e5SSnehasish Kumar 
72216575e5SSnehasish Kumar struct MockInfo {
73216575e5SSnehasish Kumar   std::string FunctionName;
74216575e5SSnehasish Kumar   uint32_t Line;
75216575e5SSnehasish Kumar   uint32_t StartLine;
76216575e5SSnehasish Kumar   uint32_t Column;
7711314f40SSnehasish Kumar   std::string FileName = "valid/path.cc";
78216575e5SSnehasish Kumar };
79216575e5SSnehasish Kumar DIInliningInfo makeInliningInfo(std::initializer_list<MockInfo> MockFrames) {
80216575e5SSnehasish Kumar   DIInliningInfo Result;
81216575e5SSnehasish Kumar   for (const auto &Item : MockFrames) {
82216575e5SSnehasish Kumar     DILineInfo Frame;
83216575e5SSnehasish Kumar     Frame.FunctionName = Item.FunctionName;
84216575e5SSnehasish Kumar     Frame.Line = Item.Line;
85216575e5SSnehasish Kumar     Frame.StartLine = Item.StartLine;
86216575e5SSnehasish Kumar     Frame.Column = Item.Column;
8711314f40SSnehasish Kumar     Frame.FileName = Item.FileName;
88216575e5SSnehasish Kumar     Result.addFrame(Frame);
89216575e5SSnehasish Kumar   }
90216575e5SSnehasish Kumar   return Result;
91216575e5SSnehasish Kumar }
92216575e5SSnehasish Kumar 
93216575e5SSnehasish Kumar llvm::SmallVector<SegmentEntry, 4> makeSegments() {
94216575e5SSnehasish Kumar   llvm::SmallVector<SegmentEntry, 4> Result;
95216575e5SSnehasish Kumar   // Mimic an entry for a non position independent executable.
96216575e5SSnehasish Kumar   Result.emplace_back(0x0, 0x40000, 0x0);
97216575e5SSnehasish Kumar   return Result;
98216575e5SSnehasish Kumar }
99216575e5SSnehasish Kumar 
100216575e5SSnehasish Kumar const DILineInfoSpecifier specifier() {
101216575e5SSnehasish Kumar   return DILineInfoSpecifier(
102216575e5SSnehasish Kumar       DILineInfoSpecifier::FileLineInfoKind::RawValue,
103216575e5SSnehasish Kumar       DILineInfoSpecifier::FunctionNameKind::LinkageName);
104216575e5SSnehasish Kumar }
105216575e5SSnehasish Kumar 
1060a418490SSnehasish Kumar MATCHER_P4(FrameContains, FunctionName, LineOffset, Column, Inline, "") {
1076dd6a616SSnehasish Kumar   const Frame &F = arg;
1086dd6a616SSnehasish Kumar 
1090edc32fdSSnehasish Kumar   const uint64_t ExpectedHash = IndexedMemProfRecord::getGUID(FunctionName);
1106dd6a616SSnehasish Kumar   if (F.Function != ExpectedHash) {
111216575e5SSnehasish Kumar     *result_listener << "Hash mismatch";
112216575e5SSnehasish Kumar     return false;
113216575e5SSnehasish Kumar   }
11467ba5c50SFangrui Song   if (F.SymbolName && *F.SymbolName != FunctionName) {
115ec51971eSSnehasish Kumar     *result_listener << "SymbolName mismatch\nWant: " << FunctionName
11667ba5c50SFangrui Song                      << "\nGot: " << *F.SymbolName;
117ec51971eSSnehasish Kumar     return false;
118ec51971eSSnehasish Kumar   }
1196dd6a616SSnehasish Kumar   if (F.LineOffset == LineOffset && F.Column == Column &&
1206dd6a616SSnehasish Kumar       F.IsInlineFrame == Inline) {
121216575e5SSnehasish Kumar     return true;
122216575e5SSnehasish Kumar   }
123216575e5SSnehasish Kumar   *result_listener << "LineOffset, Column or Inline mismatch";
124216575e5SSnehasish Kumar   return false;
125216575e5SSnehasish Kumar }
126216575e5SSnehasish Kumar 
127216575e5SSnehasish Kumar TEST(MemProf, FillsValue) {
1281708519fSKazu Hirata   auto Symbolizer = std::make_unique<MockSymbolizer>();
129216575e5SSnehasish Kumar 
13027a4f254SSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x1000},
13127a4f254SSnehasish Kumar                                                 specifier(), false))
13227a4f254SSnehasish Kumar       .Times(1) // Only once since we remember invalid PCs.
13327a4f254SSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
13427a4f254SSnehasish Kumar           {"new", 70, 57, 3, "memprof/memprof_new_delete.cpp"},
13527a4f254SSnehasish Kumar       })));
13627a4f254SSnehasish Kumar 
137216575e5SSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},
138216575e5SSnehasish Kumar                                                 specifier(), false))
139dda7b749SSnehasish Kumar       .Times(1) // Only once since we cache the result for future lookups.
140216575e5SSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
141216575e5SSnehasish Kumar           {"foo", 10, 5, 30},
142216575e5SSnehasish Kumar           {"bar", 201, 150, 20},
143216575e5SSnehasish Kumar       })));
144216575e5SSnehasish Kumar 
14527a4f254SSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x3000},
146216575e5SSnehasish Kumar                                                 specifier(), false))
147216575e5SSnehasish Kumar       .Times(1)
148216575e5SSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
1490edc32fdSSnehasish Kumar           {"xyz.llvm.123", 10, 5, 30},
15027a4f254SSnehasish Kumar           {"abc", 10, 5, 30},
151216575e5SSnehasish Kumar       })));
152216575e5SSnehasish Kumar 
153216575e5SSnehasish Kumar   CallStackMap CSM;
15427a4f254SSnehasish Kumar   CSM[0x1] = {0x1000, 0x2000, 0x3000};
155216575e5SSnehasish Kumar 
156216575e5SSnehasish Kumar   llvm::MapVector<uint64_t, MemInfoBlock> Prof;
157f89319b8SSnehasish Kumar   Prof[0x1].AllocCount = 1;
158216575e5SSnehasish Kumar 
159216575e5SSnehasish Kumar   auto Seg = makeSegments();
160216575e5SSnehasish Kumar 
161ec51971eSSnehasish Kumar   RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM,
162ec51971eSSnehasish Kumar                           /*KeepName=*/true);
163216575e5SSnehasish Kumar 
16427a4f254SSnehasish Kumar   llvm::DenseMap<llvm::GlobalValue::GUID, MemProfRecord> Records;
1659aa5848dSKazu Hirata   for (const auto &Pair : Reader)
16627a4f254SSnehasish Kumar     Records.insert({Pair.first, Pair.second});
167216575e5SSnehasish Kumar 
168fdbc9443SJay Foad   // Mock program pseudocode and expected memprof record contents.
16927a4f254SSnehasish Kumar   //
17027a4f254SSnehasish Kumar   //                              AllocSite       CallSite
17127a4f254SSnehasish Kumar   // inline foo() { new(); }         Y               N
17227a4f254SSnehasish Kumar   // bar() { foo(); }                Y               Y
17327a4f254SSnehasish Kumar   // inline xyz() { bar(); }         N               Y
17427a4f254SSnehasish Kumar   // abc() { xyz(); }                N               Y
175216575e5SSnehasish Kumar 
17627a4f254SSnehasish Kumar   // We expect 4 records. We attach alloc site data to foo and bar, i.e.
17727a4f254SSnehasish Kumar   // all frames bottom up until we find a non-inline frame. We attach call site
17827a4f254SSnehasish Kumar   // data to bar, xyz and abc.
1793074060dSKazu Hirata   ASSERT_THAT(Records, SizeIs(4));
18027a4f254SSnehasish Kumar 
18127a4f254SSnehasish Kumar   // Check the memprof record for foo.
1826dd6a616SSnehasish Kumar   const llvm::GlobalValue::GUID FooId = IndexedMemProfRecord::getGUID("foo");
1831f38b8a2SKazu Hirata   ASSERT_TRUE(Records.contains(FooId));
18427a4f254SSnehasish Kumar   const MemProfRecord &Foo = Records[FooId];
1853074060dSKazu Hirata   ASSERT_THAT(Foo.AllocSites, SizeIs(1));
18627a4f254SSnehasish Kumar   EXPECT_EQ(Foo.AllocSites[0].Info.getAllocCount(), 1U);
18727a4f254SSnehasish Kumar   EXPECT_THAT(Foo.AllocSites[0].CallStack[0],
18827a4f254SSnehasish Kumar               FrameContains("foo", 5U, 30U, true));
18927a4f254SSnehasish Kumar   EXPECT_THAT(Foo.AllocSites[0].CallStack[1],
19027a4f254SSnehasish Kumar               FrameContains("bar", 51U, 20U, false));
19127a4f254SSnehasish Kumar   EXPECT_THAT(Foo.AllocSites[0].CallStack[2],
19227a4f254SSnehasish Kumar               FrameContains("xyz", 5U, 30U, true));
19327a4f254SSnehasish Kumar   EXPECT_THAT(Foo.AllocSites[0].CallStack[3],
19427a4f254SSnehasish Kumar               FrameContains("abc", 5U, 30U, false));
19527a4f254SSnehasish Kumar   EXPECT_TRUE(Foo.CallSites.empty());
19627a4f254SSnehasish Kumar 
19727a4f254SSnehasish Kumar   // Check the memprof record for bar.
1986dd6a616SSnehasish Kumar   const llvm::GlobalValue::GUID BarId = IndexedMemProfRecord::getGUID("bar");
1991f38b8a2SKazu Hirata   ASSERT_TRUE(Records.contains(BarId));
20027a4f254SSnehasish Kumar   const MemProfRecord &Bar = Records[BarId];
2013074060dSKazu Hirata   ASSERT_THAT(Bar.AllocSites, SizeIs(1));
20227a4f254SSnehasish Kumar   EXPECT_EQ(Bar.AllocSites[0].Info.getAllocCount(), 1U);
20327a4f254SSnehasish Kumar   EXPECT_THAT(Bar.AllocSites[0].CallStack[0],
20427a4f254SSnehasish Kumar               FrameContains("foo", 5U, 30U, true));
20527a4f254SSnehasish Kumar   EXPECT_THAT(Bar.AllocSites[0].CallStack[1],
20627a4f254SSnehasish Kumar               FrameContains("bar", 51U, 20U, false));
20727a4f254SSnehasish Kumar   EXPECT_THAT(Bar.AllocSites[0].CallStack[2],
20827a4f254SSnehasish Kumar               FrameContains("xyz", 5U, 30U, true));
20927a4f254SSnehasish Kumar   EXPECT_THAT(Bar.AllocSites[0].CallStack[3],
21027a4f254SSnehasish Kumar               FrameContains("abc", 5U, 30U, false));
21127a4f254SSnehasish Kumar 
21232f7f001SKazu Hirata   EXPECT_THAT(Bar.CallSites,
21332f7f001SKazu Hirata               ElementsAre(ElementsAre(FrameContains("foo", 5U, 30U, true),
21432f7f001SKazu Hirata                                       FrameContains("bar", 51U, 20U, false))));
21527a4f254SSnehasish Kumar 
21627a4f254SSnehasish Kumar   // Check the memprof record for xyz.
2176dd6a616SSnehasish Kumar   const llvm::GlobalValue::GUID XyzId = IndexedMemProfRecord::getGUID("xyz");
2181f38b8a2SKazu Hirata   ASSERT_TRUE(Records.contains(XyzId));
21927a4f254SSnehasish Kumar   const MemProfRecord &Xyz = Records[XyzId];
22027a4f254SSnehasish Kumar   // Expect the entire frame even though in practice we only need the first
22127a4f254SSnehasish Kumar   // entry here.
22232f7f001SKazu Hirata   EXPECT_THAT(Xyz.CallSites,
22332f7f001SKazu Hirata               ElementsAre(ElementsAre(FrameContains("xyz", 5U, 30U, true),
22432f7f001SKazu Hirata                                       FrameContains("abc", 5U, 30U, false))));
22527a4f254SSnehasish Kumar 
22627a4f254SSnehasish Kumar   // Check the memprof record for abc.
2276dd6a616SSnehasish Kumar   const llvm::GlobalValue::GUID AbcId = IndexedMemProfRecord::getGUID("abc");
2281f38b8a2SKazu Hirata   ASSERT_TRUE(Records.contains(AbcId));
22927a4f254SSnehasish Kumar   const MemProfRecord &Abc = Records[AbcId];
23027a4f254SSnehasish Kumar   EXPECT_TRUE(Abc.AllocSites.empty());
23132f7f001SKazu Hirata   EXPECT_THAT(Abc.CallSites,
23232f7f001SKazu Hirata               ElementsAre(ElementsAre(FrameContains("xyz", 5U, 30U, true),
23332f7f001SKazu Hirata                                       FrameContains("abc", 5U, 30U, false))));
234216575e5SSnehasish Kumar }
235216575e5SSnehasish Kumar 
23650713461SSnehasish Kumar TEST(MemProf, PortableWrapper) {
23750713461SSnehasish Kumar   MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,
23850713461SSnehasish Kumar                     /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,
23930b93db5SMatthew Weingarten                     /*dealloc_cpu=*/4, /*Histogram=*/0, /*HistogramSize=*/0);
24050713461SSnehasish Kumar 
2416a137fbeSKazu Hirata   const auto Schema = getFullSchema();
242c9dae434SKazu Hirata   PortableMemInfoBlock WriteBlock(Info, Schema);
24350713461SSnehasish Kumar 
24450713461SSnehasish Kumar   std::string Buffer;
24550713461SSnehasish Kumar   llvm::raw_string_ostream OS(Buffer);
24650713461SSnehasish Kumar   WriteBlock.serialize(Schema, OS);
24750713461SSnehasish Kumar 
24850713461SSnehasish Kumar   PortableMemInfoBlock ReadBlock(
24950713461SSnehasish Kumar       Schema, reinterpret_cast<const unsigned char *>(Buffer.data()));
25050713461SSnehasish Kumar 
25150713461SSnehasish Kumar   EXPECT_EQ(ReadBlock, WriteBlock);
25250713461SSnehasish Kumar   // Here we compare directly with the actual counts instead of MemInfoBlock
25350713461SSnehasish Kumar   // members. Since the MemInfoBlock struct is packed and the EXPECT_EQ macros
25450713461SSnehasish Kumar   // take a reference to the params, this results in unaligned accesses.
25550713461SSnehasish Kumar   EXPECT_EQ(1UL, ReadBlock.getAllocCount());
25650713461SSnehasish Kumar   EXPECT_EQ(7ULL, ReadBlock.getTotalAccessCount());
25750713461SSnehasish Kumar   EXPECT_EQ(3UL, ReadBlock.getAllocCpuId());
25850713461SSnehasish Kumar }
25950713461SSnehasish Kumar 
260d89914f3SKazu Hirata TEST(MemProf, RecordSerializationRoundTripVerion2) {
2616a137fbeSKazu Hirata   const auto Schema = getFullSchema();
262d89914f3SKazu Hirata 
263d89914f3SKazu Hirata   MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,
264d89914f3SKazu Hirata                     /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,
26530b93db5SMatthew Weingarten                     /*dealloc_cpu=*/4, /*Histogram=*/0, /*HistogramSize=*/0);
266d89914f3SKazu Hirata 
267b6dfdd2bSKazu Hirata   llvm::SmallVector<CallStackId> CallStackIds = {0x123, 0x456};
268d89914f3SKazu Hirata 
269b6dfdd2bSKazu Hirata   llvm::SmallVector<CallStackId> CallSiteIds = {0x333, 0x444};
270d89914f3SKazu Hirata 
271d89914f3SKazu Hirata   IndexedMemProfRecord Record;
272d89914f3SKazu Hirata   for (const auto &CSId : CallStackIds) {
273d89914f3SKazu Hirata     // Use the same info block for both allocation sites.
274a4e1a3dcSKazu Hirata     Record.AllocSites.emplace_back(CSId, Info);
275d89914f3SKazu Hirata   }
276d89914f3SKazu Hirata   Record.CallSiteIds.assign(CallSiteIds);
277d89914f3SKazu Hirata 
278d89914f3SKazu Hirata   std::string Buffer;
279d89914f3SKazu Hirata   llvm::raw_string_ostream OS(Buffer);
2806a137fbeSKazu Hirata   Record.serialize(Schema, OS, Version2);
281d89914f3SKazu Hirata 
282d89914f3SKazu Hirata   const IndexedMemProfRecord GotRecord = IndexedMemProfRecord::deserialize(
2836a137fbeSKazu Hirata       Schema, reinterpret_cast<const unsigned char *>(Buffer.data()), Version2);
2840a418490SSnehasish Kumar 
2856dd6a616SSnehasish Kumar   EXPECT_EQ(Record, GotRecord);
2860a418490SSnehasish Kumar }
28711314f40SSnehasish Kumar 
288c9dae434SKazu Hirata TEST(MemProf, RecordSerializationRoundTripVersion2HotColdSchema) {
2896a137fbeSKazu Hirata   const auto Schema = getHotColdSchema();
290c9dae434SKazu Hirata 
291c9dae434SKazu Hirata   MemInfoBlock Info;
292c9dae434SKazu Hirata   Info.AllocCount = 11;
293c9dae434SKazu Hirata   Info.TotalSize = 22;
294c9dae434SKazu Hirata   Info.TotalLifetime = 33;
295c9dae434SKazu Hirata   Info.TotalLifetimeAccessDensity = 44;
296c9dae434SKazu Hirata 
297b6dfdd2bSKazu Hirata   llvm::SmallVector<CallStackId> CallStackIds = {0x123, 0x456};
298c9dae434SKazu Hirata 
299b6dfdd2bSKazu Hirata   llvm::SmallVector<CallStackId> CallSiteIds = {0x333, 0x444};
300c9dae434SKazu Hirata 
301c9dae434SKazu Hirata   IndexedMemProfRecord Record;
302c9dae434SKazu Hirata   for (const auto &CSId : CallStackIds) {
303c9dae434SKazu Hirata     // Use the same info block for both allocation sites.
304a4e1a3dcSKazu Hirata     Record.AllocSites.emplace_back(CSId, Info, Schema);
305c9dae434SKazu Hirata   }
306c9dae434SKazu Hirata   Record.CallSiteIds.assign(CallSiteIds);
307c9dae434SKazu Hirata 
308c9dae434SKazu Hirata   std::bitset<llvm::to_underlying(Meta::Size)> SchemaBitSet;
309c9dae434SKazu Hirata   for (auto Id : Schema)
310c9dae434SKazu Hirata     SchemaBitSet.set(llvm::to_underlying(Id));
311c9dae434SKazu Hirata 
312c9dae434SKazu Hirata   // Verify that SchemaBitSet has the fields we expect and nothing else, which
313c9dae434SKazu Hirata   // we check with count().
314c9dae434SKazu Hirata   EXPECT_EQ(SchemaBitSet.count(), 4U);
315c9dae434SKazu Hirata   EXPECT_TRUE(SchemaBitSet[llvm::to_underlying(Meta::AllocCount)]);
316c9dae434SKazu Hirata   EXPECT_TRUE(SchemaBitSet[llvm::to_underlying(Meta::TotalSize)]);
317c9dae434SKazu Hirata   EXPECT_TRUE(SchemaBitSet[llvm::to_underlying(Meta::TotalLifetime)]);
318c9dae434SKazu Hirata   EXPECT_TRUE(
319c9dae434SKazu Hirata       SchemaBitSet[llvm::to_underlying(Meta::TotalLifetimeAccessDensity)]);
320c9dae434SKazu Hirata 
321c9dae434SKazu Hirata   // Verify that Schema has propagated all the way to the Info field in each
322c9dae434SKazu Hirata   // IndexedAllocationInfo.
3236a137fbeSKazu Hirata   ASSERT_THAT(Record.AllocSites, SizeIs(2));
324c9dae434SKazu Hirata   EXPECT_EQ(Record.AllocSites[0].Info.getSchema(), SchemaBitSet);
325c9dae434SKazu Hirata   EXPECT_EQ(Record.AllocSites[1].Info.getSchema(), SchemaBitSet);
326c9dae434SKazu Hirata 
327c9dae434SKazu Hirata   std::string Buffer;
328c9dae434SKazu Hirata   llvm::raw_string_ostream OS(Buffer);
3296a137fbeSKazu Hirata   Record.serialize(Schema, OS, Version2);
330c9dae434SKazu Hirata 
331c9dae434SKazu Hirata   const IndexedMemProfRecord GotRecord = IndexedMemProfRecord::deserialize(
3326a137fbeSKazu Hirata       Schema, reinterpret_cast<const unsigned char *>(Buffer.data()), Version2);
333c9dae434SKazu Hirata 
334c9dae434SKazu Hirata   // Verify that Schema comes back correctly after deserialization. Technically,
335c9dae434SKazu Hirata   // the comparison between Record and GotRecord below includes the comparison
336c9dae434SKazu Hirata   // of their Schemas, but we'll verify the Schemas on our own.
3376a137fbeSKazu Hirata   ASSERT_THAT(GotRecord.AllocSites, SizeIs(2));
338c9dae434SKazu Hirata   EXPECT_EQ(GotRecord.AllocSites[0].Info.getSchema(), SchemaBitSet);
339c9dae434SKazu Hirata   EXPECT_EQ(GotRecord.AllocSites[1].Info.getSchema(), SchemaBitSet);
340c9dae434SKazu Hirata 
341c9dae434SKazu Hirata   EXPECT_EQ(Record, GotRecord);
342c9dae434SKazu Hirata }
343c9dae434SKazu Hirata 
34411314f40SSnehasish Kumar TEST(MemProf, SymbolizationFilter) {
3451708519fSKazu Hirata   auto Symbolizer = std::make_unique<MockSymbolizer>();
34611314f40SSnehasish Kumar 
34711314f40SSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x1000},
34811314f40SSnehasish Kumar                                                 specifier(), false))
34911314f40SSnehasish Kumar       .Times(1) // once since we don't lookup invalid PCs repeatedly.
35011314f40SSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
35111314f40SSnehasish Kumar           {"malloc", 70, 57, 3, "memprof/memprof_malloc_linux.cpp"},
35211314f40SSnehasish Kumar       })));
35311314f40SSnehasish Kumar 
35411314f40SSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},
35511314f40SSnehasish Kumar                                                 specifier(), false))
35611314f40SSnehasish Kumar       .Times(1) // once since we don't lookup invalid PCs repeatedly.
35711314f40SSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
35811314f40SSnehasish Kumar           {"new", 70, 57, 3, "memprof/memprof_new_delete.cpp"},
35911314f40SSnehasish Kumar       })));
36011314f40SSnehasish Kumar 
36111314f40SSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x3000},
36211314f40SSnehasish Kumar                                                 specifier(), false))
36311314f40SSnehasish Kumar       .Times(1) // once since we don't lookup invalid PCs repeatedly.
36411314f40SSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
36511314f40SSnehasish Kumar           {DILineInfo::BadString, 0, 0, 0},
36611314f40SSnehasish Kumar       })));
36711314f40SSnehasish Kumar 
36811314f40SSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x4000},
36911314f40SSnehasish Kumar                                                 specifier(), false))
37011314f40SSnehasish Kumar       .Times(1)
37111314f40SSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
372bcebadebSSnehasish Kumar           {"foo", 10, 5, 30, "memprof/memprof_test_file.cpp"},
373bcebadebSSnehasish Kumar       })));
374bcebadebSSnehasish Kumar 
375bcebadebSSnehasish Kumar   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x5000},
376bcebadebSSnehasish Kumar                                                 specifier(), false))
377bcebadebSSnehasish Kumar       .Times(1)
378bcebadebSSnehasish Kumar       .WillRepeatedly(Return(makeInliningInfo({
379bcebadebSSnehasish Kumar           // Depending on how the runtime was compiled, only the filename
380bcebadebSSnehasish Kumar           // may be present in the debug information.
381bcebadebSSnehasish Kumar           {"malloc", 70, 57, 3, "memprof_malloc_linux.cpp"},
38211314f40SSnehasish Kumar       })));
38311314f40SSnehasish Kumar 
38411314f40SSnehasish Kumar   CallStackMap CSM;
38511314f40SSnehasish Kumar   CSM[0x1] = {0x1000, 0x2000, 0x3000, 0x4000};
38611314f40SSnehasish Kumar   // This entry should be dropped since all PCs are either not
38711314f40SSnehasish Kumar   // symbolizable or belong to the runtime.
388bcebadebSSnehasish Kumar   CSM[0x2] = {0x1000, 0x2000, 0x5000};
38911314f40SSnehasish Kumar 
39011314f40SSnehasish Kumar   llvm::MapVector<uint64_t, MemInfoBlock> Prof;
39111314f40SSnehasish Kumar   Prof[0x1].AllocCount = 1;
39211314f40SSnehasish Kumar   Prof[0x2].AllocCount = 1;
39311314f40SSnehasish Kumar 
39411314f40SSnehasish Kumar   auto Seg = makeSegments();
39511314f40SSnehasish Kumar 
39611314f40SSnehasish Kumar   RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM);
39711314f40SSnehasish Kumar 
39827a4f254SSnehasish Kumar   llvm::SmallVector<MemProfRecord, 1> Records;
3999aa5848dSKazu Hirata   for (const auto &KeyRecordPair : Reader)
40027a4f254SSnehasish Kumar     Records.push_back(KeyRecordPair.second);
40127a4f254SSnehasish Kumar 
4023074060dSKazu Hirata   ASSERT_THAT(Records, SizeIs(1));
4033074060dSKazu Hirata   ASSERT_THAT(Records[0].AllocSites, SizeIs(1));
40432f7f001SKazu Hirata   EXPECT_THAT(Records[0].AllocSites[0].CallStack,
40532f7f001SKazu Hirata               ElementsAre(FrameContains("foo", 5U, 30U, false)));
40611314f40SSnehasish Kumar }
40737fd3c96SSnehasish Kumar 
40837fd3c96SSnehasish Kumar TEST(MemProf, BaseMemProfReader) {
409b6dfdd2bSKazu Hirata   IndexedMemProfData MemProfData;
41037fd3c96SSnehasish Kumar   Frame F1(/*Hash=*/IndexedMemProfRecord::getGUID("foo"), /*LineOffset=*/20,
41137fd3c96SSnehasish Kumar            /*Column=*/5, /*IsInlineFrame=*/true);
41237fd3c96SSnehasish Kumar   Frame F2(/*Hash=*/IndexedMemProfRecord::getGUID("bar"), /*LineOffset=*/10,
41337fd3c96SSnehasish Kumar            /*Column=*/2, /*IsInlineFrame=*/false);
4149b14ded1SKazu Hirata   auto F1Id = MemProfData.addFrame(F1);
4159b14ded1SKazu Hirata   auto F2Id = MemProfData.addFrame(F2);
41637fd3c96SSnehasish Kumar 
4179b14ded1SKazu Hirata   llvm::SmallVector<FrameId> CallStack{F1Id, F2Id};
418c5e4e8f8SKazu Hirata   CallStackId CSId = MemProfData.addCallStack(std::move(CallStack));
419b170ab21SKazu Hirata 
42037fd3c96SSnehasish Kumar   IndexedMemProfRecord FakeRecord;
42137fd3c96SSnehasish Kumar   MemInfoBlock Block;
42237fd3c96SSnehasish Kumar   Block.AllocCount = 1U, Block.TotalAccessDensity = 4,
42337fd3c96SSnehasish Kumar   Block.TotalLifetime = 200001;
424b170ab21SKazu Hirata   FakeRecord.AllocSites.emplace_back(/*CSId=*/CSId, /*MB=*/Block);
425ab07c515SKazu Hirata   MemProfData.Records.try_emplace(0x1234, std::move(FakeRecord));
42637fd3c96SSnehasish Kumar 
4275add295fSKazu Hirata   MemProfReader Reader(std::move(MemProfData));
42837fd3c96SSnehasish Kumar 
42937fd3c96SSnehasish Kumar   llvm::SmallVector<MemProfRecord, 1> Records;
4309aa5848dSKazu Hirata   for (const auto &KeyRecordPair : Reader)
43137fd3c96SSnehasish Kumar     Records.push_back(KeyRecordPair.second);
43237fd3c96SSnehasish Kumar 
4333074060dSKazu Hirata   ASSERT_THAT(Records, SizeIs(1));
4343074060dSKazu Hirata   ASSERT_THAT(Records[0].AllocSites, SizeIs(1));
43532f7f001SKazu Hirata   EXPECT_THAT(Records[0].AllocSites[0].CallStack,
43632f7f001SKazu Hirata               ElementsAre(FrameContains("foo", 20U, 5U, true),
43732f7f001SKazu Hirata                           FrameContains("bar", 10U, 2U, false)));
43837fd3c96SSnehasish Kumar }
4398137bd9eSKazu Hirata 
4405422eb0bSKazu Hirata TEST(MemProf, BaseMemProfReaderWithCSIdMap) {
441b6dfdd2bSKazu Hirata   IndexedMemProfData MemProfData;
4425422eb0bSKazu Hirata   Frame F1(/*Hash=*/IndexedMemProfRecord::getGUID("foo"), /*LineOffset=*/20,
4435422eb0bSKazu Hirata            /*Column=*/5, /*IsInlineFrame=*/true);
4445422eb0bSKazu Hirata   Frame F2(/*Hash=*/IndexedMemProfRecord::getGUID("bar"), /*LineOffset=*/10,
4455422eb0bSKazu Hirata            /*Column=*/2, /*IsInlineFrame=*/false);
4469b14ded1SKazu Hirata   auto F1Id = MemProfData.addFrame(F1);
4479b14ded1SKazu Hirata   auto F2Id = MemProfData.addFrame(F2);
4485422eb0bSKazu Hirata 
4499b14ded1SKazu Hirata   llvm::SmallVector<FrameId> CallStack = {F1Id, F2Id};
4509b14ded1SKazu Hirata   auto CSId = MemProfData.addCallStack(std::move(CallStack));
4515422eb0bSKazu Hirata 
4525422eb0bSKazu Hirata   IndexedMemProfRecord FakeRecord;
4535422eb0bSKazu Hirata   MemInfoBlock Block;
4545422eb0bSKazu Hirata   Block.AllocCount = 1U, Block.TotalAccessDensity = 4,
4555422eb0bSKazu Hirata   Block.TotalLifetime = 200001;
4569b14ded1SKazu Hirata   FakeRecord.AllocSites.emplace_back(/*CSId=*/CSId, /*MB=*/Block);
457ab07c515SKazu Hirata   MemProfData.Records.try_emplace(0x1234, std::move(FakeRecord));
4585422eb0bSKazu Hirata 
4595add295fSKazu Hirata   MemProfReader Reader(std::move(MemProfData));
4605422eb0bSKazu Hirata 
4615422eb0bSKazu Hirata   llvm::SmallVector<MemProfRecord, 1> Records;
4629aa5848dSKazu Hirata   for (const auto &KeyRecordPair : Reader)
4635422eb0bSKazu Hirata     Records.push_back(KeyRecordPair.second);
4645422eb0bSKazu Hirata 
4655422eb0bSKazu Hirata   ASSERT_THAT(Records, SizeIs(1));
4665422eb0bSKazu Hirata   ASSERT_THAT(Records[0].AllocSites, SizeIs(1));
46732f7f001SKazu Hirata   EXPECT_THAT(Records[0].AllocSites[0].CallStack,
46832f7f001SKazu Hirata               ElementsAre(FrameContains("foo", 20U, 5U, true),
46932f7f001SKazu Hirata                           FrameContains("bar", 10U, 2U, false)));
4705422eb0bSKazu Hirata }
4715422eb0bSKazu Hirata 
4728137bd9eSKazu Hirata TEST(MemProf, IndexedMemProfRecordToMemProfRecord) {
4738137bd9eSKazu Hirata   // Verify that MemProfRecord can be constructed from IndexedMemProfRecord with
4748137bd9eSKazu Hirata   // CallStackIds only.
4758137bd9eSKazu Hirata 
47600090ac0SKazu Hirata   IndexedMemProfData MemProfData;
4778137bd9eSKazu Hirata   Frame F1(1, 0, 0, false);
4788137bd9eSKazu Hirata   Frame F2(2, 0, 0, false);
4798137bd9eSKazu Hirata   Frame F3(3, 0, 0, false);
4808137bd9eSKazu Hirata   Frame F4(4, 0, 0, false);
4819b14ded1SKazu Hirata   auto F1Id = MemProfData.addFrame(F1);
4829b14ded1SKazu Hirata   auto F2Id = MemProfData.addFrame(F2);
4839b14ded1SKazu Hirata   auto F3Id = MemProfData.addFrame(F3);
4849b14ded1SKazu Hirata   auto F4Id = MemProfData.addFrame(F4);
4858137bd9eSKazu Hirata 
4869b14ded1SKazu Hirata   llvm::SmallVector<FrameId> CS1 = {F1Id, F2Id};
4879b14ded1SKazu Hirata   llvm::SmallVector<FrameId> CS2 = {F1Id, F3Id};
4889b14ded1SKazu Hirata   llvm::SmallVector<FrameId> CS3 = {F2Id, F3Id};
4899b14ded1SKazu Hirata   llvm::SmallVector<FrameId> CS4 = {F2Id, F4Id};
4909b14ded1SKazu Hirata   auto CS1Id = MemProfData.addCallStack(std::move(CS1));
4919b14ded1SKazu Hirata   auto CS2Id = MemProfData.addCallStack(std::move(CS2));
4929b14ded1SKazu Hirata   auto CS3Id = MemProfData.addCallStack(std::move(CS3));
4939b14ded1SKazu Hirata   auto CS4Id = MemProfData.addCallStack(std::move(CS4));
4948137bd9eSKazu Hirata 
4958137bd9eSKazu Hirata   IndexedMemProfRecord IndexedRecord;
4968137bd9eSKazu Hirata   IndexedAllocationInfo AI;
4979b14ded1SKazu Hirata   AI.CSId = CS1Id;
4988137bd9eSKazu Hirata   IndexedRecord.AllocSites.push_back(AI);
4999b14ded1SKazu Hirata   AI.CSId = CS2Id;
5008137bd9eSKazu Hirata   IndexedRecord.AllocSites.push_back(AI);
5019b14ded1SKazu Hirata   IndexedRecord.CallSiteIds.push_back(CS3Id);
5029b14ded1SKazu Hirata   IndexedRecord.CallSiteIds.push_back(CS4Id);
5038137bd9eSKazu Hirata 
504*10d054e9SKazu Hirata   IndexedCallstackIdConveter CSIdConv(MemProfData);
5058137bd9eSKazu Hirata 
50635260201SKazu Hirata   MemProfRecord Record = IndexedRecord.toMemProfRecord(CSIdConv);
5078137bd9eSKazu Hirata 
5088137bd9eSKazu Hirata   // Make sure that all lookups are successful.
509*10d054e9SKazu Hirata   ASSERT_EQ(CSIdConv.FrameIdConv.LastUnmappedId, std::nullopt);
510*10d054e9SKazu Hirata   ASSERT_EQ(CSIdConv.CSIdConv.LastUnmappedId, std::nullopt);
5118137bd9eSKazu Hirata 
5128137bd9eSKazu Hirata   // Verify the contents of Record.
5138137bd9eSKazu Hirata   ASSERT_THAT(Record.AllocSites, SizeIs(2));
5146c062afcSKazu Hirata   EXPECT_THAT(Record.AllocSites[0].CallStack, ElementsAre(F1, F2));
5156c062afcSKazu Hirata   EXPECT_THAT(Record.AllocSites[1].CallStack, ElementsAre(F1, F3));
5166c062afcSKazu Hirata   EXPECT_THAT(Record.CallSites,
5176c062afcSKazu Hirata               ElementsAre(ElementsAre(F2, F3), ElementsAre(F2, F4)));
5188137bd9eSKazu Hirata }
51926fabddeSKazu Hirata 
52026fabddeSKazu Hirata // Populate those fields returned by getHotColdSchema.
52126fabddeSKazu Hirata MemInfoBlock makePartialMIB() {
52226fabddeSKazu Hirata   MemInfoBlock MIB;
52326fabddeSKazu Hirata   MIB.AllocCount = 1;
52426fabddeSKazu Hirata   MIB.TotalSize = 5;
52526fabddeSKazu Hirata   MIB.TotalLifetime = 10;
52626fabddeSKazu Hirata   MIB.TotalLifetimeAccessDensity = 23;
52726fabddeSKazu Hirata   return MIB;
52826fabddeSKazu Hirata }
52926fabddeSKazu Hirata 
53026fabddeSKazu Hirata TEST(MemProf, MissingCallStackId) {
53126fabddeSKazu Hirata   // Use a non-existent CallStackId to trigger a mapping error in
53226fabddeSKazu Hirata   // toMemProfRecord.
5336a137fbeSKazu Hirata   IndexedAllocationInfo AI(0xdeadbeefU, makePartialMIB(), getHotColdSchema());
53426fabddeSKazu Hirata 
53526fabddeSKazu Hirata   IndexedMemProfRecord IndexedMR;
53626fabddeSKazu Hirata   IndexedMR.AllocSites.push_back(AI);
53726fabddeSKazu Hirata 
53826fabddeSKazu Hirata   // Create empty maps.
5398eb5baf5SKazu Hirata   IndexedMemProfData MemProfData;
540*10d054e9SKazu Hirata   IndexedCallstackIdConveter CSIdConv(MemProfData);
54126fabddeSKazu Hirata 
54226fabddeSKazu Hirata   // We are only interested in errors, not the return value.
54326fabddeSKazu Hirata   (void)IndexedMR.toMemProfRecord(CSIdConv);
54426fabddeSKazu Hirata 
545*10d054e9SKazu Hirata   ASSERT_TRUE(CSIdConv.CSIdConv.LastUnmappedId.has_value());
546*10d054e9SKazu Hirata   EXPECT_EQ(*CSIdConv.CSIdConv.LastUnmappedId, 0xdeadbeefU);
547*10d054e9SKazu Hirata   EXPECT_EQ(CSIdConv.FrameIdConv.LastUnmappedId, std::nullopt);
54826fabddeSKazu Hirata }
54926fabddeSKazu Hirata 
55026fabddeSKazu Hirata TEST(MemProf, MissingFrameId) {
5518eb5baf5SKazu Hirata   // An empty Frame map to trigger a mapping error.
5528eb5baf5SKazu Hirata   IndexedMemProfData MemProfData;
553fda80a4fSKazu Hirata   auto CSId = MemProfData.addCallStack(SmallVector<FrameId>{2, 3});
554fda80a4fSKazu Hirata 
555fda80a4fSKazu Hirata   IndexedMemProfRecord IndexedMR;
556fda80a4fSKazu Hirata   IndexedMR.AllocSites.emplace_back(CSId, makePartialMIB(), getHotColdSchema());
55726fabddeSKazu Hirata 
558*10d054e9SKazu Hirata   IndexedCallstackIdConveter CSIdConv(MemProfData);
55926fabddeSKazu Hirata 
56026fabddeSKazu Hirata   // We are only interested in errors, not the return value.
56126fabddeSKazu Hirata   (void)IndexedMR.toMemProfRecord(CSIdConv);
56226fabddeSKazu Hirata 
563*10d054e9SKazu Hirata   EXPECT_EQ(CSIdConv.CSIdConv.LastUnmappedId, std::nullopt);
564*10d054e9SKazu Hirata   ASSERT_TRUE(CSIdConv.FrameIdConv.LastUnmappedId.has_value());
565*10d054e9SKazu Hirata   EXPECT_EQ(*CSIdConv.FrameIdConv.LastUnmappedId, 3U);
56626fabddeSKazu Hirata }
5675c0df5feSKazu Hirata 
5685c0df5feSKazu Hirata // Verify CallStackRadixTreeBuilder can handle empty inputs.
5695c0df5feSKazu Hirata TEST(MemProf, RadixTreeBuilderEmpty) {
5706a137fbeSKazu Hirata   llvm::DenseMap<FrameId, LinearFrameId> MemProfFrameIndexes;
57164fadf17SKazu Hirata   IndexedMemProfData MemProfData;
5726a137fbeSKazu Hirata   llvm::DenseMap<FrameId, FrameStat> FrameHistogram =
57364fadf17SKazu Hirata       computeFrameHistogram<FrameId>(MemProfData.CallStacks);
5746a137fbeSKazu Hirata   CallStackRadixTreeBuilder<FrameId> Builder;
57564fadf17SKazu Hirata   Builder.build(std::move(MemProfData.CallStacks), &MemProfFrameIndexes,
576dc3f8c2fSKazu Hirata                 FrameHistogram);
5778420602bSKazu Hirata   ASSERT_THAT(Builder.getRadixArray(), IsEmpty());
578c348e265SKazu Hirata   const auto Mappings = Builder.takeCallStackPos();
5798420602bSKazu Hirata   ASSERT_THAT(Mappings, IsEmpty());
5805c0df5feSKazu Hirata }
5815c0df5feSKazu Hirata 
5825c0df5feSKazu Hirata // Verify CallStackRadixTreeBuilder can handle one trivial call stack.
5835c0df5feSKazu Hirata TEST(MemProf, RadixTreeBuilderOne) {
5846a137fbeSKazu Hirata   llvm::DenseMap<FrameId, LinearFrameId> MemProfFrameIndexes = {
5855c0df5feSKazu Hirata       {11, 1}, {12, 2}, {13, 3}};
586b6dfdd2bSKazu Hirata   llvm::SmallVector<FrameId> CS1 = {13, 12, 11};
58764fadf17SKazu Hirata   IndexedMemProfData MemProfData;
5889b14ded1SKazu Hirata   auto CS1Id = MemProfData.addCallStack(std::move(CS1));
5896a137fbeSKazu Hirata   llvm::DenseMap<FrameId, FrameStat> FrameHistogram =
59064fadf17SKazu Hirata       computeFrameHistogram<FrameId>(MemProfData.CallStacks);
5916a137fbeSKazu Hirata   CallStackRadixTreeBuilder<FrameId> Builder;
59264fadf17SKazu Hirata   Builder.build(std::move(MemProfData.CallStacks), &MemProfFrameIndexes,
593dc3f8c2fSKazu Hirata                 FrameHistogram);
59482b43794SKazu Hirata   EXPECT_THAT(Builder.getRadixArray(),
59582b43794SKazu Hirata               ElementsAre(3U, // Size of CS1,
5965c0df5feSKazu Hirata                           3U, // MemProfFrameIndexes[13]
5975c0df5feSKazu Hirata                           2U, // MemProfFrameIndexes[12]
5985c0df5feSKazu Hirata                           1U  // MemProfFrameIndexes[11]
59982b43794SKazu Hirata                           ));
600c348e265SKazu Hirata   const auto Mappings = Builder.takeCallStackPos();
6019b14ded1SKazu Hirata   EXPECT_THAT(Mappings, UnorderedElementsAre(Pair(CS1Id, 0U)));
6025c0df5feSKazu Hirata }
6035c0df5feSKazu Hirata 
6045c0df5feSKazu Hirata // Verify CallStackRadixTreeBuilder can form a link between two call stacks.
6055c0df5feSKazu Hirata TEST(MemProf, RadixTreeBuilderTwo) {
6066a137fbeSKazu Hirata   llvm::DenseMap<FrameId, LinearFrameId> MemProfFrameIndexes = {
6075c0df5feSKazu Hirata       {11, 1}, {12, 2}, {13, 3}};
608b6dfdd2bSKazu Hirata   llvm::SmallVector<FrameId> CS1 = {12, 11};
609b6dfdd2bSKazu Hirata   llvm::SmallVector<FrameId> CS2 = {13, 12, 11};
61064fadf17SKazu Hirata   IndexedMemProfData MemProfData;
6119b14ded1SKazu Hirata   auto CS1Id = MemProfData.addCallStack(std::move(CS1));
6129b14ded1SKazu Hirata   auto CS2Id = MemProfData.addCallStack(std::move(CS2));
6136a137fbeSKazu Hirata   llvm::DenseMap<FrameId, FrameStat> FrameHistogram =
61464fadf17SKazu Hirata       computeFrameHistogram<FrameId>(MemProfData.CallStacks);
6156a137fbeSKazu Hirata   CallStackRadixTreeBuilder<FrameId> Builder;
61664fadf17SKazu Hirata   Builder.build(std::move(MemProfData.CallStacks), &MemProfFrameIndexes,
617dc3f8c2fSKazu Hirata                 FrameHistogram);
6185c0df5feSKazu Hirata   EXPECT_THAT(Builder.getRadixArray(),
61982b43794SKazu Hirata               ElementsAre(2U,                        // Size of CS1
6205c0df5feSKazu Hirata                           static_cast<uint32_t>(-3), // Jump 3 steps
6215c0df5feSKazu Hirata                           3U,                        // Size of CS2
6225c0df5feSKazu Hirata                           3U,                        // MemProfFrameIndexes[13]
6235c0df5feSKazu Hirata                           2U,                        // MemProfFrameIndexes[12]
6245c0df5feSKazu Hirata                           1U                         // MemProfFrameIndexes[11]
62582b43794SKazu Hirata                           ));
626c348e265SKazu Hirata   const auto Mappings = Builder.takeCallStackPos();
6279b14ded1SKazu Hirata   EXPECT_THAT(Mappings, UnorderedElementsAre(Pair(CS1Id, 0U), Pair(CS2Id, 2U)));
6285c0df5feSKazu Hirata }
6295c0df5feSKazu Hirata 
6305c0df5feSKazu Hirata // Verify CallStackRadixTreeBuilder can form a jump to a prefix that itself has
6315c0df5feSKazu Hirata // another jump to another prefix.
6325c0df5feSKazu Hirata TEST(MemProf, RadixTreeBuilderSuccessiveJumps) {
6336a137fbeSKazu Hirata   llvm::DenseMap<FrameId, LinearFrameId> MemProfFrameIndexes = {
6345c0df5feSKazu Hirata       {11, 1}, {12, 2}, {13, 3}, {14, 4}, {15, 5}, {16, 6}, {17, 7}, {18, 8},
6355c0df5feSKazu Hirata   };
636b6dfdd2bSKazu Hirata   llvm::SmallVector<FrameId> CS1 = {14, 13, 12, 11};
637b6dfdd2bSKazu Hirata   llvm::SmallVector<FrameId> CS2 = {15, 13, 12, 11};
638b6dfdd2bSKazu Hirata   llvm::SmallVector<FrameId> CS3 = {17, 16, 12, 11};
639b6dfdd2bSKazu Hirata   llvm::SmallVector<FrameId> CS4 = {18, 16, 12, 11};
64064fadf17SKazu Hirata   IndexedMemProfData MemProfData;
6419b14ded1SKazu Hirata   auto CS1Id = MemProfData.addCallStack(std::move(CS1));
6429b14ded1SKazu Hirata   auto CS2Id = MemProfData.addCallStack(std::move(CS2));
6439b14ded1SKazu Hirata   auto CS3Id = MemProfData.addCallStack(std::move(CS3));
6449b14ded1SKazu Hirata   auto CS4Id = MemProfData.addCallStack(std::move(CS4));
6456a137fbeSKazu Hirata   llvm::DenseMap<FrameId, FrameStat> FrameHistogram =
64664fadf17SKazu Hirata       computeFrameHistogram<FrameId>(MemProfData.CallStacks);
6476a137fbeSKazu Hirata   CallStackRadixTreeBuilder<FrameId> Builder;
64864fadf17SKazu Hirata   Builder.build(std::move(MemProfData.CallStacks), &MemProfFrameIndexes,
649dc3f8c2fSKazu Hirata                 FrameHistogram);
6505c0df5feSKazu Hirata   EXPECT_THAT(Builder.getRadixArray(),
65182b43794SKazu Hirata               ElementsAre(4U,                        // Size of CS1
6525c0df5feSKazu Hirata                           4U,                        // MemProfFrameIndexes[14]
6535c0df5feSKazu Hirata                           static_cast<uint32_t>(-3), // Jump 3 steps
6545c0df5feSKazu Hirata                           4U,                        // Size of CS2
6555c0df5feSKazu Hirata                           5U,                        // MemProfFrameIndexes[15]
6565c0df5feSKazu Hirata                           3U,                        // MemProfFrameIndexes[13]
6575c0df5feSKazu Hirata                           static_cast<uint32_t>(-7), // Jump 7 steps
6585c0df5feSKazu Hirata                           4U,                        // Size of CS3
6595c0df5feSKazu Hirata                           7U,                        // MemProfFrameIndexes[17]
6605c0df5feSKazu Hirata                           static_cast<uint32_t>(-3), // Jump 3 steps
6615c0df5feSKazu Hirata                           4U,                        // Size of CS4
6625c0df5feSKazu Hirata                           8U,                        // MemProfFrameIndexes[18]
6635c0df5feSKazu Hirata                           6U,                        // MemProfFrameIndexes[16]
6645c0df5feSKazu Hirata                           2U,                        // MemProfFrameIndexes[12]
6655c0df5feSKazu Hirata                           1U                         // MemProfFrameIndexes[11]
66682b43794SKazu Hirata                           ));
667c348e265SKazu Hirata   const auto Mappings = Builder.takeCallStackPos();
6689b14ded1SKazu Hirata   EXPECT_THAT(Mappings,
6699b14ded1SKazu Hirata               UnorderedElementsAre(Pair(CS1Id, 0U), Pair(CS2Id, 3U),
6709b14ded1SKazu Hirata                                    Pair(CS3Id, 7U), Pair(CS4Id, 10U)));
6715c0df5feSKazu Hirata }
672e98396f4SKazu Hirata 
673e98396f4SKazu Hirata // Verify that we can parse YAML and retrieve IndexedMemProfData as expected.
674e98396f4SKazu Hirata TEST(MemProf, YAMLParser) {
675e98396f4SKazu Hirata   StringRef YAMLData = R"YAML(
676e98396f4SKazu Hirata ---
677e98396f4SKazu Hirata HeapProfileRecords:
678e98396f4SKazu Hirata - GUID: 0xdeadbeef12345678
679e98396f4SKazu Hirata   AllocSites:
680e98396f4SKazu Hirata   - Callstack:
681d88a0c73SKazu Hirata     - {Function: 0x100, LineOffset: 11, Column: 10, IsInlineFrame: true}
682d88a0c73SKazu Hirata     - {Function: 0x200, LineOffset: 22, Column: 20, IsInlineFrame: false}
683e98396f4SKazu Hirata     MemInfoBlock:
684e98396f4SKazu Hirata       AllocCount: 777
685e98396f4SKazu Hirata       TotalSize: 888
686e98396f4SKazu Hirata   - Callstack:
687d88a0c73SKazu Hirata     - {Function: 0x300, LineOffset: 33, Column: 30, IsInlineFrame: false}
688d88a0c73SKazu Hirata     - {Function: 0x400, LineOffset: 44, Column: 40, IsInlineFrame: true}
689e98396f4SKazu Hirata     MemInfoBlock:
690e98396f4SKazu Hirata       AllocCount: 666
691e98396f4SKazu Hirata       TotalSize: 555
692e98396f4SKazu Hirata   CallSites:
693d88a0c73SKazu Hirata   - - {Function: 0x500, LineOffset: 55, Column: 50, IsInlineFrame: true}
694d88a0c73SKazu Hirata     - {Function: 0x600, LineOffset: 66, Column: 60, IsInlineFrame: false}
695d88a0c73SKazu Hirata   - - {Function: 0x700, LineOffset: 77, Column: 70, IsInlineFrame: true}
696d88a0c73SKazu Hirata     - {Function: 0x800, LineOffset: 88, Column: 80, IsInlineFrame: false}
697e98396f4SKazu Hirata )YAML";
698e98396f4SKazu Hirata 
6996a137fbeSKazu Hirata   YAMLMemProfReader YAMLReader;
700e98396f4SKazu Hirata   YAMLReader.parse(YAMLData);
701b6dfdd2bSKazu Hirata   IndexedMemProfData MemProfData = YAMLReader.takeMemProfData();
702e98396f4SKazu Hirata 
703e98396f4SKazu Hirata   // Verify the entire contents of MemProfData.Records.
704e98396f4SKazu Hirata   ASSERT_THAT(MemProfData.Records, SizeIs(1));
7057f2fb806SKazu Hirata   const auto &[GUID, IndexedRecord] = MemProfData.Records.front();
706e98396f4SKazu Hirata   EXPECT_EQ(GUID, 0xdeadbeef12345678ULL);
7077f2fb806SKazu Hirata 
708*10d054e9SKazu Hirata   IndexedCallstackIdConveter CSIdConv(MemProfData);
7097f2fb806SKazu Hirata   MemProfRecord Record = IndexedRecord.toMemProfRecord(CSIdConv);
7107f2fb806SKazu Hirata 
711e98396f4SKazu Hirata   ASSERT_THAT(Record.AllocSites, SizeIs(2));
7127f2fb806SKazu Hirata   EXPECT_THAT(
7137f2fb806SKazu Hirata       Record.AllocSites[0].CallStack,
7147f2fb806SKazu Hirata       ElementsAre(Frame(0x100, 11, 10, true), Frame(0x200, 22, 20, false)));
715e98396f4SKazu Hirata   EXPECT_EQ(Record.AllocSites[0].Info.getAllocCount(), 777U);
716e98396f4SKazu Hirata   EXPECT_EQ(Record.AllocSites[0].Info.getTotalSize(), 888U);
7177f2fb806SKazu Hirata   EXPECT_THAT(
7187f2fb806SKazu Hirata       Record.AllocSites[1].CallStack,
7197f2fb806SKazu Hirata       ElementsAre(Frame(0x300, 33, 30, false), Frame(0x400, 44, 40, true)));
720e98396f4SKazu Hirata   EXPECT_EQ(Record.AllocSites[1].Info.getAllocCount(), 666U);
721e98396f4SKazu Hirata   EXPECT_EQ(Record.AllocSites[1].Info.getTotalSize(), 555U);
7227f2fb806SKazu Hirata   EXPECT_THAT(Record.CallSites,
7237f2fb806SKazu Hirata               ElementsAre(ElementsAre(Frame(0x500, 55, 50, true),
7247f2fb806SKazu Hirata                                       Frame(0x600, 66, 60, false)),
7257f2fb806SKazu Hirata                           ElementsAre(Frame(0x700, 77, 70, true),
7267f2fb806SKazu Hirata                                       Frame(0x800, 88, 80, false))));
727e98396f4SKazu Hirata }
728dbd920b2SKazu Hirata 
72976b49312SKazu Hirata // Verify that the YAML parser accepts a GUID expressed as a function name.
73076b49312SKazu Hirata TEST(MemProf, YAMLParserGUID) {
73176b49312SKazu Hirata   StringRef YAMLData = R"YAML(
73276b49312SKazu Hirata ---
73376b49312SKazu Hirata HeapProfileRecords:
73476b49312SKazu Hirata - GUID: _Z3fooi
73576b49312SKazu Hirata   AllocSites:
73676b49312SKazu Hirata   - Callstack:
73776b49312SKazu Hirata     - {Function: 0x100, LineOffset: 11, Column: 10, IsInlineFrame: true}
73876b49312SKazu Hirata     MemInfoBlock: {}
73976b49312SKazu Hirata   CallSites: []
74076b49312SKazu Hirata )YAML";
74176b49312SKazu Hirata 
74276b49312SKazu Hirata   YAMLMemProfReader YAMLReader;
74376b49312SKazu Hirata   YAMLReader.parse(YAMLData);
74476b49312SKazu Hirata   IndexedMemProfData MemProfData = YAMLReader.takeMemProfData();
74576b49312SKazu Hirata 
74676b49312SKazu Hirata   // Verify the entire contents of MemProfData.Records.
74776b49312SKazu Hirata   ASSERT_THAT(MemProfData.Records, SizeIs(1));
7487f2fb806SKazu Hirata   const auto &[GUID, IndexedRecord] = MemProfData.Records.front();
74976b49312SKazu Hirata   EXPECT_EQ(GUID, IndexedMemProfRecord::getGUID("_Z3fooi"));
7507f2fb806SKazu Hirata 
751*10d054e9SKazu Hirata   IndexedCallstackIdConveter CSIdConv(MemProfData);
7527f2fb806SKazu Hirata   MemProfRecord Record = IndexedRecord.toMemProfRecord(CSIdConv);
7537f2fb806SKazu Hirata 
75476b49312SKazu Hirata   ASSERT_THAT(Record.AllocSites, SizeIs(1));
7557f2fb806SKazu Hirata   EXPECT_THAT(Record.AllocSites[0].CallStack,
7567f2fb806SKazu Hirata               ElementsAre(Frame(0x100, 11, 10, true)));
7577f2fb806SKazu Hirata   EXPECT_THAT(Record.CallSites, IsEmpty());
75876b49312SKazu Hirata }
75976b49312SKazu Hirata 
760dbd920b2SKazu Hirata template <typename T> std::string serializeInYAML(T &Val) {
761dbd920b2SKazu Hirata   std::string Out;
762dbd920b2SKazu Hirata   llvm::raw_string_ostream OS(Out);
763dbd920b2SKazu Hirata   llvm::yaml::Output Yout(OS);
764dbd920b2SKazu Hirata   Yout << Val;
765dbd920b2SKazu Hirata   return Out;
766dbd920b2SKazu Hirata }
767dbd920b2SKazu Hirata 
768dbd920b2SKazu Hirata TEST(MemProf, YAMLWriterFrame) {
7699040dd46SKazu Hirata   Frame F(0x0123456789abcdefULL, 22, 33, true);
770dbd920b2SKazu Hirata 
771dbd920b2SKazu Hirata   std::string Out = serializeInYAML(F);
772dbd920b2SKazu Hirata   EXPECT_EQ(Out, R"YAML(---
7739040dd46SKazu Hirata { Function: 0x0123456789abcdef, LineOffset: 22, Column: 33, IsInlineFrame: true }
774dbd920b2SKazu Hirata ...
775dbd920b2SKazu Hirata )YAML");
776dbd920b2SKazu Hirata }
777dbd920b2SKazu Hirata 
778dbd920b2SKazu Hirata TEST(MemProf, YAMLWriterMIB) {
779dbd920b2SKazu Hirata   MemInfoBlock MIB;
780dbd920b2SKazu Hirata   MIB.AllocCount = 111;
781dbd920b2SKazu Hirata   MIB.TotalSize = 222;
782dbd920b2SKazu Hirata   MIB.TotalLifetime = 333;
783dbd920b2SKazu Hirata   MIB.TotalLifetimeAccessDensity = 444;
7846a137fbeSKazu Hirata   PortableMemInfoBlock PMIB(MIB, getHotColdSchema());
785dbd920b2SKazu Hirata 
786dbd920b2SKazu Hirata   std::string Out = serializeInYAML(PMIB);
787dbd920b2SKazu Hirata   EXPECT_EQ(Out, R"YAML(---
788dbd920b2SKazu Hirata AllocCount:      111
789dbd920b2SKazu Hirata TotalSize:       222
790dbd920b2SKazu Hirata TotalLifetime:   333
791dbd920b2SKazu Hirata TotalLifetimeAccessDensity: 444
792dbd920b2SKazu Hirata ...
793dbd920b2SKazu Hirata )YAML");
794dbd920b2SKazu Hirata }
795216575e5SSnehasish Kumar } // namespace
7966a137fbeSKazu Hirata } // namespace memprof
7976a137fbeSKazu Hirata } // namespace llvm
798