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