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