xref: /llvm-project/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp (revision cc7308a1563e5ebab4dffeb85deefdde61e6711a)
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