1 //===-------------- PGOCtxProfReadWriteTest.cpp ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 using namespace llvm::ctx_profile
;
23 class PGOCtxProfRWTest
: public ::testing::Test
{
24 std::vector
<std::unique_ptr
<char[]>> Nodes
;
25 std::map
<GUID
, const ContextNode
*> Roots
;
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
);
37 void SetUp() override
{
38 // Root (guid 1) has 2 callsites, one used for an indirect call to either
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;
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
});
65 const std::map
<GUID
, const ContextNode
*> &roots() const { return Roots
; }
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
]);
74 for (auto I
= 0U; I
< Raw
.callsites_size(); ++I
) {
75 if (Raw
.subContexts()[I
] == nullptr)
77 EXPECT_TRUE(Profile
.hasCallsite(I
));
78 const auto &ProfileTargets
= Profile
.callsite(I
);
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
);
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
);
93 TEST_F(PGOCtxProfRWTest
, RoundTrip
) {
94 llvm::unittest::TempFile
ProfileFile("ctx_profile", "", "", /*Unique*/ true);
97 raw_fd_stream
Out(ProfileFile
.path(), EC
);
100 PGOCtxProfileWriter
Writer(Out
);
101 for (auto &[_
, R
] : roots())
106 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> MB
=
107 MemoryBuffer::getFile(ProfileFile
.path());
109 ASSERT_NE(*MB
, nullptr);
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
);
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
);
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
);
132 DenseSet
<GlobalValue::GUID
> Guids
;
133 Ctxes
.at(1U).getContainedGuids(Guids
);
135 testing::WhenSorted(testing::ElementsAre(1U, 2U, 4U, 5U)));
138 Ctxes
.at(3U).getContainedGuids(Guids
);
139 EXPECT_THAT(Guids
, testing::ElementsAre(3U));
143 TEST_F(PGOCtxProfRWTest
, InvalidCounters
) {
144 auto *R
= createNode(1, 0, 1);
145 llvm::unittest::TempFile
ProfileFile("ctx_profile", "", "", /*Unique*/ true);
148 raw_fd_stream
Out(ProfileFile
.path(), EC
);
151 PGOCtxProfileWriter
Writer(Out
);
156 auto MB
= MemoryBuffer::getFile(ProfileFile
.path());
158 ASSERT_NE(*MB
, nullptr);
159 PGOCtxProfileReader
Reader((*MB
)->getBuffer());
160 auto Expected
= Reader
.loadContexts();
161 EXPECT_FALSE(Expected
);
162 consumeError(Expected
.takeError());
166 TEST_F(PGOCtxProfRWTest
, Empty
) {
167 PGOCtxProfileReader
Reader("");
168 auto Expected
= Reader
.loadContexts();
169 EXPECT_FALSE(Expected
);
170 consumeError(Expected
.takeError());
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());
180 TEST_F(PGOCtxProfRWTest
, ValidButEmpty
) {
181 llvm::unittest::TempFile
ProfileFile("ctx_profile", "", "", /*Unique*/ true);
184 raw_fd_stream
Out(ProfileFile
.path(), EC
);
187 PGOCtxProfileWriter
Writer(Out
);
188 // don't write anything - this will just produce the metadata subblock.
192 auto MB
= MemoryBuffer::getFile(ProfileFile
.path());
194 ASSERT_NE(*MB
, nullptr);
196 PGOCtxProfileReader
Reader((*MB
)->getBuffer());
197 auto Expected
= Reader
.loadContexts();
198 EXPECT_TRUE(!!Expected
);
199 EXPECT_TRUE(Expected
->empty());
203 TEST_F(PGOCtxProfRWTest
, WrongVersion
) {
204 llvm::unittest::TempFile
ProfileFile("ctx_profile", "", "", /*Unique*/ true);
207 raw_fd_stream
Out(ProfileFile
.path(), EC
);
210 PGOCtxProfileWriter
Writer(Out
, PGOCtxProfileWriter::CurrentVersion
+ 1);
214 auto MB
= MemoryBuffer::getFile(ProfileFile
.path());
216 ASSERT_NE(*MB
, nullptr);
218 PGOCtxProfileReader
Reader((*MB
)->getBuffer());
219 auto Expected
= Reader
.loadContexts();
220 EXPECT_FALSE(Expected
);
221 consumeError(Expected
.takeError());
225 TEST_F(PGOCtxProfRWTest
, DuplicateRoots
) {
226 llvm::unittest::TempFile
ProfileFile("ctx_profile", "", "", /*Unique*/ true);
229 raw_fd_stream
Out(ProfileFile
.path(), EC
);
232 PGOCtxProfileWriter
Writer(Out
);
233 Writer
.write(*createNode(1, 1, 1));
234 Writer
.write(*createNode(1, 1, 1));
238 auto MB
= MemoryBuffer::getFile(ProfileFile
.path());
240 ASSERT_NE(*MB
, nullptr);
241 PGOCtxProfileReader
Reader((*MB
)->getBuffer());
242 auto Expected
= Reader
.loadContexts();
243 EXPECT_FALSE(Expected
);
244 consumeError(Expected
.takeError());
248 TEST_F(PGOCtxProfRWTest
, DuplicateTargets
) {
249 llvm::unittest::TempFile
ProfileFile("ctx_profile", "", "", /*Unique*/ true);
252 raw_fd_stream
Out(ProfileFile
.path(), EC
);
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
);
264 auto MB
= MemoryBuffer::getFile(ProfileFile
.path());
266 ASSERT_NE(*MB
, nullptr);
267 PGOCtxProfileReader
Reader((*MB
)->getBuffer());
268 auto Expected
= Reader
.loadContexts();
269 EXPECT_FALSE(Expected
);
270 consumeError(Expected
.takeError());