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