xref: /freebsd/contrib/llvm-project/compiler-rt/lib/ctx_profile/tests/CtxInstrProfilingTest.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 #include "../CtxInstrProfiling.h"
2 #include "gtest/gtest.h"
3 #include <thread>
4 
5 using namespace __ctx_profile;
6 
7 class ContextTest : public ::testing::Test {
SetUp()8   void SetUp() override { Root.getOrAllocateContextRoot(); }
TearDown()9   void TearDown() override { __llvm_ctx_profile_free(); }
10 
11 public:
12   FunctionData Root;
13 };
14 
TEST(ArenaTest,ZeroInit)15 TEST(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 
TEST(ArenaTest,Basic)24 TEST(ArenaTest, Basic) {
25   Arena *A = Arena::allocateNewArena(1024);
26   EXPECT_EQ(A->size(), 1024U);
27   EXPECT_EQ(A->next(), nullptr);
28 
29   auto *M1 = A->tryBumpAllocate(1020);
30   EXPECT_NE(M1, nullptr);
31   auto *M2 = A->tryBumpAllocate(4);
32   EXPECT_NE(M2, nullptr);
33   EXPECT_EQ(M1 + 1020, M2);
34   EXPECT_EQ(A->tryBumpAllocate(1), nullptr);
35   Arena *A2 = Arena::allocateNewArena(2024, A);
36   EXPECT_EQ(A->next(), A2);
37   EXPECT_EQ(A2->next(), nullptr);
38   Arena::freeArenaList(A);
39   EXPECT_EQ(A, nullptr);
40 }
41 
TEST_F(ContextTest,Basic)42 TEST_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 
TEST_F(ContextTest,Callsite)62 TEST_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, &FakeCalleeAddress, 2, 3, 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 
TEST_F(ContextTest,ScratchNoCollectionProfilingNotStarted)91 TEST_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, &FakeCalleeAddress, 2, 3, 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 
TEST_F(ContextTest,ScratchNoCollectionProfilingStarted)108 TEST_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, &FakeCalleeAddress, 2, 3, 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 
TEST_F(ContextTest,ScratchDuringCollection)127 TEST_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[0], &OtherFakeCalleeAddress, 2, 3, 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(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[1], &ThirdFakeCalleeAddress, 3, 0, 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(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[2], &ThirdFakeCalleeAddress, 3, 0, 0);
162   // We expect scratch here, too, because the value placed in
163   // __llvm_ctx_profile_callsite is scratch
164   EXPECT_TRUE(isScratch(Subctx3));
165   EXPECT_EQ(FData[2].FlatCtx, nullptr);
166 
167   __llvm_ctx_profile_release_context(&Root);
168 }
169 
TEST_F(ContextTest,NeedMoreMemory)170 TEST_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, &FakeCalleeAddress, 3, 1 << 20, 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 
TEST_F(ContextTest,ConcurrentRootCollection)190 TEST_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 
TEST_F(ContextTest,Dump)216 TEST_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, &FakeCalleeAddress, 2, 3, 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, &FakeCalleeAddress, 2, 3, 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 
TEST_F(ContextTest,MustNotBeRoot)305 TEST_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, &FakeCalleeAddress, 2, 3, 1);
312   EXPECT_TRUE(isScratch(Subctx));
313 }
314