1 //===-------------- PGOCtxProfReadWriteTest.cpp ---------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Bitstream/BitstreamReader.h" 10 #include "llvm/ProfileData/CtxInstrContextNode.h" 11 #include "llvm/ProfileData/PGOCtxProfReader.h" 12 #include "llvm/ProfileData/PGOCtxProfWriter.h" 13 #include "llvm/Support/Error.h" 14 #include "llvm/Support/MemoryBuffer.h" 15 #include "llvm/Support/raw_ostream.h" 16 #include "llvm/Testing/Support/SupportHelpers.h" 17 #include "gtest/gtest.h" 18 19 using namespace llvm; 20 using namespace llvm::ctx_profile; 21 22 class PGOCtxProfRWTest : public ::testing::Test { 23 std::vector<std::unique_ptr<char[]>> Nodes; 24 std::map<GUID, const ContextNode *> Roots; 25 26 public: 27 ContextNode *createNode(GUID Guid, uint32_t NrCounters, uint32_t NrCallsites, 28 ContextNode *Next = nullptr) { 29 auto AllocSize = ContextNode::getAllocSize(NrCounters, NrCallsites); 30 auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get(); 31 std::memset(Mem, 0, AllocSize); 32 auto *Ret = new (Mem) ContextNode(Guid, NrCounters, NrCallsites, Next); 33 return Ret; 34 } 35 36 void SetUp() override { 37 // Root (guid 1) has 2 callsites, one used for an indirect call to either 38 // guid 2 or 4. 39 // guid 2 calls guid 5 40 // guid 5 calls guid 2 41 // there's also a second root, guid3. 42 auto *Root1 = createNode(1, 2, 2); 43 Root1->counters()[0] = 10; 44 Root1->counters()[1] = 11; 45 Roots.insert({1, Root1}); 46 auto *L1 = createNode(2, 1, 1); 47 L1->counters()[0] = 12; 48 Root1->subContexts()[1] = createNode(4, 3, 1, L1); 49 Root1->subContexts()[1]->counters()[0] = 13; 50 Root1->subContexts()[1]->counters()[1] = 14; 51 Root1->subContexts()[1]->counters()[2] = 15; 52 53 auto *L3 = createNode(5, 6, 3); 54 for (auto I = 0; I < 6; ++I) 55 L3->counters()[I] = 16 + I; 56 L1->subContexts()[0] = L3; 57 L3->subContexts()[2] = createNode(2, 1, 1); 58 L3->subContexts()[2]->counters()[0] = 30; 59 auto *Root2 = createNode(3, 1, 0); 60 Root2->counters()[0] = 40; 61 Roots.insert({3, Root2}); 62 } 63 64 const std::map<GUID, const ContextNode *> &roots() const { return Roots; } 65 }; 66 67 void checkSame(const ContextNode &Raw, const PGOContextualProfile &Profile) { 68 EXPECT_EQ(Raw.guid(), Profile.guid()); 69 ASSERT_EQ(Raw.counters_size(), Profile.counters().size()); 70 for (auto I = 0U; I < Raw.counters_size(); ++I) 71 EXPECT_EQ(Raw.counters()[I], Profile.counters()[I]); 72 73 for (auto I = 0U; I < Raw.callsites_size(); ++I) { 74 if (Raw.subContexts()[I] == nullptr) 75 continue; 76 EXPECT_TRUE(Profile.hasCallsite(I)); 77 const auto &ProfileTargets = Profile.callsite(I); 78 79 std::map<GUID, const ContextNode *> Targets; 80 for (const auto *N = Raw.subContexts()[I]; N; N = N->next()) 81 EXPECT_TRUE(Targets.insert({N->guid(), N}).second); 82 83 EXPECT_EQ(Targets.size(), ProfileTargets.size()); 84 for (auto It : Targets) { 85 auto PIt = ProfileTargets.find(It.second->guid()); 86 EXPECT_NE(PIt, ProfileTargets.end()); 87 checkSame(*It.second, PIt->second); 88 } 89 } 90 } 91 92 TEST_F(PGOCtxProfRWTest, RoundTrip) { 93 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 94 { 95 std::error_code EC; 96 raw_fd_stream Out(ProfileFile.path(), EC); 97 ASSERT_FALSE(EC); 98 { 99 PGOCtxProfileWriter Writer(Out); 100 for (auto &[_, R] : roots()) 101 Writer.write(*R); 102 } 103 } 104 { 105 ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 106 MemoryBuffer::getFile(ProfileFile.path()); 107 ASSERT_TRUE(!!MB); 108 ASSERT_NE(*MB, nullptr); 109 BitstreamCursor Cursor((*MB)->getBuffer()); 110 PGOCtxProfileReader Reader(Cursor); 111 auto Expected = Reader.loadContexts(); 112 ASSERT_TRUE(!!Expected); 113 auto &Ctxes = *Expected; 114 EXPECT_EQ(Ctxes.size(), roots().size()); 115 EXPECT_EQ(Ctxes.size(), 2U); 116 for (auto &[G, R] : roots()) 117 checkSame(*R, Ctxes.find(G)->second); 118 119 DenseSet<GlobalValue::GUID> Guids; 120 Ctxes.at(1U).getContainedGuids(Guids); 121 EXPECT_THAT(Guids, 122 testing::WhenSorted(testing::ElementsAre(1U, 2U, 4U, 5U))); 123 124 Guids.clear(); 125 Ctxes.at(3U).getContainedGuids(Guids); 126 EXPECT_THAT(Guids, testing::ElementsAre(3U)); 127 } 128 } 129 130 TEST_F(PGOCtxProfRWTest, InvalidCounters) { 131 auto *R = createNode(1, 0, 1); 132 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 133 { 134 std::error_code EC; 135 raw_fd_stream Out(ProfileFile.path(), EC); 136 ASSERT_FALSE(EC); 137 { 138 PGOCtxProfileWriter Writer(Out); 139 Writer.write(*R); 140 } 141 } 142 { 143 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 144 ASSERT_TRUE(!!MB); 145 ASSERT_NE(*MB, nullptr); 146 BitstreamCursor Cursor((*MB)->getBuffer()); 147 PGOCtxProfileReader Reader(Cursor); 148 auto Expected = Reader.loadContexts(); 149 EXPECT_FALSE(Expected); 150 consumeError(Expected.takeError()); 151 } 152 } 153 154 TEST_F(PGOCtxProfRWTest, Empty) { 155 BitstreamCursor Cursor(""); 156 PGOCtxProfileReader Reader(Cursor); 157 auto Expected = Reader.loadContexts(); 158 EXPECT_FALSE(Expected); 159 consumeError(Expected.takeError()); 160 } 161 162 TEST_F(PGOCtxProfRWTest, Invalid) { 163 BitstreamCursor Cursor("Surely this is not valid"); 164 PGOCtxProfileReader Reader(Cursor); 165 auto Expected = Reader.loadContexts(); 166 EXPECT_FALSE(Expected); 167 consumeError(Expected.takeError()); 168 } 169 170 TEST_F(PGOCtxProfRWTest, ValidButEmpty) { 171 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 172 { 173 std::error_code EC; 174 raw_fd_stream Out(ProfileFile.path(), EC); 175 ASSERT_FALSE(EC); 176 { 177 PGOCtxProfileWriter Writer(Out); 178 // don't write anything - this will just produce the metadata subblock. 179 } 180 } 181 { 182 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 183 ASSERT_TRUE(!!MB); 184 ASSERT_NE(*MB, nullptr); 185 BitstreamCursor Cursor((*MB)->getBuffer()); 186 PGOCtxProfileReader Reader(Cursor); 187 auto Expected = Reader.loadContexts(); 188 EXPECT_TRUE(!!Expected); 189 EXPECT_TRUE(Expected->empty()); 190 } 191 } 192 193 TEST_F(PGOCtxProfRWTest, WrongVersion) { 194 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 195 { 196 std::error_code EC; 197 raw_fd_stream Out(ProfileFile.path(), EC); 198 ASSERT_FALSE(EC); 199 { 200 PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1); 201 } 202 } 203 { 204 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 205 ASSERT_TRUE(!!MB); 206 ASSERT_NE(*MB, nullptr); 207 BitstreamCursor Cursor((*MB)->getBuffer()); 208 PGOCtxProfileReader Reader(Cursor); 209 auto Expected = Reader.loadContexts(); 210 EXPECT_FALSE(Expected); 211 consumeError(Expected.takeError()); 212 } 213 } 214 215 TEST_F(PGOCtxProfRWTest, DuplicateRoots) { 216 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 217 { 218 std::error_code EC; 219 raw_fd_stream Out(ProfileFile.path(), EC); 220 ASSERT_FALSE(EC); 221 { 222 PGOCtxProfileWriter Writer(Out); 223 Writer.write(*createNode(1, 1, 1)); 224 Writer.write(*createNode(1, 1, 1)); 225 } 226 } 227 { 228 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 229 ASSERT_TRUE(!!MB); 230 ASSERT_NE(*MB, nullptr); 231 BitstreamCursor Cursor((*MB)->getBuffer()); 232 PGOCtxProfileReader Reader(Cursor); 233 auto Expected = Reader.loadContexts(); 234 EXPECT_FALSE(Expected); 235 consumeError(Expected.takeError()); 236 } 237 } 238 239 TEST_F(PGOCtxProfRWTest, DuplicateTargets) { 240 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 241 { 242 std::error_code EC; 243 raw_fd_stream Out(ProfileFile.path(), EC); 244 ASSERT_FALSE(EC); 245 { 246 auto *R = createNode(1, 1, 1); 247 auto *L1 = createNode(2, 1, 0); 248 auto *L2 = createNode(2, 1, 0, L1); 249 R->subContexts()[0] = L2; 250 PGOCtxProfileWriter Writer(Out); 251 Writer.write(*R); 252 } 253 } 254 { 255 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 256 ASSERT_TRUE(!!MB); 257 ASSERT_NE(*MB, nullptr); 258 BitstreamCursor Cursor((*MB)->getBuffer()); 259 PGOCtxProfileReader Reader(Cursor); 260 auto Expected = Reader.loadContexts(); 261 EXPECT_FALSE(Expected); 262 consumeError(Expected.takeError()); 263 } 264 } 265