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