1#include "../CtxInstrProfiling.h"
2#include "gtest/gtest.h"
3#include <thread>
4
5using namespace __ctx_profile;
6
7class ContextTest : public ::testing::Test {
8 void SetUp() override { Root.getOrAllocateContextRoot(); }
9 void TearDown() override { __llvm_ctx_profile_free(); }
10
11public:
12 FunctionData Root;
13};
14
15TEST(ArenaTest, ZeroInit) {
16 char Buffer[1024];
17 memset(Buffer, 1, 1024);
18 Arena *A = new (Buffer) Arena(10);
19 for (auto I = 0U; I < A->size(); ++I)
20 EXPECT_EQ(A->pos()[I], static_cast<char>(0));
21 EXPECT_EQ(A->size(), 10U);
22}
23
24TEST(ArenaTest, Basic) {
25 Arena *A = Arena::allocateNewArena(Size: 1024);
26 EXPECT_EQ(A->size(), 1024U);
27 EXPECT_EQ(A->next(), nullptr);
28
29 auto *M1 = A->tryBumpAllocate(S: 1020);
30 EXPECT_NE(M1, nullptr);
31 auto *M2 = A->tryBumpAllocate(S: 4);
32 EXPECT_NE(M2, nullptr);
33 EXPECT_EQ(M1 + 1020, M2);
34 EXPECT_EQ(A->tryBumpAllocate(S: 1), nullptr);
35 Arena *A2 = Arena::allocateNewArena(Size: 2024, Prev: A);
36 EXPECT_EQ(A->next(), A2);
37 EXPECT_EQ(A2->next(), nullptr);
38 Arena::freeArenaList(A);
39 EXPECT_EQ(A, nullptr);
40}
41
42TEST_F(ContextTest, Basic) {
43 __llvm_ctx_profile_start_collection();
44 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
45 ASSERT_NE(Ctx, nullptr);
46 auto &CtxRoot = *Root.CtxRoot;
47 EXPECT_NE(CtxRoot.CurrentMem, nullptr);
48 EXPECT_EQ(CtxRoot.FirstMemBlock, CtxRoot.CurrentMem);
49 EXPECT_EQ(Ctx->size(), sizeof(ContextNode) + 10 * sizeof(uint64_t) +
50 4 * sizeof(ContextNode *));
51 EXPECT_EQ(Ctx->counters_size(), 10U);
52 EXPECT_EQ(Ctx->callsites_size(), 4U);
53 EXPECT_EQ(__llvm_ctx_profile_current_context_root, &CtxRoot);
54 CtxRoot.Taken.CheckLocked();
55 EXPECT_FALSE(CtxRoot.Taken.TryLock());
56 __llvm_ctx_profile_release_context(&Root);
57 EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr);
58 EXPECT_TRUE(CtxRoot.Taken.TryLock());
59 CtxRoot.Taken.Unlock();
60}
61
62TEST_F(ContextTest, Callsite) {
63 __llvm_ctx_profile_start_collection();
64 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
65 int FakeCalleeAddress = 0;
66 const bool IsScratch = isScratch(Ctx);
67 EXPECT_FALSE(IsScratch);
68 // This is the sequence the caller performs - it's the lowering of the
69 // instrumentation of the callsite "2". "2" is arbitrary here.
70 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
71 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
72 // This is what the callee does
73 FunctionData FData;
74 auto *Subctx =
75 __llvm_ctx_profile_get_context(FData: &FData, Callee: &FakeCalleeAddress, Guid: 2, NumCounters: 3, NumCallsites: 1);
76 // This should not have required creating a flat context.
77 EXPECT_EQ(FData.FlatCtx, nullptr);
78 // We expect the subcontext to be appropriately placed and dimensioned
79 EXPECT_EQ(Ctx->subContexts()[2], Subctx);
80 EXPECT_EQ(Subctx->counters_size(), 3U);
81 EXPECT_EQ(Subctx->callsites_size(), 1U);
82 // We reset these in _get_context.
83 EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr);
84 EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr);
85
86 EXPECT_EQ(Subctx->size(), sizeof(ContextNode) + 3 * sizeof(uint64_t) +
87 1 * sizeof(ContextNode *));
88 __llvm_ctx_profile_release_context(&Root);
89}
90
91TEST_F(ContextTest, ScratchNoCollectionProfilingNotStarted) {
92 // This test intentionally does not call __llvm_ctx_profile_start_collection.
93 EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr);
94 int FakeCalleeAddress = 0;
95 // this would be the very first function executing this. the TLS is empty,
96 // too.
97 FunctionData FData;
98 auto *Ctx =
99 __llvm_ctx_profile_get_context(FData: &FData, Callee: &FakeCalleeAddress, Guid: 2, NumCounters: 3, NumCallsites: 1);
100 // We never entered a context (_start_context was never called) - so the
101 // returned context must be a tagged pointer.
102 EXPECT_TRUE(isScratch(Ctx));
103 // Because we didn't start collection, no flat profile should have been
104 // allocated.
105 EXPECT_EQ(FData.FlatCtx, nullptr);
106}
107
108TEST_F(ContextTest, ScratchNoCollectionProfilingStarted) {
109 ASSERT_EQ(__llvm_ctx_profile_current_context_root, nullptr);
110 int FakeCalleeAddress = 0;
111 // Start collection, so the function gets a flat profile instead of scratch.
112 __llvm_ctx_profile_start_collection();
113 // this would be the very first function executing this. the TLS is empty,
114 // too.
115 FunctionData FData;
116 auto *Ctx =
117 __llvm_ctx_profile_get_context(FData: &FData, Callee: &FakeCalleeAddress, Guid: 2, NumCounters: 3, NumCallsites: 1);
118 // We never entered a context (_start_context was never called) - so the
119 // returned context must be a tagged pointer.
120 EXPECT_TRUE(isScratch(Ctx));
121 // Because we never entered a context, we should have allocated a flat context
122 EXPECT_NE(FData.FlatCtx, nullptr);
123 EXPECT_EQ(reinterpret_cast<uintptr_t>(FData.FlatCtx) + 1,
124 reinterpret_cast<uintptr_t>(Ctx));
125}
126
127TEST_F(ContextTest, ScratchDuringCollection) {
128 __llvm_ctx_profile_start_collection();
129 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
130 int FakeCalleeAddress = 0;
131 int OtherFakeCalleeAddress = 0;
132 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
133 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
134 FunctionData FData[3];
135 auto *Subctx = __llvm_ctx_profile_get_context(
136 FData: &FData[0], Callee: &OtherFakeCalleeAddress, Guid: 2, NumCounters: 3, NumCallsites: 1);
137 // We expected a different callee - so return scratch. It mimics what happens
138 // in the case of a signal handler - in this case, OtherFakeCalleeAddress is
139 // the signal handler.
140 EXPECT_TRUE(isScratch(Ctx: Subctx));
141 // We shouldn't have tried to return a flat context because we're under a
142 // root.
143 EXPECT_EQ(FData[0].FlatCtx, nullptr);
144 EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr);
145 EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr);
146
147 int ThirdFakeCalleeAddress = 0;
148 __llvm_ctx_profile_expected_callee[1] = &ThirdFakeCalleeAddress;
149 __llvm_ctx_profile_callsite[1] = &Subctx->subContexts()[0];
150
151 auto *Subctx2 = __llvm_ctx_profile_get_context(
152 FData: &FData[1], Callee: &ThirdFakeCalleeAddress, Guid: 3, NumCounters: 0, NumCallsites: 0);
153 // We again expect scratch because the '0' position is where the runtime
154 // looks, so it doesn't matter the '1' position is populated correctly.
155 EXPECT_TRUE(isScratch(Ctx: Subctx2));
156 EXPECT_EQ(FData[1].FlatCtx, nullptr);
157
158 __llvm_ctx_profile_expected_callee[0] = &ThirdFakeCalleeAddress;
159 __llvm_ctx_profile_callsite[0] = &Subctx->subContexts()[0];
160 auto *Subctx3 = __llvm_ctx_profile_get_context(
161 FData: &FData[2], Callee: &ThirdFakeCalleeAddress, Guid: 3, NumCounters: 0, NumCallsites: 0);
162 // We expect scratch here, too, because the value placed in
163 // __llvm_ctx_profile_callsite is scratch
164 EXPECT_TRUE(isScratch(Ctx: Subctx3));
165 EXPECT_EQ(FData[2].FlatCtx, nullptr);
166
167 __llvm_ctx_profile_release_context(&Root);
168}
169
170TEST_F(ContextTest, NeedMoreMemory) {
171 __llvm_ctx_profile_start_collection();
172 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
173 int FakeCalleeAddress = 0;
174 const bool IsScratch = isScratch(Ctx);
175 EXPECT_FALSE(IsScratch);
176 auto &CtxRoot = *Root.CtxRoot;
177 const auto *CurrentMem = CtxRoot.CurrentMem;
178 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
179 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
180 FunctionData FData;
181 // Allocate a massive subcontext to force new arena allocation
182 auto *Subctx =
183 __llvm_ctx_profile_get_context(FData: &FData, Callee: &FakeCalleeAddress, Guid: 3, NumCounters: 1 << 20, NumCallsites: 1);
184 EXPECT_EQ(FData.FlatCtx, nullptr);
185 EXPECT_EQ(Ctx->subContexts()[2], Subctx);
186 EXPECT_NE(CurrentMem, CtxRoot.CurrentMem);
187 EXPECT_NE(CtxRoot.CurrentMem, nullptr);
188}
189
190TEST_F(ContextTest, ConcurrentRootCollection) {
191 std::atomic<int> NonScratch = 0;
192 std::atomic<int> Executions = 0;
193
194 __sanitizer::Semaphore GotCtx;
195
196 auto Entrypoint = [&]() {
197 ++Executions;
198 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
199 GotCtx.Post();
200 const bool IS = isScratch(Ctx);
201 NonScratch += (!IS);
202 if (!IS) {
203 GotCtx.Wait();
204 GotCtx.Wait();
205 }
206 __llvm_ctx_profile_release_context(&Root);
207 };
208 std::thread T1(Entrypoint);
209 std::thread T2(Entrypoint);
210 T1.join();
211 T2.join();
212 EXPECT_EQ(NonScratch, 1);
213 EXPECT_EQ(Executions, 2);
214}
215
216TEST_F(ContextTest, Dump) {
217 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
218 int FakeCalleeAddress = 0;
219 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
220 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
221 FunctionData FData;
222 auto *Subctx =
223 __llvm_ctx_profile_get_context(FData: &FData, Callee: &FakeCalleeAddress, Guid: 2, NumCounters: 3, NumCallsites: 1);
224 (void)Subctx;
225 __llvm_ctx_profile_release_context(&Root);
226
227 class TestProfileWriter : public ProfileWriter {
228 public:
229 ContextRoot *const Root;
230 const size_t Entries;
231
232 int EnteredSectionCount = 0;
233 int ExitedSectionCount = 0;
234 int EnteredFlatCount = 0;
235 int ExitedFlatCount = 0;
236 int FlatsWritten = 0;
237
238 bool State = false;
239
240 TestProfileWriter(ContextRoot *Root, size_t Entries)
241 : Root(Root), Entries(Entries) {}
242
243 void writeContextual(const ContextNode &Node, const ContextNode *Unhandled,
244 uint64_t TotalRootEntryCount) override {
245 EXPECT_EQ(TotalRootEntryCount, Entries);
246 EXPECT_EQ(EnteredSectionCount, 1);
247 EXPECT_EQ(ExitedSectionCount, 0);
248 EXPECT_FALSE(Root->Taken.TryLock());
249 EXPECT_EQ(Node.guid(), 1U);
250 EXPECT_EQ(Node.counters()[0], Entries);
251 EXPECT_EQ(Node.counters_size(), 10U);
252 EXPECT_EQ(Node.callsites_size(), 4U);
253 EXPECT_EQ(Node.subContexts()[0], nullptr);
254 EXPECT_EQ(Node.subContexts()[1], nullptr);
255 EXPECT_NE(Node.subContexts()[2], nullptr);
256 EXPECT_EQ(Node.subContexts()[3], nullptr);
257 const auto &SN = *Node.subContexts()[2];
258 EXPECT_EQ(SN.guid(), 2U);
259 EXPECT_EQ(SN.counters()[0], Entries);
260 EXPECT_EQ(SN.counters_size(), 3U);
261 EXPECT_EQ(SN.callsites_size(), 1U);
262 EXPECT_EQ(SN.subContexts()[0], nullptr);
263 State = true;
264 }
265 void startContextSection() override { ++EnteredSectionCount; }
266 void endContextSection() override {
267 EXPECT_EQ(EnteredSectionCount, 1);
268 ++ExitedSectionCount;
269 }
270 void startFlatSection() override { ++EnteredFlatCount; }
271 void writeFlat(GUID Guid, const uint64_t *Buffer,
272 size_t BufferSize) override {
273 ++FlatsWritten;
274 EXPECT_EQ(BufferSize, 3U);
275 EXPECT_EQ(Buffer[0], 15U);
276 EXPECT_EQ(Buffer[1], 0U);
277 EXPECT_EQ(Buffer[2], 0U);
278 }
279 void endFlatSection() override { ++ExitedFlatCount; }
280 };
281
282 TestProfileWriter W(Root.CtxRoot, 1);
283 EXPECT_FALSE(W.State);
284 __llvm_ctx_profile_fetch(W);
285 EXPECT_TRUE(W.State);
286
287 // this resets all counters but not the internal structure.
288 __llvm_ctx_profile_start_collection();
289 auto *Flat =
290 __llvm_ctx_profile_get_context(FData: &FData, Callee: &FakeCalleeAddress, Guid: 2, NumCounters: 3, NumCallsites: 1);
291 (void)Flat;
292 EXPECT_NE(FData.FlatCtx, nullptr);
293 FData.FlatCtx->counters()[0] = 15U;
294 TestProfileWriter W2(Root.CtxRoot, 0);
295 EXPECT_FALSE(W2.State);
296 __llvm_ctx_profile_fetch(W2);
297 EXPECT_TRUE(W2.State);
298 EXPECT_EQ(W2.EnteredSectionCount, 1);
299 EXPECT_EQ(W2.ExitedSectionCount, 1);
300 EXPECT_EQ(W2.EnteredFlatCount, 1);
301 EXPECT_EQ(W2.FlatsWritten, 1);
302 EXPECT_EQ(W2.ExitedFlatCount, 1);
303}
304
305TEST_F(ContextTest, MustNotBeRoot) {
306 FunctionData FData;
307 FData.CtxRoot = reinterpret_cast<ContextRoot *>(1U);
308 int FakeCalleeAddress = 0;
309 __llvm_ctx_profile_start_collection();
310 auto *Subctx =
311 __llvm_ctx_profile_get_context(FData: &FData, Callee: &FakeCalleeAddress, Guid: 2, NumCounters: 3, NumCallsites: 1);
312 EXPECT_TRUE(isScratch(Ctx: Subctx));
313}
314

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of compiler-rt/lib/ctx_profile/tests/CtxInstrProfilingTest.cpp