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/Bitcode/BitcodeAnalyzer.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 110 // Check it's analyzable by the BCAnalyzer 111 BitcodeAnalyzer BA((*MB)->getBuffer()); 112 std::string AnalyzerDump; 113 raw_string_ostream OS(AnalyzerDump); 114 BCDumpOptions Opts(OS); 115 116 // As in, expect no error. 117 EXPECT_FALSE(BA.analyze(Opts)); 118 EXPECT_TRUE(AnalyzerDump.find("<Metadata BlockID") != std::string::npos); 119 EXPECT_TRUE(AnalyzerDump.find("<Context BlockID") != std::string::npos); 120 EXPECT_TRUE(AnalyzerDump.find("<CalleeIndex codeid") != std::string::npos); 121 122 PGOCtxProfileReader Reader((*MB)->getBuffer()); 123 auto Expected = Reader.loadContexts(); 124 ASSERT_TRUE(!!Expected); 125 auto &Ctxes = *Expected; 126 EXPECT_EQ(Ctxes.size(), roots().size()); 127 EXPECT_EQ(Ctxes.size(), 2U); 128 for (auto &[G, R] : roots()) 129 checkSame(*R, Ctxes.find(G)->second); 130 131 DenseSet<GlobalValue::GUID> Guids; 132 Ctxes.at(1U).getContainedGuids(Guids); 133 EXPECT_THAT(Guids, 134 testing::WhenSorted(testing::ElementsAre(1U, 2U, 4U, 5U))); 135 136 Guids.clear(); 137 Ctxes.at(3U).getContainedGuids(Guids); 138 EXPECT_THAT(Guids, testing::ElementsAre(3U)); 139 } 140 } 141 142 TEST_F(PGOCtxProfRWTest, InvalidCounters) { 143 auto *R = createNode(1, 0, 1); 144 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 145 { 146 std::error_code EC; 147 raw_fd_stream Out(ProfileFile.path(), EC); 148 ASSERT_FALSE(EC); 149 { 150 PGOCtxProfileWriter Writer(Out); 151 Writer.write(*R); 152 } 153 } 154 { 155 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 156 ASSERT_TRUE(!!MB); 157 ASSERT_NE(*MB, nullptr); 158 PGOCtxProfileReader Reader((*MB)->getBuffer()); 159 auto Expected = Reader.loadContexts(); 160 EXPECT_FALSE(Expected); 161 consumeError(Expected.takeError()); 162 } 163 } 164 165 TEST_F(PGOCtxProfRWTest, Empty) { 166 PGOCtxProfileReader Reader(""); 167 auto Expected = Reader.loadContexts(); 168 EXPECT_FALSE(Expected); 169 consumeError(Expected.takeError()); 170 } 171 172 TEST_F(PGOCtxProfRWTest, Invalid) { 173 PGOCtxProfileReader Reader("Surely this is not valid"); 174 auto Expected = Reader.loadContexts(); 175 EXPECT_FALSE(Expected); 176 consumeError(Expected.takeError()); 177 } 178 179 TEST_F(PGOCtxProfRWTest, ValidButEmpty) { 180 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 181 { 182 std::error_code EC; 183 raw_fd_stream Out(ProfileFile.path(), EC); 184 ASSERT_FALSE(EC); 185 { 186 PGOCtxProfileWriter Writer(Out); 187 // don't write anything - this will just produce the metadata subblock. 188 } 189 } 190 { 191 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 192 ASSERT_TRUE(!!MB); 193 ASSERT_NE(*MB, nullptr); 194 195 PGOCtxProfileReader Reader((*MB)->getBuffer()); 196 auto Expected = Reader.loadContexts(); 197 EXPECT_TRUE(!!Expected); 198 EXPECT_TRUE(Expected->empty()); 199 } 200 } 201 202 TEST_F(PGOCtxProfRWTest, WrongVersion) { 203 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 204 { 205 std::error_code EC; 206 raw_fd_stream Out(ProfileFile.path(), EC); 207 ASSERT_FALSE(EC); 208 { 209 PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1); 210 } 211 } 212 { 213 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 214 ASSERT_TRUE(!!MB); 215 ASSERT_NE(*MB, nullptr); 216 217 PGOCtxProfileReader Reader((*MB)->getBuffer()); 218 auto Expected = Reader.loadContexts(); 219 EXPECT_FALSE(Expected); 220 consumeError(Expected.takeError()); 221 } 222 } 223 224 TEST_F(PGOCtxProfRWTest, DuplicateRoots) { 225 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 226 { 227 std::error_code EC; 228 raw_fd_stream Out(ProfileFile.path(), EC); 229 ASSERT_FALSE(EC); 230 { 231 PGOCtxProfileWriter Writer(Out); 232 Writer.write(*createNode(1, 1, 1)); 233 Writer.write(*createNode(1, 1, 1)); 234 } 235 } 236 { 237 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 238 ASSERT_TRUE(!!MB); 239 ASSERT_NE(*MB, nullptr); 240 PGOCtxProfileReader Reader((*MB)->getBuffer()); 241 auto Expected = Reader.loadContexts(); 242 EXPECT_FALSE(Expected); 243 consumeError(Expected.takeError()); 244 } 245 } 246 247 TEST_F(PGOCtxProfRWTest, DuplicateTargets) { 248 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); 249 { 250 std::error_code EC; 251 raw_fd_stream Out(ProfileFile.path(), EC); 252 ASSERT_FALSE(EC); 253 { 254 auto *R = createNode(1, 1, 1); 255 auto *L1 = createNode(2, 1, 0); 256 auto *L2 = createNode(2, 1, 0, L1); 257 R->subContexts()[0] = L2; 258 PGOCtxProfileWriter Writer(Out); 259 Writer.write(*R); 260 } 261 } 262 { 263 auto MB = MemoryBuffer::getFile(ProfileFile.path()); 264 ASSERT_TRUE(!!MB); 265 ASSERT_NE(*MB, nullptr); 266 PGOCtxProfileReader Reader((*MB)->getBuffer()); 267 auto Expected = Reader.loadContexts(); 268 EXPECT_FALSE(Expected); 269 consumeError(Expected.takeError()); 270 } 271 } 272