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