339 lines · cpp
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-exception6//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 20using namespace llvm;21using namespace llvm::ctx_profile;22 23class PGOCtxProfRWTest : public ::testing::Test {24 std::vector<std::unique_ptr<char[]>> Nodes;25 std::map<GUID, const ContextNode *> Roots;26 27public: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 // set the entrycount to something - unless we're creating an invalid root.35 if (Ret->counters_size() > 0)36 Ret->counters()[0] = 42;37 return Ret;38 }39 40 void SetUp() override {41 // Root (guid 1) has 2 callsites, one used for an indirect call to either42 // guid 2 or 4.43 // guid 2 calls guid 544 // guid 5 calls guid 245 // there's also a second root, guid3.46 auto *Root1 = createNode(1, 2, 2);47 Root1->counters()[0] = 10;48 Root1->counters()[1] = 11;49 Roots.insert({1, Root1});50 auto *L1 = createNode(2, 1, 1);51 L1->counters()[0] = 12;52 Root1->subContexts()[1] = createNode(4, 3, 1, L1);53 Root1->subContexts()[1]->counters()[0] = 13;54 Root1->subContexts()[1]->counters()[1] = 14;55 Root1->subContexts()[1]->counters()[2] = 15;56 57 auto *L3 = createNode(5, 6, 3);58 for (auto I = 0; I < 6; ++I)59 L3->counters()[I] = 16 + I;60 L1->subContexts()[0] = L3;61 L3->subContexts()[2] = createNode(2, 1, 1);62 L3->subContexts()[2]->counters()[0] = 30;63 auto *Root2 = createNode(3, 1, 0);64 Root2->counters()[0] = 40;65 Roots.insert({3, Root2});66 }67 68 const std::map<GUID, const ContextNode *> &roots() const { return Roots; }69};70 71void checkSame(const ContextNode &Raw, const PGOCtxProfContext &Profile) {72 EXPECT_EQ(Raw.guid(), Profile.guid());73 ASSERT_EQ(Raw.counters_size(), Profile.counters().size());74 for (auto I = 0U; I < Raw.counters_size(); ++I)75 EXPECT_EQ(Raw.counters()[I], Profile.counters()[I]);76 77 for (auto I = 0U; I < Raw.callsites_size(); ++I) {78 if (Raw.subContexts()[I] == nullptr)79 continue;80 EXPECT_TRUE(Profile.hasCallsite(I));81 const auto &ProfileTargets = Profile.callsite(I);82 83 std::map<GUID, const ContextNode *> Targets;84 for (const auto *N = Raw.subContexts()[I]; N; N = N->next())85 EXPECT_TRUE(Targets.insert({N->guid(), N}).second);86 87 EXPECT_EQ(Targets.size(), ProfileTargets.size());88 for (auto It : Targets) {89 auto PIt = ProfileTargets.find(It.second->guid());90 EXPECT_NE(PIt, ProfileTargets.end());91 checkSame(*It.second, PIt->second);92 }93 }94}95 96TEST_F(PGOCtxProfRWTest, RoundTrip) {97 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);98 {99 std::error_code EC;100 raw_fd_stream Out(ProfileFile.path(), EC);101 ASSERT_FALSE(EC);102 {103 PGOCtxProfileWriter Writer(Out);104 Writer.startContextSection();105 for (auto &[_, R] : roots())106 Writer.writeContextual(*R, nullptr, 1);107 Writer.endContextSection();108 }109 }110 {111 ErrorOr<std::unique_ptr<MemoryBuffer>> MB =112 MemoryBuffer::getFile(ProfileFile.path());113 ASSERT_TRUE(!!MB);114 ASSERT_NE(*MB, nullptr);115 116 // Check it's analyzable by the BCAnalyzer117 BitcodeAnalyzer BA((*MB)->getBuffer());118 std::string AnalyzerDump;119 raw_string_ostream OS(AnalyzerDump);120 BCDumpOptions Opts(OS);121 122 // As in, expect no error.123 EXPECT_FALSE(BA.analyze(Opts));124 EXPECT_TRUE(AnalyzerDump.find("<Metadata BlockID") != std::string::npos);125 EXPECT_TRUE(AnalyzerDump.find("<Context BlockID") != std::string::npos);126 EXPECT_TRUE(AnalyzerDump.find("<CalleeIndex codeid") != std::string::npos);127 128 PGOCtxProfileReader Reader((*MB)->getBuffer());129 auto Expected = Reader.loadProfiles();130 ASSERT_TRUE(!!Expected);131 auto &Ctxes = Expected->Contexts;132 EXPECT_EQ(Ctxes.size(), roots().size());133 EXPECT_EQ(Ctxes.size(), 2U);134 for (auto &[G, R] : roots())135 checkSame(*R, Ctxes.find(G)->second);136 137 DenseSet<GlobalValue::GUID> Guids;138 Ctxes.at(1U).getContainedGuids(Guids);139 EXPECT_THAT(Guids,140 testing::WhenSorted(testing::ElementsAre(1U, 2U, 4U, 5U)));141 142 Guids.clear();143 Ctxes.at(3U).getContainedGuids(Guids);144 EXPECT_THAT(Guids, testing::ElementsAre(3U));145 }146}147 148TEST_F(PGOCtxProfRWTest, InvalidCounters) {149 auto *R = createNode(1, 0, 1);150 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);151 {152 std::error_code EC;153 raw_fd_stream Out(ProfileFile.path(), EC);154 ASSERT_FALSE(EC);155 {156 PGOCtxProfileWriter Writer(Out);157 Writer.startContextSection();158 Writer.writeContextual(*R, nullptr, 2);159 Writer.endContextSection();160 }161 }162 {163 auto MB = MemoryBuffer::getFile(ProfileFile.path());164 ASSERT_TRUE(!!MB);165 ASSERT_NE(*MB, nullptr);166 PGOCtxProfileReader Reader((*MB)->getBuffer());167 auto Expected = Reader.loadProfiles();168 EXPECT_FALSE(Expected);169 consumeError(Expected.takeError());170 }171}172 173TEST_F(PGOCtxProfRWTest, CountersAllZero) {174 auto *R = createNode(1, 2, 1);175 R->counters()[0] = 0;176 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);177 {178 std::error_code EC;179 raw_fd_stream Out(ProfileFile.path(), EC);180 ASSERT_FALSE(EC);181 {182 PGOCtxProfileWriter Writer(Out);183 Writer.startContextSection();184 Writer.writeContextual(*R, nullptr, 42);185 Writer.endContextSection();186 }187 }188 {189 auto MB = MemoryBuffer::getFile(ProfileFile.path());190 ASSERT_TRUE(!!MB);191 ASSERT_NE(*MB, nullptr);192 PGOCtxProfileReader Reader((*MB)->getBuffer());193 auto Expected = Reader.loadProfiles();194 EXPECT_TRUE(!!Expected);195 EXPECT_TRUE(Expected->Contexts.empty());196 }197}198 199TEST_F(PGOCtxProfRWTest, CountersAllZeroWithOverride) {200 auto *R = createNode(42, 2, 1);201 R->counters()[0] = 0;202 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);203 {204 std::error_code EC;205 raw_fd_stream Out(ProfileFile.path(), EC);206 ASSERT_FALSE(EC);207 {208 PGOCtxProfileWriter Writer(Out, /*VersionOverride=*/std::nullopt,209 /*IncludeEmpty=*/true);210 Writer.startContextSection();211 Writer.writeContextual(*R, nullptr, 8);212 Writer.endContextSection();213 }214 }215 {216 auto MB = MemoryBuffer::getFile(ProfileFile.path());217 ASSERT_TRUE(!!MB);218 ASSERT_NE(*MB, nullptr);219 PGOCtxProfileReader Reader((*MB)->getBuffer());220 auto Expected = Reader.loadProfiles();221 EXPECT_TRUE(!!Expected);222 EXPECT_EQ(Expected->Contexts.size(), 1U);223 EXPECT_EQ(Expected->Contexts.begin()->second.guid(), 42U);224 }225}226 227TEST_F(PGOCtxProfRWTest, Empty) {228 PGOCtxProfileReader Reader("");229 auto Expected = Reader.loadProfiles();230 EXPECT_FALSE(Expected);231 consumeError(Expected.takeError());232}233 234TEST_F(PGOCtxProfRWTest, Invalid) {235 PGOCtxProfileReader Reader("Surely this is not valid");236 auto Expected = Reader.loadProfiles();237 EXPECT_FALSE(Expected);238 consumeError(Expected.takeError());239}240 241TEST_F(PGOCtxProfRWTest, ValidButEmpty) {242 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);243 {244 std::error_code EC;245 raw_fd_stream Out(ProfileFile.path(), EC);246 ASSERT_FALSE(EC);247 {248 PGOCtxProfileWriter Writer(Out);249 // don't write anything - this will just produce the metadata subblock.250 }251 }252 {253 auto MB = MemoryBuffer::getFile(ProfileFile.path());254 ASSERT_TRUE(!!MB);255 ASSERT_NE(*MB, nullptr);256 257 PGOCtxProfileReader Reader((*MB)->getBuffer());258 auto Expected = Reader.loadProfiles();259 EXPECT_TRUE(!!Expected);260 EXPECT_TRUE(Expected->Contexts.empty());261 }262}263 264TEST_F(PGOCtxProfRWTest, WrongVersion) {265 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);266 {267 std::error_code EC;268 raw_fd_stream Out(ProfileFile.path(), EC);269 ASSERT_FALSE(EC);270 {271 PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1);272 }273 }274 {275 auto MB = MemoryBuffer::getFile(ProfileFile.path());276 ASSERT_TRUE(!!MB);277 ASSERT_NE(*MB, nullptr);278 279 PGOCtxProfileReader Reader((*MB)->getBuffer());280 auto Expected = Reader.loadProfiles();281 EXPECT_FALSE(Expected);282 consumeError(Expected.takeError());283 }284}285 286TEST_F(PGOCtxProfRWTest, DuplicateRoots) {287 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);288 {289 std::error_code EC;290 raw_fd_stream Out(ProfileFile.path(), EC);291 ASSERT_FALSE(EC);292 {293 PGOCtxProfileWriter Writer(Out, /*VersionOverride=*/std::nullopt,294 /*IncludeEmpty=*/true);295 Writer.startContextSection();296 Writer.writeContextual(*createNode(1, 1, 1), nullptr, 1);297 Writer.writeContextual(*createNode(1, 1, 1), nullptr, 1);298 Writer.endContextSection();299 }300 }301 {302 auto MB = MemoryBuffer::getFile(ProfileFile.path());303 ASSERT_TRUE(!!MB);304 ASSERT_NE(*MB, nullptr);305 PGOCtxProfileReader Reader((*MB)->getBuffer());306 auto Expected = Reader.loadProfiles();307 EXPECT_FALSE(Expected);308 consumeError(Expected.takeError());309 }310}311 312TEST_F(PGOCtxProfRWTest, DuplicateTargets) {313 llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);314 {315 std::error_code EC;316 raw_fd_stream Out(ProfileFile.path(), EC);317 ASSERT_FALSE(EC);318 {319 auto *R = createNode(1, 1, 1);320 auto *L1 = createNode(2, 1, 0);321 auto *L2 = createNode(2, 1, 0, L1);322 R->subContexts()[0] = L2;323 PGOCtxProfileWriter Writer(Out);324 Writer.startContextSection();325 Writer.writeContextual(*R, nullptr, 1);326 Writer.endContextSection();327 }328 }329 {330 auto MB = MemoryBuffer::getFile(ProfileFile.path());331 ASSERT_TRUE(!!MB);332 ASSERT_NE(*MB, nullptr);333 PGOCtxProfileReader Reader((*MB)->getBuffer());334 auto Expected = Reader.loadProfiles();335 EXPECT_FALSE(Expected);336 consumeError(Expected.takeError());337 }338}339