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 } 120 121 TEST_F(PGOCtxProfRWTest, InvalidCounters) { 122 auto *R = createNode(1, 0, 1); 123 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 124 { 125 std::error_code EC; 126 raw_fd_stream Out(ProfileFile.path(), EC); 127 ASSERT_FALSE(EC); 128 { 129 PGOCtxProfileWriter Writer(Out); 130 Writer.write(*R); 131 } 132 } 133 { 134 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 135 ASSERT_TRUE(!!MB); 136 ASSERT_NE(*MB, nullptr); 137 BitstreamCursor Cursor((*MB)->getBuffer()); 138 PGOCtxProfileReader Reader(Cursor); 139 auto Expected = Reader.loadContexts(); 140 EXPECT_FALSE(Expected); 141 consumeError(Expected.takeError()); 142 } 143 } 144 145 TEST_F(PGOCtxProfRWTest, Empty) { 146 BitstreamCursor Cursor(""); 147 PGOCtxProfileReader Reader(Cursor); 148 auto Expected = Reader.loadContexts(); 149 EXPECT_FALSE(Expected); 150 consumeError(Expected.takeError()); 151 } 152 153 TEST_F(PGOCtxProfRWTest, Invalid) { 154 BitstreamCursor Cursor("Surely this is not valid"); 155 PGOCtxProfileReader Reader(Cursor); 156 auto Expected = Reader.loadContexts(); 157 EXPECT_FALSE(Expected); 158 consumeError(Expected.takeError()); 159 } 160 161 TEST_F(PGOCtxProfRWTest, ValidButEmpty) { 162 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 163 { 164 std::error_code EC; 165 raw_fd_stream Out(ProfileFile.path(), EC); 166 ASSERT_FALSE(EC); 167 { 168 PGOCtxProfileWriter Writer(Out); 169 // don't write anything - this will just produce the metadata subblock. 170 } 171 } 172 { 173 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 174 ASSERT_TRUE(!!MB); 175 ASSERT_NE(*MB, nullptr); 176 BitstreamCursor Cursor((*MB)->getBuffer()); 177 PGOCtxProfileReader Reader(Cursor); 178 auto Expected = Reader.loadContexts(); 179 EXPECT_TRUE(!!Expected); 180 EXPECT_TRUE(Expected->empty()); 181 } 182 } 183 184 TEST_F(PGOCtxProfRWTest, WrongVersion) { 185 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 186 { 187 std::error_code EC; 188 raw_fd_stream Out(ProfileFile.path(), EC); 189 ASSERT_FALSE(EC); 190 { 191 PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1); 192 } 193 } 194 { 195 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 196 ASSERT_TRUE(!!MB); 197 ASSERT_NE(*MB, nullptr); 198 BitstreamCursor Cursor((*MB)->getBuffer()); 199 PGOCtxProfileReader Reader(Cursor); 200 auto Expected = Reader.loadContexts(); 201 EXPECT_FALSE(Expected); 202 consumeError(Expected.takeError()); 203 } 204 } 205 206 TEST_F(PGOCtxProfRWTest, DuplicateRoots) { 207 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 208 { 209 std::error_code EC; 210 raw_fd_stream Out(ProfileFile.path(), EC); 211 ASSERT_FALSE(EC); 212 { 213 PGOCtxProfileWriter Writer(Out); 214 Writer.write(*createNode(1, 1, 1)); 215 Writer.write(*createNode(1, 1, 1)); 216 } 217 } 218 { 219 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 220 ASSERT_TRUE(!!MB); 221 ASSERT_NE(*MB, nullptr); 222 BitstreamCursor Cursor((*MB)->getBuffer()); 223 PGOCtxProfileReader Reader(Cursor); 224 auto Expected = Reader.loadContexts(); 225 EXPECT_FALSE(Expected); 226 consumeError(Expected.takeError()); 227 } 228 } 229 230 TEST_F(PGOCtxProfRWTest, DuplicateTargets) { 231 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 232 { 233 std::error_code EC; 234 raw_fd_stream Out(ProfileFile.path(), EC); 235 ASSERT_FALSE(EC); 236 { 237 auto *R = createNode(1, 1, 1); 238 auto *L1 = createNode(2, 1, 0); 239 auto *L2 = createNode(2, 1, 0, L1); 240 R->subContexts()[0] = L2; 241 PGOCtxProfileWriter Writer(Out); 242 Writer.write(*R); 243 } 244 } 245 { 246 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 247 ASSERT_TRUE(!!MB); 248 ASSERT_NE(*MB, nullptr); 249 BitstreamCursor Cursor((*MB)->getBuffer()); 250 PGOCtxProfileReader Reader(Cursor); 251 auto Expected = Reader.loadContexts(); 252 EXPECT_FALSE(Expected); 253 consumeError(Expected.takeError()); 254 } 255 } 256